1
0
forked from 0ad/0ad

numerous fixes and improvments to petra ai

This was SVN commit r14896.
This commit is contained in:
mimo 2014-04-03 20:48:54 +00:00
parent ac9aaaa369
commit d04f035f9a
16 changed files with 1011 additions and 780 deletions

View File

@ -36,7 +36,7 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
for (var i in this.priorities)
this.queues[i] = new m.Queue();
this.queueManager = new m.QueueManager(this.Config, this.queues, this.priorities);
this.queueManager = new m.QueueManager(this.Config, this.queues);
this.HQ = new m.HQ(this.Config);
gameState.Config = this.Config;
@ -70,7 +70,8 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
//Engine.DumpImage("initialPath" + this.player + ".png", this.pathFinder.TotorMap.map, this.pathFinder.TotorMap.width,this.pathFinder.TotorMap.height,255);
if (path !== undefined && path[1] !== undefined && path[1] == false) {
if (path !== undefined && path[1] !== undefined && path[1] == false)
{
// path is viable and doesn't require boating.
// blackzone the last two waypoints.
this.pathFinder.markImpassableArea(path[0][0][0],path[0][0][1],20);

View File

@ -181,11 +181,14 @@ m.Army.prototype.addOwn = function (gameState, ID)
ent.setMetadata(PlayerID, "PartOfArmy", this.ID);
this.assignedTo[ID] = 0;
var formerSubrole = ent.getMetadata(PlayerID, "subrole");
if (formerSubrole && formerSubrole === "defender") // can happen when armies are merged for example
return true;
if (formerSubrole !== undefined)
ent.setMetadata(PlayerID, "formerSubrole", formerSubrole);
var plan = ent.getMetadata(PlayerID, "plan");
if (plan !== undefined)
ent.setMetadata(PlayerID, "plan", -2);
else
ent.setMetadata(PlayerID, "plan", -3);
var subrole = ent.getMetadata(PlayerID, "subrole");
if (subrole === undefined || subrole !== "defender")
ent.setMetadata(PlayerID, "formerSubrole", subrole);
ent.setMetadata(PlayerID, "subrole", "defender");
return true;
}
@ -206,6 +209,10 @@ m.Army.prototype.removeOwn = function (gameState, ID, Entity)
this.ownEntities.splice(idx, 1);
this.evaluateStrength(ent, true, true);
ent.setMetadata(PlayerID, "PartOfArmy", undefined);
if (ent.getMetadata(PlayerID, "plan") === -2)
ent.setMetadata(PlayerID, "plan", -1);
else
ent.setMetadata(PlayerID, "plan", undefined);
if (this.assignedTo[ID] !== 0)
{
@ -215,12 +222,12 @@ m.Army.prototype.removeOwn = function (gameState, ID, Entity)
}
this.assignedTo[ID] = undefined;
var formerSubrole = ent.getMetadata(PlayerID, "formerSubrole");
if (formerSubrole !== undefined)
ent.setMetadata(PlayerID, "subrole", formerSubrole);
else
ent.setMetadata(PlayerID, "subrole", undefined);
ent.setMetadata(PlayerID, "formerSubrole", undefined);
return true;
}

View File

