var PETRA = function(m) { m.TrainingPlan = function(gameState, type, metadata, number, maxMerge) { if (!m.QueuePlan.call(this, gameState, type, metadata)) { warn(" Plan training " + type + " canceled"); return false; } this.category = "unit"; this.cost = new API3.Resources(this.template.cost(), this.template._template.Cost.Population); this.number = number !== undefined ? number : 1; this.maxMerge = maxMerge !== undefined ? maxMerge : 5; return true; }; m.TrainingPlan.prototype = Object.create(m.QueuePlan.prototype); m.TrainingPlan.prototype.canStart = function(gameState) { if (this.invalidTemplate) return false; // TODO: we should probably check pop caps if (this.metadata && this.metadata.sea) var trainers = gameState.findTrainers(this.type).filter(API3.Filters.byMetadata(PlayerID, "sea", this.metadata.sea)); else var trainers = gameState.findTrainers(this.type); return (trainers.length != 0); }; m.TrainingPlan.prototype.start = function(gameState) { if (this.metadata && this.metadata.trainer) { var metadata = {}; for (var key in this.metadata) if (key !== "trainer") metadata[key] = this.metadata[key]; var trainer = gameState.getEntityById(this.metadata.trainer); if (trainer) trainer.train(this.type, this.number, metadata, this.promotedTypes(gameState)); this.onStart(gameState); return; } if (this.metadata && this.metadata.sea) var trainers = gameState.findTrainers(this.type).filter(API3.Filters.byMetadata(PlayerID, "sea", this.metadata.sea)).toEntityArray(); else if (this.metadata && this.metadata.base) var trainers = gameState.findTrainers(this.type).filter(API3.Filters.byMetadata(PlayerID, "base", this.metadata.base)).toEntityArray(); else var trainers = gameState.findTrainers(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 > 0) { var wantedIndex = undefined; if (this.metadata && this.metadata.index) wantedIndex = this.metadata.index; var workerUnit = (this.metadata && this.metadata.role && this.metadata.role == "worker"); var supportUnit = this.template.hasClass("Support"); trainers.sort(function(a, b) { let aa = a.trainingQueueTime(); let bb = b.trainingQueueTime(); if (a.hasClass("Civic") && !supportUnit) aa += 10; if (b.hasClass("Civic") && !supportUnit) bb += 10; if (supportUnit) { if (gameState.ai.HQ.isDangerousLocation(a.position())) aa += 50; if (gameState.ai.HQ.isDangerousLocation(b.position())) bb += 50; } let aBase = a.getMetadata(PlayerID, "base"); let bBase = b.getMetadata(PlayerID, "base"); if (wantedIndex) { if (!aBase || gameState.ai.HQ.baseManagers[aBase].accessIndex != wantedIndex) aa += 30; if (!bBase || gameState.ai.HQ.baseManagers[bBase].accessIndex != wantedIndex) bb += 30; } // then, if worker, small preference for bases with less workers if (workerUnit && aBase && bBase && aBase != bBase) { let apop = gameState.ai.HQ.baseManagers[aBase].workers.length; let bpop = gameState.ai.HQ.baseManagers[bBase].workers.length; if (apop > bpop) aa++; else if (bpop > apop) bb++; } return (aa - bb); }); if (this.metadata && this.metadata.base !== undefined && this.metadata.base == 0) this.metadata.base = trainers[0].getMetadata(PlayerID, "base"); trainers[0].train(this.type, this.number, this.metadata, this.promotedTypes(gameState)); } else if (gameState.ai.Config.debug > 1) warn(" no trainers for this queue " + this.type); this.onStart(gameState); }; m.TrainingPlan.prototype.addItem = function(amount) { if (amount === undefined) amount = 1; this.number += amount; }; // Find the promoted types corresponding to this.type m.TrainingPlan.prototype.promotedTypes = function(gameState) { var types = []; var promotion = this.template.promotion(); var previous; while (promotion) { types.push(promotion); previous = promotion; promotion = gameState.getTemplate(promotion).promotion(); if (previous === promotion) { if (gameState.ai.Config.debug > 0) API3.warn(" unit " + promotion + " is its own promoted unit"); promotion = undefined; } } return types; }; m.TrainingPlan.prototype.Serialize = function() { return { "type": this.type, "metadata": this.metadata, "ID": this.ID, "category": this.category, "cost": this.cost.Serialize(), "number": this.number, "maxMerge": this.maxMerge, "lastIsGo": this.lastIsGo }; }; m.TrainingPlan.prototype.Deserialize = function(gameState, data) { for (let key in data) this[key] = data[key]; let cost = new API3.Resources(); cost.Deserialize(data.cost); this.cost = cost; }; return m; }(PETRA);