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 = false; }; BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg) @@ -267,6 +268,18 @@ }; /** + * 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) +{ + if (ent !== false && this.targetUnits.indexOf(ent) !== -1) + this.focusTarget = ent; + else + this.focusTarget = false +}; + +/** * Fire arrows with random temporal distribution on prefered targets. * Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range. */ @@ -308,25 +321,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 : + (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 +343,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 +368,13 @@ ++firedArrows; continue; } - + else + { // Could not attack target, try a different target. - targets.remove(selectedTarget); + targets.splice(0,1); + this.focusTarget = false; + } } - this.arrowsLeft -= firedArrows; ++this.currentRound; }; 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