1
0
forked from 0ad/0ad

petra: first part of adaptation to cost multipliers, ref #4003

This was SVN commit r18333.
This commit is contained in:
mimo 2016-06-05 14:39:37 +00:00
parent 98f1d02d88
commit e260fab9a0
7 changed files with 111 additions and 37 deletions

View File

@ -103,26 +103,36 @@ m.Template = m.Class({
return this.get("Identity/Civ");
},
cost: function() {
"cost": function(productionQueue) {
if (!this.get("Cost"))
return undefined;
let ret = {};
let typeCost;
for (let type in this.get("Cost/Resources"))
ret[type] = +this.get("Cost/Resources/" + type);
{
typeCost = +this.get("Cost/Resources/" + type);
if (productionQueue)
typeCost *= productionQueue.techCostMultiplier(type);
ret[type] = typeCost;
}
return ret;
},
costSum: function() {
if (!this.get("Cost"))
"costSum": function(productionQueue) {
let cost = this.cost(productionQueue);
if (!cost)
return undefined;
let ret = 0;
for (let type in this.get("Cost/Resources"))
ret += +this.get("Cost/Resources/" + type);
for (let type in cost)
ret += cost[type];
return ret;
},
"techCostMultiplier": function(type) {
return +(this.get("ProductionQueue/TechCostMultiplier/"+type) || 1);
},
/**
* Returns the radius of a circle surrounding this entity's
* obstruction shape, or undefined if no obstruction.
@ -462,8 +472,11 @@ m.Template = m.Class({
return this.get("BuildRestrictions/Category");
},
buildTime: function() {
return +this.get("Cost/BuildTime");
"buildTime": function(productionQueue) {
let time = +this.get("Cost/BuildTime");
if (productionQueue)
time *= productionQueue.techCostMultiplier("time");
return time;
},
buildDistance: function() {

View File

@ -693,14 +693,29 @@ m.GameState.prototype.findAvailableTech = function()
};
/**
* Find buildings that are capable of training that template.
* Return true if we have a building able to train that template
*/
m.GameState.prototype.hasTrainer = function(template)
{
let civ = this.playerData.civ;
for (let ent of this.getOwnTrainingFacilities().values())
{
let trainable = ent.trainableEntities(civ);
if (trainable && trainable.indexOf(template) !== -1)
return true;
}
return false;
};
/**
* Find buildings able to train that template.
*/
m.GameState.prototype.findTrainers = function(template)
{
let civ = this.playerData.civ;
return this.getOwnTrainingFacilities().filter(function(ent) {
let trainable = ent.trainableEntities(civ);
return trainable && trainable.indexOf(template) != -1;
return trainable && trainable.indexOf(template) !== -1;
});
};
@ -718,7 +733,7 @@ m.GameState.prototype.findBuilder = function(template)
return undefined;
};
/** Return true if one of the buildings is capable of researching the given tech */
/** Return true if one of our buildings is capable of researching the given tech */
m.GameState.prototype.hasResearchers = function(templateName, noRequirementCheck)
{
// let's check we can research the tech.
@ -752,7 +767,7 @@ m.GameState.prototype.findResearchers = function(templateName, noRequirementChec
{
// let's check we can research the tech.
if (!this.canResearch(templateName, noRequirementCheck))
return [];
return undefined;
let self = this;
let civ = this.playerData.civ;

View File

@ -65,11 +65,29 @@ m.Technology.prototype.pairedWith = function()
return this._pairedWith;
};
m.Technology.prototype.cost = function()
m.Technology.prototype.cost = function(productionQueue)
{
if (!this._template.cost)
return undefined;
return this._template.cost;
let cost = {};
for (let type in this._template.cost)
{
cost[type] = +this._template.cost[type];
if (productionQueue)
cost[type] *= productionQueue.techCostMultiplier(type);
}
return cost;
};
m.Technology.prototype.costSum = function(productionQueue)
{
let cost = this.cost(productionQueue);
if (!cost)
return undefined;
let ret = 0;
for (let type in cost)
ret += cost[type];
return ret;
};
// seconds

View File

@ -355,10 +355,14 @@ m.QueueManager.prototype.startNextItems = function(gameState)
let item = queue.getNext();
if (this.accounts[name].canAfford(item.getCost()) && item.canStart(gameState))
{
this.finishingTime = gameState.ai.elapsedTime;
this.accounts[name].subtract(item.getCost());
queue.startNext(gameState);
queue.switched = 0;
// canStart may update the cost because of the costMultiplier so we must check it again
if (this.accounts[name].canAfford(item.getCost()))
{
this.finishingTime = gameState.ai.elapsedTime;
this.accounts[name].subtract(item.getCost());
queue.startNext(gameState);
queue.switched = 0;
}
}
}
else if (!queue.hasQueuedUnits())

View File

@ -12,13 +12,12 @@ m.QueuePlan = function(gameState, type, metadata)
this.template = gameState.getTemplate(this.type);
if (!this.template)
{
API3.warn("Tried to add the inexisting template " + this.type + " to Petra. Please report this on the forums");
API3.warn("Tried to add the inexisting template " + this.type + " to Petra.");
return false;
}
this.ID = gameState.ai.uniqueIDs.plans++;
this.cost = new API3.Resources(this.template.cost());
this.number = 1;
this.category = "";
return true;

View File

@ -9,6 +9,11 @@ m.ResearchPlan = function(gameState, type, rush = false)
if (this.template.researchTime === undefined)
return false;
// Refine the estimated cost
let researchers = this.getBestResearchers(gameState, true);
if (researchers)
this.cost = new API3.Resources(this.template.cost(researchers[0]));
this.category = "technology";
this.rush = rush;
@ -20,8 +25,34 @@ m.ResearchPlan.prototype = Object.create(m.QueuePlan.prototype);
m.ResearchPlan.prototype.canStart = function(gameState)
{
// also checks canResearch
return gameState.hasResearchers(this.type);
this.researchers = this.getBestResearchers(gameState);
if (!this.researchers)
return false;
this.cost = new API3.Resources(this.template.cost(this.researchers[0]));
return true;
};
m.ResearchPlan.prototype.getBestResearchers = function(gameState, noRequirementCheck = false)
{
let allResearchers = gameState.findResearchers(this.type, noRequirementCheck);
if (!allResearchers || !allResearchers.hasEntities())
return undefined;
// Keep only researchers with smallest cost
let costMin = Math.min();
let researchers;
for (let ent of allResearchers.values())
{
let cost = this.template.costSum(ent);
if (cost === costMin)
researchers.push(ent);
else if (cost < costMin)
{
costMin = cost;
researchers = [ent];
}
}
return researchers;
};
m.ResearchPlan.prototype.isInvalid = function(gameState)
@ -31,19 +62,13 @@ m.ResearchPlan.prototype.isInvalid = function(gameState)
m.ResearchPlan.prototype.start = function(gameState)
{
let trainers = gameState.findResearchers(this.type).toEntityArray();
// Prefer training buildings with short queues
// (TODO: this should also account for units added to the queue by
// plans that have already been executed this turn)
if (trainers.length)
{
trainers.sort((a, b) => a.trainingQueueTime() - b.trainingQueueTime());
// drop anything in the queue if we rush it.
if (this.rush)
trainers[0].stopAllProduction(0.45);
trainers[0].research(this.type);
}
// Prefer researcher with shortest queues (no need to serialize this.researchers
// as the functions canStart and start are always called on the same turn)
this.researchers.sort((a, b) => a.trainingQueueTime() - b.trainingQueueTime());
// Drop anything in the queue if we rush it.
if (this.rush)
this.researchers[0].stopAllProduction(0.45);
this.researchers[0].research(this.type);
this.onStart(gameState);
};

View File

@ -110,7 +110,7 @@ m.TrainingPlan.prototype.addItem = function(amount = 1)
this.number += amount;
};
// Find the promoted types corresponding to this.type
/** Find the promoted types corresponding to this.type */
m.TrainingPlan.prototype.promotedTypes = function(gameState)
{
let types = [];