1
0
forked from 0ad/0ad
0ad/binaries/data/mods/public/simulation/ai/jubot/economy.js
Jubal df9270a892 Hopefullyfixeslag.
This was SVN commit r9765.
2011-07-06 18:04:30 +00:00

612 lines
15 KiB
JavaScript

var EconomyManager = Class({
_init: function()
{
this.baseNumWorkers = 30; // minimum number of workers we want
this.targetNumWorkers = 55; // minimum number of workers we want
this.targetNumBuilders = 6; // number of workers we want working on construction
this.changetimeRegBui = 180*1000;
// (This is a stupid design where we just construct certain numbers
// of certain buildings in sequence)
// Greek building list
// Relative proportions of workers to assign to each resource type
this.gatherWeights = {
"food": 140,
"wood": 140,
"stone": 50,
"metal": 120,
};
},
checkBuildingList: function (gameState) {
if (gameState.displayCiv() == "hele"){
this.targetBuildings = [
{
"template": "structures/{civ}_civil_centre",
"priority": 500,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 110,
"count": 2,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 105,
"count": 1,
},
{
"template": "structures/{civ}_field",
"priority": 103,
"count": 1,
},
{
"template": "structures/{civ}_barracks",
"priority": 101,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 100,
"count": 5,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 90,
"count": 3,
},
{
"template": "structures/hele_gymnasion",
"priority": 80,
"count": 1,
},
{
"template": "structures/{civ}_field",
"priority": 70,
"count": 2,
},
{
"template": "structures/hele_fortress",
"priority": 60,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 55,
"count": 10,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 50,
"count": 5,
},
{
"template": "structures/{civ}_house",
"priority": 45,
"count": 15,
},
{
"template": "structures/{civ}_field",
"priority": 40,
"count": 3,
},
{
"template": "structures/{civ}_house",
"priority": 30,
"count": 20,
},
];
}
// Celt building list
else if (gameState.displayCiv() == "celt"){
this.targetBuildings = [
{
"template": "structures/{civ}_civil_centre",
"priority": 500,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 110,
"count": 6,
},
{
"template": "structures/{civ}_field",
"priority": 100,
"count": 2,
},
{
"template": "structures/{civ}_barracks",
"priority": 90,
"count": 1,
},
{
"template": "structures/celt_fortress_b",
"priority": 80,
"count": 1,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 60,
"count": 3,
},
{
"template": "structures/{civ}_house",
"priority": 55,
"count": 15,
},
{
"template": "structures/{civ}_field",
"priority": 40,
"count": 3,
},
{
"template": "structures/{civ}_house",
"priority": 30,
"count": 25,
},
];
}
// Celt building list
else if (gameState.displayCiv() == "iber"){
this.targetBuildings = [
{
"template": "structures/{civ}_civil_centre",
"priority": 500,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 110,
"count": 6,
},
{
"template": "structures/{civ}_field",
"priority": 100,
"count": 2,
},
{
"template": "structures/{civ}_barracks",
"priority": 100,
"count": 1,
},
{
"template": "structures/iber_fortress",
"priority": 80,
"count": 1,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 70,
"count": 3,
},
{
"template": "structures/{civ}_house",
"priority": 55,
"count": 15,
},
{
"template": "structures/{civ}_field",
"priority": 40,
"count": 3,
},
{
"template": "structures/{civ}_house",
"priority": 30,
"count": 25,
},
];
}
// Fallback option just in case
else {
this.targetBuildings = [
{
"template": "structures/{civ}_civil_centre",
"priority": 500,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 110,
"count": 2,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 105,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 100,
"count": 5,
},
{
"template": "structures/{civ}_field",
"priority": 100,
"count": 2,
},
{
"template": "structures/{civ}_barracks",
"priority": 99,
"count": 1,
},
{
"template": "structures/{civ}_house",
"priority": 98,
"count": 7,
},
{
"template": "structures/{civ}_scout_tower",
"priority": 60,
"count": 4,
},
{
"template": "structures/{civ}_house",
"priority": 55,
"count": 15,
},
{
"template": "structures/{civ}_field",
"priority": 40,
"count": 5,
},
{
"template": "structures/{civ}_house",
"priority": 30,
"count": 20,
},
];
}
},
buildMoreBuildings: function(gameState, planGroups)
{
// Limit ourselves to constructing two buildings at a time
if (gameState.findFoundations().length > 1)
return;
for each (var building in this.targetBuildings)
{
var numBuildings = gameState.countEntitiesAndQueuedWithType(gameState.applyCiv(building.template));
// If we have too few, build another
if (numBuildings < building.count)
{
planGroups.economyConstruction.addPlan(building.priority,
new BuildingConstructionPlan(gameState, building.template, 1)
);
}
}
},
trainMoreWorkers: function(gameState, planGroups)
{
// Count the workers in the world and in progress
var numWorkers = gameState.countEntitiesAndQueuedWithRole("worker");
// If we have too few, train another
// print("Want "+this.targetNumWorkers+" workers; got "+numWorkers+"\n");
if (numWorkers < this.baseNumWorkers)
{
var menorwomen = Math.random()*2;
if (gameState.displayCiv() == "hele" || gameState.displayCiv() == "celt"){
if (menorwomen < 1.5){
planGroups.economyPersonnel.addPlan(120,
new UnitTrainingPlan(gameState,
"units/{civ}_support_female_citizen", 2, { "role": "worker" })
);
}
else if (menorwomen > 1.82) {
planGroups.economyPersonnel.addPlan(120,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_spearman_b", 2, { "role": "worker" })
);
}
else {
planGroups.economyPersonnel.addPlan(120,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_javelinist_b", 2, { "role": "worker" })
);
}
}
else {
if (menorwomen < 1.5){
planGroups.economyPersonnel.addPlan(120,
new UnitTrainingPlan(gameState,
"units/{civ}_support_female_citizen", 2, { "role": "worker" })
);
}
else {
planGroups.economyPersonnel.addPlan(120,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_swordsman_b", 2, { "role": "worker" })
);
}
}
}
else if (numWorkers < this.targetNumWorkers)
{
var menorwomen = Math.random()*2;
if (gameState.displayCiv() == "hele" || gameState.displayCiv() == "celt"){
if (menorwomen < 1.5){
planGroups.economyPersonnel.addPlan(90,
new UnitTrainingPlan(gameState,
"units/{civ}_support_female_citizen", 2, { "role": "worker" })
);
}
else if (menorwomen > 1.82) {
planGroups.economyPersonnel.addPlan(90,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_spearman_b", 2, { "role": "worker" })
);
}
else {
planGroups.economyPersonnel.addPlan(90,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_javelinist_b", 2, { "role": "worker" })
);
}
}
else {
if (menorwomen < 1.5){
planGroups.economyPersonnel.addPlan(90,
new UnitTrainingPlan(gameState,
"units/{civ}_support_female_citizen", 2, { "role": "worker" })
);
}
else {
planGroups.economyPersonnel.addPlan(90,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_swordsman_b", 2, { "role": "worker" })
);
}
}
}
},
pickMostNeededResources: function(gameState)
{
var self = this;
// Find what resource type we're most in need of
var numGatherers = {};
for (var type in this.gatherWeights)
numGatherers[type] = 0;
gameState.getOwnEntitiesWithRole("worker").forEach(function(ent) {
if (ent.getMetadata("subrole") === "gatherer")
numGatherers[ent.getMetadata("gather-type")] += 1;
});
var types = Object.keys(this.gatherWeights);
types.sort(function(a, b) {
// Prefer fewer gatherers (divided by weight)
var va = numGatherers[a] / self.gatherWeights[a];
var vb = numGatherers[b] / self.gatherWeights[b];
return va - vb;
});
return types;
},
reassignRolelessUnits: function(gameState)
{
var roleless = gameState.getOwnEntitiesWithRole(undefined);
roleless.forEach(function(ent) {
if (ent.hasClass("Worker"))
ent.setMetadata("role", "worker");
else
ent.setMetadata("role", "randomcannonfodder");
});
},
reassignIdleWorkers: function(gameState, planGroups)
{
var self = this;
// Search for idle workers, and tell them to gather resources
// Maybe just pick a random nearby resource type at first;
// later we could add some timer that redistributes workers based on
// resource demand.
var idleWorkers = gameState.getOwnEntitiesWithRole("worker").filter(function(ent) {
return ent.isIdle();
});
if (idleWorkers.length)
{
var resourceSupplies = gameState.findResourceSupplies();
idleWorkers.forEach(function(ent) {
var types = self.pickMostNeededResources(gameState);
for each (var type in types)
{
// Make sure there's actually some of that type
if (!resourceSupplies[type])
continue;
// Pick the closest one.
// TODO: we should care about distance to dropsites, not (just) to the worker,
// and gather rates of workers
var workerPosition = ent.position();
var supplies = [];
resourceSupplies[type].forEach(function(supply) {
// Skip targets that are too hard to hunt
if (supply.entity.isUnhuntable())
return;
var distcheck = 1000000;
gameState.getOwnEntities().forEach(function(centre) {
if (centre.hasClass("CivCentre"))
{
var centrePosition = centre.position();
var currentdistcheck = VectorDistance(supply.position, centrePosition);
if (currentdistcheck < distcheck){
distcheck = currentdistcheck;
}
// Skip targets that are far too far away (e.g. in the enemy base)
}
});
if (distcheck > 500)
return;
var dist = VectorDistance(supply.position, workerPosition);
// Skip targets that are far too far away (e.g. in the enemy base)
if (dist > 500)
return;
supplies.push({ dist: dist, entity: supply.entity });
});
supplies.sort(function (a, b) {
// Prefer smaller distances
if (a.dist != b.dist)
return a.dist - b.dist;
return false;
});
// Start gathering
if (supplies.length)
{
// THIS SHOULD BE A GLOBAL VARIABLE
var currentposformill = supplies[0].entity.position();
var distcheckold = 10000;
// CHECK DISTANCE
gameState.getOwnEntities().forEach(function(centre) {
if (centre.hasClass("CivCentre") || centre.hasClass("Economic"))
{
var centrePosition = centre.position();
var distcheck = VectorDistance(currentposformill, centrePosition);
if (distcheck < distcheckold){
distcheckold = distcheck;
}
}
});
var foundationsyes = false;
if (gameState.findFoundations().length > 2){
foundationsyes = false;
}
else{
foundationsyes = true;
}
if (distcheckold > 60 && foundationsyes == true){
//JuBotAI.prototype.chat("Building Mill");
planGroups.economyConstruction.addPlan(80,
new BuildingConstructionPlanEcon(gameState, "structures/{civ}_mill", 1, currentposformill)
);
//JuBotAI.prototype.chat("Gathering");
ent.gather(supplies[0].entity);
ent.setMetadata("subrole", "gatherer");
ent.setMetadata("gather-type", type);
return;
}
else {
//JuBotAI.prototype.chat("Gathering");
ent.gather(supplies[0].entity);
ent.setMetadata("subrole", "gatherer");
ent.setMetadata("gather-type", type);
return;
}
}
}
// Couldn't find any types to gather
ent.setMetadata("subrole", "idle");
});
}
},
assignToFoundations: function(gameState, planGroups)
{
// If we have some foundations, and we don't have enough builder-workers,
// try reassigning some other workers who are nearby
var foundations = gameState.findFoundations();
// Check if nothing to build
if (!foundations.length)
return;
var workers = gameState.getOwnEntitiesWithRole("worker");
var builderWorkers = workers.filter(function(ent) {
return (ent.getMetadata("subrole") === "builder");
});
// Check if enough builders
var extraNeeded = this.targetNumBuilders - builderWorkers.length;
if (extraNeeded <= 0)
return;
// Pick non-builders who are closest to the first foundation,
// and tell them to start building it
var target = foundations.toEntityArray()[0];
var nonBuilderWorkers = workers.filter(function(ent) {
return (ent.getMetadata("subrole") !== "builder");
});
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), extraNeeded);
// Order each builder individually, not as a formation
nearestNonBuilders.forEach(function(ent) {
ent.repair(target);
ent.setMetadata("subrole", "builder");
});
},
// This function recalls builders to the CC every 2 minutes; theoretically, this prevents the issue where they build a farm and people try to cross it and there's a traffic jam which wrecks the AI economy.
buildRegroup: function(gameState, planGroups)
{
if (gameState.getTimeElapsed() > this.changetimeRegBui){
var buildregroupers = gameState.getOwnEntitiesWithRole("worker");
buildregroupers.forEach(function(shirk) {
if (shirk.getMetadata("subrole") == "builder"){
var targets = gameState.entities.filter(function(ent) {
return (!ent.isEnemy() && ent.hasClass("CivCentre"));
});
if (targets.length){
var target = targets.toEntityArray()[0];
var targetPos = target.position();
shirk.move(targetPos[0], targetPos[1]);
}
}
});
// Wait 4 mins to do this again.
this.changetimeRegBui = this.changetimeRegBui + (120*1000);
}
},
update: function(gameState, planGroups)
{
Engine.ProfileStart("economy update");
this.buildRegroup(gameState, planGroups)
this.checkBuildingList(gameState);
this.reassignRolelessUnits(gameState);
this.buildMoreBuildings(gameState, planGroups);
this.trainMoreWorkers(gameState, planGroups);
this.reassignIdleWorkers(gameState, planGroups);
this.assignToFoundations(gameState, planGroups);
Engine.ProfileStop();
},
});