Index: binaries/data/mods/public/gui/credits/texts/programming.json =================================================================== --- binaries/data/mods/public/gui/credits/texts/programming.json +++ binaries/data/mods/public/gui/credits/texts/programming.json @@ -36,6 +36,7 @@ { "nick": "Arfrever", "name": "Arfrever Frehtes Taifersar Arahesis" }, { "nick": "ArnH", "name": "Arno Hemelhof" }, { "nick": "Aurium", "name": "Aurélio Heckert" }, + { "nick": "azayrahmad", "name": "Aziz Rahmad" }, { "nick": "badmadblacksad", "name": "Martin F" }, { "nick": "badosu", "name": "Amadeus Folego" }, { "nick": "bb", "name": "Bouke Jansen" }, Index: binaries/data/mods/public/gui/hotkeys/spec/ingame.json =================================================================== --- binaries/data/mods/public/gui/hotkeys/spec/ingame.json +++ binaries/data/mods/public/gui/hotkeys/spec/ingame.json @@ -158,6 +158,14 @@ "session.queueunit.8": { "name": "Queue 8th unit", "desc": "Add eighth unit type to queue." + }, + "session.queueunit.autoqueueon": { + "name": "Activate auto-queue", + "desc": "Turn on Auto-Queue for selected structures." + }, + "session.queueunit.autoqueueoff": { + "name": "Deactivate auto-queue", + "desc": "Turn off Auto-Queue for selected structures." } } } Index: binaries/data/mods/public/gui/session/hotkeys/training.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/training.xml +++ binaries/data/mods/public/gui/session/hotkeys/training.xml @@ -34,4 +34,14 @@ addTrainingByPosition(6); + + + + turnAutoQueueOn(); + + + + + turnAutoQueueOff(); + 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 @@ -546,3 +546,25 @@ }) }); } + +function turnAutoQueueOn() +{ + Engine.PostNetworkCommand({ + "type": "autoqueue-on", + "entities": g_Selection.toList().filter(ent => { + let state = GetEntityState(ent); + return state && !!state.production.entities; + }) + }); +} + +function turnAutoQueueOff() +{ + Engine.PostNetworkCommand({ + "type": "autoqueue-off", + "entities": g_Selection.toList().filter(ent => { + let state = GetEntityState(ent); + return state && !!state.production.entities; + }) + }); +} Index: binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- binaries/data/mods/public/gui/session/unit_actions.js +++ binaries/data/mods/public/gui/session/unit_actions.js @@ -1715,7 +1715,47 @@ // This command button is always disabled. }, "allowedPlayers": ["Ally", "Observer"] - } + }, + + "autoqueue-on": { + "getInfo": function(entStates) + { + if (entStates.every(entState => !entState.production || !entState.production.entities || entState.production.autoqueue)) + return false; + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.queueunit.autoqueueon") + + translate("Activate auto-queue for selected structures."), + "icon": "autoqueue-on.png", + "enabled": true + }; + }, + "execute": function(entStates) + { + if (entStates.length) + turnAutoQueueOn(); + }, + "allowedPlayers": ["Player"] + }, + + "autoqueue-off": { + "getInfo": function(entStates) + { + if (entStates.every(entState => !entState.production || !entState.production.entities || !entState.production.autoqueue)) + return false; + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.queueunit.autoqueueoff") + + translate("Deactivate auto-queue for selected structures."), + "icon": "autoqueue-off.png", + "enabled": true + }; + }, + "execute": function(entStates) + { + if (entStates.length) + turnAutoQueueOff(); + }, + "allowedPlayers": ["Player"] + }, }; function playerCheck(entState, targetState, validPlayers) 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 @@ -341,7 +341,8 @@ "entities": cmpProductionQueue.GetEntitiesList(), "technologies": cmpProductionQueue.GetTechnologiesList(), "techCostMultiplier": cmpProductionQueue.GetTechCostMultiplier(), - "queue": cmpProductionQueue.GetQueue() + "queue": cmpProductionQueue.GetQueue(), + "autoqueue": cmpProductionQueue.IsAutoQueueing() }; let cmpTrader = Engine.QueryInterface(ent, IID_Trader); 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 @@ -74,6 +74,30 @@ }; /** + * @return {boolean} - Whether we are automatically queuing items. + */ +ProductionQueue.prototype.IsAutoQueueing = function() +{ + return !!this.autoqueuing; +}; + +/** + * Turn on Auto-Queue. + */ +ProductionQueue.prototype.EnableAutoQueue = function() +{ + this.autoqueuing = true; +}; + +/** + * Turn off Auto-Queue. + */ +ProductionQueue.prototype.DisableAutoQueue = function() +{ + delete this.autoqueuing; +}; + +/** * Calculate the new list of producible entities * and update any entities currently being produced. */ @@ -781,6 +805,13 @@ this.SetAnimation("training"); cmpPlayer.UnBlockTraining(); + + // AutoQueue: We add the second batch after starting the first. + // (As opposed to when the first batch finishes.) + // This is to make the feature not infinitely better than good micro. + if (this.autoqueuing) + this.AddItem(item.unitTemplate, "unit", item.count, item.metadata); + Engine.PostMessage(this.entity, MT_TrainingStarted, { "entity": this.entity }); } if (item.technologyTemplate) Index: binaries/data/mods/public/simulation/components/tests/test_ProductionQueue.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_ProductionQueue.js +++ binaries/data/mods/public/simulation/components/tests/test_ProductionQueue.js @@ -545,8 +545,81 @@ TS_ASSERT_EQUALS(cmpProductionQueue.GetQueue().length, 1); } +function test_auto_queue() +{ + 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, { + "SetInterval": (ent, iid, func) => 1 + }); + + 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 + }); + + cmpProdQueue.EnableAutoQueue(); + + cmpProdQueue.AddItem("some_template", "unit", 3); + TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 1); + cmpProdQueue.ProgressTimeout(null, 0); + TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 2); +} + testEntitiesList(); regression_test_d1879(); test_batch_adding(); test_batch_removal(); +test_auto_queue(); test_token_changes(); 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 @@ -877,6 +877,27 @@ cmpResourceDropsite.SetSharing(cmd.shared); } }, + + "autoqueue-on": function(player, cmd, data) + { + for (let ent of data.entities) + { + let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue); + if (cmpProductionQueue) + cmpProductionQueue.EnableAutoQueue(); + } + }, + + "autoqueue-off": function(player, cmd, data) + { + for (let ent of data.entities) + { + let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue); + if (cmpProductionQueue) + cmpProductionQueue.DisableAutoQueue(); + } + }, + }; /**