Index: binaries/data/mods/public/gui/session/ResearchProgress.js =================================================================== --- binaries/data/mods/public/gui/session/ResearchProgress.js +++ binaries/data/mods/public/gui/session/ResearchProgress.js @@ -55,6 +55,7 @@ this.sprite = Engine.GetGUIObjectByName("researchStartedIcon[" + i + "]"); this.progress = Engine.GetGUIObjectByName("researchStartedProgressSlider[" + i + "]"); this.timeRemaining = Engine.GetGUIObjectByName("researchStartedTimeRemaining[" + i + "]"); + this.paused = Engine.GetGUIObjectByName("researchPausedIcon[" + i + "]"); this.buttonHeight = this.button.size.bottom - this.button.size.top; this.buttonTop = this.Margin + (this.Margin + this.buttonHeight) * i; @@ -68,13 +69,18 @@ this.researcher = researchStatus.researcher; let template = GetTechnologyData(techName, g_Players[g_ViewedPlayer].civ); - this.sprite.sprite = "stretched:" + this.PortraitDirectory + template.icon; + let modifier = "stretched:"; + if (researchStatus.paused) + modifier += "color:0 0 0 127:grayscale:"; + this.sprite.sprite = modifier + this.PortraitDirectory + template.icon; let size = this.button.size; size.top = offset + this.buttonTop; size.bottom = size.top + this.buttonHeight; this.button.size = size; this.button.tooltip = getEntityNames(template); + if (researchStatus.paused) + this.button.tooltip += "\n" + translate(this.PausedResearchString); this.button.hidden = false; size = this.progress.size; @@ -85,6 +91,8 @@ Engine.FormatMillisecondsIntoDateStringGMT( researchStatus.timeRemaining, translateWithContext("countdown format", this.CountdownFormat)); + + this.paused.hidden = !researchStatus.paused; } onPress() @@ -107,3 +115,6 @@ * This format is used when displaying the remaining time of the currently viewed techs in research. */ ResearchProgressButton.prototype.CountdownFormat = markForTranslationWithContext("countdown format", "m:ss"); + +// Translation: String displayed when the research is paused. E.g. by being garrisoned or when not the first item in the queue. +ResearchProgressButton.prototype.PausedResearchString = markForTranslation("(This item is paused.)"); Index: binaries/data/mods/public/gui/session/ResearchProgress.xml =================================================================== --- binaries/data/mods/public/gui/session/ResearchProgress.xml +++ binaries/data/mods/public/gui/session/ResearchProgress.xml @@ -7,6 +7,7 @@ + Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -1521,7 +1521,8 @@ "type": "train", "template": trainEntType, "count": 1, - "entities": buildingsForTraining + "entities": buildingsForTraining, + "pushFront": Engine.HotkeyIsPressed("session.pushorderfront") }); } } @@ -1579,7 +1580,8 @@ "type": "train", "entities": appropriateBuildings.slice(0, buildingsCountToTrainFullBatch), "template": g_BatchTrainingType, - "count": batchedSize + "count": batchedSize, + "pushFront": Engine.HotkeyIsPressed("session.pushorderfront") }); // Train remainer in one more structure. @@ -1589,7 +1591,8 @@ "type": "train", "entities": [appropriateBuildings[buildingsCountToTrainFullBatch]], "template": g_BatchTrainingType, - "count": remainer + "count": remainer, + "pushFront": Engine.HotkeyIsPressed("session.pushorderfront") }); } else @@ -1597,7 +1600,8 @@ "type": "train", "entities": appropriateBuildings, "template": g_BatchTrainingType, - "count": batchedSize + "count": batchedSize, + "pushFront": Engine.HotkeyIsPressed("session.pushorderfront") }); } Index: binaries/data/mods/public/gui/session/selection_panels.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels.js +++ binaries/data/mods/public/gui/session/selection_panels.js @@ -590,10 +590,22 @@ guiObject.size = size; data.button.enabled = controlsPlayer(data.player); + + Engine.GetGUIObjectByName("unitQueuePausedIcon[" + data.i + "]").hidden = !queuedItem.paused; + if (queuedItem.paused) + // Translation: String displayed when the research is paused. E.g. by being garrisoned or when not the first item in the queue. + data.button.tooltip += "\n" + translate("(This item is paused.)"); } if (template.icon) - data.icon.sprite = (data.item.ghost ? "grayscale:" : "") + "stretched:session/portraits/" + template.icon; + { + let modifier = "stretched:"; + if (queuedItem.paused) + modifier += "color:0 0 0 127:grayscale:"; + else if (data.item.ghost) + modifier += "grayscale:" + data.icon.sprite = modifier + "session/portraits/" + template.icon; + } const showTemplateFunc = () => { showTemplateDetails(data.item.queuedItem.unitTemplate || data.item.queuedItem.technologyTemplate, data.playerState.civ); }; Index: binaries/data/mods/public/gui/session/selection_panels_helpers.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels_helpers.js +++ binaries/data/mods/public/gui/session/selection_panels_helpers.js @@ -272,7 +272,8 @@ Engine.PostNetworkCommand({ "type": "research", "entity": entity, - "template": researchType + "template": researchType, + "pushFront": Engine.HotkeyIsPressed("session.pushorderfront") }); } Index: binaries/data/mods/public/gui/session/selection_panels_right/queue_panel.xml =================================================================== --- binaries/data/mods/public/gui/session/selection_panels_right/queue_panel.xml +++ binaries/data/mods/public/gui/session/selection_panels_right/queue_panel.xml @@ -14,6 +14,7 @@ + Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -691,13 +691,16 @@ let cmpProductionQueue = Engine.QueryInterface(ret[tech].researcher, IID_ProductionQueue); if (cmpProductionQueue) { - ret[tech].progress = cmpProductionQueue.GetQueue()[0].progress; - ret[tech].timeRemaining = cmpProductionQueue.GetQueue()[0].timeRemaining; + const research = cmpProductionQueue.GetQueue().find(item => item.technologyTemplate === tech); + ret[tech].progress = research.progress; + ret[tech].timeRemaining = research.timeRemaining; + ret[tech].paused = research.paused; } else { ret[tech].progress = 0; ret[tech].timeRemaining = 0; + ret[tech].paused = true; } } return ret; Index: binaries/data/mods/public/simulation/components/ProductionQueue.js =================================================================== --- binaries/data/mods/public/simulation/components/ProductionQueue.js +++ binaries/data/mods/public/simulation/components/ProductionQueue.js @@ -47,6 +47,7 @@ "productionStarted": false, // true iff production has started (we have reserved population). "timeTotal": 15000, // msecs "timeRemaining": 10000, // msecs + "paused": false, // Whether the item is currently paused (e.g. not the first item or parent is garrisoned). "resources": { "wood": 100, ... }, // Total resources of the item when queued. "entity": { "template": "units/example", @@ -340,10 +341,11 @@ * @param {string} type - The type of production (i.e. "unit" or "technology"). * @param {number} count - The amount of units to be produced. Ignored for a tech. * @param {any} metadata - Optionally any metadata to be attached to the item. + * @param {boolean} pushFront - Whether to push the item to the front of the queue and pause any item(s) currently in progress. * * @return {boolean} - Whether the addition of the item has succeeded. */ -ProductionQueue.prototype.AddItem = function(templateName, type, count, metadata) +ProductionQueue.prototype.AddItem = function(templateName, type, count, metadata, pushFront = false) { // TODO: there should be a way for the GUI to determine whether it's going // to be possible to add a batch (based on resource costs and length limits). @@ -382,6 +384,7 @@ "metadata": metadata, "productionStarted": false, "resources": {}, // The total resource costs. + "paused": false }; // ToDo: Still some duplication here, some can might be combined, @@ -490,7 +493,14 @@ return false; item.id = this.nextID++; - this.queue.push(item); + if (pushFront) + { + if (this.queue[0]) + this.queue[0].paused = true; + this.queue.unshift(item); + } + else + this.queue.push(item); const cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); if (item.entity) @@ -618,6 +628,7 @@ "neededSlots": item.entity?.neededSlots, "progress": 1 - (item.timeRemaining / (item.timeTotal || 1)), "timeRemaining": item.timeRemaining, + "paused": item.paused, "metadata": item.metadata })); }; @@ -797,6 +808,8 @@ while (this.queue.length) { let item = this.queue[0]; + if (item.paused) + item.paused = false; if (!item.productionStarted) { if (item.entity) @@ -924,10 +937,14 @@ { this.StopTimer(); this.paused = true; + if (this.queue[0]) + this.queue[0].paused = true; }; ProductionQueue.prototype.UnpauseProduction = function() { + if (this.queue[0]) + this.queue[0].paused = false; delete this.paused; this.StartTimer(); }; Index: binaries/data/mods/public/simulation/helpers/Commands.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Commands.js +++ binaries/data/mods/public/simulation/helpers/Commands.js @@ -372,10 +372,7 @@ } } if (queue && queue.GetEntitiesList().indexOf(cmd.template) != -1) - if ("metadata" in cmd) - queue.AddItem(cmd.template, "unit", +cmd.count, cmd.metadata); - else - queue.AddItem(cmd.template, "unit", +cmd.count); + queue.AddItem(cmd.template, "unit", +cmd.count, cmd.metadata, cmd.pushFront); } }, @@ -391,7 +388,7 @@ var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue); if (queue) - queue.AddItem(cmd.template, "technology"); + queue.AddItem(cmd.template, "technology", undefined, cmd.metadata, cmd.pushFront); }, "stop-production": function(player, cmd, data)