@ -20,6 +20,10 @@ m.AttackManager = function(Config)
// More initialisation for stuff that needs the gameState
m.AttackManager.prototype.init = function(gameState, queues)
{
this.outOfPlan = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "plan", -1));
this.outOfPlan.allowQuickIter();
this.outOfPlan.registerUpdates();
this.maxRushes = 0;
if (this.Config.personality.aggressive > 0.9)
this.maxRushes = 2
@ -139,10 +143,10 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
// okay so then we'll update the attack.
if (attack.isPaused())
continue;
var remaining = attack.update(gameState,this,events);
var remaining = attack.update(gameState,this, events);
if (!remaining)
{
if (this.Config.debug)
if (this.Config.debug > 0)
warn("Military Manager: " + attack.getType() + " plan " + attack.getName() + " is finished with remaining " + remaining);
attack.Abort(gameState);
this.startedAttacks[attackType].splice(i--,1);
@ -150,6 +154,13 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
}
}
// Number of running attacks
if (this.Config.debug > 0 && gameState.ai.playedTurn%50 === 0)
{
for (var attackType in this.startedAttacks)
warn(" === current number of " + attackType + " = " + this.startedAttacks[attackType].length);
}
// creating plans after updating because an aborted plan might be reused in that case.
// TODO: remove the limitation to attacks when on water maps.
@ -158,10 +169,10 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
if (this.rushNumber < this.maxRushes && gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_barracks"), true) >= 1)
{
if (this.upcomingAttacks["Rush"].length === 0)
{
{
// we have a barracks and we want to rush, rush.
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, -1, "Rush");
if (this.Config.debug)
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Rush");
if (this.Config.debug > 0)
warn("Headquarters: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes);
this.rushNumber++;
this.totalNumber++;
@ -179,15 +190,15 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
else if (this.upcomingAttacks["CityAttack"].length === 0)
{
if (this.attackNumber < 2)
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, -1);
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber);
else
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, -1, "superSized");
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "superSized");
if (attackPlan.failed)
this.attackPlansEncounteredWater = true; // hack
else
{
if (this.Config.debug)
if (this.Config.debug > 0)
warn("Military Manager: Creating the plan " + this.totalNumber);
this.attackNumber++;
this.totalNumber++;
@ -208,7 +219,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
if (target)
{
// prepare a raid against this target
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, target.owner(), "Raid");
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Raid", target.owner(), target);
if (this.Config.debug > 0)
warn("Headquarters: Raiding plan " + this.totalNumber);
this.raidNumber++;

File diff suppressed because it is too large Load Diff

View File

@ -129,14 +129,14 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
var ent = gameState.getEntityById(evt.newentity);
if (ent === undefined)
continue;
if (evt.newentity === evt.entity) // repaired building
continue;
if (ent.getMetadata(PlayerID,"base") == this.ID)
{
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
this.assignResourceToDropsite(gameState, ent);
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant") && this.Config.debug)
for each (var ress in ent.resourceDropsiteTypes())
warn(" DPresource " + ress + " = " + this.getResourceLevel(gameState, ress));
}
}
}
@ -261,7 +261,8 @@ m.BaseManager.prototype.removeDropsite = function (gameState, ent)
return;
};
// Returns the position of the best place to build a new dropsite for the specified resource
// Returns the position of the best place to build a new dropsite for the specified resource
// TODO check dropsites of other bases ... may-be better to do a simultaneous liste of dp and foundations
m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
{
@ -562,14 +563,16 @@ m.BaseManager.prototype.reassignIdleWorkers = function(gameState)
var idleWorkers = gameState.updatingCollection("idle-workers-base-" + this.ID, filter, this.workers);
var self = this;
if (idleWorkers.length) {
if (idleWorkers.length)
{
idleWorkers.forEach(function(ent)
{
// Check that the worker isn't garrisoned
if (ent.position() === undefined)
return;
// Support elephant can only be builders
if (ent.hasClass("Support") && ent.hasClass("Elephant")) {
if (ent.hasClass("Support") && ent.hasClass("Elephant"))
{
ent.setMetadata(PlayerID, "subrole", "idle");
return;
}
@ -580,9 +583,16 @@ m.BaseManager.prototype.reassignIdleWorkers = function(gameState)
else
{
var types = gameState.ai.HQ.pickMostNeededResources(gameState);
ent.setMetadata(PlayerID, "subrole", "gatherer");
ent.setMetadata(PlayerID, "gather-type", types[0]);
m.AddTCRessGatherer(gameState, types[0]);
for (var i = 0; i < types.length; ++i)
{
var lastFailed = gameState.ai.HQ.lastFailedGather[types[i]];
if (lastFailed && gameState.ai.playedTurn - lastFailed < 20)
continue;
ent.setMetadata(PlayerID, "subrole", "gatherer");
ent.setMetadata(PlayerID, "gather-type", types[i]);
m.AddTCRessGatherer(gameState, types[i]);
break;
}
}
}
else if (ent.hasClass("Cavalry"))
@ -606,7 +616,10 @@ m.BaseManager.prototype.gatherersByType = function(gameState, type)
// They are idled immediatly and their subrole set to idle.
m.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
{
var availableWorkers = this.workers.filter(API3.Filters.not(API3.Filters.byClass("Cavalry"))).toEntityArray();
var filter = API3.Filters.not(API3.Filters.or(
API3.Filters.or(API3.Filters.byMetadata(PlayerID, "plan", -2), API3.Filters.byMetadata(PlayerID, "plan", -3)),
API3.Filters.or(API3.Filters.isGarrisoned(), API3.Filters.byClass("Cavalry"))));
var availableWorkers = this.workers.filter(filter).toEntityArray();
availableWorkers.sort(function (a,b) {
var vala = 0, valb = 0;
if (a.getMetadata(PlayerID, "subrole") == "builder")
@ -617,9 +630,9 @@ m.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
vala = -20;
if (b.getMetadata(PlayerID, "subrole") == "idle")
valb = -20;
if (a.getMetadata(PlayerID, "plan") != undefined)
if (a.getMetadata(PlayerID, "plan") !== undefined)
vala = -100;
if (b.getMetadata(PlayerID, "plan") != undefined)
if (b.getMetadata(PlayerID, "plan") !== undefined)
valb = -100;
return (vala - valb);
});

View File

@ -70,10 +70,10 @@ m.Config = function() {
"trader" : 50,
"ships" : 70,
"house" : 350,
"dropsites" : 120,
"field" : 500,
"dropsites" : 200,
"field" : 400,
"economicBuilding" : 90,
"militaryBuilding" : 240, // set to something lower after the first barracks.
"militaryBuilding" : 130,
"defenseBuilding" : 70,
"civilCentre" : 950,
"majorTech" : 700,

View File

@ -17,6 +17,8 @@ m.DefenseManager.prototype.update = function(gameState, events)
{
this.territoryMap = gameState.ai.HQ.territoryMap;
this.checkDefenseStructures(gameState, events);
this.checkEnemyArmies(gameState, events);
this.checkEnemyUnits(gameState);
this.assignDefenders(gameState);
@ -36,6 +38,16 @@ m.DefenseManager.prototype.makeIntoArmy = function(gameState, entityID)
this.armies.push(army);
};
m.DefenseManager.prototype.getArmy = function(partOfArmy)
{
// Find the army corresponding to this ID partOfArmy
for (var o in this.armies)
if (this.armies[o].ID === partOfArmy)
return this.armies[o];
return undefined;
};
// TODO: this algorithm needs to be improved, sorta.
m.DefenseManager.prototype.isDangerous = function(gameState, entity)
{
@ -52,7 +64,12 @@ m.DefenseManager.prototype.isDangerous = function(gameState, entity)
if (this.targetList.indexOf(targetId) !== -1)
return true;
var target = gameState.getEntityById(targetId);
if (target && target.hasClass("CivCentre"))
if (target && this.territoryMap.getOwner(entity.position()) === PlayerID)
{
this.targetList.push(targetId);
return true;
}
else if (target && target.hasClass("CivCentre"))
{
var myBuildings = gameState.getOwnStructures();
for (var i in myBuildings._entities)
@ -248,20 +265,17 @@ m.DefenseManager.prototype.assignDefenders = function(gameState, events)
// let's get our potential units
var potentialDefenders = [];
gameState.getOwnUnits().forEach(function(ent) {
if (ent.getMetadata(PlayerID, "PartOfArmy") !== undefined)
if (!ent.position())
return;
if (ent.getMetadata(PlayerID, "plan") !== undefined)
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
return;
if (ent.hasClass("Siege") || ent.hasClass("Support") || ent.attackTypes() === undefined)
return;
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
{
var subrole = ent.getMetadata(PlayerID, "subrole");
if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking"))
return;
if (ent.hasClass("Siege"))
return;
}
else
{
if (!ent.hasClass("Infantry") && !ent.hasClass("Cavalry"))
return;
}
potentialDefenders.push(ent.id());
});
@ -303,11 +317,57 @@ m.DefenseManager.prototype.assignDefenders = function(gameState, events)
// If shortage of defenders: increase the priority of soldiers queues
if (armiesNeeding.length !== 0)
gameState.ai.HQ.boostSoldiers(gameState, 10000, true);
gameState.ai.HQ.boostSoldiers(gameState);
else
gameState.ai.HQ.unboostSoldiers(gameState);
};
// If our defense structures are attacked, garrison soldiers inside when possible
// TODO transfer most of that code in a garrisonManager
m.DefenseManager.prototype.checkDefenseStructures = function(gameState, events)
{
var self = this;
var attackedEvents = events["Attacked"];
for (var key in attackedEvents)
{
var e = attackedEvents[key];
var target = gameState.getEntityById(e.target);
if (!target || !gameState.isEntityOwn(target) || !target.getArrowMultiplier())
continue;
if (!target.isGarrisonHolder() || gameState.ai.HQ.garrisonManager.numberOfGarrisonedUnits(target) >= target.garrisonMax())
continue;
var attacker = gameState.getEntityById(e.attacker);
if (!attacker)
continue;
var dist = API3.SquareVectorDistance(attacker.position(), target.position());
var range = target.attackRange("Ranged").max;
if (dist >= range*range)
continue;
var garrisonManager = gameState.ai.HQ.garrisonManager;
gameState.getOwnUnits().filter(API3.Filters.byClassesAnd(["Infantry", "Ranged"])).filterNearest(target.position()).forEach(function(ent) {
if (garrisonManager.numberOfGarrisonedUnits(target) >= target.garrisonMax())
return;
if (!ent.position())
return;
var army = ent.getMetadata(PlayerID, "PartOfArmy");
if (army !== undefined)
army = self.getArmy(army);
if (army !== undefined)
army.removeOwn(gameState, ent.id(), ent);
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
return;
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
{
var subrole = ent.getMetadata(PlayerID, "subrole");
if (subrole && (subrole === "completing" || subrole === "walking" || subrole === "attacking"))
return;
}
garrisonManager.garrison(gameState, ent, target, "protection");
});
}
};
// this processes the attackmessages
// So that a unit that gets attacked will not be completely dumb.
// warning: big levels of indentation coming.

View File

@ -15,7 +15,7 @@ m.IsSupplyFull = function(gamestate, supply)
if (count >= supply.maxGatherers())
return true;
return false;
}
};
// add a gatherer to the turn cache for this supply.
m.AddTCGatherer = function(gamestate, supplyID)
@ -28,7 +28,7 @@ m.AddTCGatherer = function(gamestate, supplyID)
gamestate.turnCache["ressourceGatherer"] = {};
gamestate.turnCache["ressourceGatherer"][supplyID] = 1;
}
}
};
// remove a gatherer to the turn cache for this supply.
m.RemoveTCGatherer = function(gamestate, supplyID)
@ -39,7 +39,7 @@ m.RemoveTCGatherer = function(gamestate, supplyID)
if (!gamestate.turnCache["ressourceGatherer"])
gamestate.turnCache["ressourceGatherer"] = {};
gamestate.turnCache["ressourceGatherer"][supplyID] = -1;
}
};
m.GetTCGatherer = function(gamestate, supplyID)
{
@ -47,16 +47,16 @@ m.GetTCGatherer = function(gamestate, supplyID)
return gamestate.turnCache["ressourceGatherer"][supplyID];
else
return 0;
}
};
// The next two are to register that we assigned a gatherer to a resource this turn.
// The next three are to register that we assigned a gatherer to a resource this turn.
m.AddTCRessGatherer = function(gamestate, resource)
{
if (gamestate.turnCache["ressourceGatherer-" + resource])
++gamestate.turnCache["ressourceGatherer-" + resource];
else
gamestate.turnCache["ressourceGatherer-" + resource] = 1;
}
};
m.GetTCRessGatherer = function(gamestate, resource)
{
@ -64,7 +64,7 @@ m.GetTCRessGatherer = function(gamestate, resource)
return gamestate.turnCache["ressourceGatherer-" + resource];
else
return 0;
}
};
return m;
}(PETRA);

