Do not allow upgrading when entity is producing and vice versa.
Before this patch, when entity was upgrading and producing and finished upgrading before production, production was canceled. That meant player assumed unit/tech will be ready in certain time but it will not. Also fixing interference between upgrade and production animations. Differential Revision: D2652 Reviewed by: bb Comments by: Stan, Freagarach Fixes: #5749 Refs: #2706 This was SVN commit r24088.
This commit is contained in:
parent
acfd466c32
commit
0bfaedb78d
@ -1348,7 +1348,7 @@ function getBuildingsWhichCanTrainEntity(entitiesToCheck, trainEntType)
|
||||
return entitiesToCheck.filter(entity => {
|
||||
let state = GetEntityState(entity);
|
||||
return state && state.production && state.production.entities.length &&
|
||||
state.production.entities.indexOf(trainEntType) != -1;
|
||||
state.production.entities.indexOf(trainEntType) != -1 && (!state.upgrade || !state.upgrade.isUpgrading);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -628,13 +628,18 @@ g_SelectionPanels.Research = {
|
||||
unitEntStates[0].production.technologies.map(tech => ({
|
||||
"tech": tech,
|
||||
"techCostMultiplier": unitEntStates[0].production.techCostMultiplier,
|
||||
"researchFacilityId": unitEntStates[0].id
|
||||
"researchFacilityId": unitEntStates[0].id,
|
||||
"isUpgrading": !!unitEntStates[0].upgrade && unitEntStates[0].upgrade.isUpgrading
|
||||
}));
|
||||
|
||||
for (let state of unitEntStates)
|
||||
let sortedEntStates = unitEntStates.sort((a, b) =>
|
||||
(!b.upgrade || !b.upgrade.isUpgrading) - (!a.upgrade || !a.upgrade.isUpgrading));
|
||||
|
||||
for (let state of sortedEntStates)
|
||||
{
|
||||
if (!state.production || !state.production.technologies)
|
||||
continue;
|
||||
|
||||
// Remove the techs we already have in ret (with the same name and techCostMultiplier)
|
||||
let filteredTechs = state.production.technologies.filter(
|
||||
tech => tech != null && !ret.some(
|
||||
@ -653,7 +658,8 @@ g_SelectionPanels.Research = {
|
||||
ret = ret.concat(filteredTechs.map(tech => ({
|
||||
"tech": tech,
|
||||
"techCostMultiplier": state.production.techCostMultiplier,
|
||||
"researchFacilityId": state.id
|
||||
"researchFacilityId": state.id,
|
||||
"isUpgrading": !!state.upgrade && state.upgrade.isUpgrading
|
||||
})));
|
||||
}
|
||||
return ret;
|
||||
@ -799,6 +805,14 @@ g_SelectionPanels.Research = {
|
||||
else
|
||||
button.enabled = controlsPlayer(data.player);
|
||||
|
||||
if (data.item.isUpgrading)
|
||||
{
|
||||
button.enabled = false;
|
||||
modifier += "color:0 0 0 127:grayscale:";
|
||||
button.tooltip += "\n" + coloredText(translate("Cannot research while upgrading."), "red");
|
||||
|
||||
}
|
||||
|
||||
if (template.icon)
|
||||
icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
|
||||
|
||||
@ -1013,6 +1027,13 @@ g_SelectionPanels.Training = {
|
||||
modifier = resourcesToAlphaMask(neededResources) + ":";
|
||||
}
|
||||
|
||||
if (data.unitEntStates.every(state => state.upgrade && state.upgrade.isUpgrading))
|
||||
{
|
||||
data.button.enabled = false;
|
||||
modifier = "color:0 0 0 127:grayscale:";
|
||||
data.button.tooltip += "\n" + coloredText(translate("Cannot train while upgrading."), "red");
|
||||
}
|
||||
|
||||
if (template.icon)
|
||||
data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
|
||||
|
||||
@ -1043,6 +1064,9 @@ g_SelectionPanels.Upgrade = {
|
||||
if (!template)
|
||||
return false;
|
||||
|
||||
let progressOverlay = Engine.GetGUIObjectByName("unitUpgradeProgressSlider[" + data.i + "]");
|
||||
progressOverlay.hidden = true;
|
||||
|
||||
let technologyEnabled = true;
|
||||
|
||||
if (data.item.requiredTechnology)
|
||||
@ -1051,17 +1075,21 @@ g_SelectionPanels.Upgrade = {
|
||||
"player": data.player
|
||||
});
|
||||
|
||||
let limits = getEntityLimitAndCount(data.playerState, data.item.entity);
|
||||
let upgradingEntStates = data.unitEntStates.filter(state => state.upgrade.template == data.item.entity);
|
||||
|
||||
let upgradableEntStates = data.unitEntStates.filter(state =>
|
||||
!state.upgrade.progress &&
|
||||
(!state.production || !state.production.queue || !state.production.queue.length));
|
||||
|
||||
let neededResources = data.item.cost && Engine.GuiInterfaceCall("GetNeededResources", {
|
||||
"cost": multiplyEntityCosts(data.item, data.unitEntStates.length),
|
||||
"cost": multiplyEntityCosts(data.item, upgradableEntStates.length),
|
||||
"player": data.player
|
||||
});
|
||||
|
||||
let limits = getEntityLimitAndCount(data.playerState, data.item.entity);
|
||||
let progress = data.unitEntStates[0].upgrade.progress || 0;
|
||||
let isUpgrading = data.unitEntStates[0].upgrade.template == data.item.entity;
|
||||
|
||||
let tooltip;
|
||||
if (!progress)
|
||||
let modifier = "";
|
||||
if (!upgradingEntStates.length && upgradableEntStates.length)
|
||||
{
|
||||
let tooltips = [];
|
||||
if (data.item.tooltip)
|
||||
@ -1075,7 +1103,7 @@ g_SelectionPanels.Upgrade = {
|
||||
}));
|
||||
|
||||
tooltips.push(
|
||||
getEntityCostComponentsTooltipString(data.item, undefined, data.unitEntStates.length),
|
||||
getEntityCostComponentsTooltipString(data.item, undefined, upgradableEntStates.length),
|
||||
formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
getNeededResourcesTooltip(neededResources),
|
||||
@ -1083,29 +1111,14 @@ g_SelectionPanels.Upgrade = {
|
||||
|
||||
tooltip = tooltips.filter(tip => tip).join("\n");
|
||||
|
||||
data.button.onPress = function() { upgradeEntity(data.item.entity); };
|
||||
}
|
||||
else if (isUpgrading)
|
||||
{
|
||||
tooltip = translate("Cancel Upgrading");
|
||||
data.button.onPress = function() { cancelUpgradeEntity(); };
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip = translate("Cannot upgrade when the entity is already upgrading.");
|
||||
data.button.onPress = function() {};
|
||||
}
|
||||
data.button.enabled = controlsPlayer(data.player);
|
||||
data.button.tooltip = tooltip;
|
||||
data.button.onPress = function() {
|
||||
upgradeEntity(
|
||||
data.item.entity,
|
||||
upgradableEntStates.map(state => state.id));
|
||||
};
|
||||
|
||||
data.button.onPressRight = function() {
|
||||
showTemplateDetails(data.item.entity);
|
||||
};
|
||||
|
||||
let modifier = "";
|
||||
if (!isUpgrading)
|
||||
if (progress || !technologyEnabled || limits.canBeAddedCount == 0 &&
|
||||
!hasSameRestrictionCategory(data.item.entity, data.unitEntStates[0].template))
|
||||
if (!technologyEnabled || limits.canBeAddedCount == 0 &&
|
||||
!upgradableEntStates.some(state => hasSameRestrictionCategory(data.item.entity, state.template)))
|
||||
{
|
||||
data.button.enabled = false;
|
||||
modifier = "color:0 0 0 127:grayscale:";
|
||||
@ -1116,20 +1129,45 @@ g_SelectionPanels.Upgrade = {
|
||||
modifier = resourcesToAlphaMask(neededResources) + ":";
|
||||
}
|
||||
|
||||
data.countDisplay.caption = upgradableEntStates.length > 1 ? upgradableEntStates.length : "";
|
||||
}
|
||||
else if (upgradingEntStates.length)
|
||||
{
|
||||
tooltip = translate("Cancel Upgrading");
|
||||
data.button.onPress = function() { cancelUpgradeEntity(); };
|
||||
data.countDisplay.caption = upgradingEntStates.length > 1 ? upgradingEntStates.length : "";
|
||||
|
||||
let progress = 0;
|
||||
for (let state of upgradingEntStates)
|
||||
progress = Math.max(progress, state.upgrade.progress || 1);
|
||||
let progressOverlaySize = progressOverlay.size;
|
||||
// TODO This is bad: we assume the progressOverlay is square
|
||||
progressOverlaySize.top = progressOverlaySize.bottom + Math.round((1 - progress) * (progressOverlaySize.left - progressOverlaySize.right));
|
||||
progressOverlay.size = progressOverlaySize;
|
||||
progressOverlay.hidden = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip = coloredText(translatePlural(
|
||||
"Cannot upgrade when the entity is training, researching or already upgrading.",
|
||||
"Cannot upgrade when all entities are training, researching or already upgrading.",
|
||||
data.unitEntStates.length), "red");
|
||||
|
||||
data.button.onPress = function() {};
|
||||
|
||||
data.button.enabled = false;
|
||||
modifier = "color:0 0 0 127:grayscale:";
|
||||
}
|
||||
data.button.enabled = controlsPlayer(data.player);
|
||||
data.button.tooltip = tooltip;
|
||||
|
||||
data.button.onPressRight = function() {
|
||||
showTemplateDetails(data.item.entity);
|
||||
};
|
||||
|
||||
data.icon.sprite = modifier + "stretched:session/" +
|
||||
(data.item.icon || "portraits/" + template.icon);
|
||||
|
||||
data.countDisplay.caption = data.unitEntStates.length > 1 ? data.unitEntStates.length : "";
|
||||
|
||||
let progressOverlay = Engine.GetGUIObjectByName("unitUpgradeProgressSlider[" + data.i + "]");
|
||||
if (isUpgrading)
|
||||
{
|
||||
let size = progressOverlay.size;
|
||||
size.top = size.left + Math.round(progress * (size.right - size.left));
|
||||
progressOverlay.size = size;
|
||||
}
|
||||
progressOverlay.hidden = !isUpgrading;
|
||||
|
||||
setPanelObjectPosition(data.button, data.i + getNumberOfRightPanelButtons(), data.rowLength);
|
||||
return true;
|
||||
}
|
||||
|
@ -317,11 +317,11 @@ function cancelPackUnit(pack)
|
||||
});
|
||||
}
|
||||
|
||||
function upgradeEntity(Template)
|
||||
function upgradeEntity(Template, selection)
|
||||
{
|
||||
Engine.PostNetworkCommand({
|
||||
"type": "upgrade",
|
||||
"entities": g_Selection.toList(),
|
||||
"entities": selection,
|
||||
"template": Template,
|
||||
"queued": false
|
||||
});
|
||||
|
@ -307,7 +307,8 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
ret.upgrade = {
|
||||
"upgrades": cmpUpgrade.GetUpgrades(),
|
||||
"progress": cmpUpgrade.GetProgress(),
|
||||
"template": cmpUpgrade.GetUpgradingTo()
|
||||
"template": cmpUpgrade.GetUpgradingTo(),
|
||||
"isUpgrading": cmpUpgrade.IsUpgrading()
|
||||
};
|
||||
|
||||
let cmpStatusEffects = Engine.QueryInterface(ent, IID_StatusEffectsReceiver);
|
||||
|
@ -328,6 +328,21 @@ ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
|
||||
if (!cmpPlayer)
|
||||
return;
|
||||
|
||||
if (!this.queue.length)
|
||||
{
|
||||
let cmpUpgrade = Engine.QueryInterface(this.entity, IID_Upgrade);
|
||||
if (cmpUpgrade && cmpUpgrade.IsUpgrading())
|
||||
{
|
||||
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification({
|
||||
"players": [cmpPlayer.GetPlayerID()],
|
||||
"message": markForTranslation("Entity is being upgraded. Cannot start production."),
|
||||
"translateMessage": true
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.queue.length < this.MaxQueueSize)
|
||||
{
|
||||
|
||||
@ -966,6 +981,11 @@ ProductionQueue.prototype.OnValueModification = function(msg)
|
||||
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).SetSelectionDirty(cmpPlayer.GetPlayerID());
|
||||
};
|
||||
|
||||
ProductionQueue.prototype.HasQueuedProduction = function()
|
||||
{
|
||||
return this.queue.length > 0;
|
||||
};
|
||||
|
||||
ProductionQueue.prototype.OnDisabledTemplatesChanged = function(msg)
|
||||
{
|
||||
// If the disabled templates of the player is changed,
|
||||
|
@ -229,6 +229,20 @@ Upgrade.prototype.Upgrade = function(template)
|
||||
return false;
|
||||
|
||||
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
if (!cmpPlayer)
|
||||
return false;
|
||||
|
||||
let cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
|
||||
if (cmpProductionQueue && cmpProductionQueue.HasQueuedProduction())
|
||||
{
|
||||
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification({
|
||||
"players": [cmpPlayer.GetPlayerID()],
|
||||
"message": markForTranslation("Entity is producing. Cannot start upgrading."),
|
||||
"translateMessage": true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
this.expendedResources = this.GetResourceCosts(template);
|
||||
if (!cmpPlayer || !cmpPlayer.TrySubtractResources(this.expendedResources))
|
||||
|
@ -9,6 +9,7 @@ Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
|
||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||
Engine.LoadComponentScript("interfaces/TrainingRestrictions.js");
|
||||
Engine.LoadComponentScript("interfaces/Trigger.js");
|
||||
Engine.LoadComponentScript("interfaces/Upgrade.js");
|
||||
Engine.LoadComponentScript("EntityLimits.js");
|
||||
|
||||
Engine.RegisterGlobal("Resources", {
|
||||
@ -71,6 +72,10 @@ function testEntitiesList()
|
||||
"GetCiv": () => "iber"
|
||||
});
|
||||
|
||||
AddMock(productionQueueId, IID_Upgrade, {
|
||||
"IsUpgrading": () => false
|
||||
});
|
||||
|
||||
cmpProductionQueue.CalculateEntitiesMap();
|
||||
TS_ASSERT_UNEVAL_EQUALS(
|
||||
cmpProductionQueue.GetEntitiesList(),
|
||||
@ -273,6 +278,10 @@ function regression_test_d1879()
|
||||
"PickSpawnPoint": () => ({ "x": -1, "y": -1, "z": -1 })
|
||||
});
|
||||
|
||||
AddMock(testEntity, IID_Upgrade, {
|
||||
"IsUpgrading": () => false
|
||||
});
|
||||
|
||||
cmpProdQueue.AddBatch("some_template", "unit", 3);
|
||||
Engine.QueryInterface(testEntity, IID_ProductionQueue).ProgressTimeout();
|
||||
|
||||
@ -280,6 +289,87 @@ function regression_test_d1879()
|
||||
TS_ASSERT_EQUALS(cmpEntLimits.GetCounts().some_limit, 6);
|
||||
}
|
||||
|
||||
function test_batch_adding()
|
||||
{
|
||||
let playerEnt = 2;
|
||||
let playerID = 1;
|
||||
let testEntity = 3;
|
||||
|
||||
ConstructComponent(playerEnt, "EntityLimits", {
|
||||
"Limits": {
|
||||
"some_limit": 8
|
||||
},
|
||||
"LimitChangers": {},
|
||||
"LimitRemovers": {}
|
||||
});
|
||||
|
||||
AddMock(SYSTEM_ENTITY, IID_GuiInterface, {
|
||||
"PushNotification": () => {}
|
||||
});
|
||||
|
||||
AddMock(SYSTEM_ENTITY, IID_Trigger, {
|
||||
"CallEvent": () => {}
|
||||
});
|
||||
|
||||
AddMock(SYSTEM_ENTITY, IID_Timer, {
|
||||
"SetTimeout": (ent, iid, func) => {}
|
||||
});
|
||||
|
||||
AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
|
||||
"TemplateExists": () => true,
|
||||
"GetTemplate": name => ({
|
||||
"Cost": {
|
||||
"BuildTime": 0,
|
||||
"Population": 1,
|
||||
"Resources": {}
|
||||
},
|
||||
"TrainingRestrictions": {
|
||||
"Category": "some_limit"
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
|
||||
"GetPlayerByID": id => playerEnt
|
||||
});
|
||||
|
||||
AddMock(playerEnt, IID_Player, {
|
||||
"GetCiv": () => "iber",
|
||||
"GetPlayerID": () => playerID,
|
||||
"GetTimeMultiplier": () => 0,
|
||||
"BlockTraining": () => {},
|
||||
"UnBlockTraining": () => {},
|
||||
"UnReservePopulationSlots": () => {},
|
||||
"TrySubtractResources": () => true,
|
||||
"TryReservePopulationSlots": () => false // Always have pop space.
|
||||
});
|
||||
|
||||
AddMock(testEntity, IID_Ownership, {
|
||||
"GetOwner": () => playerID
|
||||
});
|
||||
|
||||
let cmpProdQueue = ConstructComponent(testEntity, "ProductionQueue", {
|
||||
"Entities": { "_string": "some_template" },
|
||||
"BatchTimeModifier": 1
|
||||
});
|
||||
|
||||
|
||||
TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 0);
|
||||
AddMock(testEntity, IID_Upgrade, {
|
||||
"IsUpgrading": () => true
|
||||
});
|
||||
|
||||
cmpProdQueue.AddBatch("some_template", "unit", 3);
|
||||
TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 0);
|
||||
|
||||
AddMock(testEntity, IID_Upgrade, {
|
||||
"IsUpgrading": () => false
|
||||
});
|
||||
|
||||
cmpProdQueue.AddBatch("some_template", "unit", 3);
|
||||
TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 1);
|
||||
}
|
||||
|
||||
function test_token_changes()
|
||||
{
|
||||
const ent = 10;
|
||||
@ -350,4 +440,5 @@ function test_token_changes()
|
||||
|
||||
testEntitiesList();
|
||||
regression_test_d1879();
|
||||
test_batch_adding();
|
||||
test_token_changes();
|
||||
|
@ -13,6 +13,7 @@ Resources = {
|
||||
return "<interleave>" + schema + "</interleave>";
|
||||
}
|
||||
};
|
||||
Engine.LoadComponentScript("interfaces/ProductionQueue.js");
|
||||
Engine.LoadComponentScript("interfaces/ModifiersManager.js"); // Provides `IID_ModifiersManager`, used below.
|
||||
Engine.LoadComponentScript("interfaces/Timer.js"); // Provides `IID_Timer`, used below.
|
||||
|
||||
@ -120,6 +121,9 @@ AddMock(20, IID_Ownership, {
|
||||
AddMock(20, IID_Identity, {
|
||||
"GetCiv": () => civCode // Called in components/Upgrade.js::init().
|
||||
});
|
||||
AddMock(20, IID_ProductionQueue, {
|
||||
"HasQueuedProduction": () => false
|
||||
});
|
||||
let cmpUpgrade = ConstructComponent(20, "Upgrade", template.Upgrade);
|
||||
cmpUpgrade.owner = playerID;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user