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
@@ -251,6 +251,34 @@
"specificness": 9,
},
+ "building-attack": {
+ "execute": function(target, action, selection, queued, pushFront)
+ {
+ Engine.PostNetworkCommand({
+ "type": "building-attack",
+ "entities": selection,
+ "target": target,
+ "queued": queued,
+ "pushFront": pushFront
+ });
+
+ Engine.GuiInterfaceCall("PlaySound", {
+ "name": "order_attack",
+ "entity": selection[0]
+ });
+ return true;
+ },
+ "getActionInfo": function(entState, targetState)
+ {
+ if (!entState.buildingAI || !targetState || !targetState.hitpoints || entState.speed)
+ return false;
+ return {
+ "possible": true
+ };
+ },
+ "specificness": 50,
+ },
+
"call-to-arms": {
"execute": function(position, action, selection, queued, pushFront)
{
@@ -1201,6 +1229,8 @@
}
else if (targetState && playerCheck(entState, targetState, ["Enemy"]))
{
+ if (entState.buildingAI && !Engine.HotkeyIsPressed("session.autorallypoint"))
+ return false;
data.target = targetState.id;
data.command = "attack";
if (targetState.hitpoints)
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
@@ -28,6 +28,7 @@
this.archersGarrisoned = 0;
this.arrowsLeft = 0;
this.targetUnits = [];
+ this.focusTarget = [];
};
BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
@@ -267,6 +268,25 @@
};
/**
+ * Sets the index to keep track of the user-targeted unit
+ * This value returns to 0 when the unit leaves
+ */
+BuildingAI.prototype.SetFocusTarget = function(ent, queued, push)
+{
+ if (ent !== false && this.targetUnits.indexOf(ent) !== -1)
+ {
+ if (queued)
+ this.focusTarget.push(ent);
+ else if (push)
+ this.focusTarget.unshift(ent);
+ else
+ this.focusTarget = [ent];
+ }
+ else
+ this.focusTarget = [];
+};
+
+/**
* Fire arrows with random temporal distribution on prefered targets.
* Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range.
*/
@@ -308,25 +328,20 @@
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 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 = target === this.focusTarget[0] ? 0 :
+ (cmpAttack.GetPreference(target) ?? 49) + 1;
+ targets.push({"entityId": target, "preference": pref});
+ //Need to make space for pref 0, which will be the user selected target.
+ }.bind(this);
// Add the UnitAI target separately, as the UnitMotion and RangeManager implementations differ.
if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) == -1)
- addTarget(this.unitAITarget);
+ addTarget(this.unitAITarget);
for (let target of this.targetUnits)
- addTarget(target);
+ addTarget(target);
// The obstruction manager performs approximate range checks.
// so we need to verify them here.
@@ -335,10 +350,18 @@
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;
+ // Sort targets by preference and then by proximity.
+ targets.sort( (a,b) => {
+ if (a.preference > b.preference) return 1;
+ else if (a.preference < b.preference) return -1;
+ else if (PositionHelper.DistanceBetweenEntities(this.entity,a.entityId) > PositionHelper.DistanceBetweenEntities(this.entity,b.entityId)) return 1;
+ return -1;
+ });
+
+ while (firedArrows < arrowsToFire && targets.length > 0)
+ {
+ let selectedTarget = targets[0].entityId
if (this.CheckTargetVisible(selectedTarget) && cmpObstructionManager.IsInTargetParabolicRange(
this.entity,
selectedTarget,
@@ -352,11 +375,14 @@
++firedArrows;
continue;
}
-
+ else
+ {
// Could not attack target, try a different target.
- targets.remove(selectedTarget);
+ targets.splice(0,1);
+ if (this.focusTarget)
+ this.focusTarget.splice(0,1);
+ }
}
-
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
@@ -199,11 +199,25 @@
"attack": function(player, cmd, data)
{
+ for (let ent of data.entities){
+ let checkBuildingAI = Engine.QueryInterface(ent, IID_BuildingAI);
+ if (checkBuildingAI)
+ checkBuildingAI.SetFocusTarget(cmd.target, cmd.queued, cmd.pushFront);
+ }
GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI => {
cmpUnitAI.Attack(cmd.target, cmd.allowCapture, cmd.queued, cmd.pushFront);
});
},
+ "building-attack": function(player, cmd, data)
+ {
+ for (let ent of data.entities){
+ let checkBuildingAI = Engine.QueryInterface(ent, IID_BuildingAI);
+ if (checkBuildingAI)
+ checkBuildingAI.SetFocusTarget(cmd.target, cmd.queued, cmd.pushFront);
+ }
+ },
+
"patrol": function(player, cmd, data)
{
GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI =>
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
+ 3
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
+ 4
50
false