Line data Source code
1 : function EntityLimits() {} 2 : 3 2 : EntityLimits.prototype.Schema = 4 : "<a:help>Specifies per category limits on number of entities (buildings or units) that can be created for each player</a:help>" + 5 : "<a:example>" + 6 : "<Limits>" + 7 : "<Apadana>1</Apadana>" + 8 : "<Fortress>10</Fortress>" + 9 : "<Hero>1</Hero>" + 10 : "<Monument>5</Monument>" + 11 : "<Tower>25</Tower>" + 12 : "<Wonder>1</Wonder>" + 13 : "</Limits>" + 14 : "<LimitChangers>" + 15 : "<Monument>" + 16 : "<CivilCentre>2</CivilCentre>" + 17 : "</Monument>" + 18 : "</LimitChangers>" + 19 : "<LimitRemovers>" + 20 : "<CivilCentre>" + 21 : "<RequiredTechs datatype=\"tokens\">town_phase</RequiredTechs>" + 22 : "</CivilCentre>" + 23 : "</LimitRemovers>" + 24 : "</a:example>" + 25 : "<element name='Limits'>" + 26 : "<zeroOrMore>" + 27 : "<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" + 28 : "<anyName />" + 29 : "<data type='integer'/>" + 30 : "</element>" + 31 : "</zeroOrMore>" + 32 : "</element>" + 33 : "<element name='LimitChangers'>" + 34 : "<zeroOrMore>" + 35 : "<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" + 36 : "<anyName />" + 37 : "<zeroOrMore>" + 38 : "<element a:help='Specifies the class that changes the entity limit'>" + 39 : "<anyName />" + 40 : "<data type='integer'/>" + 41 : "</element>" + 42 : "</zeroOrMore>" + 43 : "</element>" + 44 : "</zeroOrMore>" + 45 : "</element>" + 46 : "<element name='LimitRemovers'>" + 47 : "<zeroOrMore>" + 48 : "<element a:help='Specifies a category of building/unit on which to remove this limit. The limit will be removed if all the followings requirements are satisfied'>" + 49 : "<anyName />" + 50 : "<oneOrMore>" + 51 : "<element a:help='Possible requirements are: RequiredTechs and RequiredClasses'>" + 52 : "<anyName />" + 53 : "<attribute name='datatype'>" + 54 : "<value>tokens</value>" + 55 : "</attribute>" + 56 : "<text/>" + 57 : "</element>" + 58 : "</oneOrMore>" + 59 : "</element>" + 60 : "</zeroOrMore>" + 61 : "</element>"; 62 : 63 : 64 2 : const TRAINING = "training"; 65 2 : const BUILD = "build"; 66 : 67 2 : EntityLimits.prototype.Init = function() 68 : { 69 3 : this.limit = {}; 70 : // Counts entities which change the limit of the given category. 71 3 : this.count = {}; 72 3 : this.changers = {}; 73 3 : this.removers = {}; 74 : // Counts entities with the given class, used in the limit removal. 75 3 : this.classCount = {}; 76 3 : this.removedLimit = {}; 77 3 : this.matchTemplateCount = {}; 78 3 : for (var category in this.template.Limits) 79 : { 80 6 : this.limit[category] = +this.template.Limits[category]; 81 6 : this.count[category] = 0; 82 6 : if (category in this.template.LimitChangers) 83 : { 84 1 : this.changers[category] = {}; 85 1 : for (var c in this.template.LimitChangers[category]) 86 1 : this.changers[category][c] = +this.template.LimitChangers[category][c]; 87 : } 88 6 : if (category in this.template.LimitRemovers) 89 : { 90 : // Keep a copy of removable limits for possible restoration. 91 2 : this.removedLimit[category] = this.limit[category]; 92 2 : this.removers[category] = {}; 93 2 : for (var c in this.template.LimitRemovers[category]) 94 : { 95 2 : this.removers[category][c] = this.template.LimitRemovers[category][c]._string.split(/\s+/); 96 2 : if (c === "RequiredClasses") 97 1 : for (var cls of this.removers[category][c]) 98 1 : this.classCount[cls] = 0; 99 : } 100 : } 101 : } 102 : }; 103 : 104 2 : EntityLimits.prototype.ChangeCount = function(category, value) 105 : { 106 26 : if (this.count[category] !== undefined) 107 26 : this.count[category] += value; 108 : }; 109 : 110 2 : EntityLimits.prototype.ChangeMatchCount = function(template, value) 111 : { 112 8 : if (!this.matchTemplateCount[template]) 113 3 : this.matchTemplateCount[template] = 0; 114 : 115 8 : this.matchTemplateCount[template] += value; 116 : }; 117 : 118 2 : EntityLimits.prototype.GetLimits = function() 119 : { 120 13 : return this.limit; 121 : }; 122 : 123 2 : EntityLimits.prototype.GetCounts = function() 124 : { 125 17 : return this.count; 126 : }; 127 : 128 2 : EntityLimits.prototype.GetMatchCounts = function() 129 : { 130 4 : return this.matchTemplateCount; 131 : }; 132 : 133 2 : EntityLimits.prototype.GetLimitChangers = function() 134 : { 135 1 : return this.changers; 136 : }; 137 : 138 2 : EntityLimits.prototype.UpdateLimitsFromTech = function(tech) 139 : { 140 3 : for (var category in this.removers) 141 6 : if ("RequiredTechs" in this.removers[category] && this.removers[category].RequiredTechs.indexOf(tech) !== -1) 142 1 : this.removers[category].RequiredTechs.splice(this.removers[category].RequiredTechs.indexOf(tech), 1); 143 : 144 3 : this.UpdateLimitRemoval(); 145 : }; 146 : 147 2 : EntityLimits.prototype.UpdateLimitRemoval = function() 148 : { 149 10 : for (var category in this.removers) 150 : { 151 18 : var nolimit = true; 152 18 : if ("RequiredTechs" in this.removers[category]) 153 9 : nolimit = !this.removers[category].RequiredTechs.length; 154 18 : if (nolimit && "RequiredClasses" in this.removers[category]) 155 9 : for (var cls of this.removers[category].RequiredClasses) 156 9 : nolimit = nolimit && this.classCount[cls] > 0; 157 : 158 18 : if (nolimit && this.limit[category] !== undefined) 159 3 : this.limit[category] = undefined; 160 15 : else if (!nolimit && this.limit[category] === undefined) 161 2 : this.limit[category] = this.removedLimit[category]; 162 : } 163 : }; 164 : 165 2 : EntityLimits.prototype.AllowedToCreate = function(limitType, category, count, templateName, matchLimit) 166 : { 167 21 : if (this.count[category] !== undefined && this.limit[category] !== undefined && 168 : this.count[category] + count > this.limit[category]) 169 : { 170 5 : this.NotifyLimit(limitType, category, this.limit[category]); 171 5 : return false; 172 : } 173 : 174 16 : if (this.matchTemplateCount[templateName] !== undefined && matchLimit !== undefined && 175 : this.matchTemplateCount[templateName] + count > matchLimit) 176 : { 177 0 : this.NotifyLimit(limitType, category, matchLimit); 178 0 : return false; 179 : } 180 : 181 16 : return true; 182 : }; 183 : 184 2 : EntityLimits.prototype.NotifyLimit = function(limitType, category, limit) 185 : { 186 5 : let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 187 5 : let notification = { 188 : "players": [cmpPlayer.GetPlayerID()], 189 : "translateMessage": true, 190 : "translateParameters": ["category"], 191 : "parameters": { "category": category, "limit": limit }, 192 : }; 193 : 194 5 : if (limitType == BUILD) 195 1 : notification.message = markForTranslation("%(category)s build limit of %(limit)s reached"); 196 4 : else if (limitType == TRAINING) 197 4 : notification.message = markForTranslation("%(category)s training limit of %(limit)s reached"); 198 : else 199 : { 200 0 : warn("EntityLimits.js: Unknown LimitType " + limitType); 201 0 : notification.message = markForTranslation("%(category)s limit of %(limit)s reached"); 202 : } 203 5 : let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 204 5 : cmpGUIInterface.PushNotification(notification); 205 : }; 206 : 207 2 : EntityLimits.prototype.AllowedToBuild = function(category) 208 : { 209 : // We pass count 0 as the creation of the building has already taken place and 210 : // the ownership has been set (triggering OnGlobalOwnershipChanged) 211 1 : return this.AllowedToCreate(BUILD, category, 0); 212 : }; 213 : 214 2 : EntityLimits.prototype.AllowedToTrain = function(category, count, templateName, matchLimit) 215 : { 216 16 : return this.AllowedToCreate(TRAINING, category, count, templateName, matchLimit); 217 : }; 218 : 219 : /** 220 : * @param {number} ent - id of the entity which would be replaced. 221 : * @param {string} template - name of the new template. 222 : * @return {boolean} - whether we can replace ent. 223 : */ 224 2 : EntityLimits.prototype.AllowedToReplace = function(ent, template) 225 : { 226 4 : let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 227 4 : let templateFrom = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent)); 228 4 : let templateTo = cmpTemplateManager.GetTemplate(template); 229 : 230 4 : if (templateTo.TrainingRestrictions) 231 : { 232 2 : let category = templateTo.TrainingRestrictions.Category; 233 2 : return this.AllowedToCreate(TRAINING, category, templateFrom.TrainingRestrictions && templateFrom.TrainingRestrictions.Category == category ? 0 : 1); 234 : } 235 : 236 2 : if (templateTo.BuildRestrictions) 237 : { 238 2 : let category = templateTo.BuildRestrictions.Category; 239 2 : return this.AllowedToCreate(BUILD, category, templateFrom.BuildRestrictions && templateFrom.BuildRestrictions.Category == category ? 0 : 1); 240 : } 241 : 242 0 : return true; 243 : }; 244 : 245 2 : EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg) 246 : { 247 : // check if we are adding or removing an entity from this player 248 19 : var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 249 19 : if (!cmpPlayer) 250 : { 251 0 : error("EntityLimits component is defined on a non-player entity"); 252 0 : return; 253 : } 254 19 : if (msg.from == cmpPlayer.GetPlayerID()) 255 9 : var modifier = -1; 256 10 : else if (msg.to == cmpPlayer.GetPlayerID()) 257 10 : var modifier = 1; 258 : else 259 0 : return; 260 : 261 : // Update entity counts 262 19 : var category = null; 263 19 : var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions); 264 19 : if (cmpBuildRestrictions) 265 2 : category = cmpBuildRestrictions.GetCategory(); 266 19 : var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions); 267 19 : if (cmpTrainingRestrictions) 268 11 : category = cmpTrainingRestrictions.GetCategory(); 269 19 : if (category) 270 13 : this.ChangeCount(category, modifier); 271 : 272 : // Update entity limits 273 19 : var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity); 274 19 : if (!cmpIdentity) 275 10 : return; 276 : 277 : // foundations shouldn't change the entity limits until they're completed 278 9 : var cmpFoundation = Engine.QueryInterface(msg.entity, IID_Foundation); 279 9 : if (cmpFoundation) 280 2 : return; 281 7 : var classes = cmpIdentity.GetClassesList(); 282 7 : for (var category in this.changers) 283 6 : for (var c in this.changers[category]) 284 6 : if (classes.indexOf(c) >= 0) 285 : { 286 2 : if (this.limit[category] != undefined) 287 2 : this.limit[category] += modifier * this.changers[category][c]; 288 2 : if (this.removedLimit[category] != undefined) // update removed limits in case we want to restore it 289 2 : this.removedLimit[category] += modifier * this.changers[category][c]; 290 : } 291 : 292 7 : for (var category in this.removers) 293 12 : if ("RequiredClasses" in this.removers[category]) 294 6 : for (var cls of this.removers[category].RequiredClasses) 295 6 : if (classes.indexOf(cls) !== -1) 296 4 : this.classCount[cls] += modifier; 297 : 298 7 : this.UpdateLimitRemoval(); 299 : }; 300 : 301 2 : Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);