Ticket #3811: t3811_buildingDensity_5.diff
File t3811_buildingDensity_5.diff, 29.9 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
function GetTemplateDataHelper(template, 140 140 "territory": template.BuildRestrictions.Territory, 141 141 "category": template.BuildRestrictions.Category, 142 142 }; 143 143 144 144 // optional properties 145 if (template.BuildRestrictions.D istance)145 if (template.BuildRestrictions.Density) 146 146 { 147 ret.buildRestrictions.d istance= {148 "fromClass": template.BuildRestrictions.D istance.FromClass,147 ret.buildRestrictions.density = { 148 "fromClass": template.BuildRestrictions.Density.FromClass, 149 149 }; 150 if (template.BuildRestrictions.Distance.MinDistance) ret.buildRestrictions.distance.min = +template.BuildRestrictions.Distance.MinDistance; 151 if (template.BuildRestrictions.Distance.MaxDistance) ret.buildRestrictions.distance.max = +template.BuildRestrictions.Distance.MaxDistance; 150 if (template.BuildRestrictions.Density.MinEntity) 151 ret.buildRestrictions.density.min = +template.BuildRestrictions.Density.MinEntity; 152 if (template.BuildRestrictions.Density.MaxEntity) 153 ret.buildRestrictions.density.max = +template.BuildRestrictions.Density.MaxEntity; 152 154 } 153 155 } 154 156 155 157 if (template.TrainingRestrictions) 156 158 { -
binaries/data/mods/public/gui/session/input.js
function updateBuildingPlacementPreview( 98 98 99 99 if (placementSupport.mode === "building") 100 100 { 101 101 if (placementSupport.template && placementSupport.position) 102 102 { 103 varresult = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {103 let result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", { 104 104 "template": placementSupport.template, 105 105 "x": placementSupport.position.x, 106 106 "z": placementSupport.position.z, 107 107 "angle": placementSupport.angle, 108 108 "actorSeed": placementSupport.actorSeed … … function updateBuildingPlacementPreview( 158 158 placementSupport.wallSnapEntitiesIncludeOffscreen, 159 159 true, // require exact template match 160 160 true // include foundations 161 161 ); 162 162 163 returnEngine.GuiInterfaceCall("SetWallPlacementPreview", {163 let result = Engine.GuiInterfaceCall("SetWallPlacementPreview", { 164 164 "wallSet": placementSupport.wallSet, 165 165 "start": placementSupport.position, 166 166 "end": placementSupport.wallEndPosition, 167 167 "snapEntities": placementSupport.wallSnapEntities, // snapping entities (towers) for starting a wall segment 168 168 }); 169 170 // Show placement info tooltip if invalid position 171 placementSupport.tooltipError = !result.success; 172 placementSupport.tooltipMessage = ""; 173 if (!result.success) 174 { 175 if (result.message && result.parameters) 176 { 177 let message = result.message; 178 if (result.translateMessage) 179 if (result.pluralMessage) 180 message = translatePlural(result.message, result.pluralMessage, result.pluralCount); 181 else 182 message = translate(message); 183 let parameters = result.parameters; 184 if (result.translateParameters) 185 translateObjectKeys(parameters, result.translateParameters); 186 placementSupport.tooltipMessage = sprintf(message, parameters); 187 } 188 } 189 return result; 169 190 } 170 191 } 171 192 172 193 return false; 173 194 } -
binaries/data/mods/public/simulation/ai/common-api/entity.js
m.Template = m.Class({ 464 464 465 465 buildTime: function() { 466 466 return +this.get("Cost/BuildTime"); 467 467 }, 468 468 469 buildD istance: function() {470 return this.get("BuildRestrictions/D istance");469 buildDensity: function() { 470 return this.get("BuildRestrictions/Density"); 471 471 }, 472 472 473 473 buildPlacementType: function() { 474 474 return this.get("BuildRestrictions/PlacementType"); 475 475 }, -
binaries/data/mods/public/simulation/ai/common-api/map-module.js
m.Map.prototype.addInfluence = function( 119 119 { 120 120 let dx = x - cx; 121 121 let r2 = dx*dx + dy*dy; 122 122 if (r2 < maxDist2) 123 123 { 124 let w = x + y * this.width; 124 let w = x + y * this.width; 125 if (this.map[w] + strength < 0) 126 this.map[w] = 0; 127 else if (this.map[w] + strength > this.maxVal) 128 this.map[w] = this.maxVal; // avoids overflow. 129 else 130 this.map[w] += strength; 131 } 132 } 133 } 134 } 135 else if (type === 'inverseConstant' || type === "inverseConstant") // TODO needed for other cases too? 136 { 137 for (let y = y0; y < y1; ++y) 138 { 139 let dy = y - cy; 140 for (let x = x0; x < x1; ++x) 141 { 142 let dx = x - cx; 143 let r2 = dx*dx + dy*dy; 144 if (r2 > maxDist2) 145 { 146 let w = x + y * this.width; 125 147 if (this.map[w] + strength < 0) 126 148 this.map[w] = 0; 127 149 else if (this.map[w] + strength > this.maxVal) 128 150 this.map[w] = this.maxVal; // avoids overflow. 129 151 else -
binaries/data/mods/public/simulation/ai/petra/mapModule.js
m.createObstructionMap = function(gameSt 82 82 } 83 83 84 84 var map = new API3.Map(gameState.sharedScript, "passability", obstructionTiles); 85 85 map.setMaxVal(255); 86 86 87 if (template && template.buildD istance())87 if (template && template.buildDensity()) 88 88 { 89 let minDist = +template.buildDistance().MinDistance; 90 let fromClass = template.buildDistance().FromClass; 91 if (minDist && fromClass) 89 let buildDensity = template.buildDensity(); 90 if (buildDensity) 92 91 { 92 let Dist = +buildDensity.Distance; 93 let fromClass = buildDensity.FromClass; 93 94 let cellSize = passabilityMap.cellSize; 94 let cellDist = 1 + minDist / cellSize;95 let cellDist = 1 + Dist / cellSize; 95 96 let structures = gameState.getOwnStructures().filter(API3.Filters.byClass(fromClass)); 96 for (let ent of structures.values()) 97 // TODO the following will have problems when both MaxEntity and MinEntity are specified 98 // and that is allowed in template. Probably it needs to get some more efficient too. 99 if (buildDensity.MaxEntity) 97 100 { 98 if (!ent.position()) 99 continue; 100 let pos = ent.position(); 101 let x = Math.round(pos[0] / cellSize); 102 let z = Math.round(pos[1] / cellSize); 103 map.addInfluence(x, z, cellDist, -255, "constant"); 101 let influence = Math.floor(-255 / (+buildDensity.MaxEntity + 1)); 102 for (let ent of structures.values()) 103 { 104 if (!ent.position()) 105 continue; 106 let pos = ent.position(); 107 let x = Math.round(pos[0] / cellSize); 108 let z = Math.round(pos[1] / cellSize); 109 map.addInfluence(x, z, cellDist, influence, "constant"); 110 } 111 } 112 if (buildDensity.MinEntity) 113 { 114 let influence = Math.floor(-255 / (+buildDensity.MinEntity)); 115 for (let ent of structures.values()) 116 { 117 if (!ent.position()) 118 continue; 119 let pos = ent.position(); 120 let x = Math.round(pos[0] / cellSize); 121 let z = Math.round(pos[1] / cellSize); 122 map.addInfluence(x, z, cellDist, influence, "inverseConstant"); 123 } 104 124 } 105 125 } 106 126 } 107 127 108 128 return map; -
binaries/data/mods/public/simulation/components/BuildRestrictions.js
BuildRestrictions.prototype.Schema = 5 5 "<a:example>" + 6 6 "<BuildRestrictions>" + 7 7 "<PlacementType>land</PlacementType>" + 8 8 "<Territory>own</Territory>" + 9 9 "<Category>Special</Category>" + 10 "<D istance>" +10 "<Density>" + 11 11 "<FromClass>CivilCentre</FromClass>" + 12 "<MaxDistance>40</MaxDistance>" + 13 "</Distance>" + 12 "<Distance>200</Distance>" + 13 "<MaxEntity>3</MaxEntity>" + 14 "<MinEntity>1</MinEntity>" + 15 "</Density>" + 14 16 "</BuildRestrictions>" + 15 17 "</a:example>" + 16 18 "<element name='PlacementType' a:help='Specifies the terrain type restriction for this building.'>" + 17 19 "<choice>" + 18 20 "<value>land</value>" + … … BuildRestrictions.prototype.Schema = 30 32 "<value>enemy</value>" + 31 33 "</choice>" + 32 34 "</oneOrMore>" + 33 35 "</list>" + 34 36 "</element>" + 35 "<element name='Category' a:help='Specifies the category of this building, for satisfying special constraints. Choices include: CivilCentre, House, DefenseTower, Farmstead, Market, Barracks, Dock, Fortress, Field, Temple, Wall, Fence, Storehouse, Stoa, Resource, Special, Wonder, Apadana, Embassy, Monument'>" +37 "<element name='Category' a:help='Specifies the category of this building, for satisfying special constraints. Choices include: Apadana, Barracks, CivilCentre, DefenseTower, Dock, Embassy, Farmstead, Field, Fence, Fortress, House, Market, Monument, Resource, Special, Stoa, Storehouse, Temple, Tower, Wall, WallTower, Wonder'>" + 36 38 "<text/>" + 37 39 "</element>" + 38 40 "<optional>" + 39 "<element name='D istance' a:help='Specifies distance restrictions on this building, relative to buildings from the given category.'>" +41 "<element name='Density' a:help='Specifies distance restrictions on this building, relative to buildings from the given category.'>" + 40 42 "<interleave>" + 41 43 "<element name='FromClass'>" + 42 44 "<text/>" + 43 45 "</element>" + 44 "<optional><element name='MinDistance'><data type='positiveInteger'/></element></optional>" + 45 "<optional><element name='MaxDistance'><data type='positiveInteger'/></element></optional>" + 46 "<element name='Distance'><data type='positiveInteger'/></element>" + 47 "<optional><element name='MinEntity'><data type='nonNegativeInteger'/></element></optional>" + 48 "<optional><element name='MaxEntity'><data type='nonNegativeInteger'/></element></optional>" + 46 49 "</interleave>" + 47 50 "</element>" + 48 51 "</optional>"; 49 52 50 53 BuildRestrictions.prototype.Init = function() 51 54 { 52 55 this.territories = this.template.Territory.split(/\s+/); 56 if (this.template.Density) 57 { 58 this.minEntity = +(this.template.Density.MinEntity || 0); 59 this.maxEntity = +(this.template.Density.MaxEntity || Infinity); 60 } 53 61 }; 54 62 55 63 /** 56 64 * Checks whether building placement is valid 57 65 * 1. Visibility is not hidden (may be fogged or visible) 58 66 * 2. Check foundation 59 67 * a. Doesn't obstruct foundation-blocking entities 60 68 * b. On valid terrain, based on passability class 61 69 * 3. Territory type is allowed (see note below) 62 70 * 4. Dock is on shoreline and facing into water 63 * 5. D istanceconstraints satisfied71 * 5. Density constraints satisfied 64 72 * 65 73 * Returns result object: 66 74 * { 67 75 * "success": true iff the placement is valid, else false 68 76 * "message": message to display in UI for invalid placement, else "" … … BuildRestrictions.prototype.Init = funct 77 85 * (template name should be "preview|"+templateName), as otherwise territory 78 86 * checks for buildings with territory influence will not work as expected. 79 87 */ 80 88 BuildRestrictions.prototype.CheckPlacement = function() 81 89 { 82 varcmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);83 varname = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";90 let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 91 let name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building"; 84 92 85 varresult = {93 let result = { 86 94 "success": false, 87 95 "message": markForTranslation("%(name)s cannot be built due to unknown error"), 88 96 "parameters": { 89 97 "name": name, 90 98 }, 91 99 "translateMessage": true, 92 100 "translateParameters": ["name"], 93 101 }; 94 102 95 103 // TODO: AI has no visibility info 96 varcmpPlayer = QueryOwnerInterface(this.entity, IID_Player);104 let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 97 105 if (!cmpPlayer.IsAI()) 98 106 { 99 107 // Check whether it's in a visible or fogged region 100 varcmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);101 varcmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);108 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 109 let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 102 110 if (!cmpRangeManager || !cmpOwnership) 103 111 return result; // Fail 104 112 105 var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden"); 106 if (!explored) 113 if (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) == "hidden") 107 114 { 108 115 result.message = markForTranslation("%(name)s cannot be built in unexplored area"); 109 116 return result; // Fail 110 117 } 111 118 } 112 119 113 120 // Check obstructions and terrain passability 114 varpassClassName = "";121 let passClassName = ""; 115 122 switch (this.template.PlacementType) 116 123 { 117 124 case "shore": 118 125 passClassName = "building-shore"; 119 126 break; … … BuildRestrictions.prototype.CheckPlaceme 127 134 case "land": 128 135 default: 129 136 passClassName = "building-land"; 130 137 } 131 138 132 varcmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);139 let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction); 133 140 if (!cmpObstruction) 134 141 return result; // Fail 135 142 136 137 if (this.template.Category == "Wall")138 {139 143 // for walls, only test the center point 144 if (this.template.Category == "Wall") 140 145 var ret = cmpObstruction.CheckFoundation(passClassName, true); 141 }142 146 else 143 {144 147 var ret = cmpObstruction.CheckFoundation(passClassName, false); 145 }146 148 147 149 if (ret != "success") 148 150 { 149 151 switch (ret) 150 152 { 151 153 case "fail_error": 152 154 case "fail_no_obstruction": 153 155 error("CheckPlacement: Error returned from CheckFoundation"); 154 156 break; 157 155 158 case "fail_obstructs_foundation": 156 159 result.message = markForTranslation("%(name)s cannot be built on another building or resource"); 157 160 break; 161 158 162 case "fail_terrain_class": 159 163 // TODO: be more specific and/or list valid terrain? 160 164 result.message = markForTranslation("%(name)s cannot be built on invalid terrain"); 161 165 } 162 166 return result; // Fail 163 167 } 164 168 165 169 // Check territory restrictions 166 varcmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);167 varcmpPlayer = QueryOwnerInterface(this.entity, IID_Player);168 varcmpPosition = Engine.QueryInterface(this.entity, IID_Position);169 if (! (cmpTerritoryManager && cmpPlayer && cmpPosition && cmpPosition.IsInWorld()))170 let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); 171 let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 172 let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 173 if (!cmpTerritoryManager || !cmpPlayer || !cmpPosition || !cmpPosition.IsInWorld()) 170 174 return result; // Fail 171 175 172 var pos = cmpPosition.GetPosition2D(); 173 var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y); 174 var isConnected = !cmpTerritoryManager.IsTerritoryBlinking(pos.x, pos.y); 175 var isOwn = tileOwner == cmpPlayer.GetPlayerID(); 176 var isMutualAlly = cmpPlayer.IsExclusiveMutualAlly(tileOwner); 177 var isNeutral = tileOwner == 0; 176 let pos = cmpPosition.GetPosition2D(); 177 let tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y); 178 let isConnected = !cmpTerritoryManager.IsTerritoryBlinking(pos.x, pos.y); 178 179 179 varinvalidTerritory = "";180 if ( isOwn)180 let invalidTerritory = ""; 181 if (tileOwner == cmpPlayer.GetPlayerID()) // Own territory 181 182 { 182 183 if (!this.HasTerritory("own")) 183 184 // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.". 184 185 invalidTerritory = markForTranslationWithContext("Territory type", "own"); 185 186 else if (!isConnected && !this.HasTerritory("neutral")) 186 187 // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.". 187 188 invalidTerritory = markForTranslationWithContext("Territory type", "unconnected own"); 188 189 } 189 else if ( isMutualAlly)190 else if (cmpPlayer.IsExclusiveMutualAlly(tileOwner)) 190 191 { 191 192 if (!this.HasTerritory("ally")) 192 193 // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.". 193 194 invalidTerritory = markForTranslationWithContext("Territory type", "allied"); 194 195 else if (!isConnected && !this.HasTerritory("neutral")) 195 196 // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.". 196 197 invalidTerritory = markForTranslationWithContext("Territory type", "unconnected allied"); 197 198 } 198 else if ( isNeutral)199 else if (tileOwner == 0) // Neutral territory 199 200 { 200 201 if (!this.HasTerritory("neutral")) 201 202 // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.". 202 203 invalidTerritory = markForTranslationWithContext("Territory type", "neutral"); 203 204 } … … BuildRestrictions.prototype.CheckPlaceme 225 226 { 226 227 // TODO: Probably should check unit passability classes here, to determine if: 227 228 // 1. ships can be spawned "nearby" 228 229 // 2. builders can pass the terrain where the dock is placed (don't worry about paths) 229 230 // so it's correct even if the criteria changes for these units 230 varcmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);231 let cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint); 231 232 if (!cmpFootprint) 232 233 return result; // Fail 233 234 234 235 // Get building's footprint 235 varshape = cmpFootprint.GetShape();236 varhalfSize = 0;236 let shape = cmpFootprint.GetShape(); 237 let halfSize = 0; 237 238 if (shape.type == "square") 238 halfSize = shape.depth /2;239 halfSize = shape.depth / 2; 239 240 else if (shape.type == "circle") 240 241 halfSize = shape.radius; 241 242 242 varcmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);243 varcmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);243 let cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain); 244 let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); 244 245 if (!cmpTerrain || !cmpWaterManager) 245 246 return result; // Fail 246 247 247 varang = cmpPosition.GetRotation().y;248 varsz = halfSize * Math.sin(ang);249 varcz = halfSize * Math.cos(ang);248 let ang = cmpPosition.GetRotation().y; 249 let sz = halfSize * Math.sin(ang); 250 let cz = halfSize * Math.cos(ang); 250 251 if ((cmpWaterManager.GetWaterLevel(pos.x + sz, pos.y + cz) - cmpTerrain.GetGroundLevel(pos.x + sz, pos.y + cz)) < 1.0 // front 251 252 || (cmpWaterManager.GetWaterLevel(pos.x - sz, pos.y - cz) - cmpTerrain.GetGroundLevel(pos.x - sz, pos.y - cz)) > 2.0) // back 252 253 { 253 254 result.message = markForTranslation("%(name)s must be built on a valid shoreline"); 254 255 return result; // Fail 255 256 } 256 257 } 257 258 258 259 // Check distance restriction 259 if (this.template.D istance)260 if (this.template.Density) 260 261 { 261 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 262 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 263 var cat = this.template.Distance.FromClass; 262 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 263 let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 264 let cat = this.template.Density.FromClass; 265 let dist = +this.template.Density.Distance 264 266 265 varfilter = function(id)267 let filter = function(id) 266 268 { 267 varcmpIdentity = Engine.QueryInterface(id, IID_Identity);269 let cmpIdentity = Engine.QueryInterface(id, IID_Identity); 268 270 return cmpIdentity.GetClassesList().indexOf(cat) > -1; 269 271 }; 270 272 271 if (this.template.Distance.MinDistance) 273 let nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter); 274 275 if (nearEnts.length > this.maxEntity) 272 276 { 273 var dist = +this.template.Distance.MinDistance; 274 var nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter); 275 if (nearEnts.length) 276 { 277 var result = markForPluralTranslation( 278 "%(name)s too close to a %(category)s, must be at least %(distance)s meter away", 279 "%(name)s too close to a %(category)s, must be at least %(distance)s meters away", 280 +this.template.Distance.MinDistance); 281 282 result.success = false; 283 result.translateMessage = true; 284 result.parameters = { 285 "name": name, 286 "category": cat, 287 "distance": this.template.Distance.MinDistance 288 }; 289 result.translateParameters = ["name", "category"]; 290 return result; // Fail 291 } 277 let result = markForPluralTranslation( 278 "%(name)s too close to a %(category)s, must be at least %(distance)s meter away", 279 "%(name)s too close to a %(category)s, must be at least %(distance)s meters away", 280 +this.template.Density.Distance); 281 282 result.success = false; 283 result.translateMessage = true; 284 result.parameters = { 285 "name": name, 286 "category": cat, 287 "distance": this.template.Density.Distance 288 }; 289 result.translateParameters = ["name", "category"]; 290 return result; 292 291 } 293 if (this.template.Distance.MaxDistance) 292 293 if (nearEnts.length < this.minEntity) 294 294 { 295 var dist = +this.template.Distance.MaxDistance; 296 var nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter); 297 if (!nearEnts.length) 298 { 299 var result = markForPluralTranslation( 300 "%(name)s too far from a %(category)s, must be within %(distance)s meter", 301 "%(name)s too far from a %(category)s, must be within %(distance)s meters", 302 +this.template.Distance.MinDistance); 303 304 result.success = false; 305 result.translateMessage = true; 306 result.parameters = { 307 "name": name, 308 "category": cat, 309 "distance": this.template.Distance.MaxDistance 310 }; 311 result.translateParameters = ["name", "category"]; 312 return result; // Fail 313 } 295 let result = markForPluralTranslation( 296 "%(name)s too far from a %(category)s, must be within %(distance)s meter", 297 "%(name)s too far from a %(category)s, must be within %(distance)s meters", 298 +this.template.Density.Distance); 299 300 result.success = false; 301 result.translateMessage = true; 302 result.parameters = { 303 "name": name, 304 "category": cat, 305 "distance": this.template.Density.Distance 306 }; 307 result.translateParameters = ["name", "category"]; 308 return result; 314 309 } 315 310 } 316 311 317 312 // Success 318 313 result.success = true; -
binaries/data/mods/public/simulation/components/GuiInterface.js
GuiInterface.prototype.SetWallPlacementP 1243 1243 1244 1244 // -------------------------------------------------------------------------------- 1245 1245 // calculate wall placement and position preview entities 1246 1246 1247 1247 let result = { 1248 "success": true, 1248 1249 "pieces": [], 1249 1250 "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0 }, 1250 1251 }; 1251 1252 1252 1253 let previewEntities = []; … … GuiInterface.prototype.SetWallPlacementP 1483 1484 { 1484 1485 error("[SetWallPlacementPreview] cmpBuildRestrictions not defined for preview entity of template '" + tpl + "'"); 1485 1486 continue; 1486 1487 } 1487 1488 1488 // TODO: Handle results of CheckPlacement 1489 validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success); 1489 let placementResult = cmpBuildRestrictions.CheckPlacement() 1490 if (!placementResult.success) 1491 { 1492 result.success = false; 1493 result.message = placementResult.message; 1494 result.parameters = placementResult.parameters; 1495 result.translateMessage = placementResult.translateMessage; 1496 result.translateParameters = placementResult.translateParameters; 1497 } 1498 1499 validPlacement = (cmpBuildRestrictions && placementResult.success); 1490 1500 1491 1501 // If a wall piece has two control groups, it's likely a segment that spans 1492 1502 // between two existing towers. To avoid placing a duplicate wall segment, 1493 1503 // check for collisions with entities that share both control groups. 1494 1504 if (validPlacement && entInfo.controlGroups && entInfo.controlGroups.length > 1) … … GuiInterface.prototype.SetWallPlacementP 1549 1559 } 1550 1560 1551 1561 // If any were entities required to build the wall, but none of them could be validly positioned, return failure 1552 1562 // (see method-level documentation). 1553 1563 if (numRequiredPieces > 0 && result.pieces.length == 0) 1554 return false;1564 return result; 1555 1565 1556 1566 if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) 1557 1567 result.startSnappedEnt = start.snappedEnt; 1558 1568 1559 1569 // We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed, -
binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
27 27 <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> 28 28 </BuildingAI> 29 29 <BuildRestrictions> 30 30 <Territory>own neutral</Territory> 31 31 <Category>CivilCentre</Category> 32 <D istance>32 <Density> 33 33 <FromClass>CivilCentre</FromClass> 34 <MinDistance>200</MinDistance> 35 </Distance> 34 <Distance>200</Distance> 35 <MaxEntity>0</MaxEntity> 36 </Density> 36 37 </BuildRestrictions> 37 38 <Capturable> 38 39 <CapturePoints>2500</CapturePoints> 39 40 <RegenRate>5.0</RegenRate> 40 41 </Capturable> -
binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml
20 20 <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> 21 21 <GarrisonArrowClasses>Infantry</GarrisonArrowClasses> 22 22 </BuildingAI> 23 23 <BuildRestrictions> 24 24 <Category>DefenseTower</Category> 25 <D istance>25 <Density> 26 26 <FromClass>DefenseTower</FromClass> 27 <MinDistance>60</MinDistance> 28 </Distance> 27 <Distance>60</Distance> 28 <MaxEntity>0</MaxEntity> 29 </Density> 29 30 </BuildRestrictions> 30 31 <Cost> 31 32 <BuildTime>150</BuildTime> 32 33 <Resources> 33 34 <wood>100</wood> -
binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
19 19 <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> 20 20 <GarrisonArrowClasses>Infantry</GarrisonArrowClasses> 21 21 </BuildingAI> 22 22 <BuildRestrictions> 23 23 <PlacementType>land-shore</PlacementType> 24 <Category>Wall</Category> 24 <Category>WallTower</Category> 25 <Density> 26 <FromClass>WallTower</FromClass> 27 <Distance>40</Distance> 28 <MaxEntity>2</MaxEntity> 29 </Density> 25 30 </BuildRestrictions> 26 31 <Capturable disable=""/> 27 32 <Repairable> 28 33 <RepairTimeRatio>4.5</RepairTimeRatio> 29 34 </Repairable> … … 50 55 <SpawnEntityOnDeath>rubble/rubble_stone_wall_tower</SpawnEntityOnDeath> 51 56 </Health> 52 57 <Identity> 53 58 <GenericName>Wall Turret</GenericName> 54 59 <Tooltip>Shoots arrows. Garrison to defend a city wall against attackers.</Tooltip> 55 <Classes datatype="tokens">-ConquestCritical StoneWall Tower </Classes>60 <Classes datatype="tokens">-ConquestCritical StoneWall Tower WallTower</Classes> 56 61 <Icon>structures/tower.png</Icon> 57 62 <RequiredTechnology>phase_town</RequiredTechnology> 58 63 </Identity> 59 64 <Loot> 60 65 <xp>100</xp> -
binaries/data/mods/public/simulation/templates/template_structure_defense_wooden_tower.xml
14 14 <MinRange>4.0</MinRange> 15 15 </Ranged> 16 16 </Attack> 17 17 <BuildRestrictions> 18 18 <Category>DefenseTower</Category> 19 <D istance>19 <Density> 20 20 <FromClass>DefenseTower</FromClass> 21 <MinDistance>40</MinDistance> 22 </Distance> 21 <Distance>40</Distance> 22 <MaxEntity>0</MaxEntity> 23 </Density> 23 24 </BuildRestrictions> 24 25 <Cost> 25 26 <BuildTime>40</BuildTime> 26 27 <Resources> 27 28 <wood>100</wood> -
binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
24 24 <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> 25 25 <GarrisonArrowClasses>Infantry Ranged</GarrisonArrowClasses> 26 26 </BuildingAI> 27 27 <BuildRestrictions> 28 28 <Category>Fortress</Category> 29 <D istance>29 <Density> 30 30 <FromClass>Fortress</FromClass> 31 <MinDistance>80</MinDistance> 32 </Distance> 31 <Distance>80</Distance> 32 <MaxEntity>0</MaxEntity> 33 </Density> 33 34 </BuildRestrictions> 34 35 <Capturable> 35 36 <CapturePoints>4000</CapturePoints> 36 37 <RegenRate>10.0</RegenRate> 37 38 </Capturable>