Index: binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png
===================================================================
--- binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png (revision 0)
+++ binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png (working copy)
Property changes on: binaries/data/mods/public/art/textures/ui/session/icons/upgrade.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js (revision 15601)
+++ binaries/data/mods/public/gui/session/input.js (working copy)
@@ -1614,27 +1614,27 @@
});
}
-// Transform a wall to a gate
-function transformWallToGate(template)
+// Upgrade an entity to another
+function upgradeEntity(Template)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
- "type": "wall-to-gate",
- "entities": selection.filter( function(e) { return getWallGateTemplate(e) == template } ),
- "template": template,
+ "type": "upgrade",
+ "entities": selection,
+ "template": Template,
+ "queued": false
});
}
-// Gets the gate form (if any) of a given long wall piece
-function getWallGateTemplate(entity)
+// Cancel upgrading entities
+function cancelUpgradeEntity()
{
- // TODO: find the gate template name in a better way
- var entState = GetEntityState(entity);
- var index;
-
- if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0)
- return entState.template.substr(0, index) + "gate";
- return undefined;
+ var selection = g_Selection.toList();
+ Engine.PostNetworkCommand({
+ "type": "cancel-upgrade",
+ "entities": selection,
+ "queued": false
+ });
}
// Set the camera to follow the given unit
Index: binaries/data/mods/public/gui/session/selection_panels.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels.js (revision 15601)
+++ binaries/data/mods/public/gui/session/selection_panels.js (working copy)
@@ -372,29 +372,8 @@
for (var i in selection)
{
var state = GetEntityState(selection[i]);
- if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
+ if (state.gate && !gates.length)
{
- var gateTemplate = getWallGateTemplate(state.id);
- if (gateTemplate)
- {
- var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
- if (!tooltipString)
- {
- warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
- tooltipString = "";
- }
- walls.push({
- "tooltip": translate(tooltipString),
- "template": gateTemplate,
- "callback": function (item) { transformWallToGate(item.template); }
- });
- }
-
- // We only need one entity per type.
- longWallTypes[state.template] = true;
- }
- else if (state.gate && !gates.length)
- {
gates.push({
"gate": state.gate,
"tooltip": translate("Lock Gate"),
@@ -424,33 +403,14 @@
},
"setTooltip": function(data)
{
- var tooltip = data.item.tooltip;
- if (data.item.template)
- {
- data.template = GetTemplateData(data.item.template);
- data.wallCount = data.selection.reduce(function (count, ent) {
- var state = GetEntityState(ent);
- if (hasClass(state, "LongWall") && !state.gate)
- count++;
- return count;
- }, 0);
-
- tooltip += "\n" + getEntityCostTooltip(data.template, data.wallCount);
-
-
- data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(data.template, data.wallCount));
- if (data.neededResources)
- tooltip += getNeededResourcesTooltip(data.neededResources);
- }
- data.button.tooltip = tooltip;
+ data.button.tooltip = data.item.tooltip;
},
"setGraphics": function(data)
{
- data.affordableMask.hidden == data.neededResources ? true : false;
var gateIcon;
if (data.item.gate)
{
- // If already a gate, show locking actions
+ // Show locking actions
gateIcon = "icons/lock_" + GATE_ACTIONS[data.item.locked ? 0 : 1] + "ed.png";
if (data.item.gate.locked === undefined)
data.guiSelection.hidden = false
@@ -457,16 +417,6 @@
else
data.guiSelection.hidden = data.item.gate.locked != data.item.locked;
}
- else
- {
- // otherwise show gate upgrade icon
- var template = GetTemplateData(data.item.template);
- if (!template)
- return;
- gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
- data.guiSelection.hidden = true;
- }
-
data.icon.sprite = "stretched:session/" + gateIcon;
},
"setPosition": function(data)
@@ -541,6 +491,142 @@
},
};
+// UPGRADE
+g_SelectionPanels.Upgrade = {
+ "getMaxNumberOfItems": function()
+ {
+ return 24 - getNumberOfRightPanelButtons();
+ },
+ "getItems": function(unitEntState, selection)
+ {
+ // Interface becomes complicated with multiple units and this is meant per-entity, so prevent it if the selection has multiple units.
+ if (selection.length > 1)
+ return false;
+ var items = [];
+
+ if (!unitEntState.upgrade)
+ return false;
+
+ for each (var upgrade in unitEntState.upgrade.upgrades)
+ {
+ var item = {};
+ item.entType = upgrade.entity;
+ item.template = GetTemplateData(upgrade.entity);
+ if (!item.template) // abort if no template
+ return false;
+
+ item.icon = upgrade.icon;
+ if (!item.icon)
+ item.icon = "portraits/" + item.template.icon;
+
+ if (upgrade.requiredTechnology !== null)
+ {
+ item.requiredTechnology = upgrade.requiredTechnology;
+ if (!item.requiredTechnology && item.template.requiredTechnology)
+ item.requiredTechnology = item.template.requiredTechnology
+ }
+ item.cost = upgrade.cost;
+ item.time = upgrade.time;
+
+ if (unitEntState.upgrade.progress === undefined)
+ {
+ item.upgrading = UPGRADING_NOT_STARTED;
+ item.tooltip = translate("Upgrade into a " + item.template.name.generic + (upgrade.tooltip? ".\n" + upgrade.tooltip : "."));
+ item.callback = function(data) { upgradeEntity(data.entType); };
+ }
+ else if (unitEntState.upgrade.template !== upgrade.entity)
+ {
+ item.upgrading = UPGRADING_CHOSEN_OTHER;
+ item.tooltip = translate("Cannot upgrade when the entity is already upgrading.");
+ item.callback = function(data) { };
+ }
+ else
+ {
+ item.upgrading = unitEntState.upgrade.progress;
+ item.tooltip = translate("Cancel Upgrading");
+ item.callback = function(data) { cancelUpgradeEntity(); };
+ }
+ items.push(item);
+ }
+ return items;
+ },
+ "addData" : function(data)
+ {
+ data.item.technologyEnabled = true;
+ if (data.item.requiredTechnology)
+ data.item.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", data.item.requiredTechnology);
+ if (data.item.cost)
+ {
+ var totalCost = {};
+ for (var i in data.item.cost)
+ totalCost[i] = data.item.cost[i];
+ data.item.neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCost);
+ }
+ data.item.limits = getEntityLimitAndCount(data.playerState, data.item.entType);
+ return true;
+ },
+ "setAction": function(data)
+ {
+ data.button.onPress = function() { data.item.callback(data.item); };
+ },
+ "setTooltip": function(data)
+ {
+ var tooltip = data.item.tooltip;
+
+ if (data.item.upgrading !== UPGRADING_NOT_STARTED)
+ {
+ data.button.tooltip = tooltip;
+ return;
+ }
+
+ if (data.item.cost)
+ tooltip += "\n" + getEntityCostTooltip(data.item);
+
+ tooltip += formatLimitString(data.item.limits.entLimit, data.item.limits.entCount, data.item.limits.entLimitChangers);
+ if (!data.item.technologyEnabled)
+ {
+ var techName = getEntityNames(GetTechnologyData(data.item.requiredTechnology));
+ tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName });
+ }
+ if (data.item.neededResources)
+ tooltip += getNeededResourcesTooltip(data.item.neededResources);
+
+ data.button.tooltip = tooltip;
+ },
+ "setGraphics": function(data)
+ {
+ var grayscale = "";
+ if (data.item.upgrading == UPGRADING_CHOSEN_OTHER || !data.item.technologyEnabled || (data.item.limits.canBeAddedCount == 0 && data.item.upgrading == UPGRADING_NOT_STARTED))
+ {
+ data.button.enabled = false;
+ grayscale = "grayscale:";
+ data.affordableMask.hidden = false;
+ data.affordableMask.sprite = "colour: 0 0 0 127";
+ }
+ else if (data.item.upgrading == UPGRADING_NOT_STARTED && data.item.neededResources)
+ {
+ data.button.enabled = false;
+ data.affordableMask.hidden = false;
+ data.affordableMask.sprite = resourcesToAlphaMask(data.item.neededResources);
+ }
+
+ data.icon.sprite = "stretched:" + grayscale + "session/" + data.item.icon;
+
+ var guiObject = Engine.GetGUIObjectByName("unitUpgradeProgressSlider["+data.i+"]");
+ var size = guiObject.size;
+ if (data.item.upgrading < 0)
+ size.top = size.right;
+ else
+ size.top = size.left + Math.round(Math.max(0,data.item.upgrading) * (size.right - size.left));
+ guiObject.size = size;
+ },
+ "setPosition": function(data)
+ {
+ var index = data.i + getNumberOfRightPanelButtons();
+ setPanelObjectPosition(data.button, index, data.rowLength);
+ },
+};
+
// QUEUE
g_SelectionPanels.Queue = {
"getMaxNumberOfItems": function()
@@ -1015,6 +1101,7 @@
// RIGHT PANE
"Gate", // must always be shown on gates
"Pack", // must always be shown on packable entities
+ "Upgrade", // must always be shown on upgradable entities
"Training",
"Construction",
"Research", // normal together with training
Index: binaries/data/mods/public/gui/session/selection_panels_helpers.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels_helpers.js (revision 15601)
+++ binaries/data/mods/public/gui/session/selection_panels_helpers.js (working copy)
@@ -8,6 +8,10 @@
// Gate constants
const GATE_ACTIONS = ["lock", "unlock"];
+// upgrade constants
+const UPGRADING_NOT_STARTED = -2;
+const UPGRADING_CHOSEN_OTHER = -1;
+
// ==============================================
// BARTER HELPERS
// Resources to sell on barter panel
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js (revision 15601)
+++ binaries/data/mods/public/gui/session/unit_commands.js (working copy)
@@ -1,5 +1,5 @@
// The number of currently visible buttons (used to optimise showing/hiding)
-var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0};
+var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0, "Upgrade": 0};
/**
* Set the position of a panel object according to the index,
@@ -289,5 +289,7 @@
sum += g_unitPanelButtons["Pack"];
if (g_SelectionPanels["Gate"].used)
sum += g_unitPanelButtons["Gate"];
+ if (g_SelectionPanels["Upgrade"].used)
+ sum += g_unitPanelButtons["Upgrade"];
return sum;
}
Index: binaries/data/mods/public/gui/session/selection_panels_right/gate_panel.xml
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels_right/gate_panel.xml (revision 15601)
+++ binaries/data/mods/public/gui/session/selection_panels_right/gate_panel.xml (working copy)
@@ -7,7 +7,6 @@
Index: binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml (revision 0)
+++ binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml (working copy)
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Property changes on: binaries/data/mods/public/gui/session/selection_panels_right/upgrade_panel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js (revision 15601)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js (working copy)
@@ -180,6 +180,7 @@
"gate": null,
"guard": null,
"pack": null,
+ "upgrade" : null,
"player": -1,
"position": null,
"production": null,
@@ -232,6 +233,16 @@
};
}
+ var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
+ if (cmpUpgrade)
+ {
+ ret.upgrade = {
+ "upgrades" : cmpUpgrade.GetUpgrades(),
+ "progress": cmpUpgrade.GetProgress(),
+ "template": cmpUpgrade.GetUpgradingTo()
+ };
+ }
+
var cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
if (cmpProductionQueue)
{
@@ -653,7 +664,6 @@
};
ret.icon = template.Identity.Icon;
ret.tooltip = template.Identity.Tooltip;
- ret.gateConversionTooltip = template.Identity.GateConversionTooltip;
ret.requiredTechnology = template.Identity.RequiredTechnology;
ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
}
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js (revision 15601)
+++ binaries/data/mods/public/simulation/components/Identity.js (working copy)
@@ -30,11 +30,6 @@
"" +
"" +
"" +
- "" +
- "" +
- "" +
- "" +
- "" +
"" +
"" +
"" +
Index: binaries/data/mods/public/simulation/components/Pack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Pack.js (revision 15601)
+++ binaries/data/mods/public/simulation/components/Pack.js (working copy)
@@ -116,76 +116,24 @@
Pack.prototype.PackProgress = function(data, lateness)
{
- if (this.elapsedTime >= this.GetPackTime())
+ if (this.elapsedTime < this.GetPackTime())
{
- this.CancelTimer();
+ this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
+ return;
+ }
- this.packed = !this.packed;
- Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
+ this.CancelTimer();
- // Done un/packing, copy our parameters to the final entity
- var newEntity = Engine.AddEntity(this.template.Entity);
- if (newEntity == INVALID_ENTITY)
- {
- // Error (e.g. invalid template names)
- error("PackProgress: Error creating entity for '" + this.template.Entity + "'");
- return;
- }
+ this.packed = !this.packed;
+ Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position);
- if (cmpPosition.IsInWorld())
- {
- var pos = cmpPosition.GetPosition2D();
- cmpNewPosition.JumpTo(pos.x, pos.y);
- }
- var rot = cmpPosition.GetRotation();
- cmpNewPosition.SetYRotation(rot.y);
- cmpNewPosition.SetXZRotation(rot.x, rot.z);
- cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
+ var newEntity = ChangeEntityTemplate(this.entity, this.template.Entity);
- var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
- cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
-
- // Maintain current health level
- var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
- var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health);
- var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
- cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
-
- var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
- var cmpNewUnitAI = Engine.QueryInterface(newEntity, IID_UnitAI);
- if (cmpUnitAI && cmpNewUnitAI)
- {
- var pos = cmpUnitAI.GetHeldPosition();
- if (pos)
- cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
- if (cmpUnitAI.GetStanceName())
- cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
- cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
- cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
- }
-
- // Maintain the list of guards
- var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard);
- var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard);
- if (cmpGuard && cmpNewGuard)
- cmpNewGuard.SetEntities(cmpGuard.GetEntities());
-
- Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity });
-
- // Play notification sound
+ if (newEntity)
+ {
var sound = this.packed ? "packed" : "unpacked";
PlaySound(sound, newEntity);
-
- // Destroy current entity
- Engine.DestroyEntity(this.entity);
}
- else
- {
- this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
- }
};
Engine.RegisterComponentType(IID_Pack, "Pack", Pack);
Index: binaries/data/mods/public/simulation/components/Upgrade.js
===================================================================
--- binaries/data/mods/public/simulation/components/Upgrade.js (revision 0)
+++ binaries/data/mods/public/simulation/components/Upgrade.js (working copy)
@@ -0,0 +1,298 @@
+function Upgrade() {}
+
+const UPGRADING_PROGRESS_INTERVAL = 250;
+
+Upgrade.prototype.Schema =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+Upgrade.prototype.Init = function()
+{
+ this.upgrading = false;
+ this.elapsedTime = 0;
+ this.timer = undefined;
+
+ this.upgradeTemplates = {};
+
+ for (var choice in this.template)
+ {
+ var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
+ var name = this.template[choice].Entity;
+ if (cmpIdentity)
+ name = name.replace(/\{civ\}/g, cmpIdentity.GetCiv());
+ this.upgradeTemplates[name] = choice;
+ }
+};
+
+// On owner change, abort the upgrade
+// This will also deal with the "OnDestroy" case.
+Upgrade.prototype.OnOwnershipChanged = function(msg)
+{
+ this.CancelUpgrade();
+
+ if (msg.to !== -1)
+ this.owner = msg.to;
+};
+
+Upgrade.prototype.ChangeUpgradedEntityCount = function(amount)
+{
+ if (!this.IsUpgrading())
+ return;
+ var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ var template = cmpTempMan.GetTemplate(this.upgrading);
+ var category = null;
+ if (template.TrainingRestrictions)
+ category = template.TrainingRestrictions.Category;
+ else if (template.BuildRestrictions)
+ category = template.BuildRestrictions.Category;
+
+ var cmpEntityLimits = QueryPlayerIDInterface(this.owner, IID_EntityLimits);
+ cmpEntityLimits.ChangeCount(category,amount);
+};
+
+Upgrade.prototype.CanUpgradeTo = function(template)
+{
+ return this.upgradeTemplates[template] !== undefined;
+};
+
+Upgrade.prototype.GetUpgrades = function()
+{
+ var ret = [];
+
+ var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
+
+ for each (var choice in this.template)
+ {
+ var entType = choice.Entity;
+ if (cmpIdentity)
+ entType = entType.replace(/\{civ\}/g, cmpIdentity.GetCiv());
+
+ var hasCosts = false;
+ var cost = {};
+ if (choice.Cost)
+ {
+ hasCosts = true;
+ for (var type in choice.Cost)
+ cost[type] = ApplyValueModificationsToTemplate("Upgrade/Cost/"+type, +choice.Cost[type], this.owner, entType);
+ }
+ if (choice.Time)
+ {
+ hasCosts = true;
+ cost["time"] = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time/1000.0, this.owner, entType);
+ }
+ ret.push(
+ {
+ "entity": entType,
+ "icon": choice.Icon || undefined,
+ "cost": hasCosts ? cost : undefined,
+ "tooltip": choice.Tooltip || undefined,
+ "requiredTechnology": "RequiredTechnology" in choice ? choice.RequiredTechnology : null,
+ });
+ }
+
+ return ret;
+};
+
+Upgrade.prototype.CancelTimer = function()
+{
+ if (this.timer)
+ {
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.timer);
+ this.timer = undefined;
+ }
+};
+
+Upgrade.prototype.IsUpgrading = function()
+{
+ return this.upgrading !== false;
+};
+
+Upgrade.prototype.GetUpgradingTo = function()
+{
+ return this.upgrading;
+};
+
+Upgrade.prototype.CheckPlacementRestrictions = function(template)
+{
+ if (!this.upgradeTemplates[template])
+ return undefined;
+
+ return ("CheckPlacementRestrictions" in this.template[this.upgradeTemplates[template]]);
+};
+
+Upgrade.prototype.GetRequiredTechnology = function(templateArg)
+{
+ if (!this.upgradeTemplates[templateArg])
+ return undefined;
+
+ var choice = this.upgradeTemplates[templateArg];
+
+ if ("RequiredTechnology" in this.template[choice] && this.template[choice].RequiredTechnology === undefined)
+ {
+ var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ var template = cmpTemplateManager.GetTemplate(this.template[choice].Entity);
+ if (template.Identity.RequiredTechnology)
+ return template.Identity.RequiredTechnology;
+ }
+ else if ("RequiredTechnology" in this.template[choice])
+ return this.template[choice].RequiredTechnology;
+
+ return null;
+};
+
+Upgrade.prototype.GetResourceCosts = function(template)
+{
+ if (!this.upgradeTemplates[template])
+ return undefined;
+
+ var choice = this.upgradeTemplates[template];
+ if (!this.template[choice].Cost)
+ return {};
+
+ var costs = {};
+ for (var r in this.template[choice].Cost)
+ {
+ costs[r] = +this.template[choice].Cost[r];
+ costs[r] = ApplyValueModificationsToEntity("Upgrade/Cost/"+r, costs[r], this.entity);
+ }
+ return costs;
+};
+
+Upgrade.prototype.Upgrade = function(template)
+{
+ if (this.IsUpgrading())
+ return false;
+
+ if (!this.upgradeTemplates[template])
+ return false;
+
+ var cmpPlayer = Engine.QueryOwnerInterface(this.entity, IID_Player);
+
+ if (!cmpPlayer.TrySubtractResources(this.GetResourceCosts(template)))
+ return false;
+
+ this.upgrading = template;
+
+ // prevent cheating
+ this.ChangeUpgradedEntityCount(1);
+
+ if (this.GetUpgradeTime(template) !== 0)
+ {
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.timer = cmpTimer.SetInterval(this.entity, IID_Upgrade, "UpgradeProgress", 0, UPGRADING_PROGRESS_INTERVAL, {"upgrading": template});
+ }
+ else
+ this.UpgradeProgress();
+
+ return true;
+};
+
+Upgrade.prototype.CancelUpgrade = function()
+{
+ if (this.IsUpgrading() === false)
+ return;
+
+ var cmpPlayer = Engine.QueryOwnerInterface(this.entity, IID_Player);
+ if (cmpPlayer)
+ {
+ var costs = this.GetResourceCosts(this.upgrading);
+ cmpPlayer.AddResources(costs);
+ }
+
+ this.ChangeUpgradedEntityCount(-1);
+
+ this.upgrading = false;
+ this.CancelTimer();
+ this.SetElapsedTime(0);
+};
+
+Upgrade.prototype.GetUpgradeTime = function(templateArg)
+{
+ var template = this.upgrading || templateArg;
+ var choice = this.upgradeTemplates[template];
+ if (!choice)
+ return undefined;
+ return this.template[choice].Time ? ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) : 0;
+};
+
+Upgrade.prototype.GetElapsedTime = function()
+{
+ return this.elapsedTime;
+};
+
+Upgrade.prototype.GetProgress = function()
+{
+ if (!this.IsUpgrading())
+ return undefined;
+ return this.GetUpgradeTime() == 0 ? 1 : this.elapsedTime / this.GetUpgradeTime();
+};
+
+Upgrade.prototype.SetElapsedTime = function(time)
+{
+ this.elapsedTime = time;
+};
+
+Upgrade.prototype.UpgradeProgress = function(data, lateness)
+{
+ if (this.elapsedTime < this.GetUpgradeTime())
+ {
+ this.SetElapsedTime(this.GetElapsedTime() + UPGRADING_PROGRESS_INTERVAL + lateness);
+ return;
+ }
+
+ this.CancelTimer();
+
+ var newEntity = ChangeEntityTemplate(this.entity, this.upgrading);
+
+ if (newEntity)
+ PlaySound("upgraded", newEntity);
+};
+
+Engine.RegisterComponentType(IID_Upgrade, "Upgrade", Upgrade);
Property changes on: binaries/data/mods/public/simulation/components/Upgrade.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Upgrade.js (revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/Upgrade.js (working copy)
@@ -0,0 +1 @@
+Engine.RegisterInterface("Upgrade");
Property changes on: binaries/data/mods/public/simulation/components/interfaces/Upgrade.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js (revision 15601)
+++ binaries/data/mods/public/simulation/helpers/Commands.js (working copy)
@@ -551,14 +551,6 @@
}
},
- "wall-to-gate": function(player, cmd, data)
- {
- for each (var ent in data.entities)
- {
- TryTransformWallToGate(ent, data.cmpPlayer, cmd.template);
- }
- },
-
"lock-gate": function(player, cmd, data)
{
for each (var ent in data.entities)
@@ -642,6 +634,66 @@
}
}
},
+
+ "upgrade": function(player, cmd, data)
+ {
+ for each (var ent in data.entities)
+ {
+ var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
+
+ if (!cmpUpgrade || !cmpUpgrade.CanUpgradeTo(cmd.template))
+ continue;
+
+ if (cmpUpgrade.CheckPlacementRestrictions(cmd.template) && ObstructionsBlockingTemplateChange(ent, cmd.template))
+ {
+ var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade as distance requirements are not verified or terrain is obstructed." };
+ var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ cmpGUIInterface.PushNotification(notification);
+ continue;
+ }
+
+ if (!CanGarrisonedChangeTemplate(ent, cmd.template))
+ {
+ var notification = {"players": [data.cmpPlayer.GetPlayerID()], "message": "Cannot upgrade a garrisoned entity." };
+ var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ cmpGUIInterface.PushNotification(notification);
+ continue;
+ }
+
+ // Check entity limits
+ var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ var template = cmpTemplateManager.GetTemplate(cmd.template);
+ var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
+ if ((template.TrainingRestrictions && !cmpEntityLimits.AllowedToTrain(template.TrainingRestrictions.Category, 1))
+ || (template.BuildRestrictions && !cmpEntityLimits.AllowedToBuild(template.BuildRestrictions.Category)))
+ {
+ if (g_DebugCommands)
+ warn("Invalid command: build limits check failed for player " + player + ": " + uneval(cmd));
+ continue;
+ }
+
+ var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager);
+ if (cmpUpgrade.GetRequiredTechnology() && !cmpTechnologyManager.IsTechnologyResearched(cmpUpgrade.GetRequiredTechnology()))
+ {
+ if (g_DebugCommands)
+ warn("Invalid command: upgrading requires unresearched technology: " + uneval(cmd));
+ continue;
+ }
+
+ cmpUpgrade.Upgrade(cmd.template, data.cmpPlayer);
+ }
+ },
+
+ "cancel-upgrade": function(player, cmd, data)
+ {
+ for each (var ent in data.entities)
+ {
+ var cmpUpgrade = Engine.QueryInterface(ent, IID_Upgrade);
+ if (cmpUpgrade)
+ cmpUpgrade.CancelUpgrade(data.cmpPlayer);
+ }
+ },
+
"dialog-answer": function(player, cmd, data)
{
// Currently nothing. Triggers can read it anyway, and send this
@@ -1512,80 +1564,6 @@
return entities.filter(function(ent) { return CanControlUnitOrIsAlly(ent, player, controlAll);} );
}
-/**
- * Try to transform a wall to a gate
- */
-function TryTransformWallToGate(ent, cmpPlayer, template)
-{
- var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
- if (!cmpIdentity)
- return;
-
- // Check if this is a valid long wall segment
- if (!cmpIdentity.HasClass("LongWall"))
- {
- if (g_DebugCommands)
- warn("Invalid command: invalid wall conversion to gate for player "+player+": "+uneval(cmd));
- return;
- }
-
- var civ = cmpIdentity.GetCiv();
- var gate = Engine.AddEntity(template);
-
- var cmpCost = Engine.QueryInterface(gate, IID_Cost);
- if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
- {
- if (g_DebugCommands)
- warn("Invalid command: convert gate cost check failed for player "+player+": "+uneval(cmd));
-
- Engine.DestroyEntity(gate);
- return;
- }
-
- ReplaceBuildingWith(ent, gate);
-}
-
-/**
- * Unconditionally replace a building with another one
- */
-function ReplaceBuildingWith(ent, building)
-{
- // Move the building to the right place
- var cmpPosition = Engine.QueryInterface(ent, IID_Position);
- var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position);
- var pos = cmpPosition.GetPosition2D();
- cmpBuildingPosition.JumpTo(pos.x, pos.y);
- var rot = cmpPosition.GetRotation();
- cmpBuildingPosition.SetYRotation(rot.y);
- cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
-
- // Copy ownership
- var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
- var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership);
- cmpBuildingOwnership.SetOwner(cmpOwnership.GetOwner());
-
- // Copy control groups
- var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
- var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction);
- cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
- cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
-
- // Copy health level from the old entity to the new
- var cmpHealth = Engine.QueryInterface(ent, IID_Health);
- var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health);
- var healthFraction = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
- var buildingHitpoints = Math.round(cmpBuildingHealth.GetMaxHitpoints() * healthFraction);
- cmpBuildingHealth.SetHitpoints(buildingHitpoints);
-
- PlaySound("constructed", building);
-
- Engine.PostMessage(ent, MT_ConstructionFinished,
- { "entity": ent, "newentity": building });
- Engine.BroadcastMessage(MT_EntityRenamed, { entity: ent, newentity: building });
-
- Engine.DestroyEntity(ent);
-}
-
Engine.RegisterGlobal("GetFormationRequirements", GetFormationRequirements);
Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
Engine.RegisterGlobal("GetDockAngle", GetDockAngle);
Index: binaries/data/mods/public/simulation/helpers/Transform.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Transform.js (revision 0)
+++ binaries/data/mods/public/simulation/helpers/Transform.js (working copy)
@@ -0,0 +1,189 @@
+// Helper functions to change an entity's template and check if the transformation is possible
+
+// returns the ID of the new entity or INVALID_ENTITY.
+var ChangeEntityTemplate = function(oldEnt, newTemplate)
+{
+ // Done un/packing, copy our parameters to the final entity
+ var newEnt = Engine.AddEntity(newTemplate);
+ if (newEnt == INVALID_ENTITY)
+ {
+ // Error (e.g. invalid template names)
+ error("Transform.js: Error replacing entity " + oldEnt + " for a '" + newTemplate + "'");
+ return INVALID_ENTITY;
+ }
+
+ var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
+ var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
+ if (cmpPosition && cmpNewPosition)
+ {
+ if (cmpPosition.IsInWorld())
+ {
+ var pos = cmpPosition.GetPosition2D();
+ cmpNewPosition.JumpTo(pos.x, pos.y);
+ }
+ var rot = cmpPosition.GetRotation();
+ cmpNewPosition.SetYRotation(rot.y);
+ cmpNewPosition.SetXZRotation(rot.x, rot.z);
+ cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
+ }
+
+ var cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership);
+ var cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership);
+ if (cmpOwnership && cmpNewOwnership)
+ cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
+
+ // Copy control groups
+ var cmpObstruction = Engine.QueryInterface(oldEnt, IID_Obstruction);
+ var cmpNewObstruction = Engine.QueryInterface(newEnt, IID_Obstruction);
+ if (cmpObstruction && cmpNewObstruction)
+ {
+ cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
+ cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
+ }
+
+ // Maintain current health level
+ var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health);
+ var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health);
+ if (cmpHealth && cmpNewHealth)
+ {
+ var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
+ cmpNewHealth.SetHitpoints(Math.round(cmpNewHealth.GetMaxHitpoints() * healthLevel));
+ }
+
+ var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
+ var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
+ if (cmpUnitAI && cmpNewUnitAI)
+ {
+ var pos = cmpUnitAI.GetHeldPosition();
+ if (pos)
+ cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
+ if (cmpUnitAI.GetStanceName())
+ cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
+ cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
+ cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf());
+ }
+
+ // Maintain the list of guards
+ var cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
+ var cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
+ if (cmpGuard && cmpNewGuard)
+ cmpNewGuard.SetEntities(cmpGuard.GetEntities());
+
+ TransferGarrisonedUnits(oldEnt, newEnt);
+
+ Engine.BroadcastMessage(MT_EntityRenamed, { entity: oldEnt, newEnt: newEnt });
+
+ // Destroy current entity
+ Engine.DestroyEntity(oldEnt);
+
+ return newEnt;
+};
+
+var CanGarrisonedChangeTemplate = function(ent, Template)
+{
+ var cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ var unitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned())
+ {
+ // We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID.
+ // TODO: change this if that ever becomes possibles
+ return false;
+ }
+ return true;
+}
+
+var ObstructionsBlockingTemplateChange = function(ent, Template)
+{
+ var previewEntity = Engine.AddEntity("preview|"+Template);
+
+ if (previewEntity == INVALID_ENTITY)
+ return true;
+
+ var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
+ var cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
+
+ // Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better
+ // Plus there are no real entities without owners currently.
+ if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership)
+ return DeleteEntityAndReturnFalse(previewEntity, false);
+
+ var pos = cmpPosition.GetPosition2D();
+ var angle = cmpPosition.GetRotation();
+ // move us away to prevent our own obstruction from blocking the upgrade.
+ cmpPosition.MoveOutOfWorld();
+
+ var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
+ cmpNewPosition.JumpTo(pos.x, pos.y);
+ cmpNewPosition.SetYRotation(angle.y);
+
+ var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
+ cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
+
+ var checkPlacement = cmpBuildRestrictions.CheckPlacement();
+ if (checkPlacement && !checkPlacement.success)
+ return DeleteEntityAndReturnFalse(previewEntity, true);
+
+ var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName());
+ var newTemplate = cmpTemplateManager.GetTemplate(Template);
+
+ // Check if units are blocking our template change
+ if (canTransform && template.Obstruction && newTemplate.Obstruction)
+ {
+ // This only needs to be done if the new template is strictly bigger than the old one
+ // "Obstructions" are annoying to test so just check.
+ if (newTemplate.Obstruction.Obstructions
+ || (template.Obstruction.Static && (template.Obstruction.Static.width > newTemplate.Obstruction.Static.width
+ || template.Obstruction.Static.depth > newTemplate.Obstruction.Static.depth))
+ || (template.Obstruction.Unit && template.Obstruction.Unit.radius > newTemplate.Obstruction.Unit.radius))
+ {
+ var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction);
+ if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag())
+ {
+ // Check for units
+ var collisions = cmpNewObstruction.GetEntityCollisions(false, true);
+ if (collisions.length !== 0)
+ return DeleteEntityAndReturnFalse(previewEntity, true);
+ }
+ }
+ }
+
+ // Move preview entity out of world so it won't interfere with additional calls
+ // to this function for ent on the same sim update.
+ // Specifically, CheckPlacement() would incorrectly spot and fail on earlier preview entities
+ // if those are left in the world.
+ cmpNewPosition.MoveOutOfWorld();
+ cmpPosition.JumpTo(pos.x, pos.y);
+ cmpPosition.SetYRotation(angle.y);
+
+ return DeleteEntityAndReturnFalse(previewEntity, false);
+};
+
+var DeleteEntityAndReturn = function(ent, ret)
+{
+ Engine.DestroyEntity(previewEntity);
+ return ret;
+};
+
+var TransferGarrisonedUnits = function(oldEnt, newEnt)
+{
+ // Transfer garrisoned units if possible, or unload them
+ var cmpGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder);
+ var cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder);
+ if (!cmpNewGarrison || !cmpGarrison || cmpGarrison.GetEntities().length === 0 )
+ return; // nothing to do as the code will by default unload all.
+
+ var garrisonedEntities = cmpGarrison.GetEntities().slice();
+ for (var j = 0; j < garrisonedEntities.length; ++j)
+ {
+ var cmpUnitAI = Engine.QueryInterface(garrisonedEntities[j], IID_UnitAI);
+ cmpGarrison.Eject(garrisonedEntities[j]);
+ cmpUnitAI.Autogarrison(newEnt);
+ cmpNewGarrison.Garrison(garrisonedEntities[j]);
+ }
+};
+
+Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
+Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate);
+Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange);
Property changes on: binaries/data/mods/public/simulation/helpers/Transform.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml (revision 15601)
+++ binaries/data/mods/public/simulation/templates/template_structure_defense_wall_long.xml (working copy)
@@ -51,6 +51,15 @@
LongWall
Long wall segments can be converted to gates.
- Convert Stone Wall into City Gate
+
+
+ structures/{civ}_wall_gate
+ This will allow you to let units circulate through your fortifications.
+
+ 60
+
+
+
+
Index: binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml (revision 15601)
+++ binaries/data/mods/public/simulation/templates/other/palisades_rocks_long.xml (working copy)
@@ -21,7 +21,6 @@
other/wallset_palisade
Palisade
Wooden Wall
- Convert Wooden Wall into Wooden Gate
-StoneWall Palisade
A cheap, quick defensive structure constructed with sharpened tree trunks
gaia/special_palisade.png
@@ -41,4 +40,14 @@
11.0
+
+
+ other/palisades_rocks_gate
+
+ 0
+ 20
+
+
+
+
Index: binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml (revision 15601)
+++ binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml (working copy)
@@ -55,7 +55,6 @@
structures/palisade.png
A wooden and turf palisade buildable in enemy and neutral territories.
- Convert Siege Wall into Siege Wall Gate
Quick building, but expensive wooden and earthen walls used to surround and siege an enemy town or fortified position. The most famous examples are the Roman sieges of the Iberian stronghold of Numantia and the Gallic stronghold of Alesia.