Ticket #3811: t3811_buildingDensity_2.diff

File t3811_buildingDensity_2.diff, 28.1 KB (added by bb, 8 years ago)
  • binaries/data/mods/public/globalscripts/Templates.js

     
    142142        };
    143143
    144144        // optional properties
    145         if (template.BuildRestrictions.Distance)
     145        if (template.BuildRestrictions.Density)
    146146        {
    147147            ret.buildRestrictions.distance = {
    148148                "fromCategory": template.BuildRestrictions.Distance.FromCategory,
    149149            };
    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;
    152154        }
    153155    }
    154156
  • binaries/data/mods/public/gui/session/input.js

     
    100100    {
    101101        if (placementSupport.template && placementSupport.position)
    102102        {
    103             var result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
     103            let result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
    104104                "template": placementSupport.template,
    105105                "x": placementSupport.position.x,
    106106                "z": placementSupport.position.z,
     
    160160                true  // include foundations
    161161            );
    162162
    163             return Engine.GuiInterfaceCall("SetWallPlacementPreview", {
     163            let result = Engine.GuiInterfaceCall("SetWallPlacementPreview", {
    164164                "wallSet": placementSupport.wallSet,
    165165                "start": placementSupport.position,
    166166                "end": placementSupport.wallEndPosition,
    167167                "snapEntities": placementSupport.wallSnapEntities,  // snapping entities (towers) for starting a wall segment
    168168            });
     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;
    169190        }
    170191    }
    171192
  • binaries/data/mods/public/simulation/components/BuildRestrictions.js

     
    3232            "</oneOrMore>" +
    3333        "</list>" +
    3434    "</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'>" +
     35    "<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'>" +
    3636        "<text/>" +
    3737    "</element>" +
    3838    "<optional>" +
    39         "<element name='Distance' a:help='Specifies distance restrictions on this building, relative to buildings from the given category.'>" +
     39        "<element name='Density' a:help='Specifies distance restrictions on this building, relative to buildings from the given category.'>" +
    4040            "<interleave>" +
    4141                "<element name='FromClass'>" +
    4242                    "<text/>" +
    4343                "</element>" +
    44                 "<optional><element name='MinDistance'><data type='positiveInteger'/></element></optional>" +
    45                 "<optional><element name='MaxDistance'><data type='positiveInteger'/></element></optional>" +
     44                "<element name='Distance'><data type='positiveInteger'/></element>" +
     45                "<optional><element name='MinEntity'><data type='nonNegativeInteger'/></element></optional>" +
     46                "<optional><element name='MaxEntity'><data type='nonNegativeInteger'/></element></optional>" +
    4647            "</interleave>" +
    4748        "</element>" +
    4849    "</optional>";
     
    5051BuildRestrictions.prototype.Init = function()
    5152{
    5253    this.territories = this.template.Territory.split(/\s+/);
     54    if (this.template.Density)
     55    {
     56        this.minEntity = +(this.template.Density.MinEntity || 0);
     57        this.maxEntity = +(this.template.Density.MaxEntity || Infinity);
     58    }
    5359};
    5460
    5561/**
     
    6066 *      b. On valid terrain, based on passability class
    6167 *  3. Territory type is allowed (see note below)
    6268 *  4. Dock is on shoreline and facing into water
    63  *  5. Distance constraints satisfied
     69 *  5. Density constraints satisfied
    6470 *
    6571 * Returns result object:
    6672 *  {
     
    7985 */
    8086BuildRestrictions.prototype.CheckPlacement = function()
    8187{
    82     var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
    83     var name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";
     88    let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     89    let name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";
    8490
    85     var result = {
     91    let result = {
    8692        "success": false,
    8793        "message": markForTranslation("%(name)s cannot be built due to unknown error"),
    8894        "parameters": {
     
    9399    };
    94100
    95101    // TODO: AI has no visibility info
    96     var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     102    let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
    97103    if (!cmpPlayer.IsAI())
    98104    {
    99105        // Check whether it's in a visible or fogged region
    100         var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    101         var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     106        let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     107        let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    102108        if (!cmpRangeManager || !cmpOwnership)
    103109            return result; // Fail
    104110
    105         var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden");
    106         if (!explored)
     111        if (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) == "hidden")
    107112        {
    108113            result.message = markForTranslation("%(name)s cannot be built in unexplored area");
    109114            return result; // Fail
     
    111116    }
    112117
    113118    // Check obstructions and terrain passability
    114     var passClassName = "";
     119    let passClassName = "";
    115120    switch (this.template.PlacementType)
    116121    {
    117122    case "shore":
     
    129134        passClassName = "building-land";
    130135    }
    131136
    132     var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
     137    let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
    133138    if (!cmpObstruction)
    134139        return result; // Fail
    135140
    136 
     141        // for walls, only test the center point
    137142    if (this.template.Category == "Wall")
    138     {
    139         // for walls, only test the center point
    140143        var ret = cmpObstruction.CheckFoundation(passClassName, true);
    141     }
    142144    else
    143     {
    144145        var ret = cmpObstruction.CheckFoundation(passClassName, false);
    145     }
    146146
    147147    if (ret != "success")
    148148    {
     
    152152        case "fail_no_obstruction":
    153153            error("CheckPlacement: Error returned from CheckFoundation");
    154154            break;
     155
    155156        case "fail_obstructs_foundation":
    156157            result.message = markForTranslation("%(name)s cannot be built on another building or resource");
    157158            break;
     159
    158160        case "fail_terrain_class":
    159161            // TODO: be more specific and/or list valid terrain?
    160162            result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
     
    163165    }
    164166
    165167    // Check territory restrictions
    166     var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
    167     var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
    168     var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    169     if (!(cmpTerritoryManager && cmpPlayer && cmpPosition && cmpPosition.IsInWorld()))
     168    let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
     169    let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     170    let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     171    if (!cmpTerritoryManager || !cmpPlayer || !cmpPosition || !cmpPosition.IsInWorld())
    170172        return result;  // Fail
    171173
    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;
     174    let pos = cmpPosition.GetPosition2D();
     175    let tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
     176    let isConnected = !cmpTerritoryManager.IsTerritoryBlinking(pos.x, pos.y);
    178177
    179     var invalidTerritory = "";
    180     if (isOwn)
     178    let invalidTerritory = "";
     179    if (tileOwner == cmpPlayer.GetPlayerID()) // Own territory
    181180    {
    182181        if (!this.HasTerritory("own"))
    183182            // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
     
    186185            // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
    187186            invalidTerritory = markForTranslationWithContext("Territory type", "unconnected own");
    188187    }
    189     else if (isMutualAlly)
     188    else if (cmpPlayer.IsExclusiveMutualAlly(tileOwner))
    190189    {
    191190        if (!this.HasTerritory("ally"))
    192191            // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
     
    195194            // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
    196195            invalidTerritory = markForTranslationWithContext("Territory type", "unconnected allied");
    197196    }
    198     else if (isNeutral)
     197    else if (tileOwner == 0) // Neutral territory
    199198    {
    200199        if (!this.HasTerritory("neutral"))
    201200            // Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
     
    227226        //      1. ships can be spawned "nearby"
    228227        //      2. builders can pass the terrain where the dock is placed (don't worry about paths)
    229228        //  so it's correct even if the criteria changes for these units
    230         var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
     229        let cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
    231230        if (!cmpFootprint)
    232231            return result;  // Fail
    233232
    234233        // Get building's footprint
    235         var shape = cmpFootprint.GetShape();
    236         var halfSize = 0;
     234        let shape = cmpFootprint.GetShape();
     235        let halfSize = 0;
    237236        if (shape.type == "square")
    238             halfSize = shape.depth/2;
     237            halfSize = shape.depth / 2;
    239238        else if (shape.type == "circle")
    240239            halfSize = shape.radius;
    241240
    242         var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
    243         var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
     241        let cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
     242        let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
    244243        if (!cmpTerrain || !cmpWaterManager)
    245244            return result;  // Fail
    246245
    247         var ang = cmpPosition.GetRotation().y;
    248         var sz = halfSize * Math.sin(ang);
    249         var cz = halfSize * Math.cos(ang);
     246        let ang = cmpPosition.GetRotation().y;
     247        let sz = halfSize * Math.sin(ang);
     248        let cz = halfSize * Math.cos(ang);
    250249        if ((cmpWaterManager.GetWaterLevel(pos.x + sz, pos.y + cz) - cmpTerrain.GetGroundLevel(pos.x + sz, pos.y + cz)) < 1.0 // front
    251250            || (cmpWaterManager.GetWaterLevel(pos.x - sz, pos.y - cz) - cmpTerrain.GetGroundLevel(pos.x - sz, pos.y - cz)) > 2.0) // back
    252251        {
     
    256255    }
    257256
    258257    // Check distance restriction
    259     if (this.template.Distance)
     258    if (this.template.Density)
    260259    {
    261         var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    262         var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
    263         var cat = this.template.Distance.FromClass;
     260        let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     261        let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     262        let cat = this.template.Density.FromClass;
     263        let dist = +this.template.Density.Distance
    264264
    265         var filter = function(id)
     265        let filter = function(id)
    266266        {
    267             var cmpIdentity = Engine.QueryInterface(id, IID_Identity);
     267            let cmpIdentity = Engine.QueryInterface(id, IID_Identity);
    268268            return cmpIdentity.GetClassesList().indexOf(cat) > -1;
    269269        };
    270270       
    271         if (this.template.Distance.MinDistance)
     271        let nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter);
     272
     273        if (nearEnts.length > this.maxEntity)
    272274        {
    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);
     275            let result = markForPluralTranslation(
     276                "%(name)s too close to a %(category)s, must be at least %(distance)s meter away",
     277                "%(name)s too close to a %(category)s, must be at least %(distance)s meters away",
     278                +this.template.Density.Distance);
    281279
    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             }
     280            result.success = false;
     281            result.translateMessage = true;
     282            result.parameters = {
     283                "name": name,
     284                "category": cat,
     285                "distance": this.template.Density.Distance
     286            };
     287            result.translateParameters = ["name", "category"];
     288            return result;  // Fail
    292289        }
    293         if (this.template.Distance.MaxDistance)
     290
     291        if (nearEnts.length < this.minEntity)
    294292        {
    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);
     293            let result = markForPluralTranslation(
     294                "%(name)s too far from a %(category)s, must be within %(distance)s meter",
     295                "%(name)s too far from a %(category)s, must be within %(distance)s meters",
     296                +this.template.Density.Distance);
    303297
    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             }
     298            result.success = false;
     299            result.translateMessage = true;
     300            result.parameters = {
     301                "name": name,
     302                "category": cat,
     303                "distance": this.template.Density.Distance
     304            };
     305            result.translateParameters = ["name", "category"];
     306            return result;  // Fail
    314307        }
    315308    }
    316309
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    988988 */
    989989GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
    990990{
    991     let result = {
    992         "success": false,
    993         "message": "",
    994         "parameters": {},
    995         "translateMessage": false,
    996         "translateParameters": [],
    997     };
     991    let result = {
     992        "success": false,
     993        "message": "",
     994        "parameters": {},
     995        "translateMessage": false,
     996        "translateParameters": [],
     997    };
    998998
    999999    // See if we're changing template
    10001000    if (!this.placementEntity || this.placementEntity[0] != cmd.template)
     
    12441244    // calculate wall placement and position preview entities
    12451245
    12461246    let result = {
     1247        "success": true,
    12471248        "pieces": [],
    12481249        "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0 },
    12491250    };
     
    14841485                continue;
    14851486            }
    14861487
    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            }
    14891497
     1498            validPlacement = (cmpBuildRestrictions && placementResult.success);
     1499
    14901500            // If a wall piece has two control groups, it's likely a segment that spans
    14911501            // between two existing towers. To avoid placing a duplicate wall segment,
    14921502            // check for collisions with entities that share both control groups.
     
    15501560    // If any were entities required to build the wall, but none of them could be validly positioned, return failure
    15511561    // (see method-level documentation).
    15521562    if (numRequiredPieces > 0 && result.pieces.length == 0)
    1553         return false;
     1563        return result;
    15541564
    15551565    if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY)
    15561566        result.startSnappedEnt = start.snappedEnt;
  • binaries/data/mods/public/simulation/helpers/Walls.js

     
    2020 */
    2121function GetWallPlacement(placementData, wallSet, start, end)
    2222{
    23     var result = [];
     23    let result = [];
    2424   
    25     var 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},
     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 },
    2929    ];
    3030   
    31     var towerWidth = placementData[wallSet.templates.tower].templateData.wallPiece.length;
     31    let towerWidth = placementData[wallSet.templates.tower].templateData.wallPiece.length;
    3232   
    33     var dir = {"x": end.pos.x - start.pos.x, "z": end.pos.z - start.pos.z};
    34     var len = 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);
    3535   
    3636    // we'll need room for at least our starting and ending towers to fit next to eachother
    3737    if (len <= towerWidth)
    3838        return result;
    3939   
    40     var placement = GetWallSegmentsRec(len, candidateSegments, wallSet.minTowerOverlap, wallSet.maxTowerOverlap, towerWidth, 0, []);
     40    let placement = GetWallSegmentsRec(len, candidateSegments, wallSet.minTowerOverlap, wallSet.maxTowerOverlap, towerWidth, 0, []);
    4141   
    4242    // TODO: make sure intermediate towers are spaced out far enough for their obstructions to not overlap, implying that
    4343    // tower's wallpiece lengths should be > their obstruction width, which is undesirable because it prevents towers with
     
    4444    // wide bases
    4545    if (placement)
    4646    {
    47         var placedEntities = placement.segments; // list of chosen candidate segments
    48         var r = placement.r; // remaining distance to target without towers (must be <= (N-1) * towerWidth)
    49         var s = r / (2 * placedEntities.length); // spacing
     47        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
    5050       
    51         var dirNormalized = {"x": dir.x / len, "z": dir.z / len};
    52         var angle = -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)
    5353       
    54         var progress = 0;
    55         for (var i = 0; i < placedEntities.length; i++)
     54        let progress = 0;
     55        for (let i = 0; i < placedEntities.length; i++)
    5656        {
    57             var placedEntity = placedEntities[i];
    58             var targetX = start.pos.x + (progress + s + placedEntity.len/2) * dirNormalized.x;
    59             var targetZ = 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;
    6060           
    6161            result.push({
    6262                "template": placedEntity.template,
    63                 "pos": {"x": targetX, "z": targetZ},
     63                "pos": { "x": targetX, "z": targetZ },
    6464                "angle": angle,
    6565            });
    6666           
    6767            if (i < placedEntities.length - 1)
    6868            {
    69                 var towerX = start.pos.x + (progress + placedEntity.len + 2*s) * dirNormalized.x;
    70                 var towerZ = 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;
    7171               
    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                    });
    7777            }
    7878           
    7979            progress += placedEntity.len + 2*s;
     
    160160    // backtrack and try a wall segment of the next length instead. Note that we should prefer to use the long segments first since
    161161    // they can be replaced by gates.
    162162   
    163     for each (var candSegment in candidateSegments)
     163    for (let candSegment of candidateSegments)
    164164    {
    165165        segments.push(candSegment);
    166166       
    167         var newDistSoFar = distSoFar + candSegment.len;
    168         var r = d - newDistSoFar;
     167        let newDistSoFar = distSoFar + candSegment.len;
     168        let r = d - newDistSoFar;
    169169       
    170         var rLowerBound = (1 - 2 * maxOverlap) * segments.length * t;
    171         var rUpperBound = (1 - 2 * minOverlap) * segments.length * t;
     170        let rLowerBound = (1 - 2 * maxOverlap) * segments.length * t;
     171        let rUpperBound = (1 - 2 * minOverlap) * segments.length * t;
    172172       
    173173        if (r < rLowerBound)
    174174        {
     
    179179        }
    180180        else if (r > rUpperBound)
    181181        {
    182             var recursiveResult = GetWallSegmentsRec(d, candidateSegments, minOverlap, maxOverlap, t, newDistSoFar, segments);
     182            let recursiveResult = GetWallSegmentsRec(d, candidateSegments, minOverlap, maxOverlap, t, newDistSoFar, segments);
    183183            if (!recursiveResult)
    184184            {
    185185                // recursive search with this piece yielded no results, pop it and try the next one
     
    192192        else
    193193        {
    194194            // found a placement
    195             return {"segments": segments, "r": r};
     195            return { "segments": segments, "r": r };
    196196        }
    197197    }
    198198   
  • binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml

     
    2929  <BuildRestrictions>
    3030    <Territory>own neutral</Territory>
    3131    <Category>CivilCentre</Category>
    32     <Distance>
     32    <Density>
    3333      <FromClass>CivilCentre</FromClass>
    34       <MinDistance>200</MinDistance>
    35     </Distance>
     34      <Distance>200</Distance>
     35      <MaxEntity>0</MaxEntity>
     36    </Density>
    3637  </BuildRestrictions>
    3738  <Capturable>
    3839    <CapturePoints>2500</CapturePoints>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml

     
    2222  </BuildingAI>
    2323  <BuildRestrictions>
    2424    <Category>DefenseTower</Category>
    25     <Distance>
     25    <Density>
    2626      <FromClass>DefenseTower</FromClass>
    27       <MinDistance>60</MinDistance>
    28     </Distance>
     27      <Distance>60</Distance>
     28      <MaxEntity>0</MaxEntity>
     29    </Density>
    2930  </BuildRestrictions>
    3031  <Cost>
    3132    <BuildTime>150</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml

     
    2121  </BuildingAI>
    2222  <BuildRestrictions>
    2323    <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>
    2530  </BuildRestrictions>
    2631  <Capturable disable=""/>
    2732  <Repairable>
     
    5257  <Identity>
    5358    <GenericName>Wall Turret</GenericName>
    5459    <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>
    5661    <Icon>structures/tower.png</Icon>
    5762    <RequiredTechnology>phase_town</RequiredTechnology>
    5863  </Identity>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wooden_tower.xml

     
    1616  </Attack>
    1717  <BuildRestrictions>
    1818    <Category>DefenseTower</Category>
    19     <Distance>
     19    <Density>
    2020      <FromClass>DefenseTower</FromClass>
    21       <MinDistance>40</MinDistance>
    22     </Distance>
     21      <Distance>40</Distance>
     22      <MaxEntity>0</MaxEntity>
     23    </Density>
    2324  </BuildRestrictions>
    2425  <Cost>
    2526    <BuildTime>40</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml

     
    2626  </BuildingAI>
    2727  <BuildRestrictions>
    2828    <Category>Fortress</Category>
    29     <Distance>
     29    <Density>
    3030      <FromClass>Fortress</FromClass>
    31       <MinDistance>80</MinDistance>
    32     </Distance>
     31      <Distance>80</Distance>
     32      <MaxEntity>0</MaxEntity>
     33    </Density>
    3334  </BuildRestrictions>
    3435  <Capturable>
    3536    <CapturePoints>4000</CapturePoints>