View File

@ -0,0 +1,158 @@
var PETRA = function(m)
{
/**
* Manage the garrisonHolders
* When a unit is ordered to garrison, it must be done through this.garrison() function so that
* an object in this.holders is created. This object contains an array with the entities
* in the process of being garrisoned. To have all garrisoned units, we must add those in holder.garrisoned().
* Futhermore garrison units have a metadata garrison described the reason of this garrison (protection, transport, ...)
*/
m.GarrisonManager = function()
{
this.holders = {};
};
m.GarrisonManager.prototype.update = function(gameState, queues)
{
for (var id in this.holders)
{
if (this.holders[id] === undefined)
continue;
var holder = gameState.getEntityById(id);
if (!holder) // this holder was certainly destroyed. Let's remove it
{
for each (var entId in this.holders[id])
{
var ent = gameState.getEntityById(entId);
if (ent && ent.getMetadata(PlayerID, "garrison-holder") === id)
this.leaveGarrison(ent);
}
this.holders[id] = undefined;
continue;
}
var list = this.holders[id];
// Update the list of garrisoned units
for (var j = 0; j < list.length; ++j)
{
var ent = gameState.getEntityById(list[j]);
if (!ent) // unit must have been killed while garrisoning
list.splice(j--, 1);
else if (holder._entity.garrisoned.indexOf(list[j]) !== -1) // unit is garrisoned
{
this.leaveGarrison(ent);
list.splice(j--, 1);
}
}
if (!holder.position()) // could happen with siege unit inside a ship
continue;
if (gameState.ai.playedTurn - holder.getMetadata(PlayerID, "lastUpdate") > 5)
{
var range = holder.attackRange("Ranged").max;
var enemiesAround = gameState.getEnemyEntities().toEntityArray().some(function(ent) {
if (!ent.position() || ent.owner() === 0)
return false;
var dist = API3.SquareVectorDistance(ent.position(), holder.position());
if (dist < range*range)
return true;
return false;
});
var healer = holder.buffHeal();
for each (var entId in holder._entity.garrisoned)
{
var ent = gameState.getEntityById(entId);
if (!this.keepGarrisoned(ent, holder, enemiesAround))
holder.unload(entId);
}
for (var j = 0; j < list.length; ++j)
{
var ent = gameState.getEntityById(list[j]);
if (this.keepGarrisoned(ent, holder, enemiesAround))
continue;
if (ent.getMetadata(PlayerID, "garrison-holder") === id)
this.leaveGarrison(ent);
list.splice(j--, 1);
}
if (this.numberOfGarrisonedUnits(holder) === 0)
this.holders[id] = undefined;
else
holder.setMetadata(PlayerID, "lastUpdate", gameState.ai.playedTurn);
}
}
};
// TODO should add the units garrisoned inside garrisoned units
m.GarrisonManager.prototype.numberOfGarrisonedUnits = function(holder)
{
if (!this.holders[holder.id()])
return holder.garrisoned().length;
return (holder.garrisoned().length + this.holders[holder.id()].length);
};
// This is just a pre-garrison state, while the entity walk to the garrison holder
m.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type)
{
if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax())
return;
if (!this.holders[holder.id()])
{
this.holders[holder.id()] = [ent.id()];
holder.setMetadata(PlayerID, "lastUpdate", gameState.ai.playedTurn);
}
else
this.holders[holder.id()].push(ent.id());
if (ent.getMetadata(PlayerID, "plan") !== undefined)
ent.setMetadata(PlayerID, "plan", -2);
else
ent.setMetadata(PlayerID, "plan", -3);
ent.setMetadata(PlayerID, "subrole", "garrisoning");
ent.setMetadata(PlayerID, "garrison-holder", holder.id());
ent.setMetadata(PlayerID, "garrison-type", type);
ent.garrison(holder);
};
// This is the end of the pre-garrison state, either because the entity really garrsioned
// or because it has changed its order (i.e. because the garrisonHolder was destroyed).
m.GarrisonManager.prototype.leaveGarrison = function(ent)
{
ent.setMetadata(PlayerID, "subrole", undefined);
if (ent.getMetadata(PlayerID, "plan") === -2)
ent.setMetadata(PlayerID, "plan", -1);
else
ent.setMetadata(PlayerID, "plan", undefined);
ent.setMetadata(PlayerID, "garrison-holder", undefined);
};
m.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, enemiesAround)
{
switch (ent.getMetadata(PlayerID, "garrison-type"))
{
case 'force': // force the ungarrisoning
return false;
case 'trade': // trader garrisoned in ship
return true;
case 'protection': // hurt unit for healing or ranged infantry for defense
var healer = holder.buffHeal();
if (healer && healer > 0 && ent.isHurt())
return true;
if (enemiesAround && (ent.hasClass("Support") || (ent.hasClass("Ranged") && ent.hasClass("Infantry"))))
return true;
return false;
default:
warn("unknown type in garrisonManager " + ent.getMetadata(PlayerID, "garrison-type"));
return true;
}
};
return m;
}(PETRA);

