numerous fixes and improvments to petra ai
This was SVN commit r14896.
This commit is contained in:
parent
ac9aaaa369
commit
d04f035f9a
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
158
binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
Normal file
158
binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
Normal 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);
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user