Ticket #3811: t3811_buildingDensity_4.diff
File t3811_buildingDensity_4.diff, 32.9 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
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 -
binaries/data/mods/public/gui/session/input.js
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, … … 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 -
binaries/data/mods/public/simulation/ai/common-api/entity.js
485 485 return +this.get("Cost/BuildTime"); 486 486 }, 487 487 488 buildD istance: function() {489 if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/D istance"))488 buildDensity: function() { 489 if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Density")) 490 490 return undefined; 491 return this.get("BuildRestrictions/D istance");491 return this.get("BuildRestrictions/Density"); 492 492 }, 493 493 494 494 buildPlacementType: function() { -
binaries/data/mods/public/simulation/ai/common-api/map-module.js
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 125 if (this.map[w] + strength < 0) 126 126 this.map[w] = 0; 127 127 else if (this.map[w] + strength > this.maxVal) … … 132 132 } 133 133 } 134 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; 147 if (this.map[w] + strength < 0) 148 this.map[w] = 0; 149 else if (this.map[w] + strength > this.maxVal) 150 this.map[w] = this.maxVal; // avoids overflow. 151 else 152 this.map[w] += strength; 153 } 154 } 155 } 156 } 135 157 }; 136 158 137 159 m.Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) -
binaries/data/mods/public/simulation/ai/petra/mapModule.js
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 } 104 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 } 124 } 105 125 } 106 126 } 107 127 -
binaries/data/mods/public/simulation/components/BuildRestrictions.js
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.'>" + … … 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>"; … … 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 /** … … 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 * { … … 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": { … … 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 … … 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": … … 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 143 // for walls, only test the center point 137 144 if (this.template.Category == "Wall") 138 {139 // for walls, only test the center point140 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 { … … 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"); … … 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.". … … 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.". … … 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.". … … 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 { … … 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); 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 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 } 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; // Fail 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); 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); 303 299 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 } 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; // Fail 314 309 } 315 310 } 316 311 -
binaries/data/mods/public/simulation/components/GuiInterface.js
988 988 */ 989 989 GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd) 990 990 { 991 992 993 994 995 996 997 991 let result = { 992 "success": false, 993 "message": "", 994 "parameters": {}, 995 "translateMessage": false, 996 "translateParameters": [], 997 }; 998 998 999 999 // See if we're changing template 1000 1000 if (!this.placementEntity || this.placementEntity[0] != cmd.template) … … 1244 1244 // calculate wall placement and position preview entities 1245 1245 1246 1246 let result = { 1247 "success": true, 1247 1248 "pieces": [], 1248 1249 "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0 }, 1249 1250 }; … … 1484 1485 continue; 1485 1486 } 1486 1487 1487 // TODO: Handle results of CheckPlacement 1488 validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success); 1488 let placementResult = cmpBuildRestrictions.CheckPlacement() 1489 if (!placementResult.success) 1490 { 1491 result.success = false; 1492 result.message = placementResult.message; 1493 result.parameters = placementResult.parameters; 1494 result.translateMessage = placementResult.translateMessage; 1495 result.translateParameters = placementResult.translateParameters; 1496 } 1489 1497 1498 validPlacement = (cmpBuildRestrictions && placementResult.success); 1499 1490 1500 // If a wall piece has two control groups, it's likely a segment that spans 1491 1501 // between two existing towers. To avoid placing a duplicate wall segment, 1492 1502 // check for collisions with entities that share both control groups. … … 1550 1560 // If any were entities required to build the wall, but none of them could be validly positioned, return failure 1551 1561 // (see method-level documentation). 1552 1562 if (numRequiredPieces > 0 && result.pieces.length == 0) 1553 return false;1563 return result; 1554 1564 1555 1565 if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY) 1556 1566 result.startSnappedEnt = start.snappedEnt; -
binaries/data/mods/public/simulation/helpers/Walls.js
20 20 */ 21 21 function GetWallPlacement(placementData, wallSet, start, end) 22 22 { 23 varresult = [];23 let result = []; 24 24 25 varcandidateSegments = [26 { "template": wallSet.templates.long, "len": placementData[wallSet.templates.long].templateData.wallPiece.length},27 { "template": wallSet.templates.medium, "len": placementData[wallSet.templates.medium].templateData.wallPiece.length},28 { "template": wallSet.templates.short, "len": placementData[wallSet.templates.short].templateData.wallPiece.length},25 let candidateSegments = [ 26 { "template": wallSet.templates.long, "len": placementData[wallSet.templates.long].templateData.wallPiece.length }, 27 { "template": wallSet.templates.medium, "len": placementData[wallSet.templates.medium].templateData.wallPiece.length }, 28 { "template": wallSet.templates.short, "len": placementData[wallSet.templates.short].templateData.wallPiece.length }, 29 29 ]; 30 30 31 vartowerWidth = placementData[wallSet.templates.tower].templateData.wallPiece.length;31 let towerWidth = placementData[wallSet.templates.tower].templateData.wallPiece.length; 32 32 33 var dir = {"x": end.pos.x - start.pos.x, "z": end.pos.z - start.pos.z};34 varlen = Math.sqrt(dir.x * dir.x + dir.z * dir.z);33 let dir = { "x": end.pos.x - start.pos.x, "z": end.pos.z - start.pos.z }; 34 let len = Math.sqrt(dir.x * dir.x + dir.z * dir.z); 35 35 36 36 // we'll need room for at least our starting and ending towers to fit next to eachother 37 37 if (len <= towerWidth) 38 38 return result; 39 39 40 varplacement = GetWallSegmentsRec(len, candidateSegments, wallSet.minTowerOverlap, wallSet.maxTowerOverlap, towerWidth, 0, []);40 let placement = GetWallSegmentsRec(len, candidateSegments, wallSet.minTowerOverlap, wallSet.maxTowerOverlap, towerWidth, 0, []); 41 41 42 42 // TODO: make sure intermediate towers are spaced out far enough for their obstructions to not overlap, implying that 43 43 // tower's wallpiece lengths should be > their obstruction width, which is undesirable because it prevents towers with … … 44 44 // wide bases 45 45 if (placement) 46 46 { 47 varplacedEntities = placement.segments; // list of chosen candidate segments48 varr = placement.r; // remaining distance to target without towers (must be <= (N-1) * towerWidth)49 vars = r / (2 * placedEntities.length); // spacing47 let placedEntities = placement.segments; // list of chosen candidate segments 48 let r = placement.r; // remaining distance to target without towers (must be <= (N-1) * towerWidth) 49 let s = r / (2 * placedEntities.length); // spacing 50 50 51 var dirNormalized = {"x": dir.x / len, "z": dir.z / len};52 varangle = -Math.atan2(dir.z, dir.x); // angle of this wall segment (relative to world-space X/Z axes)51 let dirNormalized = { "x": dir.x / len, "z": dir.z / len }; 52 let angle = -Math.atan2(dir.z, dir.x); // angle of this wall segment (relative to world-space X/Z axes) 53 53 54 varprogress = 0;55 for ( vari = 0; i < placedEntities.length; i++)54 let progress = 0; 55 for (let i = 0; i < placedEntities.length; i++) 56 56 { 57 varplacedEntity = placedEntities[i];58 vartargetX = start.pos.x + (progress + s + placedEntity.len/2) * dirNormalized.x;59 vartargetZ = start.pos.z + (progress + s + placedEntity.len/2) * dirNormalized.z;57 let placedEntity = placedEntities[i]; 58 let targetX = start.pos.x + (progress + s + placedEntity.len/2) * dirNormalized.x; 59 let targetZ = start.pos.z + (progress + s + placedEntity.len/2) * dirNormalized.z; 60 60 61 61 result.push({ 62 62 "template": placedEntity.template, 63 "pos": { "x": targetX, "z": targetZ},63 "pos": { "x": targetX, "z": targetZ }, 64 64 "angle": angle, 65 65 }); 66 66 67 67 if (i < placedEntities.length - 1) 68 68 { 69 vartowerX = start.pos.x + (progress + placedEntity.len + 2*s) * dirNormalized.x;70 vartowerZ = start.pos.z + (progress + placedEntity.len + 2*s) * dirNormalized.z;69 let towerX = start.pos.x + (progress + placedEntity.len + 2*s) * dirNormalized.x; 70 let towerZ = start.pos.z + (progress + placedEntity.len + 2*s) * dirNormalized.z; 71 71 72 result.push({73 "template": wallSet.templates.tower,74 "pos": {"x": towerX, "z": towerZ},75 "angle": angle,76 });72 result.push({ 73 "template": wallSet.templates.tower, 74 "pos": { "x": towerX, "z": towerZ }, 75 "angle": angle, 76 }); 77 77 } 78 78 79 79 progress += placedEntity.len + 2*s; … … 160 160 // backtrack and try a wall segment of the next length instead. Note that we should prefer to use the long segments first since 161 161 // they can be replaced by gates. 162 162 163 for each (var candSegment incandidateSegments)163 for (let candSegment of candidateSegments) 164 164 { 165 165 segments.push(candSegment); 166 166 167 varnewDistSoFar = distSoFar + candSegment.len;168 varr = d - newDistSoFar;167 let newDistSoFar = distSoFar + candSegment.len; 168 let r = d - newDistSoFar; 169 169 170 varrLowerBound = (1 - 2 * maxOverlap) * segments.length * t;171 varrUpperBound = (1 - 2 * minOverlap) * segments.length * t;170 let rLowerBound = (1 - 2 * maxOverlap) * segments.length * t; 171 let rUpperBound = (1 - 2 * minOverlap) * segments.length * t; 172 172 173 173 if (r < rLowerBound) 174 174 { … … 179 179 } 180 180 else if (r > rUpperBound) 181 181 { 182 varrecursiveResult = GetWallSegmentsRec(d, candidateSegments, minOverlap, maxOverlap, t, newDistSoFar, segments);182 let recursiveResult = GetWallSegmentsRec(d, candidateSegments, minOverlap, maxOverlap, t, newDistSoFar, segments); 183 183 if (!recursiveResult) 184 184 { 185 185 // recursive search with this piece yielded no results, pop it and try the next one … … 192 192 else 193 193 { 194 194 // found a placement 195 return { "segments": segments, "r": r};195 return { "segments": segments, "r": r }; 196 196 } 197 197 } 198 198 -
binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
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> -
binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml
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> -
binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
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> … … 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> -
binaries/data/mods/public/simulation/templates/template_structure_defense_wooden_tower.xml
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> -
binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
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>