|
|
|
@ -81,7 +81,8 @@ BuildRestrictions.prototype.Init = function()
|
|
|
|
|
* (template name should be "preview|"+templateName), as otherwise territory
|
|
|
|
|
* checks for buildings with territory influence will not work as expected.
|
|
|
|
|
*/
|
|
|
|
|
BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
BuildRestrictions.prototype.CheckPlacement = function()
|
|
|
|
|
{
|
|
|
|
|
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
|
|
|
|
var name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";
|
|
|
|
|
|
|
|
|
@ -100,7 +101,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
return result; // Fail
|
|
|
|
|
|
|
|
|
|
// TODO: AI has no visibility info
|
|
|
|
|
if (!cmpPlayer.IsAI()) {
|
|
|
|
|
if (!cmpPlayer.IsAI())
|
|
|
|
|
{
|
|
|
|
|
// Check whether it's in a visible or fogged region
|
|
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
|
|
|
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
|
|
|
@ -108,7 +110,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
return result; // Fail
|
|
|
|
|
|
|
|
|
|
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden");
|
|
|
|
|
if (!explored) {
|
|
|
|
|
if (!explored)
|
|
|
|
|
{
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built in unexplored area");
|
|
|
|
|
return result; // Fail
|
|
|
|
|
}
|
|
|
|
@ -116,20 +119,21 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
|
|
|
|
|
// Check obstructions and terrain passability
|
|
|
|
|
var passClassName = "";
|
|
|
|
|
switch (this.template.PlacementType) {
|
|
|
|
|
case "shore":
|
|
|
|
|
passClassName = "building-shore";
|
|
|
|
|
break;
|
|
|
|
|
switch (this.template.PlacementType)
|
|
|
|
|
{
|
|
|
|
|
case "shore":
|
|
|
|
|
passClassName = "building-shore";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "land-shore":
|
|
|
|
|
// 'default-terrain-only' is everywhere a normal unit can go, ignoring
|
|
|
|
|
// obstructions (i.e. on passable land, and not too deep in the water)
|
|
|
|
|
passClassName = "default-terrain-only";
|
|
|
|
|
break;
|
|
|
|
|
case "land-shore":
|
|
|
|
|
// 'default-terrain-only' is everywhere a normal unit can go, ignoring
|
|
|
|
|
// obstructions (i.e. on passable land, and not too deep in the water)
|
|
|
|
|
passClassName = "default-terrain-only";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "land":
|
|
|
|
|
default:
|
|
|
|
|
passClassName = "building-land";
|
|
|
|
|
case "land":
|
|
|
|
|
default:
|
|
|
|
|
passClassName = "building-land";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
|
|
|
|
@ -137,38 +141,30 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
return result; // Fail
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.template.Category == "Wall") {
|
|
|
|
|
// allow walls to be built on top of trees
|
|
|
|
|
let collisions = cmpObstruction.GetEntitiesBlockingConstruction();
|
|
|
|
|
let areWoodEnts = collisions.length > 0;
|
|
|
|
|
for (let ent of collisions) {
|
|
|
|
|
// don't let entities with unitmotion prevent us from laying a foundation
|
|
|
|
|
let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
|
|
|
|
|
let cmpResource = Engine.QueryInterface(ent, IID_ResourceSupply);
|
|
|
|
|
if ((!cmpResource || cmpResource.GetType().generic !== "wood") && !cmpUnitMotion) {
|
|
|
|
|
areWoodEnts = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.template.Category == "Wall")
|
|
|
|
|
{
|
|
|
|
|
// for walls, only test the center point
|
|
|
|
|
var ret = areWoodEnts ? "success" : cmpObstruction.CheckFoundation(passClassName, true);
|
|
|
|
|
var ret = cmpObstruction.CheckFoundation(passClassName, true);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var ret = cmpObstruction.CheckFoundation(passClassName, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret != "success") {
|
|
|
|
|
switch (ret) {
|
|
|
|
|
case "fail_error":
|
|
|
|
|
case "fail_no_obstruction":
|
|
|
|
|
error("CheckPlacement: Error returned from CheckFoundation");
|
|
|
|
|
break;
|
|
|
|
|
case "fail_obstructs_foundation":
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built on another building or resource");
|
|
|
|
|
break;
|
|
|
|
|
case "fail_terrain_class":
|
|
|
|
|
// TODO: be more specific and/or list valid terrain?
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
|
|
|
|
|
if (ret != "success")
|
|
|
|
|
{
|
|
|
|
|
switch (ret)
|
|
|
|
|
{
|
|
|
|
|
case "fail_error":
|
|
|
|
|
case "fail_no_obstruction":
|
|
|
|
|
error("CheckPlacement: Error returned from CheckFoundation");
|
|
|
|
|
break;
|
|
|
|
|
case "fail_obstructs_foundation":
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built on another building or resource");
|
|
|
|
|
break;
|
|
|
|
|
case "fail_terrain_class":
|
|
|
|
|
// TODO: be more specific and/or list valid terrain?
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
|
|
|
|
|
}
|
|
|
|
|
return result; // Fail
|
|
|
|
|
}
|
|
|
|
@ -187,7 +183,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
var isNeutral = tileOwner == 0;
|
|
|
|
|
|
|
|
|
|
var invalidTerritory = "";
|
|
|
|
|
if (isOwn) {
|
|
|
|
|
if (isOwn)
|
|
|
|
|
{
|
|
|
|
|
if (!this.HasTerritory("own"))
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "own");
|
|
|
|
@ -195,7 +192,8 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "unconnected own");
|
|
|
|
|
}
|
|
|
|
|
else if (isMutualAlly) {
|
|
|
|
|
else if (isMutualAlly)
|
|
|
|
|
{
|
|
|
|
|
if (!this.HasTerritory("ally"))
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "allied");
|
|
|
|
@ -203,19 +201,22 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "unconnected allied");
|
|
|
|
|
}
|
|
|
|
|
else if (isNeutral) {
|
|
|
|
|
else if (isNeutral)
|
|
|
|
|
{
|
|
|
|
|
if (!this.HasTerritory("neutral"))
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "neutral");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// consider everything else enemy territory
|
|
|
|
|
if (!this.HasTerritory("enemy"))
|
|
|
|
|
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
|
|
|
|
|
invalidTerritory = markForTranslationWithContext("Territory type", "enemy");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (invalidTerritory) {
|
|
|
|
|
if (invalidTerritory)
|
|
|
|
|
{
|
|
|
|
|
result.message = markForTranslation("%(name)s cannot be built in %(territoryType)s territory. Valid territories: %(validTerritories)s");
|
|
|
|
|
result.translateParameters.push("territoryType");
|
|
|
|
|
result.translateParameters.push("validTerritories");
|
|
|
|
@ -226,8 +227,10 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check special requirements
|
|
|
|
|
if (this.template.PlacementType == "shore") {
|
|
|
|
|
if (!cmpObstruction.CheckShorePlacement()) {
|
|
|
|
|
if (this.template.PlacementType == "shore")
|
|
|
|
|
{
|
|
|
|
|
if (!cmpObstruction.CheckShorePlacement())
|
|
|
|
|
{
|
|
|
|
|
result.message = markForTranslation("%(name)s must be built on a valid shoreline");
|
|
|
|
|
return result; // Fail
|
|
|
|
|
}
|
|
|
|
@ -239,18 +242,22 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
let template = cmpTemplateManager.GetTemplate(removeFiltersFromTemplateName(templateName));
|
|
|
|
|
|
|
|
|
|
// Check distance restriction
|
|
|
|
|
if (this.template.Distance) {
|
|
|
|
|
if (this.template.Distance)
|
|
|
|
|
{
|
|
|
|
|
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
|
|
|
|
var cat = this.template.Distance.FromClass;
|
|
|
|
|
|
|
|
|
|
var filter = function (id) {
|
|
|
|
|
var filter = function(id)
|
|
|
|
|
{
|
|
|
|
|
var cmpIdentity = Engine.QueryInterface(id, IID_Identity);
|
|
|
|
|
return cmpIdentity.GetClassesList().indexOf(cat) > -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (this.template.Distance.MinDistance !== undefined) {
|
|
|
|
|
if (this.template.Distance.MinDistance !== undefined)
|
|
|
|
|
{
|
|
|
|
|
let minDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MinDistance", +this.template.Distance.MinDistance, cmpPlayer.GetPlayerID(), template);
|
|
|
|
|
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter)) {
|
|
|
|
|
if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
|
|
|
|
|
{
|
|
|
|
|
let result = markForPluralTranslation(
|
|
|
|
|
"%(name)s too close to a %(category)s, must be at least %(distance)s meter away",
|
|
|
|
|
"%(name)s too close to a %(category)s, must be at least %(distance)s meters away",
|
|
|
|
@ -267,9 +274,11 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
return result; // Fail
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.template.Distance.MaxDistance !== undefined) {
|
|
|
|
|
if (this.template.Distance.MaxDistance !== undefined)
|
|
|
|
|
{
|
|
|
|
|
let maxDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MaxDistance", +this.template.Distance.MaxDistance, cmpPlayer.GetPlayerID(), template);
|
|
|
|
|
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter)) {
|
|
|
|
|
if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
|
|
|
|
|
{
|
|
|
|
|
let result = markForPluralTranslation(
|
|
|
|
|
"%(name)s too far from a %(category)s, must be within %(distance)s meter",
|
|
|
|
|
"%(name)s too far from a %(category)s, must be within %(distance)s meters",
|
|
|
|
@ -294,11 +303,6 @@ BuildRestrictions.prototype.CheckPlacement = function () {
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
BuildRestrictions.prototype.IsWoodEntity = function (entity) {
|
|
|
|
|
let cmpResource = Engine.QueryInterface(entity, IID_ResourceSupply);
|
|
|
|
|
return cmpResource && cmpResource.GetType().generic === "wood";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BuildRestrictions.prototype.GetCategory = function()
|
|
|
|
|
{
|
|
|
|
|
return this.template.Category;
|
|
|
|
|