diff --git a/binaries/data/mods/public/gui/gui.dtd b/binaries/data/mods/public/gui/gui.dtd
index 8c30c22804..8b0799ddc3 100644
--- a/binaries/data/mods/public/gui/gui.dtd
+++ b/binaries/data/mods/public/gui/gui.dtd
@@ -135,7 +135,6 @@
sprite_bar_vertical_pressed CDATA #IMPLIED
sprite_back_vertical CDATA #IMPLIED
minimum_bar_size CDATA #IMPLIED
- maximum_bar_size CDATA #IMPLIED
>
-
+
-
@@ -950,10 +950,10 @@
>
-
+
-
+
@@ -991,25 +991,21 @@
-
+
-
-
-
-
-
-
+
+
-
+
-
+
@@ -1017,18 +1013,18 @@
-
+
-
+
-
+
-
+
@@ -1036,13 +1032,13 @@
-
+
-
+
@@ -1216,7 +1212,7 @@
-
+
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index aad16514a3..56501a16f4 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -26,13 +26,13 @@ GuiInterface.prototype.Init = function()
};
/*
- * All of the functions defined below are called via Engine.GuiInterfaceCall(name, arg)
- * from GUI scripts, and executed here with arguments (player, arg).
- *
- * CAUTION: The input to the functions in this module is not network-synchronised, so it
- * mustn't affect the simulation state (i.e. the data that is serialised and can affect
- * the behaviour of the rest of the simulation) else it'll cause out-of-sync errors.
- */
+ * All of the functions defined below are called via Engine.GuiInterfaceCall(name, arg)
+ * from GUI scripts, and executed here with arguments (player, arg).
+ *
+ * CAUTION: The input to the functions in this module is not network-synchronised, so it
+ * mustn't affect the simulation state (i.e. the data that is serialised and can affect
+ * the behaviour of the rest of the simulation) else it'll cause out-of-sync errors.
+ */
/**
* Returns global information about the current game state.
@@ -41,29 +41,29 @@ GuiInterface.prototype.Init = function()
GuiInterface.prototype.GetSimulationState = function(player)
{
var ret = {
- "players": []
- };
-
- var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
- var n = cmpPlayerMan.GetNumPlayers();
+ "players": []
+ };
+
+ var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ var n = cmpPlayerMan.GetNumPlayers();
for (var i = 0; i < n; ++i)
{
var playerEnt = cmpPlayerMan.GetPlayerByID(i);
- var cmpPlayerEntityLimits = Engine.QueryInterface(playerEnt, IID_EntityLimits);
- var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
-
- // Work out what phase we are in
- var cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
+ var cmpPlayerEntityLimits = Engine.QueryInterface(playerEnt, IID_EntityLimits);
+ var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
+
+ // Work out what phase we are in
+ var cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
var phase = "";
if (cmpTechnologyManager.IsTechnologyResearched("phase_city"))
phase = "city";
else if (cmpTechnologyManager.IsTechnologyResearched("phase_town"))
phase = "town";
- else if (cmpTechnologyManager.IsTechnologyResearched("phase_village"))
- phase = "village";
-
- // store player ally/neutral/enemy data as arrays
- var allies = [];
+ else if (cmpTechnologyManager.IsTechnologyResearched("phase_village"))
+ phase = "village";
+
+ // store player ally/neutral/enemy data as arrays
+ var allies = [];
var mutualAllies = [];
var neutrals = [];
var enemies = [];
@@ -108,11 +108,11 @@ GuiInterface.prototype.GetSimulationState = function(player)
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager)
{
- ret.circularMap = cmpRangeManager.GetLosCircular();
- }
-
- // Add timeElapsed
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ ret.circularMap = cmpRangeManager.GetLosCircular();
+ }
+
+ // Add timeElapsed
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
ret.timeElapsed = cmpTimer.GetTime();
return ret;
@@ -167,11 +167,11 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"rank": cmpIdentity.GetRank(),
"classes": cmpIdentity.GetClassesList(),
"selectionGroupName": cmpIdentity.GetSelectionGroupName()
- };
- }
-
- var cmpPosition = Engine.QueryInterface(ent, IID_Position);
- if (cmpPosition && cmpPosition.IsInWorld())
+ };
+ }
+
+ var cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ if (cmpPosition && cmpPosition.IsInWorld())
{
ret.position = cmpPosition.GetPosition();
ret.rotation = cmpPosition.GetRotation();
@@ -214,11 +214,11 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
else
{
// not in world, set a default?
- ret.attack.elevationAdaptedRange = ret.attack.maxRange;
- }
-
- }
- else
+ ret.attack.elevationAdaptedRange = ret.attack.maxRange;
+ }
+
+ }
+ else
{
// not a ranged attack, set some defaults
ret.attack.elevationBonus = 0;
@@ -298,14 +298,11 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"max": cmpResourceSupply.GetMaxAmount(),
"amount": cmpResourceSupply.GetCurrentAmount(),
"type": cmpResourceSupply.GetType(),
- "killBeforeGather": cmpResourceSupply.GetKillBeforeGather(),
- "maxGatherers": cmpResourceSupply.GetMaxGatherers(),
- "gatherers": cmpResourceSupply.GetGatherers(),
- "throughput": cmpResourceSupply.GetGatherers().reduce(function (total, e)
- total + Engine.QueryInterface(e, IID_ResourceGatherer).GetTargetGatherRate(ent)
- , 0)
- };
- }
+ "killBeforeGather": cmpResourceSupply.GetKillBeforeGather(),
+ "maxGatherers": cmpResourceSupply.GetMaxGatherers(),
+ "gatherers": cmpResourceSupply.GetGatherers()
+ };
+ }
var cmpResourceGatherer = Engine.QueryInterface(ent, IID_ResourceGatherer);
if (cmpResourceGatherer)
@@ -335,11 +332,11 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"entities": cmpGarrisonHolder.GetEntities(),
"allowedClasses": cmpGarrisonHolder.GetAllowedClassesList(),
"capacity": cmpGarrisonHolder.GetCapacity()
- };
- }
-
- var cmpPromotion = Engine.QueryInterface(ent, IID_Promotion);
- if (cmpPromotion)
+ };
+ }
+
+ var cmpPromotion = Engine.QueryInterface(ent, IID_Promotion);
+ if (cmpPromotion)
{
ret.promotion = {
"curr": cmpPromotion.GetCurrentXp(),
@@ -362,22 +359,22 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
if (cmpGate)
{
ret.gate = {
- "locked": cmpGate.IsLocked(),
- };
- }
-
- if (!cmpFoundation && cmpIdentity && cmpIdentity.HasClass("BarterMarket"))
+ "locked": cmpGate.IsLocked(),
+ };
+ }
+
+ if (!cmpFoundation && cmpIdentity && cmpIdentity.HasClass("BarterMarket"))
{
var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
ret.barterMarket = { "prices": cmpBarter.GetPrices() };
}
var cmpHeal = Engine.QueryInterface(ent, IID_Heal);
- if (cmpHeal)
- {
- ret.Healer = {
- "unhealableClasses": cmpHeal.GetUnhealableClasses(),
- "healableClasses": cmpHeal.GetHealableClasses(),
+ if (cmpHeal)
+ {
+ ret.Healer = {
+ "unhealableClasses": cmpHeal.GetUnhealableClasses(),
+ "healableClasses": cmpHeal.GetHealableClasses(),
};
}
@@ -425,11 +422,11 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
"hack": GetTechModifiedProperty(techMods, template, "Armour/Hack", +template.Armour.Hack),
"pierce": GetTechModifiedProperty(techMods, template, "Armour/Pierce", +template.Armour.Pierce),
"crush": GetTechModifiedProperty(techMods, template, "Armour/Crush", +template.Armour.Crush),
- };
- }
-
- if (template.Attack)
- {
+ };
+ }
+
+ if (template.Attack)
+ {
ret.attack = {};
for (var type in template.Attack)
{
@@ -441,20 +438,20 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
"maxRange": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/MaxRange", +template.Attack[type].MaxRange),
"elevationBonus": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/ElevationBonus", +(template.Attack[type].ElevationBonus || 0)),
};
- }
- }
-
- if (template.BuildRestrictions)
- {
+ }
+ }
+
+ if (template.BuildRestrictions)
+ {
// required properties
ret.buildRestrictions = {
"placementType": template.BuildRestrictions.PlacementType,
"territory": template.BuildRestrictions.Territory,
- "category": template.BuildRestrictions.Category,
- };
-
- // optional properties
- if (template.BuildRestrictions.Distance)
+ "category": template.BuildRestrictions.Category,
+ };
+
+ // optional properties
+ if (template.BuildRestrictions.Distance)
{
ret.buildRestrictions.distance = {
"fromCategory": template.BuildRestrictions.Distance.FromCategory,
@@ -480,23 +477,23 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
if (template.Cost.Resources.metal) ret.cost.metal = GetTechModifiedProperty(techMods, template, "Cost/Resources/metal", +template.Cost.Resources.metal);
if (template.Cost.Population) ret.cost.population = GetTechModifiedProperty(techMods, template, "Cost/Population", +template.Cost.Population);
if (template.Cost.PopulationBonus) ret.cost.populationBonus = GetTechModifiedProperty(techMods, template, "Cost/PopulationBonus", +template.Cost.PopulationBonus);
- if (template.Cost.BuildTime) ret.cost.time = GetTechModifiedProperty(techMods, template, "Cost/BuildTime", +template.Cost.BuildTime);
- }
-
- if (template.Footprint)
- {
- ret.footprint = {"height": template.Footprint.Height};
-
- if (template.Footprint.Square)
- ret.footprint.square = {"width": +template.Footprint.Square["@width"], "depth": +template.Footprint.Square["@depth"]};
+ if (template.Cost.BuildTime) ret.cost.time = GetTechModifiedProperty(techMods, template, "Cost/BuildTime", +template.Cost.BuildTime);
+ }
+
+ if (template.Footprint)
+ {
+ ret.footprint = {"height": template.Footprint.Height};
+
+ if (template.Footprint.Square)
+ ret.footprint.square = {"width": +template.Footprint.Square["@width"], "depth": +template.Footprint.Square["@depth"]};
else if (template.Footprint.Circle)
ret.footprint.circle = {"radius": +template.Footprint.Circle["@radius"]};
else
- warn("[GetTemplateData] Unrecognized Footprint type");
- }
-
- if (template.Obstruction)
- {
+ warn("[GetTemplateData] Unrecognized Footprint type");
+ }
+
+ if (template.Obstruction)
+ {
ret.obstruction = {
"active": ("" + template.Obstruction.Active == "true"),
"blockMovement": ("" + template.Obstruction.BlockMovement == "true"),
@@ -505,11 +502,11 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
"blockConstruction": ("" + template.Obstruction.BlockConstruction == "true"),
"disableBlockMovement": ("" + template.Obstruction.DisableBlockMovement == "true"),
"disableBlockPathfinding": ("" + template.Obstruction.DisableBlockPathfinding == "true"),
- "shape": {}
- };
-
- if (template.Obstruction.Static)
- {
+ "shape": {}
+ };
+
+ if (template.Obstruction.Static)
+ {
ret.obstruction.shape.type = "static";
ret.obstruction.shape.width = +template.Obstruction.Static["@width"];
ret.obstruction.shape.depth = +template.Obstruction.Static["@depth"];
@@ -574,11 +571,11 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
},
"maxTowerOverlap": +template.WallSet.MaxTowerOverlap,
"minTowerOverlap": +template.WallSet.MinTowerOverlap,
- };
- }
-
- if (template.WallPiece)
- {
+ };
+ }
+
+ if (template.WallPiece)
+ {
ret.wallPiece = {"length": +template.WallPiece.Length};
}
@@ -587,30 +584,30 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
GuiInterface.prototype.GetTechnologyData = function(player, name)
{
- var cmpTechTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TechnologyTemplateManager);
- var template = cmpTechTempMan.GetTemplate(name);
-
- if (!template)
- {
+ var cmpTechTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TechnologyTemplateManager);
+ var template = cmpTechTempMan.GetTemplate(name);
+
+ if (!template)
+ {
warn("Tried to get data for invalid technology: " + name);
- return null;
- }
-
- var ret = {};
-
- // Get specific name for this civ or else the generic specific name
- var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
- var specific = undefined;
+ return null;
+ }
+
+ var ret = {};
+
+ // Get specific name for this civ or else the generic specific name
+ var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
+ var specific = undefined;
if (template.specificName)
{
if (template.specificName[cmpPlayer.GetCiv()])
specific = template.specificName[cmpPlayer.GetCiv()];
else
- specific = template.specificName['generic'];
- }
-
- ret.name = {
- "specific": specific,
+ specific = template.specificName['generic'];
+ }
+
+ ret.name = {
+ "specific": specific,
"generic": template.genericName,
};
ret.icon = "technologies/" + template.icon;
@@ -620,39 +617,39 @@ GuiInterface.prototype.GetTechnologyData = function(player, name)
"metal": template.cost ? (+template.cost.metal) : 0,
"stone": template.cost ? (+template.cost.stone) : 0,
"time": template.researchTime ? (+template.researchTime) : 0,
- }
- ret.tooltip = template.tooltip;
-
- if (template.requirementsTooltip)
- ret.requirementsTooltip = template.requirementsTooltip;
- else
- ret.requirementsTooltip = "";
-
- ret.description = template.description;
-
- return ret;
-};
+ }
+ ret.tooltip = template.tooltip;
+
+ if (template.requirementsTooltip)
+ ret.requirementsTooltip = template.requirementsTooltip;
+ else
+ ret.requirementsTooltip = "";
+
+ ret.description = template.description;
+
+ return ret;
+};
GuiInterface.prototype.IsTechnologyResearched = function(player, tech)
-{
- var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
-
- if (!cmpTechnologyManager)
- return false;
-
- return cmpTechnologyManager.IsTechnologyResearched(tech);
-};
+{
+ var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
+
+ if (!cmpTechnologyManager)
+ return false;
+
+ return cmpTechnologyManager.IsTechnologyResearched(tech);
+};
// Checks whether the requirements for this technology have been met
GuiInterface.prototype.CheckTechnologyRequirements = function(player, tech)
-{
- var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
-
- if (!cmpTechnologyManager)
- return false;
-
- return cmpTechnologyManager.CanResearch(tech);
-};
+{
+ var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
+
+ if (!cmpTechnologyManager)
+ return false;
+
+ return cmpTechnologyManager.CanResearch(tech);
+};
// Returns technologies that are being actively researched, along with
// which entity is researching them and how far along the research is.
@@ -752,11 +749,11 @@ GuiInterface.prototype.IsStanceSelected = function(player, data)
GuiInterface.prototype.SetSelectionHighlight = function(player, cmd, selected)
{
- var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
- var playerColours = {}; // cache of owner -> colour map
-
- for each (var ent in cmd.entities)
- {
+ var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ var playerColours = {}; // cache of owner -> colour map
+
+ for each (var ent in cmd.entities)
+ {
var cmpSelectable = Engine.QueryInterface(ent, IID_Selectable);
if (!cmpSelectable)
continue;
@@ -797,15 +794,15 @@ GuiInterface.prototype.GetPlayerEntities = function(player)
return cmpRangeManager.GetEntitiesByPlayer(player);
};
-/**
- * Displays the rally points of a given list of entities (carried in cmd.entities).
- *
- * The 'cmd' object may carry its own x/z coordinate pair indicating the location where the rally point should
- * be rendered, in order to support instantaneously rendering a rally point marker at a specified location
- * instead of incurring a delay while PostNetworkCommand processes the set-rallypoint command (see input.js).
- * If cmd doesn't carry a custom location, then the position to render the marker at will be read from the
- * RallyPoint component.
- */
+/**
+ * Displays the rally points of a given list of entities (carried in cmd.entities).
+ *
+ * The 'cmd' object may carry its own x/z coordinate pair indicating the location where the rally point should
+ * be rendered, in order to support instantaneously rendering a rally point marker at a specified location
+ * instead of incurring a delay while PostNetworkCommand processes the set-rallypoint command (see input.js).
+ * If cmd doesn't carry a custom location, then the position to render the marker at will be read from the
+ * RallyPoint component.
+ */
GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
{
var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
@@ -816,20 +813,20 @@ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
{
var cmpRallyPointRenderer = Engine.QueryInterface(ent, IID_RallyPointRenderer);
if (cmpRallyPointRenderer)
- cmpRallyPointRenderer.SetDisplayed(false);
- }
-
- this.entsRallyPointsDisplayed = [];
-
- // Show the rally points for the passed entities
- for each (var ent in cmd.entities)
+ cmpRallyPointRenderer.SetDisplayed(false);
+ }
+
+ this.entsRallyPointsDisplayed = [];
+
+ // Show the rally points for the passed entities
+ for each (var ent in cmd.entities)
{
var cmpRallyPointRenderer = Engine.QueryInterface(ent, IID_RallyPointRenderer);
- if (!cmpRallyPointRenderer)
- continue;
-
- // entity must have a rally point component to display a rally point marker
- // (regardless of whether cmd specifies a custom location)
+ if (!cmpRallyPointRenderer)
+ continue;
+
+ // entity must have a rally point component to display a rally point marker
+ // (regardless of whether cmd specifies a custom location)
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
if (!cmpRallyPoint)
continue;
@@ -842,11 +839,11 @@ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
// If the command was passed an explicit position, use that and
// override the real rally point position; otherwise use the real position
- var pos;
- if (cmd.x && cmd.z)
- pos = cmd;
- else
- pos = cmpRallyPoint.GetPositions()[0]; // may return undefined if no rally point is set
+ var pos;
+ if (cmd.x && cmd.z)
+ pos = cmd;
+ else
+ pos = cmpRallyPoint.GetPositions()[0]; // may return undefined if no rally point is set
if (pos)
{
@@ -854,22 +851,22 @@ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
if (cmd.queued == true)
cmpRallyPointRenderer.AddPosition({'x': pos.x, 'y': pos.z}); // AddPosition takes a CFixedVector2D which has X/Y components, not X/Z
else if (cmd.queued == false)
- cmpRallyPointRenderer.SetPosition({'x': pos.x, 'y': pos.z}); // SetPosition takes a CFixedVector2D which has X/Y components, not X/Z
- cmpRallyPointRenderer.SetDisplayed(true);
-
- // remember which entities have their rally points displayed so we can hide them again
- this.entsRallyPointsDisplayed.push(ent);
+ cmpRallyPointRenderer.SetPosition({'x': pos.x, 'y': pos.z}); // SetPosition takes a CFixedVector2D which has X/Y components, not X/Z
+ cmpRallyPointRenderer.SetDisplayed(true);
+
+ // remember which entities have their rally points displayed so we can hide them again
+ this.entsRallyPointsDisplayed.push(ent);
}
}
};
/**
* Display the building placement preview.
- * cmd.template is the name of the entity template, or "" to disable the preview.
- * cmd.x, cmd.z, cmd.angle give the location.
- *
- * Returns result object from CheckPlacement:
- * {
+ * cmd.template is the name of the entity template, or "" to disable the preview.
+ * cmd.x, cmd.z, cmd.angle give the location.
+ *
+ * Returns result object from CheckPlacement:
+ * {
* "success": true iff the placement is valid, else false
* "message": message to display in UI for invalid placement, else empty string
* }
@@ -937,47 +934,47 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
return result;
};
-
-/**
- * Previews the placement of a wall between cmd.start and cmd.end, or just the starting piece of a wall if cmd.end is not
- * specified. Returns an object with information about the list of entities that need to be newly constructed to complete
- * at least a part of the wall, or false if there are entities required to build at least part of the wall but none of
- * them can be validly constructed.
- *
- * It's important to distinguish between three lists of entities that are at play here, because they may be subsets of one
- * another depending on things like snapping and whether some of the entities inside them can be validly positioned.
+
+/**
+ * Previews the placement of a wall between cmd.start and cmd.end, or just the starting piece of a wall if cmd.end is not
+ * specified. Returns an object with information about the list of entities that need to be newly constructed to complete
+ * at least a part of the wall, or false if there are entities required to build at least part of the wall but none of
+ * them can be validly constructed.
+ *
+ * It's important to distinguish between three lists of entities that are at play here, because they may be subsets of one
+ * another depending on things like snapping and whether some of the entities inside them can be validly positioned.
* We have:
* - The list of entities that previews the wall. This list is usually equal to the entities required to construct the
- * entire wall. However, if there is snapping to an incomplete tower (i.e. a foundation), it includes extra entities
- * to preview the completed tower on top of its foundation.
- *
- * - The list of entities that need to be newly constructed to build the entire wall. This list is regardless of whether
- * any of them can be validly positioned. The emphasishere here is on 'newly'; this list does not include any existing
+ * entire wall. However, if there is snapping to an incomplete tower (i.e. a foundation), it includes extra entities
+ * to preview the completed tower on top of its foundation.
+ *
+ * - The list of entities that need to be newly constructed to build the entire wall. This list is regardless of whether
+ * any of them can be validly positioned. The emphasishere here is on 'newly'; this list does not include any existing
* towers at either side of the wall that we snapped to. Or, more generally; it does not include any _entities_ that we
- * snapped to; we might still snap to e.g. terrain, in which case the towers on either end will still need to be newly
- * constructed.
- *
- * - The list of entities that need to be newly constructed to build at least a part of the wall. This list is the same
- * as the one above, except that it is truncated at the first entity that cannot be validly positioned. This happens
- * e.g. if the player tries to build a wall straight through an obstruction. Note that any entities that can be validly
- * constructed but come after said first invalid entity are also truncated away.
- *
- * With this in mind, this method will return false if the second list is not empty, but the third one is. That is, if there
- * were entities that are needed to build the wall, but none of them can be validly constructed. False is also returned in
- * case of unexpected errors (typically missing components), and when clearing the preview by passing an empty wallset
- * argument (see below). Otherwise, it will return an object with the following information:
- *
- * result: {
- * 'startSnappedEnt': ID of the entity that we snapped to at the starting side of the wall. Currently only supports towers.
+ * snapped to; we might still snap to e.g. terrain, in which case the towers on either end will still need to be newly
+ * constructed.
+ *
+ * - The list of entities that need to be newly constructed to build at least a part of the wall. This list is the same
+ * as the one above, except that it is truncated at the first entity that cannot be validly positioned. This happens
+ * e.g. if the player tries to build a wall straight through an obstruction. Note that any entities that can be validly
+ * constructed but come after said first invalid entity are also truncated away.
+ *
+ * With this in mind, this method will return false if the second list is not empty, but the third one is. That is, if there
+ * were entities that are needed to build the wall, but none of them can be validly constructed. False is also returned in
+ * case of unexpected errors (typically missing components), and when clearing the preview by passing an empty wallset
+ * argument (see below). Otherwise, it will return an object with the following information:
+ *
+ * result: {
+ * 'startSnappedEnt': ID of the entity that we snapped to at the starting side of the wall. Currently only supports towers.
* 'endSnappedEnt': ID of the entity that we snapped to at the (possibly truncated) ending side of the wall. Note that this
* can only be set if no truncation of the second list occurs; if we snapped to an entity at the ending side
* but the wall construction was truncated before we could reach it, it won't be set here. Currently only
* supports towers.
- * 'pieces': Array with the following data for each of the entities in the third list:
- * [{
- * 'template': Template name of the entity.
- * 'x': X coordinate of the entity's position.
- * 'z': Z coordinate of the entity's position.
+ * 'pieces': Array with the following data for each of the entities in the third list:
+ * [{
+ * 'template': Template name of the entity.
+ * 'x': X coordinate of the entity's position.
+ * 'z': Z coordinate of the entity's position.
* 'angle': Rotation around the Y axis of the entity (in radians).
* },
* ...]
@@ -988,11 +985,11 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
* 'metal': ...,
* 'population': ...,
* 'populationBonus': ...,
- * }
- * }
- *
- * @param cmd.wallSet Object holding the set of wall piece template names. Set to an empty value to clear the preview.
- * @param cmd.start Starting point of the wall segment being created.
+ * }
+ * }
+ *
+ * @param cmd.wallSet Object holding the set of wall piece template names. Set to an empty value to clear the preview.
+ * @param cmd.start Starting point of the wall segment being created.
* @param cmd.end (Optional) Ending point of the wall segment being created. If not defined, it is understood that only
* the starting point of the wall is available at this time (e.g. while the player is still in the process
* of picking a starting point), and that therefore only the first entity in the wall (a tower) should be
@@ -1000,45 +997,45 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
* @param cmd.snapEntities List of candidate entities to snap the start and ending positions to.
*/
GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
-{
- var wallSet = cmd.wallSet;
-
- var start = {
- "pos": cmd.start,
+{
+ var wallSet = cmd.wallSet;
+
+ var start = {
+ "pos": cmd.start,
"angle": 0,
"snapped": false, // did the start position snap to anything?
- "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
- };
-
- var end = {
- "pos": cmd.end,
+ "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
+ };
+
+ var end = {
+ "pos": cmd.end,
"angle": 0,
"snapped": false, // did the start position snap to anything?
- "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
- };
-
- // --------------------------------------------------------------------------------
- // do some entity cache management and check for snapping
-
- if (!this.placementWallEntities)
- this.placementWallEntities = {};
-
- if (!wallSet)
- {
+ "snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
+ };
+
+ // --------------------------------------------------------------------------------
+ // do some entity cache management and check for snapping
+
+ if (!this.placementWallEntities)
+ this.placementWallEntities = {};
+
+ if (!wallSet)
+ {
// we're clearing the preview, clear the entity cache and bail
var numCleared = 0;
for (var tpl in this.placementWallEntities)
{
- for each (var ent in this.placementWallEntities[tpl].entities)
- Engine.DestroyEntity(ent);
-
- this.placementWallEntities[tpl].numUsed = 0;
- this.placementWallEntities[tpl].entities = [];
- // keep template data around
- }
-
- return false;
- }
+ for each (var ent in this.placementWallEntities[tpl].entities)
+ Engine.DestroyEntity(ent);
+
+ this.placementWallEntities[tpl].numUsed = 0;
+ this.placementWallEntities[tpl].entities = [];
+ // keep template data around
+ }
+
+ return false;
+ }
else
{
// Move all existing cached entities outside of the world and reset their use count
@@ -1048,39 +1045,39 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
{
var pos = Engine.QueryInterface(ent, IID_Position);
if (pos)
- pos.MoveOutOfWorld();
- }
-
- this.placementWallEntities[tpl].numUsed = 0;
- }
-
- // Create cache entries for templates we haven't seen before
- for each (var tpl in wallSet.templates)
+ pos.MoveOutOfWorld();
+ }
+
+ this.placementWallEntities[tpl].numUsed = 0;
+ }
+
+ // Create cache entries for templates we haven't seen before
+ for each (var tpl in wallSet.templates)
{
if (!(tpl in this.placementWallEntities))
{
this.placementWallEntities[tpl] = {
"numUsed": 0,
"entities": [],
- "templateData": this.GetTemplateData(player, tpl),
- };
-
- // ensure that the loaded template data contains a wallPiece component
- if (!this.placementWallEntities[tpl].templateData.wallPiece)
+ "templateData": this.GetTemplateData(player, tpl),
+ };
+
+ // ensure that the loaded template data contains a wallPiece component
+ if (!this.placementWallEntities[tpl].templateData.wallPiece)
{
error("[SetWallPlacementPreview] No WallPiece component found for wall set template '" + tpl + "'");
return false;
}
}
- }
- }
-
- // prevent division by zero errors further on if the start and end positions are the same
- if (end.pos && (start.pos.x === end.pos.x && start.pos.z === end.pos.z))
- end.pos = undefined;
-
- // See if we need to snap the start and/or end coordinates to any of our list of snap entities. Note that, despite the list
- // of snapping candidate entities, it might still snap to e.g. terrain features. Use the "ent" key in the returned snapping
+ }
+ }
+
+ // prevent division by zero errors further on if the start and end positions are the same
+ if (end.pos && (start.pos.x === end.pos.x && start.pos.z === end.pos.z))
+ end.pos = undefined;
+
+ // See if we need to snap the start and/or end coordinates to any of our list of snap entities. Note that, despite the list
+ // of snapping candidate entities, it might still snap to e.g. terrain features. Use the "ent" key in the returned snapping
// data to determine whether it snapped to an entity (if any), and to which one (see GetFoundationSnapData).
if (cmd.snapEntities)
{
@@ -1090,84 +1087,84 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
"z": start.pos.z,
"template": wallSet.templates.tower,
"snapEntities": cmd.snapEntities,
- "snapRadius": snapRadius,
- });
-
- if (startSnapData)
- {
+ "snapRadius": snapRadius,
+ });
+
+ if (startSnapData)
+ {
start.pos.x = startSnapData.x;
start.pos.z = startSnapData.z;
- start.angle = startSnapData.angle;
- start.snapped = true;
-
- if (startSnapData.ent)
- start.snappedEnt = startSnapData.ent;
- }
-
- if (end.pos)
- {
+ start.angle = startSnapData.angle;
+ start.snapped = true;
+
+ if (startSnapData.ent)
+ start.snappedEnt = startSnapData.ent;
+ }
+
+ if (end.pos)
+ {
var endSnapData = this.GetFoundationSnapData(player, {
"x": end.pos.x,
"z": end.pos.z,
"template": wallSet.templates.tower,
"snapEntities": cmd.snapEntities,
- "snapRadius": snapRadius,
- });
-
- if (endSnapData)
- {
+ "snapRadius": snapRadius,
+ });
+
+ if (endSnapData)
+ {
end.pos.x = endSnapData.x;
end.pos.z = endSnapData.z;
- end.angle = endSnapData.angle;
- end.snapped = true;
-
- if (endSnapData.ent)
- end.snappedEnt = endSnapData.ent;
+ end.angle = endSnapData.angle;
+ end.snapped = true;
+
+ if (endSnapData.ent)
+ end.snappedEnt = endSnapData.ent;
}
- }
- }
-
- // clear the single-building preview entity (we'll be rolling our own)
- this.SetBuildingPlacementPreview(player, {"template": ""});
-
- // --------------------------------------------------------------------------------
- // calculate wall placement and position preview entities
-
- var result = {
- "pieces": [],
- "cost": {"food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0},
- };
-
- var previewEntities = [];
- if (end.pos)
- previewEntities = GetWallPlacement(this.placementWallEntities, wallSet, start, end); // see helpers/Walls.js
-
- // For wall placement, we may (and usually do) need to have wall pieces overlap each other more than would
- // otherwise be allowed by their obstruction shapes. However, during this preview phase, this is not so much of
- // an issue, because all preview entities have their obstruction components deactivated, meaning that their
- // obstruction shapes do not register in the simulation and hence cannot affect it. This implies that the preview
- // entities cannot be found to obstruct each other, which largely solves the issue of overlap between wall pieces.
-
- // Note that they will still be obstructed by existing shapes in the simulation (that have the BLOCK_FOUNDATION
- // flag set), which is what we want. The only exception to this is when snapping to existing towers (or
+ }
+ }
+
+ // clear the single-building preview entity (we'll be rolling our own)
+ this.SetBuildingPlacementPreview(player, {"template": ""});
+
+ // --------------------------------------------------------------------------------
+ // calculate wall placement and position preview entities
+
+ var result = {
+ "pieces": [],
+ "cost": {"food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0},
+ };
+
+ var previewEntities = [];
+ if (end.pos)
+ previewEntities = GetWallPlacement(this.placementWallEntities, wallSet, start, end); // see helpers/Walls.js
+
+ // For wall placement, we may (and usually do) need to have wall pieces overlap each other more than would
+ // otherwise be allowed by their obstruction shapes. However, during this preview phase, this is not so much of
+ // an issue, because all preview entities have their obstruction components deactivated, meaning that their
+ // obstruction shapes do not register in the simulation and hence cannot affect it. This implies that the preview
+ // entities cannot be found to obstruct each other, which largely solves the issue of overlap between wall pieces.
+
+ // Note that they will still be obstructed by existing shapes in the simulation (that have the BLOCK_FOUNDATION
+ // flag set), which is what we want. The only exception to this is when snapping to existing towers (or
// foundations thereof); the wall segments that connect up to these will be found to be obstructed by the
// existing tower/foundation, and be shaded red to indicate that they cannot be placed there. To prevent this,
// we manually set the control group of the outermost wall pieces equal to those of the snapped-to towers, so
- // that they are free from mutual obstruction (per definition of obstruction control groups). This is done by
- // assigning them an extra "controlGroup" field, which we'll then set during the placement loop below.
-
- // Additionally, in the situation that we're snapping to merely a foundation of a tower instead of a fully
- // constructed one, we'll need an extra preview entity for the starting tower, which also must not be obstructed
- // by the foundation it snaps to.
-
- if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY)
- {
+ // that they are free from mutual obstruction (per definition of obstruction control groups). This is done by
+ // assigning them an extra "controlGroup" field, which we'll then set during the placement loop below.
+
+ // Additionally, in the situation that we're snapping to merely a foundation of a tower instead of a fully
+ // constructed one, we'll need an extra preview entity for the starting tower, which also must not be obstructed
+ // by the foundation it snaps to.
+
+ if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY)
+ {
var startEntObstruction = Engine.QueryInterface(start.snappedEnt, IID_Obstruction);
- if (previewEntities.length > 0 && startEntObstruction)
- previewEntities[0].controlGroups = [startEntObstruction.GetControlGroup()];
-
- // if we're snapping to merely a foundation, add an extra preview tower and also set it to the same control group
- var startEntState = this.GetEntityState(player, start.snappedEnt);
+ if (previewEntities.length > 0 && startEntObstruction)
+ previewEntities[0].controlGroups = [startEntObstruction.GetControlGroup()];
+
+ // if we're snapping to merely a foundation, add an extra preview tower and also set it to the same control group
+ var startEntState = this.GetEntityState(player, start.snappedEnt);
if (startEntState.foundation)
{
var cmpPosition = Engine.QueryInterface(start.snappedEnt, IID_Position);
@@ -1183,50 +1180,50 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
}
}
}
- else
- {
- // Didn't snap to an existing entity, add the starting tower manually. To prevent odd-looking rotation jumps
- // when shift-clicking to build a wall, reuse the placement angle that was last seen on a validly positioned
- // wall piece.
-
- // To illustrate the last point, consider what happens if we used some constant instead, say, 0. Issuing the
- // build command for a wall is asynchronous, so when the preview updates after shift-clicking, the wall piece
- // foundations are not registered yet in the simulation. This means they cannot possibly be picked in the list
- // of candidate entities for snapping. In the next preview update, we therefore hit this case, and would rotate
- // the preview to 0 radians. Then, after one or two simulation updates or so, the foundations register and
- // onSimulationUpdate in session.js updates the preview again. It first grabs a new list of snapping candidates,
- // which this time does include the new foundations; so we snap to the entity, and rotate the preview back to
- // the foundation's angle.
-
- // The result is a noticeable rotation to 0 and back, which is undesirable. So, for a split second there until
- // the simulation updates, we fake it by reusing the last angle and hope the player doesn't notice.
+ else
+ {
+ // Didn't snap to an existing entity, add the starting tower manually. To prevent odd-looking rotation jumps
+ // when shift-clicking to build a wall, reuse the placement angle that was last seen on a validly positioned
+ // wall piece.
+
+ // To illustrate the last point, consider what happens if we used some constant instead, say, 0. Issuing the
+ // build command for a wall is asynchronous, so when the preview updates after shift-clicking, the wall piece
+ // foundations are not registered yet in the simulation. This means they cannot possibly be picked in the list
+ // of candidate entities for snapping. In the next preview update, we therefore hit this case, and would rotate
+ // the preview to 0 radians. Then, after one or two simulation updates or so, the foundations register and
+ // onSimulationUpdate in session.js updates the preview again. It first grabs a new list of snapping candidates,
+ // which this time does include the new foundations; so we snap to the entity, and rotate the preview back to
+ // the foundation's angle.
+
+ // The result is a noticeable rotation to 0 and back, which is undesirable. So, for a split second there until
+ // the simulation updates, we fake it by reusing the last angle and hope the player doesn't notice.
previewEntities.unshift({
"template": wallSet.templates.tower,
"pos": start.pos,
"angle": (previewEntities.length > 0 ? previewEntities[0].angle : this.placementWallLastAngle)
- });
- }
-
- if (end.pos)
- {
+ });
+ }
+
+ if (end.pos)
+ {
// Analogous to the starting side case above
if (end.snappedEnt && end.snappedEnt != INVALID_ENTITY)
- {
- var endEntObstruction = Engine.QueryInterface(end.snappedEnt, IID_Obstruction);
-
- // Note that it's possible for the last entity in previewEntities to be the same as the first, i.e. the
- // same wall piece snapping to both a starting and an ending tower. And it might be more common than you would
+ {
+ var endEntObstruction = Engine.QueryInterface(end.snappedEnt, IID_Obstruction);
+
+ // Note that it's possible for the last entity in previewEntities to be the same as the first, i.e. the
+ // same wall piece snapping to both a starting and an ending tower. And it might be more common than you would
// expect; the allowed overlap between wall segments and towers facilitates this to some degree. To deal with
// the possibility of dual initial control groups, we use a '.controlGroups' array rather than a single
// '.controlGroup' property. Note that this array can only ever have 0, 1 or 2 elements (checked at a later time).
if (previewEntities.length > 0 && endEntObstruction)
{
previewEntities[previewEntities.length-1].controlGroups = (previewEntities[previewEntities.length-1].controlGroups || []);
- previewEntities[previewEntities.length-1].controlGroups.push(endEntObstruction.GetControlGroup());
- }
-
- // if we're snapping to a foundation, add an extra preview tower and also set it to the same control group
- var endEntState = this.GetEntityState(player, end.snappedEnt);
+ previewEntities[previewEntities.length-1].controlGroups.push(endEntObstruction.GetControlGroup());
+ }
+
+ // if we're snapping to a foundation, add an extra preview tower and also set it to the same control group
+ var endEntState = this.GetEntityState(player, end.snappedEnt);
if (endEntState.foundation)
{
var cmpPosition = Engine.QueryInterface(end.snappedEnt, IID_Position);
@@ -1249,41 +1246,41 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
"pos": end.pos,
"angle": (previewEntities.length > 0 ? previewEntities[previewEntities.length-1].angle : this.placementWallLastAngle)
});
- }
- }
-
- var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
- if (!cmpTerrain)
+ }
+ }
+
+ var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
+ if (!cmpTerrain)
{
error("[SetWallPlacementPreview] System Terrain component not found");
- return false;
- }
-
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
- if (!cmpRangeManager)
+ return false;
+ }
+
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ if (!cmpRangeManager)
{
error("[SetWallPlacementPreview] System RangeManager component not found");
- return false;
- }
-
- // Loop through the preview entities, and construct the subset of them that need to be, and can be, validly constructed
- // to build at least a part of the wall (meaning that the subset is truncated after the first entity that needs to be,
- // but cannot validly be, constructed). See method-level documentation for more details.
-
- var allPiecesValid = true;
- var numRequiredPieces = 0; // number of entities that are required to build the entire wall, regardless of validity
-
- for (var i = 0; i < previewEntities.length; ++i)
- {
- var entInfo = previewEntities[i];
-
- var ent = null;
- var tpl = entInfo.template;
- var tplData = this.placementWallEntities[tpl].templateData;
- var entPool = this.placementWallEntities[tpl];
-
- if (entPool.numUsed >= entPool.entities.length)
- {
+ return false;
+ }
+
+ // Loop through the preview entities, and construct the subset of them that need to be, and can be, validly constructed
+ // to build at least a part of the wall (meaning that the subset is truncated after the first entity that needs to be,
+ // but cannot validly be, constructed). See method-level documentation for more details.
+
+ var allPiecesValid = true;
+ var numRequiredPieces = 0; // number of entities that are required to build the entire wall, regardless of validity
+
+ for (var i = 0; i < previewEntities.length; ++i)
+ {
+ var entInfo = previewEntities[i];
+
+ var ent = null;
+ var tpl = entInfo.template;
+ var tplData = this.placementWallEntities[tpl].templateData;
+ var entPool = this.placementWallEntities[tpl];
+
+ if (entPool.numUsed >= entPool.entities.length)
+ {
// allocate new entity
ent = Engine.AddLocalEntity("preview|" + tpl);
entPool.entities.push(ent);
@@ -1291,86 +1288,86 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
else
{
// reuse an existing one
- ent = entPool.entities[entPool.numUsed];
- }
-
- if (!ent)
- {
+ ent = entPool.entities[entPool.numUsed];
+ }
+
+ if (!ent)
+ {
error("[SetWallPlacementPreview] Failed to allocate or reuse preview entity of template '" + tpl + "'");
- continue;
- }
-
- // move piece to right location
- // TODO: consider reusing SetBuildingPlacementReview for this, enhanced to be able to deal with multiple entities
+ continue;
+ }
+
+ // move piece to right location
+ // TODO: consider reusing SetBuildingPlacementReview for this, enhanced to be able to deal with multiple entities
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
if (cmpPosition)
{
- cmpPosition.JumpTo(entInfo.pos.x, entInfo.pos.z);
- cmpPosition.SetYRotation(entInfo.angle);
-
- // if this piece is a tower, then it should have a Y position that is at least as high as its surrounding pieces
- if (tpl === wallSet.templates.tower)
+ cmpPosition.JumpTo(entInfo.pos.x, entInfo.pos.z);
+ cmpPosition.SetYRotation(entInfo.angle);
+
+ // if this piece is a tower, then it should have a Y position that is at least as high as its surrounding pieces
+ if (tpl === wallSet.templates.tower)
{
- var terrainGroundPrev = null;
- var terrainGroundNext = null;
-
- if (i > 0)
- terrainGroundPrev = cmpTerrain.GetGroundLevel(previewEntities[i-1].pos.x, previewEntities[i-1].pos.z);
- if (i < previewEntities.length - 1)
- terrainGroundNext = cmpTerrain.GetGroundLevel(previewEntities[i+1].pos.x, previewEntities[i+1].pos.z);
-
- if (terrainGroundPrev != null || terrainGroundNext != null)
- {
+ var terrainGroundPrev = null;
+ var terrainGroundNext = null;
+
+ if (i > 0)
+ terrainGroundPrev = cmpTerrain.GetGroundLevel(previewEntities[i-1].pos.x, previewEntities[i-1].pos.z);
+ if (i < previewEntities.length - 1)
+ terrainGroundNext = cmpTerrain.GetGroundLevel(previewEntities[i+1].pos.x, previewEntities[i+1].pos.z);
+
+ if (terrainGroundPrev != null || terrainGroundNext != null)
+ {
var targetY = Math.max(terrainGroundPrev, terrainGroundNext);
cmpPosition.SetHeightFixed(targetY);
}
- }
- }
-
- var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
- if (!cmpObstruction)
+ }
+ }
+
+ var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
+ if (!cmpObstruction)
{
error("[SetWallPlacementPreview] Preview entity of template '" + tpl + "' does not have an Obstruction component");
- continue;
- }
-
- // Assign any predefined control groups. Note that there can only be 0, 1 or 2 predefined control groups; if there are
- // more, we've made a programming error. The control groups are assigned from the entInfo.controlGroups array on a
- // first-come first-served basis; the first value in the array is always assigned as the primary control group, and
- // any second value as the secondary control group.
-
- // By default, we reset the control groups to their standard values. Remember that we're reusing entities; if we don't
- // reset them, then an ending wall segment that was e.g. at one point snapped to an existing tower, and is subsequently
- // reused as a non-snapped ending wall segment, would no longer be capable of being obstructed by the same tower it was
- // once snapped to.
-
- var primaryControlGroup = ent;
- var secondaryControlGroup = INVALID_ENTITY;
-
- if (entInfo.controlGroups && entInfo.controlGroups.length > 0)
- {
+ continue;
+ }
+
+ // Assign any predefined control groups. Note that there can only be 0, 1 or 2 predefined control groups; if there are
+ // more, we've made a programming error. The control groups are assigned from the entInfo.controlGroups array on a
+ // first-come first-served basis; the first value in the array is always assigned as the primary control group, and
+ // any second value as the secondary control group.
+
+ // By default, we reset the control groups to their standard values. Remember that we're reusing entities; if we don't
+ // reset them, then an ending wall segment that was e.g. at one point snapped to an existing tower, and is subsequently
+ // reused as a non-snapped ending wall segment, would no longer be capable of being obstructed by the same tower it was
+ // once snapped to.
+
+ var primaryControlGroup = ent;
+ var secondaryControlGroup = INVALID_ENTITY;
+
+ if (entInfo.controlGroups && entInfo.controlGroups.length > 0)
+ {
if (entInfo.controlGroups.length > 2)
{
error("[SetWallPlacementPreview] Encountered preview entity of template '" + tpl + "' with more than 2 initial control groups");
- break;
- }
-
- primaryControlGroup = entInfo.controlGroups[0];
- if (entInfo.controlGroups.length > 1)
- secondaryControlGroup = entInfo.controlGroups[1];
- }
-
- cmpObstruction.SetControlGroup(primaryControlGroup);
- cmpObstruction.SetControlGroup2(secondaryControlGroup);
-
- // check whether this wall piece can be validly positioned here
- var validPlacement = false;
-
- var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
- cmpOwnership.SetOwner(player);
-
- // Check whether it's in a visible or fogged region
- // tell GetLosVisibility to force RetainInFog because preview entities set this to false,
+ break;
+ }
+
+ primaryControlGroup = entInfo.controlGroups[0];
+ if (entInfo.controlGroups.length > 1)
+ secondaryControlGroup = entInfo.controlGroups[1];
+ }
+
+ cmpObstruction.SetControlGroup(primaryControlGroup);
+ cmpObstruction.SetControlGroup2(secondaryControlGroup);
+
+ // check whether this wall piece can be validly positioned here
+ var validPlacement = false;
+
+ var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
+ cmpOwnership.SetOwner(player);
+
+ // Check whether it's in a visible or fogged region
+ // tell GetLosVisibility to force RetainInFog because preview entities set this to false,
// which would show them as hidden instead of fogged
// TODO: should definitely reuse SetBuildingPlacementPreview, this is just straight up copy/pasta
var visible = (cmpRangeManager.GetLosVisibility(ent, player, true) != "hidden");
@@ -1380,11 +1377,11 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
if (!cmpBuildRestrictions)
{
error("[SetWallPlacementPreview] cmpBuildRestrictions not defined for preview entity of template '" + tpl + "'");
- continue;
- }
-
- // TODO: Handle results of CheckPlacement
- validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success);
+ continue;
+ }
+
+ // TODO: Handle results of CheckPlacement
+ validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success);
// If a wall piece has two control groups, it's likely a segment that spans
// between two existing towers. To avoid placing a duplicate wall segment,
@@ -1392,32 +1389,32 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
if (validPlacement && entInfo.controlGroups && entInfo.controlGroups.length > 1)
validPlacement = cmpObstruction.CheckDuplicateFoundation();
}
-
- allPiecesValid = allPiecesValid && validPlacement;
-
- // The requirement below that all pieces so far have to have valid positions, rather than only this single one,
- // ensures that no more foundations will be placed after a first invalidly-positioned piece. (It is possible
- // for pieces past some invalidly-positioned ones to still have valid positions, e.g. if you drag a wall
- // through and past an existing building).
-
- // Additionally, the excludeFromResult flag is set for preview entities that were manually added to be placed
- // on top of foundations of incompleted towers that we snapped to; they must not be part of the result.
-
- if (!entInfo.excludeFromResult)
- numRequiredPieces++;
-
- if (allPiecesValid && !entInfo.excludeFromResult)
- {
+
+ allPiecesValid = allPiecesValid && validPlacement;
+
+ // The requirement below that all pieces so far have to have valid positions, rather than only this single one,
+ // ensures that no more foundations will be placed after a first invalidly-positioned piece. (It is possible
+ // for pieces past some invalidly-positioned ones to still have valid positions, e.g. if you drag a wall
+ // through and past an existing building).
+
+ // Additionally, the excludeFromResult flag is set for preview entities that were manually added to be placed
+ // on top of foundations of incompleted towers that we snapped to; they must not be part of the result.
+
+ if (!entInfo.excludeFromResult)
+ numRequiredPieces++;
+
+ if (allPiecesValid && !entInfo.excludeFromResult)
+ {
result.pieces.push({
"template": tpl,
"x": entInfo.pos.x,
"z": entInfo.pos.z,
"angle": entInfo.angle,
- });
- this.placementWallLastAngle = entInfo.angle;
-
- // grab the cost of this wall piece and add it up (note; preview entities don't have their Cost components
- // copied over, so we need to fetch it from the template instead).
+ });
+ this.placementWallLastAngle = entInfo.angle;
+
+ // grab the cost of this wall piece and add it up (note; preview entities don't have their Cost components
+ // copied over, so we need to fetch it from the template instead).
// TODO: we should really use a Cost object or at least some utility functions for this, this is mindless
// boilerplate that's probably duplicated in tons of places.
result.cost.food += tplData.cost.food;
@@ -1443,31 +1440,31 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
cmpVisual.SetShadingColour(1, 1, 1, 1);
}
- entPool.numUsed++;
- }
-
- // If any were entities required to build the wall, but none of them could be validly positioned, return failure
- // (see method-level documentation).
- if (numRequiredPieces > 0 && result.pieces.length == 0)
- return false;
-
- if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY)
- result.startSnappedEnt = start.snappedEnt;
-
- // We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed,
- // i.e. are included in result.pieces (see docs for the result object).
- if (end.pos && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid)
- result.endSnappedEnt = end.snappedEnt;
-
- return result;
-};
+ entPool.numUsed++;
+ }
+
+ // If any were entities required to build the wall, but none of them could be validly positioned, return failure
+ // (see method-level documentation).
+ if (numRequiredPieces > 0 && result.pieces.length == 0)
+ return false;
+
+ if (start.snappedEnt && start.snappedEnt != INVALID_ENTITY)
+ result.startSnappedEnt = start.snappedEnt;
+
+ // We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed,
+ // i.e. are included in result.pieces (see docs for the result object).
+ if (end.pos && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid)
+ result.endSnappedEnt = end.snappedEnt;
+
+ return result;
+};
/**
- * Given the current position {data.x, data.z} of an foundation of template data.template, returns the position and angle to snap
- * it to (if necessary/useful).
- *
- * @param data.x The X position of the foundation to snap.
- * @param data.z The Z position of the foundation to snap.
+ * Given the current position {data.x, data.z} of an foundation of template data.template, returns the position and angle to snap
+ * it to (if necessary/useful).
+ *
+ * @param data.x The X position of the foundation to snap.
+ * @param data.z The Z position of the foundation to snap.
* @param data.template The template to get the foundation snapping data for.
* @param data.snapEntities Optional; list of entity IDs to snap to if {data.x, data.z} is within a circle of radius data.snapRadius
* around the entity. Only takes effect when used in conjunction with data.snapRadius.
@@ -1485,63 +1482,63 @@ GuiInterface.prototype.GetFoundationSnapData = function(player, data)
if (!template)
{
warn("[GetFoundationSnapData] Failed to load template '" + data.template + "'");
- return false;
- }
-
- if (data.snapEntities && data.snapRadius && data.snapRadius > 0)
- {
- // see if {data.x, data.z} is inside the snap radius of any of the snap entities; and if so, to which it is closest
- // (TODO: break unlikely ties by choosing the lowest entity ID)
-
- var minDist2 = -1;
- var minDistEntitySnapData = null;
- var radius2 = data.snapRadius * data.snapRadius;
-
- for each (var ent in data.snapEntities)
- {
+ return false;
+ }
+
+ if (data.snapEntities && data.snapRadius && data.snapRadius > 0)
+ {
+ // see if {data.x, data.z} is inside the snap radius of any of the snap entities; and if so, to which it is closest
+ // (TODO: break unlikely ties by choosing the lowest entity ID)
+
+ var minDist2 = -1;
+ var minDistEntitySnapData = null;
+ var radius2 = data.snapRadius * data.snapRadius;
+
+ for each (var ent in data.snapEntities)
+ {
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
- if (!cmpPosition || !cmpPosition.IsInWorld())
- continue;
-
- var pos = cmpPosition.GetPosition();
- var dist2 = (data.x - pos.x) * (data.x - pos.x) + (data.z - pos.z) * (data.z - pos.z);
- if (dist2 > radius2)
- continue;
-
- if (minDist2 < 0 || dist2 < minDist2)
- {
+ if (!cmpPosition || !cmpPosition.IsInWorld())
+ continue;
+
+ var pos = cmpPosition.GetPosition();
+ var dist2 = (data.x - pos.x) * (data.x - pos.x) + (data.z - pos.z) * (data.z - pos.z);
+ if (dist2 > radius2)
+ continue;
+
+ if (minDist2 < 0 || dist2 < minDist2)
+ {
minDist2 = dist2;
minDistEntitySnapData = {"x": pos.x, "z": pos.z, "angle": cmpPosition.GetRotation().y, "ent": ent};
- }
- }
-
- if (minDistEntitySnapData != null)
- return minDistEntitySnapData;
- }
-
- if (template.BuildRestrictions.Category == "Dock")
- {
+ }
+ }
+
+ if (minDistEntitySnapData != null)
+ return minDistEntitySnapData;
+ }
+
+ if (template.BuildRestrictions.Category == "Dock")
+ {
// warning: copied almost identically in helpers/command.js , "GetDockAngle".
var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
if (!cmpTerrain || !cmpWaterManager)
{
- return false;
- }
-
- // Get footprint size
- var halfSize = 0;
+ return false;
+ }
+
+ // Get footprint size
+ var halfSize = 0;
if (template.Footprint.Square)
{
halfSize = Math.max(template.Footprint.Square["@depth"], template.Footprint.Square["@width"])/2;
}
else if (template.Footprint.Circle)
{
- halfSize = template.Footprint.Circle["@radius"];
- }
-
- /* Find direction of most open water, algorithm:
- * 1. Pick points in a circle around dock
+ halfSize = template.Footprint.Circle["@radius"];
+ }
+
+ /* Find direction of most open water, algorithm:
+ * 1. Pick points in a circle around dock
* 2. If point is in water, add to array
* 3. Scan array looking for consecutive points
* 4. Find longest sequence of consecutive points
@@ -1557,11 +1554,11 @@ GuiInterface.prototype.GetFoundationSnapData = function(player, data)
{
var angle = (i/numPoints)*2*Math.PI;
var d = halfSize*(dist+1);
- var nx = data.x - d*Math.sin(angle);
- var nz = data.z + d*Math.cos(angle);
-
- if (cmpTerrain.GetGroundLevel(nx, nz) < cmpWaterManager.GetWaterLevel(nx, nz))
- {
+ var nx = data.x - d*Math.sin(angle);
+ var nz = data.z + d*Math.cos(angle);
+
+ if (cmpTerrain.GetGroundLevel(nx, nz) < cmpWaterManager.GetWaterLevel(nx, nz))
+ {
waterPoints.push(i);
}
}
@@ -1591,11 +1588,11 @@ GuiInterface.prototype.GetFoundationSnapData = function(player, data)
{
start = c;
count = consec[c];
- }
- }
-
- // If we've found a shoreline, stop searching
- if (count != numPoints-1)
+ }
+ }
+
+ // If we've found a shoreline, stop searching
+ if (count != numPoints-1)
{
return {"x": data.x, "z": data.z, "angle": -(((waterPoints[start] + consec[start]/2) % numPoints)/numPoints*2*Math.PI)};
}
@@ -1616,11 +1613,11 @@ GuiInterface.prototype.PlaySound = function(player, data)
function isIdleUnit(ent, idleClass)
{
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
-
- // TODO: Do something with garrisoned idle units
- return (cmpUnitAI && cmpIdentity && cmpUnitAI.IsIdle() && !cmpUnitAI.IsGarrisoned() && idleClass && cmpIdentity.HasClass(idleClass));
+ var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+
+ // TODO: Do something with garrisoned idle units
+ return (cmpUnitAI && cmpIdentity && cmpUnitAI.IsIdle() && !cmpUnitAI.IsGarrisoned() && idleClass && cmpIdentity.HasClass(idleClass));
}
GuiInterface.prototype.FindIdleUnits = function(player, data)
@@ -1768,11 +1765,11 @@ GuiInterface.prototype.OnGlobalEntityRenamed = function(msg)
// (GUI scripts are non-deterministic and untrusted, so these functions must be
// appropriately careful. They are called with a first argument "player", which is
// trusted and indicates the player associated with the current client; no data should
-// be returned unless this player is meant to be able to see it.)
-var exposedFunctions = {
-
- "GetSimulationState": 1,
- "GetExtendedSimulationState": 1,
+// be returned unless this player is meant to be able to see it.)
+var exposedFunctions = {
+
+ "GetSimulationState": 1,
+ "GetExtendedSimulationState": 1,
"GetRenamedEntities": 1,
"ClearRenamedEntities": 1,
"GetEntityState": 1,
diff --git a/binaries/data/mods/public/simulation/components/ResourceSupply.js b/binaries/data/mods/public/simulation/components/ResourceSupply.js
index e3677ab6c5..e0686d162c 100644
--- a/binaries/data/mods/public/simulation/components/ResourceSupply.js
+++ b/binaries/data/mods/public/simulation/components/ResourceSupply.js
@@ -28,33 +28,10 @@ ResourceSupply.prototype.Schema =
"treasure.stone" +
"treasure.metal" +
"treasure.food" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "linear" +
- "quadratic" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
+ "" +
+ "" +
+ "" +
+ "" +
"" +
"" +
"" +
@@ -66,18 +43,10 @@ ResourceSupply.prototype.Init = function()
{
// Current resource amount (non-negative)
this.amount = this.GetMaxAmount();
- this.gatherers = []; // list of IDs
- this.infinite = !isFinite(+this.template.Amount);
- if (this.template.Regeneration) {
- this.regenRate = +this.template.Regeneration.Rate;
- if (this.template.Regeneration.Acceleration)
- this.regenAccel = +this.template.Regeneration.Acceleration;
- this.regenDelay = +this.template.Regeneration.Delay;
- }
- if (this.IsRegenerative())
- this.RegenerateResources();
-};
-
+ this.gatherers = []; // list of IDs
+ this.infinite = !isFinite(+this.template.Amount);
+};
+
ResourceSupply.prototype.IsInfinite = function()
{
return this.infinite;
@@ -125,53 +94,16 @@ ResourceSupply.prototype.TakeResources = function(rate)
var old = this.amount;
this.amount = Math.max(0, old - rate);
var change = old - this.amount;
-
- // Remove entities that have been exhausted
- if (this.amount == 0 && !this.IsRegenerative())
- Engine.DestroyEntity(this.entity);
-
- Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": old, "to": this.amount });
-
- return { "amount": change, "exhausted": this.amount == 0 };
-};
-
-ResourceSupply.prototype.RegenerateResources = function(data, lateness)
-{
- var max = this.GetMaxAmount();
- if (this.gatherers.length == 0 && !this.regenDelayTimer && this.amount < max)
- {
- var old = this.amount;
- if (this.regenGrowth == "linear")
- this.amount = Math.min(max, this.amount + data.rate);
- else
- this.amount = Math.min(max, this.amount + Math.max(1, data.rate * max * (data.acceleration * this.amount / max -Math.pow(this.amount / max, 2)) / 100));
- Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": old, "to": this.amount });
- }
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- var regenRate = this.GetRegenerationRate();
- var absRegen = Math.abs(regenRate);
- if (Math.floor(regenRate) == regenRate || this.regenGrowth == "quadratic")
- cmpTimer.SetTimeout(this.entity, IID_ResourceSupply, "RegenerateResources", 1000, { "rate": regenRate, "acceleration": this.GetRegenerationAcceleration() });
- else
- cmpTimer.SetTimeout(this.entity, IID_ResourceSupply, "RegenerateResources", 1000 / absRegen,
- { "rate": absRegen == regenRate ? 1 : -1 });
-};
-
-ResourceSupply.prototype.StartRegenerationDelayTimer = function()
-{
- if (!this.regenDelayTimer) {
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- this.regenDelayTimer = cmpTimer.SetTimeout(this.entity, IID_ResourceSupply, "CancelRegenerationDelayTimer", this.GetRegenerationDelay() * 1000, null);
- }
-};
-
-ResourceSupply.prototype.CancelRegenerationDelayTimer = function()
-{
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- cmpTimer.CancelTimer(this.regenDelayTimer);
- this.regenDelayTimer = null;
-};
-
+
+ // Remove entities that have been exhausted
+ if (this.amount == 0)
+ Engine.DestroyEntity(this.entity);
+
+ Engine.PostMessage(this.entity, MT_ResourceSupplyChanged, { "from": old, "to": this.amount });
+
+ return { "amount": change, "exhausted": (this.amount == 0) };
+};
+
ResourceSupply.prototype.GetType = function()
{
// All resources must have both type and subtype
@@ -185,54 +117,22 @@ ResourceSupply.prototype.IsAvailable = function(gathererID)
if (this.gatherers.length < this.GetMaxGatherers() || this.gatherers.indexOf(gathererID) !== -1)
return true;
return false;
-};
-
-ResourceSupply.prototype.IsRegenerative = function()
-{
- return this.GetRegenerationRate() != 0;
-};
-
-ResourceSupply.prototype.GetTerritoryOwner = function ()
-{
- var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
- var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (!(cmpPosition && cmpPosition.IsInWorld()))
- return 0; // Something's wrong, just say we're in neutral territory.
- var pos = cmpPosition.GetPosition2D();
- return cmpPlayerManager.GetPlayerByID(cmpTerritoryManager.GetOwner(pos.x, pos.y));
-};
-
-ResourceSupply.prototype.GetRegenerationRate = function()
-{
- return ApplyTechModificationsToPlayer("ResourceSupply/Regeneration/Rate", this.regenRate, this.GetTerritoryOwner());
-};
-
-ResourceSupply.prototype.GetRegenerationAcceleration = function()
-{
- return ApplyTechModificationsToPlayer("ResourceSupply/Regeneration/Acceleration", this.regenAccel, this.GetTerritoryOwner());
-};
-
-ResourceSupply.prototype.GetRegenerationDelay = function()
-{
- return ApplyTechModificationsToPlayer("ResourcesSupply/Regeneration/Delay", this.regenDelay, this.GetTerritoryOwner());
-};
-
-ResourceSupply.prototype.AddGatherer = function(gathererID)
-{
- if (!this.IsAvailable(gathererID))
- return false;
-
- if (this.gatherers.indexOf(gathererID) === -1)
- {
- this.gatherers.push(gathererID);
- this.CancelRegenerationDelayTimer();
- // broadcast message, mainly useful for the AIs.
- Engine.PostMessage(this.entity, MT_ResourceSupplyGatherersChanged, { "to": this.gatherers });
- }
-
- return true;
-};
+};
+
+ResourceSupply.prototype.AddGatherer = function(gathererID)
+{
+ if (!this.IsAvailable(gathererID))
+ return false;
+
+ if (this.gatherers.indexOf(gathererID) === -1)
+ {
+ this.gatherers.push(gathererID);
+ // broadcast message, mainly useful for the AIs.
+ Engine.PostMessage(this.entity, MT_ResourceSupplyGatherersChanged, { "to": this.gatherers });
+ }
+
+ return true;
+};
// should this return false if the gatherer didn't gather from said resource?
ResourceSupply.prototype.RemoveGatherer = function(gathererID)
@@ -241,10 +141,8 @@ ResourceSupply.prototype.RemoveGatherer = function(gathererID)
{
this.gatherers.splice(this.gatherers.indexOf(gathererID),1);
// broadcast message, mainly useful for the AIs.
- Engine.PostMessage(this.entity, MT_ResourceSupplyGatherersChanged, { "to": this.gatherers });
- }
- if (this.gatherers.length == 0)
- this.StartRegenerationDelayTimer();
-};
-
+ Engine.PostMessage(this.entity, MT_ResourceSupplyGatherersChanged, { "to": this.gatherers });
+ }
+};
+
Engine.RegisterComponentType(IID_ResourceSupply, "ResourceSupply", ResourceSupply);
diff --git a/binaries/data/mods/public/simulation/components/StatisticsTracker.js b/binaries/data/mods/public/simulation/components/StatisticsTracker.js
index 5ccb40034c..ab87560e93 100644
--- a/binaries/data/mods/public/simulation/components/StatisticsTracker.js
+++ b/binaries/data/mods/public/simulation/components/StatisticsTracker.js
@@ -98,10 +98,7 @@ StatisticsTracker.prototype.KilledEntity = function(targetEntity)
{
var cmpTargetEntityIdentity = Engine.QueryInterface(targetEntity, IID_Identity);
var cmpCost = Engine.QueryInterface(targetEntity, IID_Cost);
- if (cmpCost)
- var costs = cmpCost.GetResourceCosts();
- else
- var costs = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
+ var costs = cmpCost.GetResourceCosts();
if (cmpTargetEntityIdentity)
{
var cmpFoundation = Engine.QueryInterface(targetEntity, IID_Foundation);
@@ -113,7 +110,7 @@ StatisticsTracker.prototype.KilledEntity = function(targetEntity)
var targetIsCivCentre = cmpTargetEntityIdentity.HasClass("CivCentre");
var cmpTargetOwnership = Engine.QueryInterface(targetEntity, IID_Ownership);
-
+
// Don't increase counters if target player is gaia (player 0)
if (cmpTargetOwnership.GetOwner() != 0)
{
@@ -124,7 +121,7 @@ StatisticsTracker.prototype.KilledEntity = function(targetEntity)
{
this.enemyUnitsKilledValue += costs[r];
}
- }
+ }
if (targetIsStructure)
{
this.enemyBuildingsDestroyed++;
@@ -143,10 +140,7 @@ StatisticsTracker.prototype.LostEntity = function(lostEntity)
{
var cmpLostEntityIdentity = Engine.QueryInterface(lostEntity, IID_Identity);
var cmpCost = Engine.QueryInterface(lostEntity, IID_Cost);
- if (cmpCost)
- var costs = cmpCost.GetResourceCosts();
- else
- var costs = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
+ var costs = cmpCost.GetResourceCosts();
if (cmpLostEntityIdentity)
{
var cmpFoundation = Engine.QueryInterface(lostEntity, IID_Foundation);
@@ -162,8 +156,8 @@ StatisticsTracker.prototype.LostEntity = function(lostEntity)
for (var r in costs)
{
this.unitsLostValue += costs[r];
- }
- }
+ }
+ }
if (lostEntityIsStructure)
{
this.buildingsLost++;
@@ -183,7 +177,7 @@ StatisticsTracker.prototype.LostEntity = function(lostEntity)
StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType)
{
this.resourcesGathered[type] += amount;
-
+
if (type == "food" && (specificType == "fruit" || specificType == "grain"))
this.resourcesGathered["vegetarianFood"] += amount;
};
diff --git a/binaries/data/mods/public/simulation/templates/gaia/flora_tree_apple.xml b/binaries/data/mods/public/simulation/templates/gaia/flora_tree_apple.xml
index 3997da45aa..93fe826cdb 100644
--- a/binaries/data/mods/public/simulation/templates/gaia/flora_tree_apple.xml
+++ b/binaries/data/mods/public/simulation/templates/gaia/flora_tree_apple.xml
@@ -10,13 +10,10 @@
gaia/flora_bush_berry.png
-
-
-
- 150
-
-
- 400
+
+
+
+ 400food.fruit
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml b/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
index 7a431d3c45..4cf040fb7d 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
@@ -13,32 +13,15 @@
food
-
-
-
- 100
- 0
- vanish
- true
- false
-
-
- 1.0
- 1.0
- 1.0
-
-
- false
- 200
- food.fruit
- 8
-
- 3
- 10
- linear
-
-
-
+
+
+
+ false
+ 200
+ food.fruit
+ 8
+
+
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml b/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
index 9719190443..995213aebf 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_fauna_fish.xml
@@ -20,17 +20,11 @@
false
- 1000
- food.fish
- 4
-
- 1
- 1.03
- 5
- quadratic
-
-
-
+ 1000
+ food.fish
+ 4
+
+
diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp
index b93c749271..37403929a3 100644
--- a/source/gui/CGUI.cpp
+++ b/source/gui/CGUI.cpp
@@ -1775,15 +1775,6 @@ void CGUI::Xeromyces_ReadScrollBarStyle(XMBElement Element, CXeromyces* pFile)
scrollbar.m_MinimumBarSize = f;
}
else
- if (attr_name == "maximum_bar_size")
- {
- float f;
- if (!GUI::ParseString(attr_value.FromUTF8(), f))
- LOGERROR(L"GUI: Error parsing '%hs' (\"%hs\")", attr_name.c_str(), attr_value.c_str());
- else
- scrollbar.m_MaximumBarSize = f;
- }
- else
if (attr_name == "sprite_button_top")
scrollbar.m_SpriteButtonTop = attr_value;
else
diff --git a/source/gui/CGUIScrollBarVertical.cpp b/source/gui/CGUIScrollBarVertical.cpp
index d8a591c68b..f5f078bff1 100644
--- a/source/gui/CGUIScrollBarVertical.cpp
+++ b/source/gui/CGUIScrollBarVertical.cpp
@@ -22,6 +22,7 @@ IGUIScrollBar
#include "precompiled.h"
#include "GUI.h"
#include "CGUIScrollBarVertical.h"
+
#include "ps/CLogger.h"
@@ -38,22 +39,18 @@ void CGUIScrollBarVertical::SetPosFromMousePos(const CPos &mouse)
if (!GetStyle())
return;
- /**
- * Calculate the position for the top of the item being scrolled
- */
- m_Pos = m_PosWhenPressed + GetMaxPos() * (mouse.y - m_BarPressedAtPos.y) / (m_Length - GetStyle()->m_Width * 2 - m_BarSize);
+ m_Pos = (m_PosWhenPressed + m_ScrollRange*(mouse.y-m_BarPressedAtPos.y)/(m_Length-GetStyle()->m_Width*2));
}
void CGUIScrollBarVertical::Draw()
{
if (!GetStyle())
{
- LOGWARNING(L"Attempt to draw scrollbar without a style.");
+ // TODO Gee: Report in error log
return;
}
- // Only draw the scrollbar if the GUI exists and there is something to scroll.
- if (GetGUI() && GetMaxPos() != 1)
+ if (GetGUI())
{
CRect outline = GetOuterRect();
@@ -82,7 +79,7 @@ void CGUIScrollBarVertical::Draw()
}
else button_top = &GetStyle()->m_SpriteButtonTop;
- // figure out what sprite to use for bottom button
+ // figure out what sprite to use for top button
if (m_ButtonPlusHovered)
{
if (m_ButtonPlusPressed)
@@ -114,10 +111,22 @@ void CGUIScrollBarVertical::Draw()
}
// Draw bar
- GetGUI()->DrawSprite(GetStyle()->m_SpriteBarVertical,
- 0,
- m_Z + 0.2f,
- GetBarRect());
+ /*if (m_BarPressed)
+ GetGUI()->DrawSprite(GUI<>::FallBackSprite(GetStyle()->m_SpriteBarVerticalPressed, GetStyle()->m_SpriteBarVertical),
+ 0,
+ m_Z+0.2f,
+ GetBarRect());
+ else
+ if (m_BarHovered)
+ GetGUI()->DrawSprite(GUI<>::FallBackSprite(GetStyle()->m_SpriteBarVerticalOver, GetStyle()->m_SpriteBarVertical),
+ 0,
+ m_Z+0.2f,
+ GetBarRect());
+ else*/
+ GetGUI()->DrawSprite(GetStyle()->m_SpriteBarVertical,
+ 0,
+ m_Z+0.2f,
+ GetBarRect());
}
}
@@ -132,19 +141,32 @@ CRect CGUIScrollBarVertical::GetBarRect() const
if (!GetStyle())
return ret;
- // Get from where the scroll area begins to where it ends
- float from = m_Y;
- float to = m_Y + m_Length - m_BarSize;
+ float size;
+ float from, to;
+ // is edge buttons used?
if (m_UseEdgeButtons)
{
- from += GetStyle()->m_Width;
- to -= GetStyle()->m_Width;
+ size = (m_Length-GetStyle()->m_Width*2.f)*m_BarSize;
+ if (size < GetStyle()->m_MinimumBarSize)
+ size = GetStyle()->m_MinimumBarSize;
+
+ from = m_Y+GetStyle()->m_Width;
+ to = m_Y+m_Length-GetStyle()->m_Width-size;
+ }
+ else
+ {
+ size = m_Length*m_BarSize;
+ if (size < GetStyle()->m_MinimumBarSize)
+ size = GetStyle()->m_MinimumBarSize;
+
+ from = m_Y;
+ to = m_Y+m_Length-size;
}
// Setup rectangle
- ret.top = from + (to - from) * (m_Pos / GetMaxPos());
- ret.bottom = ret.top + m_BarSize;
+ ret.top = (from + (to-from)*(m_Pos/(std::max(1.f, m_ScrollRange - m_ScrollSpace))));
+ ret.bottom = ret.top+size;
ret.right = m_X + ((m_RightAligned)?(0.f):(GetStyle()->m_Width));
ret.left = ret.right - GetStyle()->m_Width;
diff --git a/source/gui/CGUIScrollBarVertical.h b/source/gui/CGUIScrollBarVertical.h
index 76dad8241e..49751a66b3 100644
--- a/source/gui/CGUIScrollBarVertical.h
+++ b/source/gui/CGUIScrollBarVertical.h
@@ -74,7 +74,7 @@ public:
virtual void HandleMessage(SGUIMessage &Message);
/**
- * Set m_Pos with g_mouse_x/y input, i.e. when dragging.
+ * Set m_Pos with g_mouse_x/y input, i.e. when draggin.
*/
virtual void SetPosFromMousePos(const CPos &mouse);
diff --git a/source/gui/IGUIScrollBar.cpp b/source/gui/IGUIScrollBar.cpp
index a88410cbbe..4c7d489d27 100644
--- a/source/gui/IGUIScrollBar.cpp
+++ b/source/gui/IGUIScrollBar.cpp
@@ -21,8 +21,7 @@ IGUIScrollBar
#include "precompiled.h"
#include "GUI.h"
-#include "maths/MathUtil.h"
-#include "ps/CLogger.h"
+
//-------------------------------------------------------------------
// IGUIScrollBar
@@ -31,7 +30,7 @@ IGUIScrollBar::IGUIScrollBar() : m_pStyle(NULL), m_pGUI(NULL),
m_X(300.f), m_Y(300.f),
m_ScrollRange(1.f), m_ScrollSpace(0.f), // MaxPos: not 0, due to division.
m_Length(200.f), m_Width(20.f),
- m_BarSize(0.f), m_Pos(0.f),
+ m_BarSize(0.5f), m_Pos(0.f),
m_UseEdgeButtons(true),
m_ButtonPlusPressed(false),
m_ButtonMinusPressed(false),
@@ -48,25 +47,7 @@ IGUIScrollBar::~IGUIScrollBar()
void IGUIScrollBar::SetupBarSize()
{
- float min = GetStyle()->m_MinimumBarSize;
- float max = GetStyle()->m_MaximumBarSize;
- float length = m_Length;
-
- // Check for edge buttons
- if (m_UseEdgeButtons)
- length -= GetStyle()->m_Width * 2.f;
-
- // Check min and max are valid
- if (min > length)
- {
- LOGWARNING(L"Minimum scrollbar size of %g is larger than the total scrollbar size of %g", min, length);
- min = 0.f;
- }
- if (max < min)
- max = length;
-
- // Clamp size to not exceed a minimum or maximum.
- m_BarSize = clamp(length * std::min((float)m_ScrollSpace / (float)m_ScrollRange, 1.f), min, max);
+ m_BarSize = std::min((float)m_ScrollSpace/(float)m_ScrollRange, 1.f);
}
const SGUIScrollBarStyle *IGUIScrollBar::GetStyle() const
@@ -91,8 +72,8 @@ void IGUIScrollBar::UpdatePosBoundaries()
m_ScrollRange < m_ScrollSpace) // <= scrolling not applicable
m_Pos = 0.f;
else
- if (m_Pos > GetMaxPos())
- m_Pos = GetMaxPos();
+ if (m_Pos > m_ScrollRange - m_ScrollSpace)
+ m_Pos = m_ScrollRange - m_ScrollSpace;
}
void IGUIScrollBar::HandleMessage(SGUIMessage &Message)
@@ -112,10 +93,12 @@ void IGUIScrollBar::HandleMessage(SGUIMessage &Message)
UpdatePosBoundaries();
}
+ CRect bar_rect = GetBarRect();
// check if components are being hovered
- m_BarHovered = GetBarRect().PointInside(mouse);
- m_ButtonMinusHovered = HoveringButtonMinus(mouse);
- m_ButtonPlusHovered = HoveringButtonPlus(mouse);
+ m_BarHovered = bar_rect.PointInside(mouse);
+
+ m_ButtonMinusHovered = HoveringButtonMinus(m_pHostObject->GetMousePos());
+ m_ButtonPlusHovered = HoveringButtonPlus(m_pHostObject->GetMousePos());
if (!m_ButtonMinusHovered)
m_ButtonMinusPressed = false;
diff --git a/source/gui/IGUIScrollBar.h b/source/gui/IGUIScrollBar.h
index 45beefbcd8..a99a3f3810 100644
--- a/source/gui/IGUIScrollBar.h
+++ b/source/gui/IGUIScrollBar.h
@@ -90,13 +90,6 @@ struct SGUIScrollBarStyle
*/
float m_MinimumBarSize;
- /**
- * Sometimes you would like your scroll bar to have a fixed maximum size
- * so that the texture does not get too stretched, you can set a maximum
- * in pixels.
- */
- float m_MaximumBarSize;
-
//@}
//--------------------------------------------------------
/** @name Horizontal Sprites */
@@ -206,27 +199,27 @@ public:
virtual void SetPos(float f) { m_Pos = f; UpdatePosBoundaries(); }
/**
- * Get the value of m_Pos that corresponds to the bottom of the scrollable region
+ * Get the value of Pos that corresponds to the bottom of the scrollable region
*/
- float GetMaxPos() const { return std::max(1.f, m_ScrollRange - m_ScrollSpace); }
+ float GetMaxPos() const { return m_ScrollRange - m_ScrollSpace; }
/**
- * Increase scroll one step
+ * Scroll towards 1.0 one step
*/
virtual void ScrollPlus() { m_Pos += 30.f; UpdatePosBoundaries(); }
/**
- * Decrease scroll one step
+ * Scroll towards 0.0 one step
*/
virtual void ScrollMinus() { m_Pos -= 30.f; UpdatePosBoundaries(); }
/**
- * Increase scroll three steps
+ * Scroll towards 1.0 one step
*/
virtual void ScrollPlusPlenty() { m_Pos += 90.f; UpdatePosBoundaries(); }
/**
- * Decrease scroll three steps
+ * Scroll towards 0.0 one step
*/
virtual void ScrollMinusPlenty() { m_Pos -= 90.f; UpdatePosBoundaries(); }