View File

@ -31,6 +31,8 @@ m.HQ = function(Config)
// cache the rates.
this.wantedRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
this.currentRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
this.lastFailedGather = { "wood": undefined, "stone": undefined, "metal": undefined };
// this means we'll have about a big third of women, and thus we can maximize resource gathering rates.
this.femaleRatio = this.Config.Economy.femaleRatio;
@ -47,6 +49,7 @@ m.HQ = function(Config)
this.attackManager = new m.AttackManager(this.Config);
this.defenseManager = new m.DefenseManager(this.Config);
this.tradeManager = new m.TradeManager(this.Config);
this.garrisonManager = new m.GarrisonManager();
this.navalManager = new m.NavalManager();
this.boostedSoldiers = undefined;
@ -288,25 +291,23 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
if (numTotal > this.targetNumWorkers || (numTotal >= this.Config.Economy.popForTown
&& gameState.currentPhase() === 1 && !gameState.isResearching(gameState.townPhase())))
return;
if (numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
return;
}
else if (numQueuedS > 20)
return;
if (numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
return;
// default template and size
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
var size = Math.min(5, Math.ceil(numTotal / 10));
if (numTotal < 12)
size = 1;
// Choose whether we want soldiers instead.
if ((numFemales+numQueuedF)/numTotal > this.femaleRatio || this.boostedSoldiers)
if ((numFemales+numQueuedF) > 8 && (numFemales+numQueuedF)/numTotal > this.femaleRatio)
{
if (numTotal < 35)
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]);
if (numTotal < 45)
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost", 1], ["speed", 0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]);
else
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength",1] ]);
if (!template && this.boostedSoldiers)
return;
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength", 1] ]);
if (!template)
template = gameState.applyCiv("units/{civ}_support_female_citizen");
}
@ -321,12 +322,41 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
};
// picks the best template based on parameters and classes
m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters) {
m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, requirements)
{
var units = gameState.findTrainableUnits(classes);
if (units.length === 0)
return undefined;
var parameters = requirements.slice();
var remainingResources = this.getTotalResourceLevel(gameState); // resources (estimation) still gatherable in our territory
var availableResources = gameState.ai.queueManager.getAvailableResources(gameState); // available (gathered) resources
for (var type in remainingResources)
{
if (type === "food")
continue;
if (availableResources[type] > 800)
continue;
if (remainingResources[type] > 800)
continue;
else if (remainingResources[type] > 400)
var costsResource = 0.6;
else
var costsResource = 0.2;
var toAdd = true;
for each (var param in parameters)
{
if (param[0] !== "costsResource" || param[2] !== type)
continue;
param[1] = Math.min( param[1], costsResource );
toAdd = false;
break;
}
if (toAdd)
parameters.push( [ "costsResource", costsResource, type ] );
}
units.sort(function(a, b) {// }) {
var aDivParam = 0, bDivParam = 0;
var aTopParam = 0, bTopParam = 0;
@ -449,7 +479,7 @@ m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number)
return workers;
};
m.HQ.prototype.GetTotalResourceLevel = function(gameState)
m.HQ.prototype.getTotalResourceLevel = function(gameState)
{
var total = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
for (var i in this.baseManagers)
@ -879,17 +909,18 @@ m.HQ.prototype.findDefensiveLocation = function(gameState, template)
for each (var str in ownStructures)
{
var strPos = str.position();
if (!strPos)
continue;
var dist = API3.SquareVectorDistance(strPos, pos);
if ((template.hasClass("Tower") && str.hasClass("Tower")) || (template.hasClass("Fortress") && str.hasClass("Fortress")))
var cutDist = 4225; // TODO check on true buildrestrictions instead of this 65*65
else
var cutDist = 900; // 30*30 TODO maybe increase it
if (dist < cutDist)
{
var strPos = str.position();
if (!strPos)
continue;
var dist = API3.SquareVectorDistance(strPos, pos);
if (dist < 4225) // TODO check on true buildrestrictions instead of this 65*65
{
minDist = -1;
break;
}
minDist = -1;
break;
}
}
if (minDist < 0)
@ -954,8 +985,10 @@ m.HQ.prototype.buildFarmstead = function(gameState, queues)
// Only build one farmstead for the time being ("DropsiteFood" does not refer to CCs)
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_farmstead"), true) > 0)
return;
// Wait to have at least one house before the farmstead
if (gameState.countEntitiesByType(gameState.applyCiv("foundation|structures/{civ}_house"), true) == 0)
// Wait to have at least one dropsite and house before the farmstead
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_storehouse"), true) == 0)
return;
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_house"), true) == 0)
return;
if (queues.economicBuilding.countQueuedUnitsWithClass("DropsiteFood") > 0)
return;
@ -1025,14 +1058,24 @@ m.HQ.prototype.tryBartering = function(gameState)
if (needs[sell] > 0 || available[sell] < 500) // do not sell if we need it or do not have enough buffer
continue;
var barterRateMin = 70;
if (available[sell] > 1000)
barterRateMin = 50;
if (sell === "food")
barterRateMin -= 40;
else if (buy === "food")
barterRateMin += 20;
{
var barterRateMin = 30;
if (available[sell] > 40000)
barterRateMin = 0;
else if (available[sell] > 15000)
barterRateMin = 5;
else if (available[sell] > 1000)
barterRateMin = 10;
}
else
{
var barterRateMin = 70;
if (available[sell] > 1000)
barterRateMin = 50;
if (buy === "food")
barterRateMin += 20;
}
var barterRate = getBarterRate(prices, buy, sell);
if (barterRate > bestRate && barterRate > barterRateMin)
@ -1044,7 +1087,7 @@ m.HQ.prototype.tryBartering = function(gameState)
if (bestToSell !== undefined)
{
markets[0].barter(buy, bestToSell, 100);
if (this.Config.debug > 0)
if (this.Config.debug > 1)
warn("Necessity bartering: sold " + bestToSell +" for " + buy + " >> need sell " + needs[bestToSell]
+ " need buy " + needs[buy] + " rate buy " + rates[buy] + " available sell " + available[bestToSell]
+ " available buy " + available[buy] + " barterRate " + bestRate);
@ -1074,7 +1117,7 @@ m.HQ.prototype.tryBartering = function(gameState)
if (bestToBuy !== undefined)
{
markets[0].barter(bestToBuy, "food", 100);
if (this.Config.debug > 0)
if (this.Config.debug > 1)
warn("Contingency bartering: sold food for " + bestToBuy + " available sell " + available["food"]
+ " available buy " + available[bestToBuy] + " barterRate " + getBarterRate(prices, bestToBuy, "food"));
return true;
@ -1101,7 +1144,11 @@ m.HQ.prototype.buildTradeRoute = function(gameState, queues)
if (!market1[0] || !market1[0].position())
return false;
// We require at least two finished bases
if (!this.baseManagers[2] || this.baseManagers[2].constructing)
var nbase = 0;
for (var i in this.baseManagers)
if (this.baseManagers[i] && !this.baseManagers[i].constructing)
nbase++;
if (nbase < 2)
return false;
if (queues.economicBuilding.countQueuedUnitsWithClass("Market") > 0 ||
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market"), true) >= needed)
@ -1109,7 +1156,8 @@ m.HQ.prototype.buildTradeRoute = function(gameState, queues)
if (!this.canBuild(gameState, "structures/{civ}_market"))
return false;
// We have to build a second market ... try to put it as far as possible from the first one
// TODO improve, for the time being, we affect it to the farthest base
// we affect it temporarily to the farthest base, but the real placement will be done
// in queueplan-building.js
var marketBase = market1[0].getMetadata(PlayerID, "base");
var distmax = -1;
var base = -1;
@ -1223,6 +1271,22 @@ m.HQ.prototype.buildMoreHouses = function(gameState,queues)
}
}
}
// Change house priority if needed
var HouseNb = gameState.countEntitiesByType(gameState.applyCiv("foundation|structures/{civ}_house"), true);
var freeSlots = 0;
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber" ||
gameState.civ() == "maur" || gameState.civ() == "ptol")
var popBonus = 5;
else
var popBonus = 10;
freeSlots = gameState.getPopulationLimit() + HouseNb*popBonus - gameState.getPopulation();
if (freeSlots < 5)
var priority = 2*this.Config.priorities.house;
else
var priority = this.Config.priorities.house;
if (priority !== gameState.ai.queueManager.getPriority("house"))
gameState.ai.queueManager.changePriority("house", priority);
};
// checks the status of the territory expansion. If no new economic bases created, build some strategic ones.
@ -1253,7 +1317,7 @@ m.HQ.prototype.buildNewBase = function(gameState, queues, type)
{
if (gameState.currentPhase() === 1 && !gameState.isResearching(gameState.townPhase()))
return false;
if (gameState.countFoundationsByType(gameState.applyCiv(this.bBase[0]), true) !== 0 || queues.civilCentre.length() !== 0)
if (gameState.countFoundationsByType(gameState.applyCiv(this.bBase[0]), true) > 0 || queues.civilCentre.length() > 0)
return false;
if (!this.canBuild(gameState, this.bBase[0]))
return false;
@ -1277,7 +1341,7 @@ m.HQ.prototype.buildDefenses = function(gameState, queues)
for (var i in this.bFort)
numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]), true);
if (gameState.getTimeElapsed() > (1 + 0.05*numFortresses)*this.fortressLapseTime + this.fortressStartTime)
if (gameState.getTimeElapsed() > (1 + 0.10*numFortresses)*this.fortressLapseTime + this.fortressStartTime)
{
this.fortressStartTime = gameState.getTimeElapsed();
// TODO should affect it to the right base
@ -1287,8 +1351,8 @@ m.HQ.prototype.buildDefenses = function(gameState, queues)
// let's add a siege building plan to the current attack plan if there is none currently.
var numSiegeBuilder = 0;
if (gameState.civ() === "mace")
numSiegeBuilder = gameState.countEntitiesByType(gameState.applyCiv("siege_workshop"), true);
if (gameState.civ() === "mace" || gameState.civ() === "maur")
numSiegeBuilder = gameState.countEntitiesByType(gameState.applyCiv(this.bAdvanced[0]), true);
else
for (var i in this.bFort)
numSiegeBuilder += gameState.countEntitiesByType(gameState.applyCiv(this.bFort[i]), true);
@ -1310,7 +1374,7 @@ m.HQ.prototype.buildDefenses = function(gameState, queues)
return;
var numTowers = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_defense_tower"), true);
if (gameState.getTimeElapsed() > (1 + 0.05*numTowers)*this.towerLapseTime + this.towerStartTime)
if (gameState.getTimeElapsed() > (1 + 0.10*numTowers)*this.towerLapseTime + this.towerStartTime)
{
this.towerStartTime = gameState.getTimeElapsed();
queues.defenseBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_defense_tower"));
@ -1324,6 +1388,9 @@ m.HQ.prototype.buildBlacksmith = function(gameState, queues)
|| queues.militaryBuilding.length() !== 0
|| gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_blacksmith"), true) > 0)
return;
// build a market before the blacksmith
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market"), true) == 0)
return;
if (this.canBuild(gameState, "structures/{civ}_blacksmith"))
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith"));
@ -1335,44 +1402,36 @@ m.HQ.prototype.buildBlacksmith = function(gameState, queues)
// TODO: building placement is bad. Choice of buildings is also fairly dumb.
m.HQ.prototype.constructTrainingBuildings = function(gameState, queues)
{
var workersNumber = gameState.getOwnEntitiesByRole("worker", true).filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "plan"))).length;
var barrackNb = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_barracks"), true);
var bestBase = this.findBestBaseForMilitary(gameState);
if (this.canBuild(gameState, "structures/{civ}_barracks"))
if (this.canBuild(gameState, "structures/{civ}_barracks") && queues.militaryBuilding.length() === 0)
{
// first barracks.
if (workersNumber > this.Config.Military.popForBarracks1 || (this.econState == "townPhasing" && gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length < 5))
if (gameState.getPopulation() > this.Config.Military.popForBarracks1 ||
(this.econState == "townPhasing" && gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length < 5))
{
if (barrackNb + queues.militaryBuilding.length() < 1)
if (barrackNb === 0)
{
var plan = new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase });
plan.onStart = function(gameState) { gameState.ai.queueManager.changePriority("militaryBuilding", 130); };
var priority = this.Config.priorities.militaryBuilding;
gameState.ai.queueManager.changePriority("militaryBuilding", 2*priority);
var plan = new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base": bestBase });
plan.onStart = function(gameState) { gameState.ai.queueManager.changePriority("militaryBuilding", priority); };
queues.militaryBuilding.addItem(plan);
}
}
// second barracks.
if (barrackNb < 2 && workersNumber > this.Config.Military.popForBarracks2)
if (queues.militaryBuilding.length() < 1)
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
// third barracks (optional 4th/5th for some civs as they rely on barracks more.)
if (barrackNb === 2 && barrackNb + queues.militaryBuilding.length() < 3 && workersNumber > 125)
if (queues.militaryBuilding.length() === 0)
{
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber")
{
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
}
}
// second barracks, then 3rd barrack, and optional 4th for some civs as they rely on barracks more.
if (barrackNb === 1 && gameState.getPopulation() > this.Config.Military.popForBarracks2)
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base": bestBase }));
else if (barrackNb === 2 && gameState.getPopulation() > 125)
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
else if (barrackNb === 3 && (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber"))
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "base" : bestBase }));
}
//build advanced military buildings
if (workersNumber >= this.Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){
if (gameState.getPopulation() > this.Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){
if (queues.militaryBuilding.length() === 0)
{
var inConst = 0;
@ -1388,7 +1447,7 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues)
}
}
// build second advanced building except for some civs.
if (gameState.currentPhase() > 2 && gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" && workersNumber > 130)
if (gameState.currentPhase() > 2 && gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" && gameState.getPopulation() > 130)
{
var Const = 0;
for (var i in this.bAdvanced)
@ -1430,18 +1489,13 @@ m.HQ.prototype.findBestBaseForMilitary = function(gameState)
return bestBase;
};
m.HQ.prototype.boostSoldiers = function(gameState, val, emergency)
m.HQ.prototype.boostSoldiers = function(gameState)
{
if (this.boostedSoldiers && this.boostedSoldiers >= val)
if (this.boostedSoldiers)
return;
if (!this.boostedSoldiers)
this.nominalSoldierPriority = this.Config.priorities.citizenSoldier;
this.boostedSoldiers = val;
gameState.ai.queueManager.changePriority("citizenSoldier", val);
if (!emergency)
return;
// Emergency: reset accounts from all other queues
this.boostedSoldiers = true;
gameState.ai.queueManager.changePriority("citizenSoldier", 5*this.Config.priorities.citizenSoldier);
// Reset accounts from all other queues
for (var p in gameState.ai.queueManager.queues)
if (p != "citizenSoldier")
gameState.ai.queueManager.accounts[p].reset();
@ -1451,7 +1505,7 @@ m.HQ.prototype.unboostSoldiers = function(gameState)
{
if (!this.boostedSoldiers)
return;
gameState.ai.queueManager.changePriority("citizenSoldier", this.nominalSoldierPriority);
gameState.ai.queueManager.changePriority("citizenSoldier", this.Config.priorities.citizenSoldier);
this.boostedSoldiers = undefined;
};
@ -1627,9 +1681,10 @@ m.HQ.prototype.update = function(gameState, queues, events)
this.trainMoreWorkers(gameState, queues);
if (gameState.ai.playedTurn % 2 === 0)
if (gameState.ai.playedTurn % 2 === 1)
this.buildMoreHouses(gameState,queues);
else
if (gameState.ai.playedTurn % 4 === 2)
this.buildFarmstead(gameState, queues);
if (this.waterMap)
@ -1657,6 +1712,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
}
}
this.garrisonManager.update(gameState, events);
this.defenseManager.update(gameState, events);
this.constructTrainingBuildings(gameState, queues);
@ -1675,7 +1731,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
if (this.Config.difficulty > 0)
this.attackManager.update(gameState, queues, events);
Engine.ProfileStop(); // Heaquarters update
};

