Index: globalscripts/Templates.js
===================================================================
--- globalscripts/Templates.js (revision 17349)
+++ globalscripts/Templates.js (working copy)
@@ -226,7 +226,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: gui/session/unit_commands.js
===================================================================
--- gui/session/unit_commands.js (revision 17349)
+++ 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, "Alert": 0, "Barter": 0, "Construction": 0, "Command": 0, "AllyCommand": 0, "Stance": 0, "Gate": 0, "Pack": 0};
+var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Alert": 0, "Barter": 0, "Construction": 0, "Command": 0, "AllyCommand": 0, "Stance": 0, "Gate": 0, "Pack": 0, "Upgrade": 0};
/**
* Set the position of a panel object according to the index,
@@ -279,5 +279,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: gui/session/selection_panels_helpers.js
===================================================================
--- gui/session/selection_panels_helpers.js (revision 17349)
+++ 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: gui/session/selection_panels.js
===================================================================
--- gui/session/selection_panels.js (revision 17349)
+++ gui/session/selection_panels.js (working copy)
@@ -472,36 +472,12 @@
},
"getItems": function(unitEntState, selection)
{
- // Allow long wall pieces to be converted to gates
- var longWallTypes = {};
- var walls = [];
var gates = [];
for (var ent of selection)
{
var state = GetEntityState(ent);
- 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"),
@@ -520,10 +496,7 @@
for (var j = 0; j < gates.length; ++j)
delete gates[j].gate.locked;
}
-
- // Place wall conversion options after gate lock/unlock icons.
- var items = gates.concat(walls);
- return items;
+ return gates;
},
"setAction": function(data)
{
@@ -531,33 +504,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;
@@ -564,16 +518,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)
@@ -648,6 +592,144 @@
},
};
+// 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.
+ // TODO: if the units are all the same, this should probably still be possible.
+ if (selection.length > 1)
+ return false;
+
+ if (!unitEntState.upgrade)
+ return false;
+
+ var items = [];
+
+ 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 = sprintf(translate("Upgrade into a %(name)s.%(tooltip)s"), { name: item.template.name.generic, tooltip: (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()
@@ -1139,6 +1221,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: gui/session/input.js
===================================================================
--- gui/session/input.js (revision 17349)
+++ gui/session/input.js (working copy)
@@ -1655,27 +1655,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: gui/session/selection_panels_right/upgrade_panel.xml
===================================================================
--- gui/session/selection_panels_right/upgrade_panel.xml (nonexistent)
+++ gui/session/selection_panels_right/upgrade_panel.xml (working copy)
@@ -0,0 +1,16 @@
+
+
Property changes on: gui/session/selection_panels_right/upgrade_panel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: gui/session/selection_panels_right/gate_panel.xml
===================================================================
--- gui/session/selection_panels_right/gate_panel.xml (revision 17349)
+++ gui/session/selection_panels_right/gate_panel.xml (working copy)
@@ -7,7 +7,6 @@
-
Index: simulation/components/Upgrade.js
===================================================================
--- simulation/components/Upgrade.js (nonexistent)
+++ simulation/components/Upgrade.js (working copy)
@@ -0,0 +1,300 @@
+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());
+ if (name in this.upgradeTemplates)
+ warn("Upgrade Component: entity " + this.entity + " has two upgrades to the same entity, only the last will be used.");
+ 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.WillCheckPlacementRestrictions = 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 = 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 = 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: simulation/components/Upgrade.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: simulation/components/Pack.js
===================================================================
--- simulation/components/Pack.js (revision 17349)
+++ simulation/components/Pack.js (working copy)
@@ -116,87 +116,24 @@
Pack.prototype.PackProgress = function(data, lateness)
{
- if (this.elapsedTime >= this.GetPackTime())
+ if (this.elapsedTime < this.GetPackTime())
{
+ this.SetElapsedTime(this.GetElapsedTime() + PACKING_INTERVAL + lateness);
+ return;
+ }
+
this.CancelTimer();
this.packed = !this.packed;
- this.packing = false;
Engine.PostMessage(this.entity, MT_PackFinished, { packed: this.packed });
- // 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;
- }
+ var newEntity = ChangeEntityTemplate(this.entity, this.template.Entity);
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- var cmpNewPosition = Engine.QueryInterface(newEntity, IID_Position);
- if (cmpPosition.IsInWorld())
+ if (newEntity)
{
- 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(this.entity, IID_Ownership);
- var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
- cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
-
- // rescale capture points
- var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
- var cmpNewCapturable = Engine.QueryInterface(newEntity, IID_Capturable);
- if (cmpCapturable && cmpNewCapturable)
- {
- let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
- let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
- cmpNewCapturable.SetCapturePoints(newCp);
- }
-
- // 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
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: simulation/components/Identity.js
===================================================================
--- simulation/components/Identity.js (revision 17349)
+++ simulation/components/Identity.js (working copy)
@@ -40,11 +40,6 @@
"" +
"" +
"" +
- "" +
- "" +
- "" +
- "" +
- "" +
"" +
"" +
"" +
Index: simulation/components/GuiInterface.js
===================================================================
--- simulation/components/GuiInterface.js (revision 17349)
+++ simulation/components/GuiInterface.js (working copy)
@@ -232,6 +232,7 @@
"guard": null,
"mirage": null,
"pack": null,
+ "upgrade" : null,
"player": -1,
"position": null,
"production": null,
@@ -294,6 +295,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)
{
Index: simulation/components/interfaces/Upgrade.js
===================================================================
--- simulation/components/interfaces/Upgrade.js (nonexistent)
+++ simulation/components/interfaces/Upgrade.js (working copy)
@@ -0,0 +1 @@
+Engine.RegisterInterface("Upgrade");
Property changes on: simulation/components/interfaces/Upgrade.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: simulation/helpers/Transform.js
===================================================================
--- simulation/helpers/Transform.js (nonexistent)
+++ simulation/helpers/Transform.js (working copy)
@@ -0,0 +1,208 @@
+// 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());
+ }
+
+ // rescale capture points
+ var cmpCapturable = Engine.QueryInterface(oldEnt, IID_Capturable);
+ var cmpNewCapturable = Engine.QueryInterface(newEnt, IID_Capturable);
+ if (cmpCapturable && cmpNewCapturable)
+ {
+ let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
+ let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
+ cmpNewCapturable.SetCapturePoints(newCp);
+ }
+
+ // 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, newentity: 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, templateArg)
+{
+ var previewEntity = Engine.AddEntity("preview|"+templateArg);
+
+ 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);
+
+ var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
+
+ // 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 DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
+
+ var pos = cmpPosition.GetPosition2D();
+ var angle = cmpPosition.GetRotation();
+ // move us away to prevent our own obstruction from blocking the upgrade.
+ cmpPosition.MoveOutOfWorld();
+
+ 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 DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
+
+ var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
+ var newTemplate = cmpTemplateManager.GetTemplate(templateArg);
+
+ // Check if units are blocking our template change
+ if (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.
+ // This is kind of ugly, sorry about that.
+ if (newTemplate.Obstruction.Obstructions
+
+ || (newTemplate.Obstruction.Static && template.Obstruction.Static
+ && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"]
+ || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]))
+ || (newTemplate.Obstruction.Static && template.Obstruction.Unit
+ && (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"]
+ || newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]))
+
+ || (newTemplate.Obstruction.Unit && template.Obstruction.Unit
+ && newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"])
+ || (newTemplate.Obstruction.Unit && template.Obstruction.Static
+ && (newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"]
+ || newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"])))
+ {
+ 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 DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
+ }
+ }
+ }
+
+ return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
+};
+
+var DeleteEntityAndReturn = function(ent, cmpPosition, position, angle, cmpNewPosition, ret)
+{
+ cmpNewPosition.MoveOutOfWorld(); // prevent preview from interfering in the world
+ cmpPosition.JumpTo(position.x, position.y);
+ cmpPosition.SetYRotation(angle.y);
+
+ Engine.DestroyEntity(ent);
+ 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: simulation/helpers/Transform.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: simulation/helpers/Commands.js
===================================================================
--- simulation/helpers/Commands.js (revision 17349)
+++ simulation/helpers/Commands.js (working copy)
@@ -576,14 +576,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)
@@ -668,6 +660,65 @@
}
},
+ "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.WillCheckPlacementRestrictions(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);
+ }
+ },
+
"attack-request": function(player, cmd, data)
{
// Send a chat message to human players
@@ -1561,80 +1612,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: simulation/templates/template_unit_support_female_citizen.xml
===================================================================
--- simulation/templates/template_unit_support_female_citizen.xml (revision 17349)
+++ simulation/templates/template_unit_support_female_citizen.xml (working copy)
@@ -108,6 +108,24 @@
0.0
+
+
+ units/{civ}_support_trader
+ Upgrade to a traderBiatch.
+
+ 60
+
+
+
+
+ units/{civ}_support_slave
+ Haha!
+
+ 10
+
+
+
+
32
Index: simulation/templates/template_structure_defense_wall_long.xml
===================================================================
--- simulation/templates/template_structure_defense_wall_long.xml (revision 17349)
+++ simulation/templates/template_structure_defense_wall_long.xml (working copy)
@@ -56,6 +56,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: simulation/templates/other/palisades_rocks_long.xml
===================================================================
--- simulation/templates/other/palisades_rocks_long.xml (revision 17349)
+++ simulation/templates/other/palisades_rocks_long.xml (working copy)
@@ -26,7 +26,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
@@ -46,4 +45,14 @@
11.0
+
+
+ other/palisades_rocks_gate
+
+ 0
+ 20
+
+
+
+
Index: simulation/templates/structures/rome_siege_wall_long.xml
===================================================================
--- simulation/templates/structures/rome_siege_wall_long.xml (revision 17349)
+++ simulation/templates/structures/rome_siege_wall_long.xml (working copy)
@@ -55,7 +55,6 @@
structures/palisade_wall.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.