Line data Source code
1 : /** 2 : * One task of this manager is to cache the list of structures we have builders for, 3 : * to avoid having to loop on all entities each time. 4 : * It also takes care of the structures we can't currently build and should not try to build endlessly. 5 : */ 6 : 7 0 : PETRA.BuildManager = function() 8 : { 9 : // List of buildings we have builders for, with number of possible builders. 10 0 : this.builderCounters = new Map(); 11 : // List of buildings we can't currently build (because no room, no builder or whatever), 12 : // with time we should wait before trying again to build it. 13 0 : this.unbuildables = new Map(); 14 : }; 15 : 16 : /** Initialization at start of game */ 17 0 : PETRA.BuildManager.prototype.init = function(gameState) 18 : { 19 0 : let civ = gameState.getPlayerCiv(); 20 0 : for (let ent of gameState.getOwnUnits().values()) 21 0 : this.incrementBuilderCounters(civ, ent, 1); 22 : }; 23 : 24 0 : PETRA.BuildManager.prototype.incrementBuilderCounters = function(civ, ent, increment) 25 : { 26 0 : for (let buildable of ent.buildableEntities(civ)) 27 : { 28 0 : if (this.builderCounters.has(buildable)) 29 : { 30 0 : let count = this.builderCounters.get(buildable) + increment; 31 0 : if (count < 0) 32 : { 33 0 : API3.warn(" Petra error in incrementBuilderCounters for " + buildable + " with count < 0"); 34 0 : continue; 35 : } 36 0 : this.builderCounters.set(buildable, count); 37 : } 38 0 : else if (increment > 0) 39 0 : this.builderCounters.set(buildable, increment); 40 : else 41 0 : API3.warn(" Petra error in incrementBuilderCounters for " + buildable + " not yet set"); 42 : } 43 : }; 44 : 45 : /** Update the builders counters */ 46 0 : PETRA.BuildManager.prototype.checkEvents = function(gameState, events) 47 : { 48 0 : this.elapsedTime = gameState.ai.elapsedTime; 49 0 : let civ = gameState.getPlayerCiv(); 50 : 51 0 : for (let evt of events.Create) 52 : { 53 0 : if (events.Destroy.some(e => e.entity == evt.entity)) 54 0 : continue; 55 0 : let ent = gameState.getEntityById(evt.entity); 56 0 : if (ent && ent.isOwn(PlayerID) && ent.hasClass("Unit")) 57 0 : this.incrementBuilderCounters(civ, ent, 1); 58 : } 59 : 60 0 : for (let evt of events.Destroy) 61 : { 62 0 : if (events.Create.some(e => e.entity == evt.entity) || !evt.entityObj) 63 0 : continue; 64 0 : let ent = evt.entityObj; 65 0 : if (ent && ent.isOwn(PlayerID) && ent.hasClass("Unit")) 66 0 : this.incrementBuilderCounters(civ, ent, -1); 67 : } 68 : 69 0 : for (let evt of events.OwnershipChanged) // capture events 70 : { 71 : let increment; 72 0 : if (evt.from == PlayerID) 73 0 : increment = -1; 74 0 : else if (evt.to == PlayerID) 75 0 : increment = 1; 76 : else 77 0 : continue; 78 0 : let ent = gameState.getEntityById(evt.entity); 79 0 : if (ent && ent.hasClass("Unit")) 80 0 : this.incrementBuilderCounters(civ, ent, increment); 81 : } 82 : 83 0 : for (let evt of events.ValueModification) 84 : { 85 0 : if (evt.component != "Builder" || 86 0 : !evt.valueNames.some(val => val.startsWith("Builder/Entities/"))) 87 0 : continue; 88 : 89 : // Unfortunately there really is not an easy way to determine the changes 90 : // at this stage, so we simply have to dump the cache. 91 0 : this.builderCounters = new Map(); 92 : 93 0 : let civ = gameState.getPlayerCiv(); 94 0 : for (let ent of gameState.getOwnUnits().values()) 95 0 : this.incrementBuilderCounters(civ, ent, 1); 96 : } 97 : }; 98 : 99 : 100 : /** 101 : * Get the buildable structures passing a filter. 102 : */ 103 0 : PETRA.BuildManager.prototype.findStructuresByFilter = function(gameState, filter) 104 : { 105 0 : const result = []; 106 0 : for (let [templateName, count] of this.builderCounters) 107 : { 108 0 : if (!count || gameState.isTemplateDisabled(templateName)) 109 0 : continue; 110 0 : let template = gameState.getTemplate(templateName); 111 0 : if (!template || !template.available(gameState)) 112 0 : continue; 113 0 : if (filter.func(template)) 114 0 : result.push(templateName); 115 : } 116 0 : return result; 117 : }; 118 : 119 : /** 120 : * Get the first buildable structure with a given class 121 : * TODO when several available, choose the best one 122 : */ 123 0 : PETRA.BuildManager.prototype.findStructureWithClass = function(gameState, classes) 124 : { 125 0 : return this.findStructuresByFilter(gameState, API3.Filters.byClasses(classes))[0]; 126 : }; 127 : 128 0 : PETRA.BuildManager.prototype.hasBuilder = function(template) 129 : { 130 0 : let numBuilders = this.builderCounters.get(template); 131 0 : return numBuilders && numBuilders > 0; 132 : }; 133 : 134 0 : PETRA.BuildManager.prototype.isUnbuildable = function(gameState, template) 135 : { 136 0 : return this.unbuildables.has(template) && this.unbuildables.get(template).time > gameState.ai.elapsedTime; 137 : }; 138 : 139 0 : PETRA.BuildManager.prototype.setBuildable = function(template) 140 : { 141 0 : if (this.unbuildables.has(template)) 142 0 : this.unbuildables.delete(template); 143 : }; 144 : 145 : /** Time is the duration in second that we will wait before checking again if it is buildable */ 146 0 : PETRA.BuildManager.prototype.setUnbuildable = function(gameState, template, time = 90, reason = "room") 147 : { 148 0 : if (!this.unbuildables.has(template)) 149 0 : this.unbuildables.set(template, { "reason": reason, "time": gameState.ai.elapsedTime + time }); 150 : else 151 : { 152 0 : let unbuildable = this.unbuildables.get(template); 153 0 : if (unbuildable.time < gameState.ai.elapsedTime + time) 154 : { 155 0 : unbuildable.reason = reason; 156 0 : unbuildable.time = gameState.ai.elapsedTime + time; 157 : } 158 : } 159 : }; 160 : 161 : /** Return the number of unbuildables due to missing room */ 162 0 : PETRA.BuildManager.prototype.numberMissingRoom = function(gameState) 163 : { 164 0 : let num = 0; 165 0 : for (let unbuildable of this.unbuildables.values()) 166 0 : if (unbuildable.reason == "room" && unbuildable.time > gameState.ai.elapsedTime) 167 0 : ++num; 168 0 : return num; 169 : }; 170 : 171 : /** Reset the unbuildables due to missing room */ 172 0 : PETRA.BuildManager.prototype.resetMissingRoom = function(gameState) 173 : { 174 0 : for (let [key, unbuildable] of this.unbuildables) 175 0 : if (unbuildable.reason == "room") 176 0 : this.unbuildables.delete(key); 177 : }; 178 : 179 0 : PETRA.BuildManager.prototype.Serialize = function() 180 : { 181 0 : return { 182 : "builderCounters": this.builderCounters, 183 : "unbuildables": this.unbuildables 184 : }; 185 : }; 186 : 187 0 : PETRA.BuildManager.prototype.Deserialize = function(data) 188 : { 189 0 : for (let key in data) 190 0 : this[key] = data[key]; 191 : };