View File

@ -156,7 +156,7 @@ m.createFrontierMap = function(gameState, borderMap)
for (var j = 0; j < territory.length; ++j)
{
if (territory.getOwnerIndex(j) !== PlayerID || borderMap.map[j] === 2)
if (territory.getOwnerIndex(j) !== PlayerID || (borderMap && borderMap.map[j] > 1))
continue;
var ix = j%width;
var iz = Math.floor(j/width);

View File

@ -28,6 +28,8 @@ m.Queue.prototype.addItem = function(plan)
this.queue[i].addItem(plan.number)
return;
}
else if (plan.category === "technology" && this.queue[i].type === plan.type)
return;
}
this.queue.push(plan);
};

View File

@ -20,11 +20,13 @@ var PETRA = function(m)
//
// This system should be improved. It's probably not flexible enough.
m.QueueManager = function(Config, queues, priorities)
m.QueueManager = function(Config, queues)
{
this.Config = Config;
this.queues = queues;
this.priorities = priorities;
this.priorities = {};
for (var i in Config.priorities)
this.priorities[i] = Config.priorities[i];
this.accounts = {};
// the sorting is updated on priority change.
@ -88,7 +90,7 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
// short queue is the first item of a queue, assumed to be ready in 30s
// medium queue is the second item of a queue, assumed to be ready in 60s
// long queue contains the is the isGo=false items, assumed to be ready in 300s
var totalShort = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
var totalShort = { "food": 200, "wood": 200, "stone": 100, "metal": 100 };
var totalMedium = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
var totalLong = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
var total;
@ -120,7 +122,7 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
}
}
// global rates
var rates = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 };
var rates = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
var diff;
for (var type in rates)
{
@ -135,11 +137,7 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
totalMedium[type] -= diff;
current[type] -= diff;
if (current[type] > 0)
{
diff = Math.min(current[type], totalLong[type]);
totalLong[type] -= diff;
current[type] -= diff;
}
totalLong[type] -= Math.min(current[type], totalLong[type]);
}
}
rates[type] = totalShort[type]/30 + totalMedium[type]/60 + totalLong[type]/300;
@ -496,7 +494,7 @@ m.QueueManager.prototype.update = function(gameState)
this.accounts[j][ress] += diff;
this.accounts[i][ress] -= diff;
++otherQueue.switched;
if (this.Config.debug)
if (this.Config.debug > 1)
warn ("switching queue " + ress + " from " + i + " to " + j + " in amount " + diff);
break;
}
@ -605,8 +603,15 @@ m.QueueManager.prototype.removeQueue = function(queueName)
}
};
m.QueueManager.prototype.getPriority = function(queueName)
{
return this.priorities[queueName];
};
m.QueueManager.prototype.changePriority = function(queueName, newPriority)
{
if (this.Config.debug > 0)
warn(">>> Priority of queue " + queueName + " changed from " + this.priorities[queueName] + " to " + newPriority);
var self = this;
if (this.queues[queueName] !== undefined)
this.priorities[queueName] = newPriority;

View File

@ -185,8 +185,8 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
else
friendlyTiles.addInfluence(x, z, 15, -40); // and further away from other stuffs
}
else if (template.hasClass("Farmstead") && !ent.hasClass("Field"))
friendlyTiles.addInfluence(x, z, 25, -25); // move farmsteads away to make room.
else if (template.hasClass("Farmstead") && (!ent.hasClass("Field") && (!ent.hasClass("StoneWall") || ent.hasClass("Gates"))))
friendlyTiles.addInfluence(x, z, 25, -25); // move farmsteads away to make room (StoneWall test needed for iber)
else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House")
friendlyTiles.addInfluence(x, z, 30, -50);
else if (template.hasClass("Military"))
@ -206,18 +206,28 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
}
// requires to be inside our territory, and inside our base territory if required
// and if our first market, put it on border if possible to maximize distance with next market
var border = template.hasClass("BarterMarket");
if (this.metadata && this.metadata.base !== undefined)
{
var base = this.metadata.base;
for (var j = 0; j < friendlyTiles.map.length; ++j)
{
if (gameState.ai.HQ.basesMap.map[j] !== base)
friendlyTiles.map[j] = 0;
else if (border && gameState.ai.HQ.borderMap.map[j] > 0)
friendlyTiles.map[j] += 50;
}
}
else
{
for (var j = 0; j < friendlyTiles.map.length; ++j)
{
if (gameState.ai.HQ.basesMap.map[j] === 0)
friendlyTiles.map[j] = 0;
else if (border && gameState.ai.HQ.borderMap.map[j] > 0)
friendlyTiles.map[j] += 50;
}
}
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, this
@ -226,7 +236,8 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
// also not for fields who can be stacked quite a bit
var radius = 0;
if (template.hasClass("Fortress") || this.type == "structures/{civ}_siege_workshop")
if (template.hasClass("Fortress") || this.type === "structures/{civ}_siege_workshop"
|| this.type === "structures/{civ}_elephant_stables")
radius = Math.floor(template.obstructionRadius() / cellSize) + 3;
else if (template.buildCategory() === "Dock")
radius = 1;

