diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js index eb12b2c68e..43970302fc 100644 --- a/binaries/data/mods/public/gui/session/input.js +++ b/binaries/data/mods/public/gui/session/input.js @@ -1868,6 +1868,9 @@ function performCommand(entity, commandName) if (focusTarget !== null) Engine.CameraMoveTo(focusTarget.x, focusTarget.z); + break; + case "back-to-work": + backToWork(); break; default: break; @@ -2131,6 +2134,18 @@ function unloadAll() Engine.PostNetworkCommand({"type": "unload-all", "garrisonHolders": garrisonHolders}); } +function backToWork() +{ + // Filter out all entities that can't go back to work. + var workers = g_Selection.toList().filter(function(e) { + var state = GetEntityState(e); + return (state && state.unitAI && state.unitAI.lastWorkOrder); + }); + + Engine.PostNetworkCommand({"type": "back-to-work", "workers": workers}); + +} + function clearSelection() { if(inputState==INPUT_BUILDING_PLACEMENT || inputState==INPUT_BUILDING_WALL_PATHING) diff --git a/binaries/data/mods/public/gui/session/utility_functions.js b/binaries/data/mods/public/gui/session/utility_functions.js index 64f31e26f0..ace9cc5222 100644 --- a/binaries/data/mods/public/gui/session/utility_functions.js +++ b/binaries/data/mods/public/gui/session/utility_functions.js @@ -293,6 +293,15 @@ function getEntityCommandsList(entState) "icon": "focus-rally.png" }); } + + if (entState.unitAI && entState.unitAI.lastWorkOrder) + { + commands.push({ + "name": "back-to-work", + "tooltip": "Back to Work", + "icon": "production.png" + }); + } return commands; } diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index a1b53d8665..1bb537e3f5 100644 --- a/binaries/data/mods/public/simulation/components/GuiInterface.js +++ b/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -348,6 +348,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent) ret.unitAI = { "state": cmpUnitAI.GetCurrentState(), "orders": cmpUnitAI.GetOrders(), + "lastWorkOrder": cmpUnitAI.GetLastWorkOrder(), }; // Add some information needed for ungarrisoning if (cmpUnitAI.isGarrisoned && ret.player !== undefined) diff --git a/binaries/data/mods/public/simulation/components/Promotion.js b/binaries/data/mods/public/simulation/components/Promotion.js index 0ef547fff4..94d957d75f 100644 --- a/binaries/data/mods/public/simulation/components/Promotion.js +++ b/binaries/data/mods/public/simulation/components/Promotion.js @@ -78,6 +78,8 @@ Promotion.prototype.Promote = function(promotedTemplateName) cmpPromotedUnitAI.Cheer(); var orders = cmpCurrentUnitAI.GetOrders(); cmpPromotedUnitAI.AddOrders(orders); + var lastWorkOrder = cmpCurrentUnitAI.GetLastWorkOrder(); + cmpPromotedUnitAI.SetLastWorkOrder(lastWorkOrder); Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: promotedUnitEntity }); diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 687e9a4a4d..342f10f1c0 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -2525,6 +2525,9 @@ UnitAI.prototype.Init = function() this.isIdle = false; this.lastFormationName = ""; this.finishedOrder = false; // used to find if all formation members finished the order + + // To go back to work later + this.lastWorkOrder = undefined; // For preventing increased action rate due to Stop orders or target death. this.lastAttacked = undefined; @@ -2801,6 +2804,17 @@ UnitAI.prototype.FsmStateNameChanged = function(state) Engine.PostMessage(this.entity, MT_UnitAIStateChanged, { "to": state }); }; +UnitAI.prototype.BackToWork = function() +{ + if(this.lastWorkOrder) + { + this.ReplaceOrder(this.lastWorkOrder.type, this.lastWorkOrder.data); + return true; + } + else + return false; +}; + /** * Call when the current order has been completed (or failed). * Removes the current order from the queue, and processes the @@ -2817,8 +2831,17 @@ UnitAI.prototype.FinishOrder = function() error("FinishOrder called for entity " + this.entity + " (" + template + ") when order queue is empty\n" + stack); } - this.orderQueue.shift(); + // Remove the order from the queue, then forget it if it was a work to avoid trying to go back to it later. + var finishedOrder = this.orderQueue.shift(); + if(finishedOrder.type == "Gather" || finishedOrder.type == "Trade" || finishedOrder.type == "Repair") + this.lastWorkOrder = undefined; + this.order = this.orderQueue[0]; + // If the worker was returning a resource, we shall + // -> (if the worker is gathering) keep remembering the "Gather" order which is this.order + // -> (if the worker was directly ordered to return a resource) forget the "ReturnResource" order which is finished + if(finishedOrder.type == "ReturnResource" && (!this.order || this.order.type != "Gather")) + this.lastWorkOrder = undefined; if (this.orderQueue.length) { @@ -2869,6 +2892,9 @@ UnitAI.prototype.PushOrder = function(type, data) { var order = { "type": type, "data": data }; this.orderQueue.push(order); + + if(type == "Gather" || type == "Trade" || type == "Repair" || type == "ReturnResource") + this.lastWorkOrder = order; // If we didn't already have an order, then process this new one if (this.orderQueue.length == 1) @@ -2972,6 +2998,16 @@ UnitAI.prototype.GetOrderData = function() return orders; }; +UnitAI.prototype.GetLastWorkOrder = function() +{ + return this.lastWorkOrder; +}; + +UnitAI.prototype.SetLastWorkOrder = function(order) +{ + this.lastWorkOrder = order; +}; + UnitAI.prototype.TimerHandler = function(data, lateness) { // Reset the timer diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js index 38a56f3337..98f71ecbfa 100644 --- a/binaries/data/mods/public/simulation/helpers/Commands.js +++ b/binaries/data/mods/public/simulation/helpers/Commands.js @@ -390,6 +390,16 @@ function ProcessCommand(player, cmd) notifyUnloadFailure(player, garrisonHolder) } break; + + case "back-to-work": + var entities = FilterEntityList(cmd.workers, player, controlAllUnits); + for each (var worker in entities) + { + var cmpUnitAI = Engine.QueryInterface(worker, IID_UnitAI); + if (!cmpUnitAI || !cmpUnitAI.BackToWork()) + notifyBackToWorkFailure(player, worker) + } + break; case "formation": GetFormationUnitAIs(entities, player, cmd.name).forEach(function(cmpUnitAI) { @@ -517,6 +527,17 @@ function notifyUnloadFailure(player, garrisonHolder) cmpGUIInterface.PushNotification(notification); } +/** + * Sends a GUI notification about worker(s) that failed to go back to work. + */ +function notifyBackToWorkFailure(player, worker) +{ + var cmpPlayer = QueryPlayerIDInterface(player, IID_Player); + var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Some unit(s) can't go back to work" }; + var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGUIInterface.PushNotification(notification); +} + /** * Get some information about the formations used by entities. * The entities must have a UnitAI component.