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
@@ -1041,8 +1041,10 @@
"entities": selection,
"x": position.x,
"z": position.z,
+ "target": action.target,
"data": action.data,
- "queued": queued
+ "queued": queued,
+ "pushFront": pushFront
});
// Display rally point at the new coordinates, to avoid display lag
@@ -1246,11 +1248,13 @@
"target": null,
"tooltip": actionInfo.tooltip
};
-
+ if (Engine.HotkeyIsPressed("session.autorallypoint"))
+ target = null;
return actionInfo.possible && {
"type": "set-rallypoint",
"cursor": actionInfo.cursor,
"data": actionInfo.data,
+ "target": target,
"tooltip": actionInfo.tooltip,
"position": actionInfo.position,
"firstAbleEntity": actionInfo.entity
Index: binaries/data/mods/public/simulation/components/BuildingAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildingAI.js
+++ binaries/data/mods/public/simulation/components/BuildingAI.js
@@ -20,7 +20,7 @@
"" +
"";
-BuildingAI.prototype.MAX_PREFERENCE_BONUS = 2;
+BuildingAI.prototype.MIN_PREFERENCE = 50;
BuildingAI.prototype.Init = function()
{
@@ -28,6 +28,7 @@
this.archersGarrisoned = 0;
this.arrowsLeft = 0;
this.targetUnits = [];
+ this.focusTargets = [];
};
BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
@@ -50,6 +51,7 @@
BuildingAI.prototype.OnOwnershipChanged = function(msg)
{
this.targetUnits = [];
+ this.focusTargets = [];
this.SetupRangeQuery();
this.SetupGaiaRangeQuery();
};
@@ -267,6 +269,24 @@
};
/**
+ * Adds index to keep track of the user-targeted units supporting a queue
+ * @param {ent} - Target of rallypoint selection when selection is an enemy unit from unit_actions.js
+ */
+BuildingAI.prototype.AddFocusTarget = function(ent, queued, push)
+{
+ if (!ent || this.targetUnits.indexOf(ent) === -1)
+ return;
+ if (queued)
+ this.focusTargets.push({"entityId": ent});
+ else if (push)
+ this.focusTargets.unshift({"entityId": ent});
+ else
+ this.focusTargets = [{"entityId": ent}];
+
+// targeted entities are added as objects to match the structure of targets in fireArrows.
+};
+
+/**
* Fire arrows with random temporal distribution on prefered targets.
* Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range.
*/
@@ -308,26 +328,32 @@
return;
}
- // Add targets to a weighted list, to allow preferences.
- let targets = new WeightedList();
- let maxPreference = this.MAX_PREFERENCE_BONUS;
- let addTarget = function(target)
+ // Add targets to a list.
+ let minPreference = this.MIN_PREFERENCE;
+ let targets = [];
+ let addTarget = function(target)
{
- let preference = cmpAttack.GetPreference(target);
- let weight = 1;
-
- if (preference !== null && preference !== undefined)
- weight += maxPreference / (1 + preference);
-
- targets.push(target, weight);
+ const pref = cmpAttack.GetPreference(target) ?? minPreference;
+ targets.push({"entityId": target, "preference": pref});
};
// Add the UnitAI target separately, as the UnitMotion and RangeManager implementations differ.
if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) == -1)
- addTarget(this.unitAITarget);
- for (let target of this.targetUnits)
- addTarget(target);
+ addTarget(this.unitAITarget);
+ if (!this.focusTargets.length)
+ {
+ for (let target of this.targetUnits)
+ addTarget(target);
+ // Sort targets by preference and then by proximity.
+ targets.sort((a, b) => a.preference - b.preference ||
+ PositionHelper.DistanceBetweenEntities(this.entity, a.entityId) - PositionHelper.DistanceBetweenEntities(this.entity, b.entityId)
+ );
+ }
+ // If the user has targeted some units, skip preference and sorting, and use the focusTargets list for targeting.
+ else
+ targets = this.focusTargets;
+
// The obstruction manager performs approximate range checks.
// so we need to verify them here.
// TODO: perhaps an optional 'precise' mode to range queries would be more performant.
@@ -335,10 +361,16 @@
const range = cmpAttack.GetRange(attackType);
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
- let firedArrows = 0;
- while (firedArrows < arrowsToFire && targets.length())
- {
- const selectedTarget = targets.randomItem();
+ let firedArrows = 0;
+ let targetIndex = 0;
+ // targetIndex tracks the current target.
+
+ while (firedArrows < arrowsToFire && targetIndex < targets.length)
+ {
+ // Select a target to attempt shooting.
+ let selectedTarget = targets[targetIndex].entityId;
+
+ // Shooting at selectedTarget.
if (this.CheckTargetVisible(selectedTarget) && cmpObstructionManager.IsInTargetParabolicRange(
this.entity,
selectedTarget,
@@ -350,13 +382,16 @@
cmpAttack.PerformAttack(attackType, selectedTarget);
PlaySound("attack_" + attackType.toLowerCase(), this.entity);
++firedArrows;
- continue;
}
-
- // Could not attack target, try a different target.
- targets.remove(selectedTarget);
+ // If target is already killed or otherwise missed, increment targetIndex to shoot the next target.
+ else
+ {
+ // Could not attack target, try a different target.
+ ++targetIndex;
+ }
}
-
+ // After firing is complete, remove killed/missed targets. Targets are also removed from this.focusTargets.
+ targets.splice(0, targetIndex);
this.arrowsLeft -= firedArrows;
++this.currentRound;
};
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
@@ -435,6 +435,8 @@
{
for (let ent of data.entities)
{
+ if (cmd.data.command == "attack" && cmd.data.target)
+ Engine.QueryInterface(ent, IID_BuildingAI)?.AddFocusTarget(cmd.target, cmd.queued, cmd.pushFront);
var cmpRallyPoint = Engine.QueryInterface(ent, IID_RallyPoint);
if (cmpRallyPoint)
{
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
@@ -17,7 +17,7 @@
2000
100
- 1.5
+ 2
50
false
Index: binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
@@ -11,7 +11,7 @@
2000
100
- 1.5
+ 3
50
false