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+/);
};
/**
* Returns true iff this entity can be built at its current position.
*/
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);