diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg
index 7a7ff2e..6d9bfaa 100644
--- a/binaries/data/config/default.cfg
+++ b/binaries/data/config/default.cfg
@@ -119,12 +119,12 @@ hotkey.pause = Pause ; Pause/unpause game
hotkey.screenshot = F2 ; Take PNG screenshot
hotkey.bigscreenshot = "Shift+F2" ; Take large BMP screenshot
hotkey.togglefullscreen = "Alt+Return" ; Toggle fullscreen/windowed mode
-hotkey.screenshot.watermark = "K" ; Toggle product/company watermark for official screenshots
+hotkey.screenshot.watermark = "R" ; Toggle product/company watermark for official screenshots
hotkey.wireframe = "Alt+W" ; Toggle wireframe mode
hotkey.silhouettes = "Alt+S" ; Toggle unit silhouettes
; > CAMERA SETTINGS
-hotkey.camera.reset = "H" ; Reset camera rotation to default.
+hotkey.camera.reset = "X" ; Reset camera rotation to default.
hotkey.camera.follow = "F" ; Follow the first unit in the selection
hotkey.camera.zoom.in = Plus, Equals, NumPlus ; Zoom camera in (continuous control)
hotkey.camera.zoom.out = Minus, NumMinus ; Zoom camera out (continuous control)
@@ -155,6 +155,11 @@ hotkey.selection.add = Shift ; Add units to selection
hotkey.selection.milonly = Alt ; Add only military units to selection
hotkey.selection.remove = Ctrl ; Remove units from selection
hotkey.selection.idleworker = Period ; Select next idle worker
+hotkey.selection.structure = Y ; Select structure
+hotkey.selection.civilcenter = U ; Select towncenter
+hotkey.selection.dropsiteres = I ; Select dropsite stone/wood/metal
+hotkey.selection.dropsitefood = O ; Select dropsite food
+hotkey.selection.market = P ; Select market
hotkey.selection.idlewarrior = Comma ; Select next idle warrior
hotkey.selection.offscreen = Alt ; Include offscreen units in selection
hotkey.selection.group.select.0 = 0
@@ -188,6 +193,26 @@ hotkey.selection.group.select.9 = 9
hotkey.selection.group.save.9 = "Ctrl+9"
hotkey.selection.group.add.9 = "Shift+9"
+hotkey.selection.group.action.0 = G
+hotkey.selection.group.action.1 = H
+hotkey.selection.group.action.2 = J
+hotkey.selection.group.action.3 = K
+hotkey.selection.group.action.4 = L
+hotkey.selection.group.action.5 = Semicolon
+hotkey.selection.group.action.6 = SingleQuote
+hotkey.selection.group.action.7 = "Ctrl+G"
+hotkey.selection.group.action.8 = "Ctrl+H"
+hotkey.selection.group.action.9 = "Ctrl+J"
+hotkey.selection.group.action.10 = "Ctrl+K"
+hotkey.selection.group.action.11 = "Ctrl+L"
+hotkey.selection.group.action.12 = "Ctrl+Semicolon"
+hotkey.selection.group.action.13 = "Ctrl+SingleQuote"
+hotkey.selection.group.reset.0 = ForwardSlash
+hotkey.selection.group.stance.0 = V
+hotkey.selection.group.formation.0 = B
+hotkey.selection.group.garrison.0 = N
+hotkey.selection.group.ungarrison.0 = M
+
; > SESSION CONTROLS
hotkey.session.kill = Delete ; Destroy selected units
hotkey.session.garrison = Ctrl ; Modifier to garrison when clicking on building
diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js
index ff39b74..a2cdc0a 100644
--- a/binaries/data/mods/public/gui/session/input.js
+++ b/binaries/data/mods/public/gui/session/input.js
@@ -14,9 +14,17 @@ const SDLK_LALT = 308;
const ACTION_NONE = 0;
const ACTION_GARRISON = 1;
-const ACTION_REPAIR = 2;
+const ACTION_BUILD = 2;
+const ACTION_TRAIN = 4;
+const ACTION_UNGARRISON = 8;
+const ACTION_FORMATION = 16;
+const ACTION_STANCE = 32;
+const ACTION_BARTER = 64;
+const ACTION_TRADER =128;
var preSelectedAction = ACTION_NONE;
+var followQueue = [];
+
const INPUT_NORMAL = 0;
const INPUT_SELECTING = 1;
const INPUT_BANDBOXING = 2;
@@ -27,6 +35,7 @@ const INPUT_BATCHTRAINING = 6;
const INPUT_PRESELECTEDACTION = 7;
const INPUT_BUILDING_WALL_CLICK = 8;
const INPUT_BUILDING_WALL_PATHING = 9;
+const INPUT_ACTION_STATE = 10;
var inputState = INPUT_NORMAL;
var placementSupport = new PlacementSupport();
@@ -137,6 +146,32 @@ function updateBuildingPlacementPreview()
return false;
}
+function initBuildingPlacementView()
+{
+ if (placementSupport.mode === "wall")
+ {
+ // Including only the on-screen towers in the next snap candidate list is sufficient here, since the user is
+ // still selecting a starting point (which must necessarily be on-screen). (The update of the snap entities
+ // itself happens in the call to updateBuildingPlacementPreview below).
+ placementSupport.wallSnapEntitiesIncludeOffscreen = false;
+ }
+ else
+ {
+ var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
+ "template": placementSupport.template,
+ "x": placementSupport.position.x,
+ "z": placementSupport.position.z,
+ });
+ if (snapData)
+ {
+ placementSupport.angle = snapData.angle;
+ placementSupport.position.x = snapData.x;
+ placementSupport.position.z = snapData.z;
+ }
+ }
+ updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
+}
+
function findGatherType(gatherer, supply)
{
if (!gatherer || !supply)
@@ -148,6 +183,15 @@ function findGatherType(gatherer, supply)
return undefined;
}
+function resetPreselectedAction()
+{
+ preSelectedAction = ACTION_NONE;
+ inputState = INPUT_NORMAL;
+ placementSupport.Reset(); // or a hotkey may leave a building snapshot
+ updateCursorAndTooltip();
+
+}
+
function getActionInfo(action, target)
{
var selection = g_Selection.toList();
@@ -423,8 +467,10 @@ function determineAction(x, y, fromMinimap)
{
target = targets[0];
}
+ if (Engine.HotkeyIsPressed("session.garrison"))
+ preSelectedAction = ACTION_GARRISON;
- if (preSelectedAction != ACTION_NONE)
+ if (preSelectedAction & (ACTION_GARRISON|ACTION_BUILD))
{
switch (preSelectedAction)
{
@@ -434,7 +480,7 @@ function determineAction(x, y, fromMinimap)
else
return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
break;
- case ACTION_REPAIR:
+ case ACTION_BUILD:
if (getActionInfo("repair", target).possible)
return {"type": "repair", "cursor": "action-repair", "target": target};
else
@@ -442,13 +488,6 @@ function determineAction(x, y, fromMinimap)
break;
}
}
- else if (Engine.HotkeyIsPressed("session.garrison"))
- {
- if (getActionInfo("garrison", target).possible)
- return {"type": "garrison", "cursor": "action-garrison", "target": target};
- else
- return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
- }
else
{
var actionInfo = undefined;
@@ -623,7 +662,18 @@ function handleInputBeforeGui(ev, hoveredObject)
mouseY = ev.y;
break;
}
-
+ if (followQueue.length)
+ {
+ var gents = followQueue;
+ var state = GetEntityState(gents[0]);
+ if (state.visibility == "hidden")
+ return true;
+ Engine.CameraFollow(0);
+ Engine.CameraFollow(gents[0]);
+ g_Selection.addList(gents);
+ resetPreselectedAction();
+ followQueue = []; // for next time.
+ }
// Remember whether the mouse is over a GUI object or not
mouseIsOverObject = (hoveredObject != null);
@@ -751,7 +801,7 @@ function handleInputBeforeGui(ev, hoveredObject)
if (queued)
inputState = INPUT_BUILDING_PLACEMENT;
else
- inputState = INPUT_NORMAL;
+ resetPreselectedAction();
}
else
{
@@ -765,8 +815,7 @@ function handleInputBeforeGui(ev, hoveredObject)
if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel building
- placementSupport.Reset();
- inputState = INPUT_NORMAL;
+ resetPreselectedAction();
return true;
}
break;
@@ -975,6 +1024,7 @@ function handleInputAfterGui(ev)
switch (inputState)
{
case INPUT_NORMAL:
+ case INPUT_ACTION_STATE:
switch (ev.type)
{
case "mousemotion":
@@ -1004,27 +1054,8 @@ function handleInputAfterGui(ev)
break;
case "hotkeydown":
- if (ev.hotkey.indexOf("selection.group.") == 0)
- {
- var now = new Date();
- if ((now.getTime() - doublePressTimer < doublePressTime) && (ev.hotkey == prevHotkey))
- {
- if (ev.hotkey.indexOf("selection.group.select.") == 0)
- {
- var sptr = ev.hotkey.split(".");
- performGroup("snap", sptr[3]);
- }
- }
- else
- {
- var sptr = ev.hotkey.split(".");
- performGroup(sptr[2], sptr[3]);
-
- doublePressTimer = now.getTime();
- prevHotkey = ev.hotkey;
- }
- }
- break;
+ handleHotkey(ev);
+ break;
}
break;
@@ -1035,25 +1066,24 @@ function handleInputAfterGui(ev)
if (ev.button == SDL_BUTTON_LEFT && preSelectedAction != ACTION_NONE)
{
var action = determineAction(ev.x, ev.y);
+ resetPreselectedAction();
if (!action)
break;
- preSelectedAction = ACTION_NONE;
- inputState = INPUT_NORMAL;
return doAction(action, ev);
}
- else if (ev.button == SDL_BUTTON_RIGHT && preSelectedAction != ACTION_NONE)
+ else if (ev.button == SDL_BUTTON_RIGHT)
{
- preSelectedAction = ACTION_NONE;
- inputState = INPUT_NORMAL;
+ resetPreselectedAction();
break;
}
- // else
+ case "hotkeydown":
+ handleHotkey(ev);
+ break;
default:
// Slight hack: If selection is empty, reset the input state
if (g_Selection.toList().length == 0)
{
- preSelectedAction = ACTION_NONE;
- inputState = INPUT_NORMAL;
+ resetPreselectedAction();
break;
}
}
@@ -1084,83 +1114,18 @@ function handleInputAfterGui(ev)
if (!ents.length)
{
g_Selection.reset();
- resetIdleUnit();
- inputState = INPUT_NORMAL;
+ resetEntitySearch();
+ resetPreselectedAction();
return true;
}
-
var selectedEntity = ents[0];
- var now = new Date();
// If camera following and we select different unit, stop
if (Engine.GetFollowedEntity() != selectedEntity)
{
Engine.CameraFollow(0);
}
-
- if ((now.getTime() - doubleClickTimer < doubleClickTime) && (selectedEntity == prevClickedEntity))
- {
- // Double click or triple click has occurred
- var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
- var matchRank = true;
- var templateToMatch;
-
- // Check for double click or triple click
- if (!doubleClicked)
- {
- // If double click hasn't already occurred, this is a double click.
- // Select similar units regardless of rank
- templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).identity.selectionGroupName;
- if (templateToMatch)
- {
- matchRank = false;
- }
- else
- { // No selection group name defined, so fall back to exact match
- templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
- }
-
- doubleClicked = true;
- // Reset the timer so the user has an extra period 'doubleClickTimer' to do a triple-click
- doubleClickTimer = now.getTime();
- }
- else
- {
- // Double click has already occurred, so this is a triple click.
- // Select units matching exact template name (same rank)
- templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
- }
-
- // TODO: Should we handle "control all units" here as well?
- ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
- }
- else
- {
- // It's single click right now but it may become double or triple click
- doubleClicked = false;
- doubleClickTimer = now.getTime();
- prevClickedEntity = selectedEntity;
-
- // We only want to include the first picked unit in the selection
- ents = [ents[0]];
- }
-
- // Update the list of selected units
- if (Engine.HotkeyIsPressed("selection.add"))
- {
- g_Selection.addList(ents);
- }
- else if (Engine.HotkeyIsPressed("selection.remove"))
- {
- g_Selection.removeList(ents);
- }
- else
- {
- g_Selection.reset();
- g_Selection.addList(ents);
- }
-
- inputState = INPUT_NORMAL;
+ ClickSelected(ents, (selectedEntity == prevClickedEntity));
return true;
}
break;
@@ -1171,32 +1136,8 @@ function handleInputAfterGui(ev)
switch (ev.type)
{
case "mousemotion":
-
placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
-
- if (placementSupport.mode === "wall")
- {
- // Including only the on-screen towers in the next snap candidate list is sufficient here, since the user is
- // still selecting a starting point (which must necessarily be on-screen). (The update of the snap entities
- // itself happens in the call to updateBuildingPlacementPreview below).
- placementSupport.wallSnapEntitiesIncludeOffscreen = false;
- }
- else
- {
- var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
- "template": placementSupport.template,
- "x": placementSupport.position.x,
- "z": placementSupport.position.z,
- });
- if (snapData)
- {
- placementSupport.angle = snapData.angle;
- placementSupport.position.x = snapData.x;
- placementSupport.position.z = snapData.z;
- }
- }
-
- updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
+ initBuildingPlacementView();
return false; // continue processing mouse motion
case "mousebuttondown":
@@ -1213,8 +1154,8 @@ function handleInputAfterGui(ev)
else
{
placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
- dragStart = [ ev.x, ev.y ];
- inputState = INPUT_BUILDING_CLICK;
+ dragStart = [ ev.x, ev.y ];
+ inputState = INPUT_BUILDING_CLICK;
}
return true;
}
@@ -1222,7 +1163,7 @@ function handleInputAfterGui(ev)
{
// Cancel building
placementSupport.Reset();
- inputState = INPUT_NORMAL;
+ resetPreselectedAction();
return true;
}
break;
@@ -1241,6 +1182,8 @@ function handleInputAfterGui(ev)
placementSupport.angle -= rotation_step;
updateBuildingPlacementPreview();
break;
+ default:
+ handleHotkey(ev); // to switch buildings
}
break;
@@ -1250,6 +1193,75 @@ function handleInputAfterGui(ev)
return false;
}
+function ClickSelected(ents, is_same)
+{
+ var selectedEntity = ents[0];
+ var now = new Date();
+
+ if ((now.getTime() - doubleClickTimer < doubleClickTime) && is_same)
+ {
+ // Double click or triple click has occurred
+ var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
+ var matchRank = true;
+ var templateToMatch;
+
+ // Check for double click or triple click
+ if (!doubleClicked)
+ {
+ // If double click hasn't already occurred, this is a double click.
+ // Select similar units regardless of rank
+ templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).identity.selectionGroupName;
+ if (templateToMatch)
+ {
+ matchRank = false;
+ }
+ else
+ { // No selection group name defined, so fall back to exact match
+ templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
+ }
+
+ doubleClicked = true;
+ // Reset the timer so the user has an extra period 'doubleClickTimer' to do a triple-click
+ doubleClickTimer = now.getTime();
+ }
+ else
+ {
+ // Double click has already occurred, so this is a triple click.
+ // Select units matching exact template name (same rank)
+ templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
+ }
+
+ // TODO: Should we handle "control all units" here as well?
+ ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
+ }
+ else
+ {
+ // It's single click right now but it may become double or triple click
+ doubleClicked = false;
+ doubleClickTimer = now.getTime();
+ prevClickedEntity = selectedEntity;
+
+ // We only want to include the first picked unit in the selection
+ ents = [ents[0]];
+ }
+
+ // Update the list of selected units
+ if (Engine.HotkeyIsPressed("selection.add"))
+ {
+ g_Selection.addList(ents);
+ }
+ else if (Engine.HotkeyIsPressed("selection.remove"))
+ {
+ g_Selection.removeList(ents);
+ }
+ else
+ {
+ g_Selection.reset();
+ g_Selection.addList(ents);
+ }
+ resetPreselectedAction();
+}
+
function doAction(action, ev)
{
var selection = g_Selection.toList();
@@ -1533,7 +1545,7 @@ function performCommand(entity, commandName)
break;
case "repair":
inputState = INPUT_PRESELECTEDACTION;
- preSelectedAction = ACTION_REPAIR;
+ preSelectedAction = ACTION_BUILD;
break;
case "unload-all":
unloadAll(entity);
@@ -1577,8 +1589,28 @@ function performFormation(entity, formationName)
}
}
+function handleHotkey(ev)
+{
+ if (ev.hotkey && ev.hotkey.indexOf("selection.group.") == 0)
+ {
+ var sptr = ev.hotkey.split(".");
+ var now = new Date();
+ if ((inputState == INPUT_NORMAL) && (ev.hotkey == prevHotkey) &&
+ ev.hotkey.indexOf("selection.group.select.") == 0 &&
+ (now.getTime() - doublePressTimer < doublePressTime)) {
+ performGroup("snap", sptr[3]);
+ }
+ else
+ {
+ performGroup(sptr[2], sptr[3]);
+ doublePressTimer = now.getTime();
+ prevHotkey = ev.hotkey;
+ }
+ }
+}
+
// Performs the specified group
-function performGroup(action, groupId)
+function performGroup(action, x)
{
switch (action)
{
@@ -1587,7 +1619,7 @@ function performGroup(action, groupId)
case "add":
var toSelect = [];
g_Groups.update();
- for (var ent in g_Groups.groups[groupId].ents)
+ for (var ent in g_Groups.groups[x].ents)
toSelect.push(+ent);
if (action != "add")
@@ -1597,14 +1629,214 @@ function performGroup(action, groupId)
if (action == "snap" && toSelect.length)
Engine.CameraFollow(toSelect[0]);
- break;
+ return;
case "save":
var selection = g_Selection.toList();
- g_Groups.groups[groupId].reset();
- g_Groups.addEntities(groupId, selection);
+ g_Groups.groups[x].reset();
+ g_Groups.addEntities(x, selection);
updateGroups();
+ return;
+ case "action":
+ var selection = g_Selection.toList();
+ var player = Engine.GetPlayerID();
+ if (inputState != INPUT_ACTION_STATE)
+ preSelectedAction = getDefaultActionForSelection(player, selection);
break;
+ case "reset":
+ resetPreselectedAction();
+ return;
+ case "garrison":
+ preSelectedAction = ACTION_GARRISON;
+ inputState = INPUT_PRESELECTEDACTION;
+ return;
+ case "ungarrison":
+ preSelectedAction = ACTION_UNGARRISON;
+ inputState = INPUT_ACTION_STATE;
+ return;
+ case "stance":
+ preSelectedAction = ACTION_STANCE;
+ inputState = INPUT_ACTION_STATE;
+ return;
+ case "formation":
+ preSelectedAction = ACTION_FORMATION;
+ inputState = INPUT_ACTION_STATE;
+ return;
+ }
+ if (inputState == INPUT_ACTION_STATE)
+ inputState = INPUT_NORMAL;
+ if (preSelectedAction == ACTION_NONE)
+ return;
+
+ var lst = selectAction(player, selection);
+ if (!lst) // shouldn't happen
+ return;
+
+ if (preSelectedAction & ACTION_GARRISON)
+ {
+ if (++x > lst.length)
+ x = lst.length;
+ } else if (preSelectedAction != ACTION_UNGARRISON) {
+ if (x >= lst.length)
+ return;
+ lst = lst[x];
}
+
+ switch (preSelectedAction)
+ {
+ case ACTION_BUILD:
+ var tmpl = GetTemplateData(lst);
+ if (!tmpl.requiredTechnology || Engine.GuiInterfaceCall("IsTechnologyResearched", tmpl.requiredTechnology)) {
+ // N.B. sets inputState = INPUT_BUILDING_PLACEMENT:
+ startBuildingPlacement(lst);
+ placementSupport.position = Engine.GetTerrainAtScreenPoint(mouseX, mouseY);
+ initBuildingPlacementView();
+ }
+ break;
+ case ACTION_TRAIN:
+ var tmpl = GetTemplateData(lst);
+ if (!tmpl.requiredTechnology || Engine.GuiInterfaceCall("IsTechnologyResearched", tmpl.requiredTechnology)) {
+ var ents = g_Selection.toList();
+ addTrainingToQueue(ents, lst);
+ }
+ break;
+ case ACTION_GARRISON: // we garison x+1 count
+ g_Selection.reset();
+ lst.splice(x, lst.length - x)
+ g_Selection.addList(lst);
+ inputState = INPUT_PRESELECTEDACTION;
+ break;
+ case ACTION_UNGARRISON:
+ if (lst.length > 1 || x == 0) {
+ if (x >= lst.length)
+ x = lst.length - 1;
+ if (lst.length != 1 && x == 0) {
+ // unload all from all buildings
+ for each (var gh in lst)
+ unloadAll(gh);
+ } else {
+ if (lst.length != 1)
+ x--;
+ unloadAll(lst[x]);
+ }
+ g_Selection.reset();
+ } else {
+ lst = lst[0];
+ var state = GetEntityState(lst);
+ var gents = state.garrisonHolder.entities;
+ if (x > gents.length)
+ x = gents.length;
+ while (x--)
+ Engine.PostNetworkCommand({"type": "unload", "entities": [gents[x]], "garrisonHolder": lst});
+ // we could assign a key to follow them here
+ }
+ break;
+ case ACTION_FORMATION:
+ ents = g_Selection.toList();
+ var formationOk = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", {
+ "ents": g_Selection.toList(),
+ "formationName": lst
+ });
+ if (formationOk)
+ performFormation(ents, lst);
+ break
+ case ACTION_STANCE:
+ ents = g_Selection.toList();
+ performStance(ents, lst);
+ break;
+ case ACTION_BARTER: //TODO
+ break;
+ case ACTION_TRADER: //TODO
+ break;
+ }
+}
+
+// This returns a list of actions. Caller still has to check Technology availability
+function selectAction(player, selection)
+{
+ if (preSelectedAction == ACTION_FORMATION)
+ return Engine.GuiInterfaceCall("GetAvailableFormations");
+ else if (preSelectedAction == ACTION_STANCE)
+ return ["violent", "aggressive", "passive", "defensive", "standground"];
+
+ var ret = [];
+ for each (var ent in selection)
+ {
+ var state = GetEntityState(ent);
+ if (!state || (state.player != player && !g_DevSettings.controlAll))
+ continue;
+
+ switch (preSelectedAction)
+ {
+ case ACTION_GARRISON:
+ if (state.buildEntities || (hasClass(state, "Unit") && !hasClass(state, "Animal") && !state.garrisonHolder)) {
+ inputState = INPUT_PRESELECTEDACTION;
+ ret.push(ent);
+ }
+ break;
+ case ACTION_BUILD:
+ if (state.buildEntities && state.buildEntities.length) {
+ if (inputState != INPUT_BUILDING_PLACEMENT)
+ inputState = INPUT_PRESELECTEDACTION;
+ return state.buildEntities;
+ }
+ break;
+ case ACTION_TRAIN:
+ if (state.production && state.production.entities.length)
+ return state.production.entities;
+ break;
+ case ACTION_UNGARRISON:
+ if (state.garrisonHolder && state.garrisonHolder.entities && state.garrisonHolder.entities.length)
+ ret.push(ent);
+ break;
+ case ACTION_BARTER:
+ if (state.barterMarket)
+ ret.push(ent);
+ break;
+ case ACTION_TRADER:
+ if (state.trader)
+ ret.push([ent, state]);
+ break;
+ }
+ }
+ return ret;
+}
+
+function getDefaultActionForSelection(player, selection)
+{
+ var action;
+ var count = 0;
+ for each (var ent in selection)
+ {
+ var state = GetEntityState(ent);
+ if (!state || (state.player != player && !g_DevSettings.controlAll))
+ continue;
+
+ if (state.buildEntities && state.buildEntities.length)
+ return ACTION_BUILD; // by default
+
+ if (state.production && state.production.entities.length)
+ action |= ACTION_TRAIN;
+ else if (!hasClass(state, "Unit") || hasClass(state, "Animal") ||
+ !state.garrisonHolder) {
+ if (++count > 1) //only works with at least two units
+ action |= ACTION_FORMATION;
+ } else if (state.barterMarket)
+ action |= ACTION_BARTER;
+ else if (state.trader)
+ action |= ACTION_TRADER;
+ }
+ if (action & ACTION_TRAIN)
+ return ACTION_TRAIN;
+ else if (action & ACTION_FORMATION)
+ return ACTION_FORMATION;
+ else if (action & ACTION_BARTER) {
+ setupUnitBarterPanel(state);
+ return ACTION_BARTER;
+ } else if (action & ACTION_TRADER) {
+ setupUnitTradingPanel(state, selection);
+ return ACTION_TRADER;
+ }
+ return 0; //shouldn't happen
}
// Performs the specified stance
@@ -1639,41 +1871,76 @@ function setCameraFollow(entity)
Engine.CameraFollow(0);
}
-var lastIdleUnit = 0;
-var currIdleClass = 0;
+var lastEntity = 0;
+var currEntClass = 0;
+
+function resetEntitySearch()
+{
+ lastEntity = 0;
+ currEntClass = 0;
+}
-function resetIdleUnit()
+function findAllVisibleEntities(classes, mode)
{
- lastIdleUnit = 0;
- currIdleClass = 0;
+ var player = Engine.GetPlayerID();
+ var plEnts = Engine.PickFriendlyEntitiesOnScreen(player);
+ // Or we could do somthing alike the below, but this is resolution dependent
+ //var plEnts = Engine.PickFriendlyEntitiesInRect(250, 250, 1400, 750, player);
+ if (!plEnts.length)
+ return;
+ var visibleEnts = [];
+ for each (cls in classes)
+ {
+ var data = {
+ entClass: cls,
+ searchMode: mode,
+ playerEnts: plEnts,
+ };
+ var entities = Engine.GuiInterfaceCall("SubsetMatchedEnts", data);
+ if (entities.length)
+ visibleEnts = visibleEnts.concat(entities);
+ }
+
+ if (visibleEnts.length) {
+ if (!getDefaultActionForSelection(player, visibleEnts).length)
+ resetPreselectedAction();
+ g_Selection.reset();
+ g_Selection.addList(visibleEnts);
+ Engine.CameraFollow(visibleEnts[0]);
+ }
}
-function findIdleUnit(classes)
+// mode 0 is to search for idle, 1 to search for any
+function findEntity(classes, mode)
{
+ var queued = Engine.HotkeyIsPressed("session.queue");
+ if (queued) {
+ findAllVisibleEntities(classes, mode)
+ return;
+ }
+ var player = Engine.GetPlayerID();
+
// Cycle through idling classes before giving up
for (var i = 0; i <= classes.length; ++i)
{
- var data = { prevUnit: lastIdleUnit, idleClass: classes[currIdleClass] };
- var newIdleUnit = Engine.GuiInterfaceCall("FindIdleUnit", data);
-
- // Check if we have new valid entity
- if (newIdleUnit && newIdleUnit != lastIdleUnit)
+ var data = { prevEntity: lastEntity, entClass: classes[currEntClass], searchMode: mode};
+ var newEntity = Engine.GuiInterfaceCall("FindEntity", data);
+ if (newEntity && newEntity != lastEntity)
{
- lastIdleUnit = newIdleUnit;
- g_Selection.reset()
- g_Selection.addList([lastIdleUnit]);
- Engine.CameraFollow(lastIdleUnit);
-
+ if (!selectAction(player, [newEntity]).length)
+ resetPreselectedAction();
+ else if (preSelectedAction == ACTION_BUILD)
+ updateBuildingPlacementPreview();
+ lastEntity = newEntity;
+ ClickSelected([newEntity], 1);
return;
}
-
- lastIdleUnit = 0;
- currIdleClass = (currIdleClass + 1) % classes.length;
+ lastEntity = 0;
+ currEntClass = (currEntClass + 1) % classes.length;
}
-
// TODO: display a message or play a sound to indicate no more idle units, or something
// Reset for next cycle
- resetIdleUnit();
+ resetEntitySearch();
}
function unload(garrisonHolder, entities)
@@ -1686,5 +1953,8 @@ function unload(garrisonHolder, entities)
function unloadAll(garrisonHolder)
{
+ var state = GetEntityState(garrisonHolder);
+ var gents = state.garrisonHolder.entities;
+ followQueue = followQueue.concat(gents);
Engine.PostNetworkCommand({"type": "unload-all", "garrisonHolder": garrisonHolder});
}
diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
index 3ff530a..a55a7bd 100644
--- a/binaries/data/mods/public/gui/session/session.xml
+++ b/binaries/data/mods/public/gui/session/session.xml
@@ -85,9 +85,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -527,7 +548,7 @@
>
- findIdleUnit(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"]);
+ findEntity(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"], 1);
@@ -586,7 +607,6 @@
-