Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js
+++ binaries/data/mods/public/simulation/components/UnitAI.js
@@ -162,11 +162,15 @@
},
"LosRangeUpdate": function(msg) {
- // ignore newly-seen units by default
+ // Ignore newly-seen units by default.
},
"LosHealRangeUpdate": function(msg) {
- // ignore newly-seen injured units by default
+ // Ignore newly-seen injured units by default.
+ },
+
+ "LosAttackRangeUpdate": function(msg) {
+ // Ignore newly-seen enemy units by default.
},
"Attacked": function(msg) {
@@ -1473,11 +1477,13 @@
},
"leave": function() {
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.losRangeQuery)
cmpRangeManager.DisableActiveQuery(this.losRangeQuery);
if (this.losHealRangeQuery)
cmpRangeManager.DisableActiveQuery(this.losHealRangeQuery);
+ if (this.losAttackRangeQuery)
+ cmpRangeManager.DisableActiveQuery(this.losAttackRangeQuery);
this.StopTimer();
@@ -1488,23 +1494,26 @@
}
},
+ // On the range updates:
+ // We check for idleness to prevent an entity to react only to newly seen entities
+ // when receiving a Los*RangeUpdate on the same turn as the entity becomes idle
+ // since this.FindNew*Targets is called in the timer.
+
"LosRangeUpdate": function(msg) {
- // Start attacking one of the newly-seen enemy (if any).
- // We check for idleness to prevent an entity to attack only newly seen enemies
- // when receiving a LosRangeUpdate on the same turn as the entity becomes idle
- // since this.FindNewTargets is called in the timer.
- if (this.isIdle && msg.data.added.length && this.GetStance().targetVisibleEnemies)
- this.AttackEntitiesByPreference(msg.data.added);
+ if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length)
+ this.RespondToSightedEntities(msg.data.added);
},
"LosHealRangeUpdate": function(msg) {
- // We check for idleness to prevent an entity to heal only newly seen entities
- // when receiving a LosHealRangeUpdate on the same turn as the entity becomes idle
- // since this.FindNewHealTargets is called in the timer.
- if (this.isIdle && msg.data.added.length)
+ if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length)
this.RespondToHealableEntities(msg.data.added);
},
+ "LosAttackRangeUpdate": function(msg) {
+ if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length && this.GetStance().targetVisibleEnemies)
+ this.AttackEntitiesByPreference(msg.data.added);
+ },
+
"Timer": function(msg) {
// If the unit is guarding/escorting, go back to its duty.
if (this.isGuardOf)
@@ -1522,10 +1531,13 @@
// If we entered the idle state we must have nothing better to do,
// so immediately check whether there's anybody nearby to attack.
- // (If anyone approaches later, it'll be handled via LosRangeUpdate.)
+ // (If anyone approaches later, it'll be handled via LosAttackRangeUpdate.)
if (this.FindNewTargets())
return;
+ if (this.FindSightedEnemies())
+ return;
+
if (this.formationOffset && this.formationController)
{
this.PushOrder("FormationWalk", {
@@ -1707,8 +1719,7 @@
return false;
},
- "LosRangeUpdate": function(msg) {
- // Start attacking one of the newly-seen enemy (if any)
+ "LosAttackRangeUpdate": function(msg) {
if (this.GetStance().targetVisibleEnemies)
this.AttackEntitiesByPreference(msg.data.added);
},
@@ -3267,22 +3278,16 @@
},
"LosRangeUpdate": function(msg) {
- if (this.template.NaturalBehaviour == "skittish")
- {
- if (msg.data.added.length > 0)
- {
- this.Flee(msg.data.added[0], false);
- return;
- }
- }
- // Start attacking one of the newly-seen enemy (if any)
- else if (this.IsDangerousAnimal())
- {
+ if (msg && msg.data && msg.data.added && msg.data.added.length)
+ this.RespondToSightedEntities(msg.data.added);
+ },
+
+ "LosAttackRangeUpdate": function(msg) {
+ if (this.IsDangerousAnimal() && msg && msg.data && msg.data.added && msg.data.added.length)
this.AttackVisibleEntity(msg.data.added);
- }
// TODO: if two units enter our range together, we'll attack the
- // first and then the second won't trigger another LosRangeUpdate
+ // first and then the second won't trigger another LosAttackRangeUpdate
// so we won't notice it. Probably we should do something with
// ResetActiveQuery in ROAMING.enter/FEEDING.enter in order to
// find any units that are already in range.
@@ -3312,19 +3317,13 @@
},
"LosRangeUpdate": function(msg) {
- if (this.template.NaturalBehaviour == "skittish")
- {
- if (msg.data.added.length > 0)
- {
- this.Flee(msg.data.added[0], false);
- return;
- }
- }
- // Start attacking one of the newly-seen enemy (if any)
- else if (this.template.NaturalBehaviour == "violent")
- {
+ if (msg && msg.data && msg.data.added && msg.data.added.length)
+ this.RespondToSightedEntities(msg.data.added);
+ },
+
+ "LosAttackRangeUpdate": function(msg) {
+ if (this.template.NaturalBehaviour == "violent" && msg && msg.data && msg.data.added && msg.data.added.length)
this.AttackVisibleEntity(msg.data.added);
- }
},
"Timer": function(msg) {
@@ -3549,12 +3548,13 @@
// Switch to an empty state to let states execute their leave handlers.
this.UnitFsm.SwitchToNextState(this, "");
- // Clean up range queries
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.losRangeQuery)
cmpRangeManager.DestroyActiveQuery(this.losRangeQuery);
if (this.losHealRangeQuery)
cmpRangeManager.DestroyActiveQuery(this.losHealRangeQuery);
+ if (this.losAttackRangeQuery)
+ cmpRangeManager.DestroyActiveQuery(this.losAttackRangeQuery);
};
UnitAI.prototype.OnVisionRangeChanged = function(msg)
@@ -3593,63 +3593,73 @@
}
};
-// Wrapper function that sets up the normal and healer range queries.
+/**
+ * Wrapper function that sets up the LOS, healer and attack range queries.
+ * This should be called whenever our ownership changes.
+ */
UnitAI.prototype.SetupRangeQueries = function()
{
- this.SetupRangeQuery();
+ // Only skittish animals use this for now.
+ if (this.template.NaturalBehaviour && this.template.NaturalBehaviour == "skittish")
+ this.SetupLOSRangeQuery();
if (this.IsHealer())
this.SetupHealRangeQuery();
+
+ if (Engine.QueryInterface(this.entity, IID_Attack))
+ this.SetupAttackRangeQuery();
};
UnitAI.prototype.UpdateRangeQueries = function()
{
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.losRangeQuery)
- this.SetupRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losRangeQuery));
+ this.SetupLOSRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losRangeQuery));
if (this.IsHealer() && this.losHealRangeQuery)
this.SetupHealRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losHealRangeQuery));
+
+ if (this.losAttackRangeQuery)
+ this.SetupAttackRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losAttackRangeQuery));
};
-// Set up a range query for all enemy and gaia units within LOS range
-// which can be attacked.
-// This should be called whenever our ownership changes.
-UnitAI.prototype.SetupRangeQuery = function(enable = true)
+/**
+ * Set up a range query for all enemy units within LOS range.
+ * @param {boolean} enable - Optional parameter whether to enable the query.
+ */
+UnitAI.prototype.SetupLOSRangeQuery = function(enable = true)
{
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
-
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.losRangeQuery)
{
cmpRangeManager.DestroyActiveQuery(this.losRangeQuery);
this.losRangeQuery = undefined;
}
- var cmpPlayer = QueryOwnerInterface(this.entity);
- // If we are being destructed (owner -1), creating a range query is pointless
+ let cmpPlayer = QueryOwnerInterface(this.entity);
+ // If we are being destructed (owner == -1), creating a range query is pointless.
if (!cmpPlayer)
return;
- // Exclude allies, and self
- // TODO: How to handle neutral players - Special query to attack military only?
let players = cmpPlayer.GetEnemies();
if (!players.length)
return;
- var range = this.GetQueryRange(IID_Attack);
-
- this.losRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
+ let range = this.GetQueryRange(IID_Vision);
+ this.losRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Identity, cmpRangeManager.GetEntityFlagMask("normal"));
if (enable)
cmpRangeManager.EnableActiveQuery(this.losRangeQuery);
};
-// Set up a range query for all own or ally units within LOS range
-// which can be healed.
-// This should be called whenever our ownership changes.
+/**
+ * Set up a range query for all own or ally units within LOS range
+ * which can be healed.
+ * @param {boolean} enable - Optional parameter whether to enable the query.
+ */
UnitAI.prototype.SetupHealRangeQuery = function(enable = true)
{
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.losHealRangeQuery)
{
@@ -3657,13 +3667,13 @@
this.losHealRangeQuery = undefined;
}
- var cmpPlayer = QueryOwnerInterface(this.entity);
- // If we are being destructed (owner -1), creating a range query is pointless
+ let cmpPlayer = QueryOwnerInterface(this.entity);
+ // If we are being destructed (owner == -1), creating a range query is pointless.
if (!cmpPlayer)
return;
- var players = cmpPlayer.GetAllies();
- var range = this.GetQueryRange(IID_Heal);
+ let players = cmpPlayer.GetAllies();
+ let range = this.GetQueryRange(IID_Heal);
this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Health, cmpRangeManager.GetEntityFlagMask("injured"));
@@ -3671,6 +3681,38 @@
cmpRangeManager.EnableActiveQuery(this.losHealRangeQuery);
};
+/**
+ * Set up a range query for all enemy and gaia units within range
+ * which can be attacked.
+ * @param {boolean} enable - Optional parameter whether to enable the query.
+ */
+UnitAI.prototype.SetupAttackRangeQuery = function(enable = true)
+{
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+
+ if (this.losAttackRangeQuery)
+ {
+ cmpRangeManager.DestroyActiveQuery(this.losAttackRangeQuery);
+ this.losAttackRangeQuery = undefined;
+ }
+
+ let cmpPlayer = QueryOwnerInterface(this.entity);
+ // If we are being destructed (owner == -1), creating a range query is pointless.
+ if (!cmpPlayer)
+ return;
+
+ // TODO: How to handle neutral players - Special query to attack military only?
+ let players = cmpPlayer.GetEnemies();
+ if (!players.length)
+ return;
+
+ let range = this.GetQueryRange(IID_Attack);
+ this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
+
+ if (enable)
+ cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery);
+};
+
//// FSM linkage functions ////
@@ -4185,9 +4227,11 @@
UnitAI.prototype.OnRangeUpdate = function(msg)
{
if (msg.tag == this.losRangeQuery)
- this.UnitFsm.ProcessMessage(this, {"type": "LosRangeUpdate", "data": msg});
+ this.UnitFsm.ProcessMessage(this, { "type": "LosRangeUpdate", "data": msg });
else if (msg.tag == this.losHealRangeQuery)
- this.UnitFsm.ProcessMessage(this, {"type": "LosHealRangeUpdate", "data": msg});
+ this.UnitFsm.ProcessMessage(this, { "type": "LosHealRangeUpdate", "data": msg });
+ else if (msg.tag == this.losAttackRangeQuery)
+ this.UnitFsm.ProcessMessage(this, { "type": "LosAttackRangeUpdate", "data": msg });
};
UnitAI.prototype.OnPackFinished = function(msg)
@@ -4967,6 +5011,24 @@
};
/**
+ * @param {number} ents - An array of the IDs of the spotted entities.
+ * @return {boolean} - Whether we responded.
+ */
+UnitAI.prototype.RespondToSightedEntities = function(ents)
+{
+ if (!ents || !ents.length)
+ return false;
+
+ if (this.template.NaturalBehaviour && this.template.NaturalBehaviour == "skittish")
+ {
+ this.Flee(ents[0], false);
+ return true;
+ }
+
+ return false;
+};
+
+/**
* Try to respond to healable entities.
* Returns true if it responded.
*/
@@ -5805,19 +5867,45 @@
};
/**
- * Resets losRangeQuery, and if there are some targets in range that we can
+ * Resets the losRangeQuery.
+ * @return {boolean} - Whether there are targets in range that we ought to react upon.
+ */
+UnitAI.prototype.FindSightedEnemies = function()
+{
+ if (!this.losRangeQuery)
+ return false;
+
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ return this.RespondToSightedEntities(cmpRangeManager.ResetActiveQuery(this.losRangeQuery));
+};
+
+/**
+ * Resets losHealRangeQuery, and if there are some targets in range that we can heal
+ * then we start healing and this returns true; otherwise, returns false.
+ */
+UnitAI.prototype.FindNewHealTargets = function()
+{
+ if (!this.losHealRangeQuery)
+ return false;
+
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ return this.RespondToHealableEntities(cmpRangeManager.ResetActiveQuery(this.losHealRangeQuery));
+};
+
+/**
+ * Resets losAttackRangeQuery, and if there are some targets in range that we can
* attack then we start attacking and this returns true; otherwise, returns false.
*/
UnitAI.prototype.FindNewTargets = function()
{
- if (!this.losRangeQuery)
+ if (!this.losAttackRangeQuery)
return false;
if (!this.GetStance().targetVisibleEnemies)
return false;
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
- return this.AttackEntitiesByPreference(cmpRangeManager.ResetActiveQuery(this.losRangeQuery));
+ return this.AttackEntitiesByPreference(cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery));
};
UnitAI.prototype.FindWalkAndFightTargets = function()
@@ -5888,7 +5976,7 @@
UnitAI.prototype.GetTargetsFromUnit = function()
{
- if (!this.losRangeQuery)
+ if (!this.losAttackRangeQuery)
return [];
if (!this.GetStance().targetVisibleEnemies)
@@ -5907,26 +5995,13 @@
};
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
- var entities = cmpRangeManager.ResetActiveQuery(this.losRangeQuery);
+ var entities = cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery);
var targets = entities.filter(function(v) { return cmpAttack.CanAttack(v) && attackfilter(v); })
.sort(function(a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); });
return targets;
};
-/**
- * Resets losHealRangeQuery, and if there are some targets in range that we can heal
- * then we start healing and this returns true; otherwise, returns false.
- */
-UnitAI.prototype.FindNewHealTargets = function()
-{
- if (!this.losHealRangeQuery)
- return false;
-
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
- return this.RespondToHealableEntities(cmpRangeManager.ResetActiveQuery(this.losHealRangeQuery));
-};
-
UnitAI.prototype.GetQueryRange = function(iid)
{
let ret = { "min": 0, "max": 0 };
@@ -5936,6 +6011,12 @@
return ret;
let visionRange = cmpVision.GetRange();
+ if (iid === IID_Vision)
+ {
+ ret.max = visionRange;
+ return ret;
+ }
+
if (this.GetStance().respondStandGround)
{
let range = this.GetRange(iid);
Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
+++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
@@ -194,7 +194,7 @@
unitAI.OnCreate();
- unitAI.SetupRangeQuery(1);
+ unitAI.SetupAttackRangeQuery(1);
if (mode == 1)
@@ -352,7 +352,7 @@
unitAI.OnCreate();
- unitAI.SetupRangeQuery(1);
+ unitAI.SetupAttackRangeQuery(1);
unitAIs.push(unitAI);
}
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
@@ -44,6 +44,6 @@
true
- 0
+ 20