petra: first part of adaptation to cost multipliers, ref #4003
This was SVN commit r18333.
This commit is contained in:
parent
98f1d02d88
commit
e260fab9a0
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -354,6 +354,9 @@ m.QueueManager.prototype.startNextItems = function(gameState)
|
||||
{
|
||||
let item = queue.getNext();
|
||||
if (this.accounts[name].canAfford(item.getCost()) && item.canStart(gameState))
|
||||
{
|
||||
// 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());
|
||||
@ -361,6 +364,7 @@ m.QueueManager.prototype.startNextItems = function(gameState)
|
||||
queue.switched = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!queue.hasQueuedUnits())
|
||||
{
|
||||
this.accounts[name].reset();
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
// 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)
|
||||
trainers[0].stopAllProduction(0.45);
|
||||
trainers[0].research(this.type);
|
||||
}
|
||||
this.researchers[0].stopAllProduction(0.45);
|
||||
this.researchers[0].research(this.type);
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
|
@ -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 = [];
|
||||
|
Loading…
Reference in New Issue
Block a user