View File

@ -50,6 +50,15 @@ m.TradeManager.prototype.update = function(gameState, queues)
{
if (!this.tradeRoute)
return;
var source = this.tradeRoute.source;
var target = this.tradeRoute.target;
if (!source || !target || !gameState.getEntityById(source.id()) || !gameState.getEntityById(target.id()))
{
if (this.Config.debug > 1)
warn("We have lost our trade route");
this.tradeRoute = undefined;
return;
}
var self = this;
if (gameState.ai.playedTurn % 100 === 9)
this.setTradingGoods(gameState);
@ -73,7 +82,7 @@ m.TradeManager.prototype.setTradingGoods = function(gameState)
{
var tradingGoods = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
// first, try to anticipate future needs
var stocks = gameState.ai.HQ.GetTotalResourceLevel(gameState);
var stocks = gameState.ai.HQ.getTotalResourceLevel(gameState);
var remaining = 100;
this.targetNumTraders = this.Config.Economy.targetNumTraders;
for (var type in stocks)
@ -97,9 +106,13 @@ m.TradeManager.prototype.setTradingGoods = function(gameState)
var mainNeed = Math.floor(remaining * 70 / 100)
var nextNeed = remaining - mainNeed;
var wantedRates = gameState.ai.queueManager.wantedGatherRates(gameState);
var mostNeeded = gameState.ai.HQ.pickMostNeededResources(gameState);
tradingGoods[mostNeeded[0]] += mainNeed;
tradingGoods[mostNeeded[1]] += nextNeed;
if (wantedRates[mostNeeded[1]] > 0)
tradingGoods[mostNeeded[1]] += nextNeed;
else
tradingGoods[mostNeeded[0]] += nextNeed;
Engine.PostCommand(PlayerID, {"type": "set-trading-goods", "tradingGoods": tradingGoods});
if (this.Config.debug == 2)
warn(" trading goods set to " + uneval(tradingGoods));

View File

@ -8,21 +8,17 @@ var PETRA = function(m)
m.Worker = function(ent) {
this.ent = ent;
this.baseID = 0;
this.lastUpdate = undefined;
this.lastIdle = undefined;
this.consecutiveIdle = 0;
};
m.Worker.prototype.update = function(baseManager, gameState) {
this.lastUpdate = gameState.ai.playedTurn;
this.baseID = baseManager.ID;
var subrole = this.ent.getMetadata(PlayerID, "subrole");
if (!this.ent.position() || (this.ent.getMetadata(PlayerID,"fleeing") && gameState.getTimeElapsed() - this.ent.getMetadata(PlayerID,"fleeing") < 8000)){
// If the worker has no position then no work can be done
if (!this.ent.position())
return;
}
if (this.ent.getMetadata(PlayerID,"fleeing"))
this.ent.setMetadata(PlayerID,"fleeing", undefined);
// Okay so we have a few tasks.
// If we're gathering, we'll check that we haven't run idle.
// ANd we'll also check that we're gathering a resource we want to gather.
@ -245,11 +241,11 @@ m.Worker.prototype.startGathering = function(gameState, baseManager)
}
}
if (gameState.ai.HQ.Config.debug > 0)
{
// If we are here, we have nothing left to gather ... certainly no more resources of this type
gameState.ai.HQ.lastFailedGather[resource] = gameState.ai.playedTurn;
if (gameState.ai.HQ.Config.debug > 1)
warn(" >>>>> worker with gather-type " + resource + " with nothing to gather ");
}
this.ent.setMetadata(PlayerID, "subrole", "idle");
return false;
};
@ -348,8 +344,9 @@ m.Worker.prototype.startHunting = function(gameState, baseManager)
if (supply.footprintRadius() < 1)
{
var fakeMap = new API3.Map(gameState.sharedScript, gameState.getMap().data);
var id = fakeMap.gamePosToMapPos(supply.position())[0] + fakeMap.width*fakeMap.gamePosToMapPos(supply.position())[1];
if ((gameState.sharedScript.passabilityClasses["pathfinderObstruction"] & gameState.getMap().data[id]))
var mapPos = fakeMap.gamePosToMapPos(supply.position());
var id = mapPos[0] + fakeMap.width*mapPos[1];
if (gameState.sharedScript.passabilityClasses["pathfinderObstruction"] & gameState.getMap().data[id])
{
supply.setMetadata(PlayerID, "inaccessible", true)
return;