forked from 0ad/0ad
New patch for JuBot
- Speeds up a lot of functionality by using persistent entity collections, speed boost should be significant and noticeable. - Adds defence towers covering all dropsites, with garrisons This was SVN commit r11487.
This commit is contained in:
parent
3ca6ee461c
commit
5184edde14
@ -372,6 +372,9 @@ var EconomyManager = Class({
|
||||
buildMoreBuildings: function(gameState, planGroups)
|
||||
{
|
||||
var numCCs = gameState.countEntitiesAndQueuedWithType(gameState.applyCiv("structures/{civ}_civil_centre"));
|
||||
var numMills = gameState.countEntitiesAndQueuedWithType(gameState.applyCiv("structures/{civ}_mill"));
|
||||
var numFarmsteads = gameState.countEntitiesAndQueuedWithType(gameState.applyCiv("structures/{civ}_farmstead"));
|
||||
var defensequot = numMills + numFarmsteads
|
||||
if (numCCs < 1)
|
||||
{
|
||||
planGroups.economyConstruction.addPlan(1000,
|
||||
@ -398,6 +401,15 @@ var EconomyManager = Class({
|
||||
if (gameState.findFoundations().length > 0)
|
||||
return;
|
||||
|
||||
var numTowers = gameState.countEntitiesAndQueuedWithType(gameState.applyCiv("structures/{civ}_defense_tower"));
|
||||
if (numTowers < defensequot)
|
||||
{
|
||||
planGroups.economyConstruction.addPlan(150,
|
||||
new BuildingConstructionPlanDefensePoints(gameState, "structures/{civ}_defense_tower", 1)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// START BY GETTING ALL CCs UP TO SMALL VILLAGE LEVEL
|
||||
for each (var building in this.villageBuildings)
|
||||
{
|
||||
@ -625,11 +637,11 @@ var EconomyManager = Class({
|
||||
for (var type in this.gatherWeights)
|
||||
numGatherers[type] = 0;
|
||||
|
||||
gameState.getOwnEntitiesWithRole("worker").forEach(function(ent) {
|
||||
gameState.getOwnRoleGroup("worker").forEach(function(ent) {
|
||||
if (ent.getMetadata("subrole") === "gatherer")
|
||||
numGatherers[ent.getMetadata("gather-type")] += 1;
|
||||
});
|
||||
gameState.getOwnEntitiesWithRole("militia").forEach(function(ent) {
|
||||
gameState.getOwnRoleGroup("militia").forEach(function(ent) {
|
||||
if (ent.getMetadata("subrole") === "gatherer")
|
||||
numGatherers[ent.getMetadata("gather-type")] += 1;
|
||||
});
|
||||
@ -647,7 +659,7 @@ var EconomyManager = Class({
|
||||
|
||||
reassignRolelessUnits: function(gameState)
|
||||
{
|
||||
var roleless = gameState.getOwnEntitiesWithRole(undefined);
|
||||
var roleless = gameState.getOwnRoleGroup(undefined);
|
||||
|
||||
|
||||
roleless.forEach(function(ent)
|
||||
@ -659,12 +671,12 @@ var EconomyManager = Class({
|
||||
else if (ent.hasClass("CitizenSoldier") && ent.hasClass("Infantry"))
|
||||
{
|
||||
var currentPosition = ent.position();
|
||||
var targets = gameState.entities.filter(function(enten) {
|
||||
var targets = gameState.getJustEnemies().filter(function(enten) {
|
||||
var foeposition = enten.position();
|
||||
if (foeposition)
|
||||
{
|
||||
var dist = SquareVectorDistance(foeposition, currentPosition);
|
||||
return (enten.isEnemy() && enten.owner()!= 0 && dist < 2500);
|
||||
return (dist < 2500);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -69,6 +69,15 @@ var GameState = Class({
|
||||
return this.entities;
|
||||
},
|
||||
|
||||
updatingCollection: function(id, filter, collection){
|
||||
if (!this.store[id]){
|
||||
this.store[id] = collection.filter(filter);
|
||||
this.store[id].registerUpdates();
|
||||
}
|
||||
|
||||
return this.store[id];
|
||||
},
|
||||
|
||||
getOwnEntities: function()
|
||||
{
|
||||
if (!this.store.ownEntities){
|
||||
@ -79,6 +88,69 @@ var GameState = Class({
|
||||
return this.store.ownEntities;
|
||||
},
|
||||
|
||||
getOwnRoleGroup: function(role) {
|
||||
return this.updatingCollection("RoleGroup" + role, Filters.byMetadata("role", role), this.getOwnEntities());
|
||||
},
|
||||
|
||||
getOwnWithClass: function(aclass) {
|
||||
return this.updatingCollection("RoleGroup" + aclass, Filters.byClass(aclass), this.getOwnEntities());
|
||||
},
|
||||
|
||||
getNotGaia: function() {
|
||||
var collection = this.updatingCollection("NotGaia", Filters.byNotOwner(0), this.getEntities());
|
||||
return collection;
|
||||
},
|
||||
|
||||
getJustEnemies: function() {
|
||||
var collection = this.updatingCollection("JustEnemies", Filters.byNotOwner(this.player), this.getNotGaia());
|
||||
//warn(collection.length + " enemy unit objects")
|
||||
return collection;
|
||||
},
|
||||
|
||||
// These get enemies things are copied from qbot, to avoid duplication of a ton of effort (yay laziness)
|
||||
getEnemies: function(){
|
||||
var ret = [];
|
||||
for (i in this.playerData.isEnemy){
|
||||
if (this.playerData.isEnemy[i]){
|
||||
ret.push(i);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
getEnemyEntities: function() {
|
||||
var diplomacyChange = false;
|
||||
var enemies = this.getEnemies();
|
||||
warn(enemies.length + " enemy factions")
|
||||
if (this.store.enemies){
|
||||
if (this.store.enemies.length != enemies.length){
|
||||
diplomacyChange = true;
|
||||
}
|
||||
else{
|
||||
for (var i = 0; i < enemies.length; i++){
|
||||
if (enemies[i] !== this.store.enemies[i]){
|
||||
diplomacyChange = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.store.enemyEntities){
|
||||
var filter = Filters.byOwners(enemies);
|
||||
this.store.enemyEntities = this.getEntities().filter(filter);
|
||||
this.store.enemyEntities.registerUpdates();
|
||||
this.store.enemies = enemies;
|
||||
}
|
||||
if (diplomacyChange == true){
|
||||
var filter = Filters.byOwners(enemies);
|
||||
this.store.enemyEntities = this.getEntities().filter(filter);
|
||||
this.store.enemyEntities.registerUpdates();
|
||||
this.store.enemies = enemies;
|
||||
}
|
||||
warn(this.store.enemyEntities.length + " enemy objects")
|
||||
return this.store.enemyEntities;
|
||||
},
|
||||
/// Laziness ends here.
|
||||
|
||||
getOwnEntitiesWithRole: Memoize('getOwnEntitiesWithRole', function(role)
|
||||
{
|
||||
var metas = this.ai._entityMetadata;
|
||||
|
@ -51,43 +51,41 @@ var MilitaryAttackManager = Class({
|
||||
regroup: function(gameState, planGroups)
|
||||
{
|
||||
if (gameState.getTimeElapsed() > this.changetimeReg && this.killstrat != 3){
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p1");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p1");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p2");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p2");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p3");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p3");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack-pending_3p1");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack-pending_3p1");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack-pending_3p2");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack-pending_3p2");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack-pending_3p3");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack-pending_3p3");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("fighting");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("fighting");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending");
|
||||
});
|
||||
var regroupneededPartB = gameState.getOwnEntitiesWithRole("attack-pending");
|
||||
var regroupneededPartB = gameState.getOwnRoleGroup("attack-pending");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("CivCentre"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("CivCentre");
|
||||
if (targets.length){
|
||||
var target = targets.toEntityArray()[0];
|
||||
var targetPos = target.position();
|
||||
@ -99,7 +97,7 @@ var MilitaryAttackManager = Class({
|
||||
this.changetimeReg = this.changetimeReg + (60*4000);
|
||||
}
|
||||
else if (gameState.getTimeElapsed() > this.changetimeReg && this.killstrat == 3){
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
var section = Math.random();
|
||||
if (section < 0.3){
|
||||
@ -112,7 +110,7 @@ var MilitaryAttackManager = Class({
|
||||
ent.setMetadata("role", "attack-pending_3p3");
|
||||
}
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack-pending");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack-pending");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
var section = Math.random();
|
||||
if (section < 0.3){
|
||||
@ -125,19 +123,19 @@ var MilitaryAttackManager = Class({
|
||||
ent.setMetadata("role", "attack-pending_3p3");
|
||||
}
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p1");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p1");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending_3p1");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p2");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p2");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending_3p2");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("attack_3p3");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("attack_3p3");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
ent.setMetadata("role", "attack-pending_3p3");
|
||||
});
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole("fighting");
|
||||
var regroupneeded = gameState.getOwnRoleGroup("fighting");
|
||||
regroupneeded.forEach(function(ent) {
|
||||
var section = Math.random();
|
||||
if (section < 0.3){
|
||||
@ -152,11 +150,9 @@ var MilitaryAttackManager = Class({
|
||||
});
|
||||
// MOVE THEM ALL
|
||||
// GROUP ONE
|
||||
var regroupneededPartB = gameState.getOwnEntitiesWithRole("attack-pending_3p1");
|
||||
var regroupneededPartB = gameState.getOwnRoleGroup("attack-pending_3p1");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("CivCentre"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("CivCentre");
|
||||
if (targets.length){
|
||||
var target = targets.toEntityArray()[0];
|
||||
var targetPos = target.position();
|
||||
@ -165,11 +161,9 @@ var MilitaryAttackManager = Class({
|
||||
regroupneededPartB.move(targetPos[0], targetPos[1]);
|
||||
}
|
||||
// MOVING GROUP TWO
|
||||
var regroupneededPartB = gameState.getOwnEntitiesWithRole("attack-pending_3p2");
|
||||
var regroupneededPartB = gameState.getOwnRoleGroup("attack-pending_3p2");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("CivCentre"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("CivCentre");
|
||||
if (targets.length){
|
||||
var target = targets.toEntityArray()[0];
|
||||
var targetPos = target.position();
|
||||
@ -178,11 +172,9 @@ var MilitaryAttackManager = Class({
|
||||
regroupneededPartB.move(targetPos[0], targetPos[1]);
|
||||
}
|
||||
// MOVING GROUP THREE
|
||||
var regroupneededPartB = gameState.getOwnEntitiesWithRole("attack-pending_3p3");
|
||||
var regroupneededPartB = gameState.getOwnRoleGroup("attack-pending_3p3");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("CivCentre"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("CivCentre");
|
||||
if (targets.length){
|
||||
var target = targets.toEntityArray()[0];
|
||||
var targetPos = target.position();
|
||||
@ -197,10 +189,10 @@ var MilitaryAttackManager = Class({
|
||||
|
||||
combatcheck: function(gameState, planGroups, assaultgroup)
|
||||
{
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole(assaultgroup);
|
||||
var regroupneeded = gameState.getOwnRoleGroup(assaultgroup);
|
||||
regroupneeded.forEach(function(troop) {
|
||||
var currentPosition = troop.position();
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
var targets = gameState.getJustEnemies().filter(function(ent) {
|
||||
var foeposition = ent.position();
|
||||
if (foeposition){
|
||||
var dist = SquareVectorDistance(foeposition, currentPosition);
|
||||
@ -271,26 +263,26 @@ var MilitaryAttackManager = Class({
|
||||
|
||||
combatcheckMilitia: function(gameState, planGroups, assaultgroup)
|
||||
{
|
||||
var regroupneeded = gameState.getOwnEntitiesWithRole(assaultgroup);
|
||||
var regroupneeded = gameState.getOwnRoleGroup(assaultgroup);
|
||||
regroupneeded.forEach(function(troop) {
|
||||
var currentPosition = troop.position();
|
||||
// Find nearby enemies
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
var targets = gameState.getJustEnemies().filter(function(ent) {
|
||||
var foeposition = ent.position();
|
||||
if (foeposition){
|
||||
var dist = SquareVectorDistance(foeposition, currentPosition);
|
||||
return (ent.isEnemy() && ent.owner()!= 0 && dist < 2500);
|
||||
return (dist < 2500);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// Check that some of our own buildings are nearby
|
||||
var ownbuildings = gameState.getOwnEntities().filter(function(ent) {
|
||||
var ownbuildings = gameState.getOwnWithClass("Village").filter(function(ent) {
|
||||
var foeposition = ent.position();
|
||||
if (foeposition){
|
||||
var dist = SquareVectorDistance(foeposition, currentPosition);
|
||||
return (dist < 2500 && ent.hasClass("Village"));
|
||||
return (dist < 2500);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
@ -328,7 +320,7 @@ var MilitaryAttackManager = Class({
|
||||
var targetPos = target.position();
|
||||
}
|
||||
// Change 'em back to militia
|
||||
var defenseregroupers = gameState.getOwnEntitiesWithRole("militiafighter");
|
||||
var defenseregroupers = gameState.getOwnRoleGroup("militiafighter");
|
||||
defenseregroupers.forEach(function(ent) {
|
||||
// If we have a target to go home to, move to it
|
||||
ent.move(targetPos[0], targetPos[1]);
|
||||
@ -342,11 +334,9 @@ var MilitaryAttackManager = Class({
|
||||
waitingregroup: function(gameState, planGroups)
|
||||
{
|
||||
if (gameState.getTimeElapsed() > this.changetimeRegWaiting){
|
||||
var regroupneededPartC = gameState.getOwnEntitiesWithRole("attack-pending");
|
||||
var regroupneededPartC = gameState.getOwnRoleGroup("attack-pending");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("Village"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("Village");
|
||||
// If we have a target, move to it
|
||||
if (targets.length)
|
||||
{
|
||||
@ -356,11 +346,9 @@ var MilitaryAttackManager = Class({
|
||||
// TODO: this should be an attack-move command
|
||||
regroupneededPartC.move(targetPos[0], targetPos[1]);
|
||||
}
|
||||
var regroupneededPartC = gameState.getOwnEntitiesWithRole("attack-pending_3p1");
|
||||
var regroupneededPartC = gameState.getOwnRoleGroup("attack-pending_3p1");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("Village"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("Village");
|
||||
// If we have a target, move to it
|
||||
if (targets.length)
|
||||
{
|
||||
@ -370,11 +358,9 @@ var MilitaryAttackManager = Class({
|
||||
// TODO: this should be an attack-move command
|
||||
regroupneededPartC.move(targetPos[0], targetPos[1]);
|
||||
}
|
||||
var regroupneededPartC = gameState.getOwnEntitiesWithRole("attack-pending_3p2");
|
||||
var regroupneededPartC = gameState.getOwnRoleGroup("attack-pending_3p2");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("Village"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("Village");
|
||||
// If we have a target, move to it
|
||||
if (targets.length)
|
||||
{
|
||||
@ -384,11 +370,9 @@ var MilitaryAttackManager = Class({
|
||||
// TODO: this should be an attack-move command
|
||||
regroupneededPartC.move(targetPos[0], targetPos[1]);
|
||||
}
|
||||
var regroupneededPartC = gameState.getOwnEntitiesWithRole("attack-pending_3p3");
|
||||
var regroupneededPartC = gameState.getOwnRoleGroup("attack-pending_3p3");
|
||||
//Find a friendsly CC
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("Village"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("Village");
|
||||
// If we have a target, move to it
|
||||
if (targets.length)
|
||||
{
|
||||
@ -409,18 +393,17 @@ var MilitaryAttackManager = Class({
|
||||
{
|
||||
var trainup = 0;
|
||||
var pendingdefense = gameState.countEntitiesAndQueuedWithRole("defenders");
|
||||
var targets = gameState.entities.filter(function(ent) {
|
||||
return (!ent.isEnemy() && ent.hasClass("GarrisonTower"));
|
||||
});
|
||||
var targets = gameState.getOwnWithClass("GarrisonTower");
|
||||
if (targets.length)
|
||||
{
|
||||
targets.forEach(function(tower) {
|
||||
if (tower.foundationProgress() === undefined){
|
||||
var defno = tower.garrisoned().length;
|
||||
var defneed = 3 - defno;
|
||||
warn("Need " + defneed + " men in the tower, with " + pendingdefense + " available.");
|
||||
//warn("Need " + defneed + " men in the tower, with " + pendingdefense + " available.");
|
||||
if (defneed >= 1) {
|
||||
if (pendingdefense >= 1) {
|
||||
gameState.getOwnEntitiesWithRole("defenders").forEach(function(ent) {
|
||||
gameState.getOwnRoleGroup("defenders").forEach(function(ent) {
|
||||
ent.garrison(tower);
|
||||
ent.setMetadata("role", "towerGuard");
|
||||
});
|
||||
@ -429,6 +412,29 @@ var MilitaryAttackManager = Class({
|
||||
trainup = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var targetsII = gameState.getOwnWithClass("GarrisonFortress");
|
||||
if (targetsII.length)
|
||||
{
|
||||
targetsII.forEach(function(tower) {
|
||||
if (tower.foundationProgress() === undefined){
|
||||
var defno = tower.garrisoned().length;
|
||||
var defneed = 6 - defno;
|
||||
//warn("Need " + defneed + " men in the tower, with " + pendingdefense + " available.");
|
||||
if (defneed >= 1) {
|
||||
if (pendingdefense >= 1) {
|
||||
gameState.getOwnRoleGroup("defenders").forEach(function(ent) {
|
||||
ent.garrison(tower);
|
||||
ent.setMetadata("role", "towerGuard");
|
||||
});
|
||||
}
|
||||
else {
|
||||
trainup = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (trainup == 1){
|
||||
|
@ -0,0 +1,277 @@
|
||||
var BuildingConstructionPlanDefensePoints = Class({
|
||||
|
||||
_init: function(gameState, type, indno)
|
||||
{
|
||||
this.type = gameState.applyCiv(type);
|
||||
|
||||
var template = gameState.getTemplate(this.type);
|
||||
if (!template)
|
||||
{
|
||||
this.invalidTemplate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.cost = new Resources(template.cost());
|
||||
},
|
||||
|
||||
canExecute: function(gameState)
|
||||
{
|
||||
if (this.invalidTemplate)
|
||||
return false;
|
||||
|
||||
// TODO: verify numeric limits etc
|
||||
|
||||
var builders = gameState.findBuilders(this.type);
|
||||
|
||||
return (builders.length != 0);
|
||||
},
|
||||
|
||||
execute: function(gameState)
|
||||
{
|
||||
// warn("Executing BuildingConstructionPlan "+uneval(this));
|
||||
|
||||
var builders = gameState.findBuilders(this.type).toEntityArray();
|
||||
|
||||
// We don't care which builder we assign, since they won't actually
|
||||
// do the building themselves - all we care about is that there is
|
||||
// some unit that can start the foundation
|
||||
|
||||
var pos = this.findGoodPosition(gameState);
|
||||
|
||||
builders[0].construct(this.type, pos.x, pos.z, pos.angle);
|
||||
},
|
||||
|
||||
getCost: function()
|
||||
{
|
||||
return this.cost;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make each cell's 16-bit value at least one greater than each of its
|
||||
* neighbours' values. (If the grid is initialised with 0s and 65535s,
|
||||
* the result of each cell is its Manhattan distance to the nearest 0.)
|
||||
*
|
||||
* TODO: maybe this should be 8-bit (and clamp at 255)?
|
||||
*/
|
||||
expandInfluences: function(grid, w, h)
|
||||
{
|
||||
for (var y = 0; y < h; ++y)
|
||||
{
|
||||
var min = 65535;
|
||||
for (var x = 0; x < w; ++x)
|
||||
{
|
||||
var g = grid[x + y*w];
|
||||
if (g > min) grid[x + y*w] = min;
|
||||
else if (g < min) min = g;
|
||||
++min;
|
||||
}
|
||||
|
||||
for (var x = w-2; x >= 0; --x)
|
||||
{
|
||||
var g = grid[x + y*w];
|
||||
if (g > min) grid[x + y*w] = min;
|
||||
else if (g < min) min = g;
|
||||
++min;
|
||||
}
|
||||
}
|
||||
|
||||
for (var x = 0; x < w; ++x)
|
||||
{
|
||||
var min = 65535;
|
||||
for (var y = 0; y < h; ++y)
|
||||
{
|
||||
var g = grid[x + y*w];
|
||||
if (g > min) grid[x + y*w] = min;
|
||||
else if (g < min) min = g;
|
||||
++min;
|
||||
}
|
||||
|
||||
for (var y = h-2; y >= 0; --y)
|
||||
{
|
||||
var g = grid[x + y*w];
|
||||
if (g > min) grid[x + y*w] = min;
|
||||
else if (g < min) min = g;
|
||||
++min;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a circular linear-falloff shape to a grid.
|
||||
*/
|
||||
addInfluence: function(grid, w, h, cx, cy, maxDist)
|
||||
{
|
||||
var x0 = Math.max(0, cx - maxDist);
|
||||
var y0 = Math.max(0, cy - maxDist);
|
||||
var x1 = Math.min(w, cx + maxDist);
|
||||
var y1 = Math.min(h, cy + maxDist);
|
||||
for (var y = y0; y < y1; ++y)
|
||||
{
|
||||
for (var x = x0; x < x1; ++x)
|
||||
{
|
||||
var dx = x - cx;
|
||||
var dy = y - cy;
|
||||
var r = Math.sqrt(dx*dx + dy*dy);
|
||||
if (r < maxDist)
|
||||
grid[x + y*w] += maxDist - r;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a circular linear-falloff shape to a grid.
|
||||
*/
|
||||
subtractInfluence: function(grid, w, h, cx, cy, maxDist)
|
||||
{
|
||||
var x0 = Math.max(0, cx - maxDist);
|
||||
var y0 = Math.max(0, cy - maxDist);
|
||||
var x1 = Math.min(w, cx + maxDist);
|
||||
var y1 = Math.min(h, cy + maxDist);
|
||||
for (var y = y0; y < y1; ++y)
|
||||
{
|
||||
for (var x = x0; x < x1; ++x)
|
||||
{
|
||||
var dx = x - cx;
|
||||
var dy = y - cy;
|
||||
var r = Math.sqrt(dx*dx + dy*dy);
|
||||
if (r < maxDist)
|
||||
grid[x + y*w] -= maxDist - r;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findGoodPosition: function(gameState)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
var cellSize = 4; // size of each tile
|
||||
|
||||
var template = gameState.getTemplate(this.type);
|
||||
|
||||
// Find all tiles in valid territory that are far enough away from obstructions:
|
||||
var passabilityMap = gameState.getPassabilityMap();
|
||||
var territoryMap = gameState.getTerritoryMap();
|
||||
const TERRITORY_PLAYER_MASK = 0x7F;
|
||||
var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction");
|
||||
|
||||
if (passabilityMap.data.length != territoryMap.data.length)
|
||||
error("passability and territory data are not matched!");
|
||||
|
||||
// See BuildRestrictions.js
|
||||
switch(template.buildPlacementType())
|
||||
{
|
||||
case "shore":
|
||||
obstructionMask |= gameState.getPassabilityClassMask("building-shore");
|
||||
break;
|
||||
case "land":
|
||||
default:
|
||||
obstructionMask |= gameState.getPassabilityClassMask("building-land");
|
||||
}
|
||||
|
||||
var playerID = gameState.getPlayerID();
|
||||
var buildOwn = template.hasBuildTerritory("own");
|
||||
var buildAlly = template.hasBuildTerritory("ally");
|
||||
var buildNeutral = template.hasBuildTerritory("neutral");
|
||||
var buildEnemy = template.hasBuildTerritory("enemy");
|
||||
|
||||
var obstructionTiles = new Uint16Array(passabilityMap.data.length);
|
||||
for (var i = 0; i < passabilityMap.data.length; ++i)
|
||||
{
|
||||
var tilePlayer = (territoryMap.data[i] & TERRITORY_PLAYER_MASK);
|
||||
var invalidTerritory = (
|
||||
(!buildOwn && tilePlayer == playerID) ||
|
||||
(!buildAlly && gameState.isPlayerAlly(tilePlayer) && tilePlayer != playerID) ||
|
||||
(!buildNeutral && tilePlayer == 0) ||
|
||||
(!buildEnemy && gameState.isPlayerEnemy(tilePlayer) && tilePlayer !=0)
|
||||
);
|
||||
obstructionTiles[i] = (invalidTerritory || (passabilityMap.data[i] & obstructionMask)) ? 0 : 65535;
|
||||
}
|
||||
|
||||
// Engine.DumpImage("tiles0.png", obstructionTiles, passabilityMap.width, passabilityMap.height, 64);
|
||||
|
||||
this.expandInfluences(obstructionTiles, passabilityMap.width, passabilityMap.height);
|
||||
|
||||
// TODO: handle distance restrictions for e.g. CivCentres
|
||||
|
||||
// Compute each tile's closeness to friendly structures:
|
||||
|
||||
var friendlyTiles = new Uint16Array(passabilityMap.data.length);
|
||||
|
||||
gameState.getOwnEntities().forEach(function(ent) {
|
||||
if (ent.hasClass("Structure"))
|
||||
{
|
||||
var infl = 0;
|
||||
if (ent.hasClass("Economic") && ent.getMetadata("tower") != 1)
|
||||
{
|
||||
infl = 20;
|
||||
ent.setMetadata("tower", 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
infl = 0;
|
||||
}
|
||||
|
||||
var pos = ent.position();
|
||||
var x = Math.round(pos[0] / cellSize);
|
||||
var z = Math.round(pos[1] / cellSize);
|
||||
self.addInfluence(friendlyTiles, passabilityMap.width, passabilityMap.height, x, z, infl);
|
||||
}
|
||||
});
|
||||
|
||||
// var foetargets = gameState.entities.filter(function(ent) {
|
||||
// return (ent.isEnemy());
|
||||
// });
|
||||
// foetargets.forEach(function(ent) {
|
||||
// if (ent.hasClass("CivCentre"))
|
||||
// {
|
||||
// var infl = 100;
|
||||
// var pos = ent.position();
|
||||
// var x = Math.round(pos[0] / cellSize);
|
||||
// var z = Math.round(pos[1] / cellSize);
|
||||
// self.subtractInfluence(friendlyTiles, passabilityMap.width, passabilityMap.height, x, z, infl);
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// Find target building's approximate obstruction radius,
|
||||
// and expand by a bit to make sure we're not too close
|
||||
var radius = Math.ceil(template.obstructionRadius() / cellSize) + 1;
|
||||
|
||||
// Find the best non-obstructed tile
|
||||
var bestIdx = 0;
|
||||
var bestVal = -1;
|
||||
for (var i = 0; i < passabilityMap.data.length; ++i)
|
||||
{
|
||||
if (obstructionTiles[i] > radius)
|
||||
{
|
||||
var v = friendlyTiles[i];
|
||||
//var foe = enemyTiles[i];
|
||||
//JuBotAI.prototype.chat(v);
|
||||
//JuBotAI.prototype.chat(i);
|
||||
//JuBotAI.prototype.chat(foe);
|
||||
if (v >= bestVal)
|
||||
{
|
||||
bestVal = v;
|
||||
bestIdx = i;
|
||||
//JuBotAI.prototype.chat("BestVal is " + bestVal + ", and bestIdx is " + bestIdx + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
var x = ((bestIdx % passabilityMap.width) + 0.5) * cellSize;
|
||||
var z = (Math.floor(bestIdx / passabilityMap.width) + 0.5) * cellSize;
|
||||
|
||||
// Engine.DumpImage("tiles1.png", obstructionTiles, passabilityMap.width, passabilityMap.height, 32);
|
||||
// Engine.DumpImage("tiles2.png", friendlyTiles, passabilityMap.width, passabilityMap.height, 256);
|
||||
|
||||
// TODO: special dock placement requirements
|
||||
|
||||
// Fixed angle to match fixed starting cam
|
||||
var angle = 0.75*Math.PI;
|
||||
|
||||
return {
|
||||
"x": x,
|
||||
"z": z,
|
||||
"angle": angle
|
||||
};
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user