function BuildRestrictions() {} BuildRestrictions.prototype.Schema = "Specifies building placement restrictions as they relate to terrain, territories, and distance." + "" + "" + "land" + "own" + "Special" + "" + "CivilCentre" + "40" + "" + "" + "" + "" + "" + "land" + "shore" + "" + "" + "" + "" + "" + "" + "own" + "ally" + "neutral" + "enemy" + "" + "" + "" + "" + "" + "" + "CivilCentre" + "House" + "DefenseTower" + "Farmstead" + "Market" + "Barracks" + "Dock" + "Fortress" + "Field" + "Temple" + "Wall" + "Fence" + "Mill" + "Stoa" + "Resource" + "Special" + "" + "" + "" + "" + "" + "" + "" + "CivilCentre" + "" + "" + "" + "" + "" + "" + ""; // TODO: add phases, prerequisites, etc BuildRestrictions.prototype.Init = function() { this.territories = this.template.Territory.split(/\s+/); }; BuildRestrictions.prototype.CheckPlacement = function(player) { // TODO: Return error code for invalid placement, which can be handled by the UI // Check obstructions and terrain passability var passClassName = ""; switch (this.template.PlacementType) { case "shore": passClassName = "building-shore"; break; case "land": default: passClassName = "building-land"; } var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction); if (!cmpObstruction || !cmpObstruction.CheckFoundation(passClassName)) { return false; // Fail } // Check territory restrictions var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); var cmpPlayer = QueryPlayerIDInterface(player, IID_Player); var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); if (!(cmpTerritoryManager && cmpPlayer && cmpPosition && cmpPosition.IsInWorld())) { return false; // Fail } var pos = cmpPosition.GetPosition2D(); var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y); var isOwn = (tileOwner == player); var isNeutral = (tileOwner == 0); var isAlly = !isOwn && cmpPlayer.IsAlly(tileOwner); var isEnemy = !isNeutral && cmpPlayer.IsEnemy(tileOwner); if ((isAlly && !this.HasTerritory("ally")) || (isOwn && !this.HasTerritory("own")) || (isNeutral && !this.HasTerritory("neutral")) || (isEnemy && !this.HasTerritory("enemy"))) { return false; // Fail } // Check special requirements if (this.template.Category == "Dock") { // TODO: Probably should check unit passability classes here, to determine if: // 1. ships can be spawned "nearby" // 2. builders can pass the terrain where the dock is placed (don't worry about paths) // so it's correct even if the criteria changes for these units var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint); if (!cmpFootprint) { return false; // Fail } // Get building's footprint var shape = cmpFootprint.GetShape(); var halfSize = 0; if (shape.type == "square") { halfSize = shape.depth/2; } else if (shape.type == "circle") { halfSize = shape.radius; } var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain); var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); if (!cmpTerrain || !cmpWaterManager) { return false; // Fail } var ang = cmpPosition.GetRotation().y; var sz = halfSize * Math.sin(ang); var cz = halfSize * Math.cos(ang); if ((cmpWaterManager.GetWaterLevel(pos.x + sz, pos.y + cz) - cmpTerrain.GetGroundLevel(pos.x + sz, pos.y + cz)) < 1.0 // front || (cmpWaterManager.GetWaterLevel(pos.x - sz, pos.y - cz) - cmpTerrain.GetGroundLevel(pos.x - sz, pos.y - cz)) > 2.0) // back { return false; // Fail } } // Check distance restriction if (this.template.Distance) { var nearest = 65535; var ents = Engine.GetEntitiesWithInterface(IID_BuildRestrictions); for each (var ent in ents) { var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions); if (cmpBuildRestrictions.GetCategory() == this.template.Distance.FromCategory && IsOwnedByPlayer(player, ent)) { // Find nearest building matching this category var cmpEntPosition = Engine.QueryInterface(ent, IID_Position); if (cmpEntPosition && cmpEntPosition.IsInWorld()) { var entPos = cmpEntPosition.GetPosition2D(); var dist = Math.sqrt((pos.x-entPos.x)*(pos.x-entPos.x) + (pos.y-entPos.y)*(pos.y-entPos.y)); if (dist < nearest) { nearest = dist; } } } } if ((this.template.Distance.MinDistance && nearest < this.template.Distance.MinDistance) || (this.template.Distance.MaxDistance && nearest > this.template.Distance.MaxDistance)) { return false; // Fail } } // Success return true; }; BuildRestrictions.prototype.GetCategory = function() { return this.template.Category; }; BuildRestrictions.prototype.GetTerritories = function() { return this.territories; }; BuildRestrictions.prototype.HasTerritory = function(territory) { return (this.territories.indexOf(territory) != -1); }; Engine.RegisterComponentType(IID_BuildRestrictions, "BuildRestrictions", BuildRestrictions);