Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -390,6 +390,7 @@ snaptoedges = "disabled" ; Possible values: disabled, enabled. snaptoedgesdistancethreshold = 15 ; On which distance we don't snap to edges disjointcontrolgroups = "true" ; Whether control groups are disjoint sets or entities can be in multiple control groups at the same time. +defaultformation = "special/formations/box" ; For walking orders, automatically put units into this formation if they don't have one already. [gui.session.minimap] blinkduration = 1.7 ; The blink duration while pinging Index: binaries/data/mods/public/globalscripts/Formation.js =================================================================== --- /dev/null +++ binaries/data/mods/public/globalscripts/Formation.js @@ -0,0 +1,4 @@ +/** + * The 'null' formation means that units are individuals. + */ +const NULL_FORMATION = "special/formations/null"; Index: binaries/data/mods/public/gui/session/AutoFormation.js =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/session/AutoFormation.js @@ -0,0 +1,59 @@ +/** + * Handles the logic related to the 'default formation' feature. + * When given a walking order, units that aren't in formation will be put + * in the default formation, to improve pathfinding and reactivity. + * However, when given other tasks (such as e.g. gather), they will be removed + * from any formation they are in, as those orders don't work very well with formations. + * + * Set the default formation to the null formation to disable this entirely. + * + * TODO: it would be nice to let players choose default formations for different orders, + * but that would be neater if orders where defined somewhere unique, + * instead of mostly in unit_actions.js + */ +class AutoFormation +{ + constructor() + { + this.defaultFormation = Engine.ConfigDB_GetValue("user", "gui.session.defaultformation"); + if (!this.defaultFormation) + this.setDefault(NULL_FORMATION); + } + + /** + * Set the default formation to @param formation. + * TODO: would be good to validate, particularly since some formations aren't + * usable with any arbitrary unit type, we may want to warn then. + */ + setDefault(formation) + { + this.defaultFormation = formation; + Engine.ConfigDB_CreateValue("user", "gui.session.defaultformation", this.defaultFormation); + // TODO: It's extremely terrible that we have to explicitly flush the config... + Engine.ConfigDB_SetChanges("user", true); + Engine.ConfigDB_WriteFile("user", "config/user.cfg"); + return true; + } + + isDefault(formation) + { + return formation == this.defaultFormation; + } + + /** + * @return the default formation, or "undefined" if the null formation was chosen, + * otherwise units in formation would disband on any order, which isn't desirable. + */ + getDefault() + { + return this.defaultFormation == NULL_FORMATION ? undefined : this.defaultFormation; + } + + /** + * @return the null formation, or "undefined" if the null formation is the default. + */ + getNull() + { + return this.defaultFormation == NULL_FORMATION ? undefined : NULL_FORMATION; + } +} 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 @@ -309,7 +309,7 @@ if (!g_FormationsInfo.has(data.item)) g_FormationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item })); - let formationOk = data.item == "special/formations/null" || canMoveSelectionIntoFormation(data.item); + let formationOk = canMoveSelectionIntoFormation(data.item); let unitIds = data.unitEntStates.map(state => state.id); let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", { "ents": unitIds, @@ -320,8 +320,21 @@ performFormation(unitIds, data.item); }; + data.button.onMouseRightPress = () => g_AutoFormation.setDefault(data.item); + let formationInfo = g_FormationsInfo.get(data.item); let tooltip = translate(formationInfo.name); + + let isDefaultFormation = g_AutoFormation.isDefault(data.item); + if (data.item === NULL_FORMATION) + tooltip += "\n" + (isDefaultFormation ? + translate("Default formation is disabled.") : + translate("Right-click to disable the default formation feature.")); + else + tooltip += "\n" + (isDefaultFormation ? + translate("This is the default formation, used for movement orders.") : + translate("Right-click to set this as the default formation.")); + if (!formationOk && formationInfo.tooltip) tooltip += "\n" + coloredText(translate(formationInfo.tooltip), "red"); data.button.tooltip = tooltip; @@ -329,6 +342,7 @@ data.button.enabled = formationOk && controlsPlayer(data.player); let grayscale = formationOk ? "" : "grayscale:"; data.guiSelection.hidden = !formationSelected; + data.countDisplay.hidden = !isDefaultFormation; data.icon.sprite = "stretched:" + grayscale + "session/icons/" + formationInfo.icon; setPanelObjectPosition(data.button, data.i, data.rowLength); 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 @@ -8,6 +8,8 @@ function canMoveSelectionIntoFormation(formationTemplate) { + if (formationTemplate == NULL_FORMATION) + return true; if (!(formationTemplate in g_canMoveIntoFormation)) g_canMoveIntoFormation[formationTemplate] = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", { "ents": g_Selection.toList(), @@ -272,7 +274,7 @@ Engine.PostNetworkCommand({ "type": "formation", "entities": entities, - "name": formationTemplate + "formation": formationTemplate }); } Index: binaries/data/mods/public/gui/session/selection_panels_left/formation_panel.xml =================================================================== --- binaries/data/mods/public/gui/session/selection_panels_left/formation_panel.xml +++ binaries/data/mods/public/gui/session/selection_panels_left/formation_panel.xml @@ -7,6 +7,7 @@