Index: ps/trunk/binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Attack.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/Attack.js
@@ -455,6 +455,10 @@
if (!this.CanAttack(target, [type]))
return false;
+ let cmpResistance = Engine.QueryInterface(target, IID_Resistance);
+ if (!cmpResistance || !cmpResistance.AddAttacker(this.entity))
+ return false;
+
let timings = this.GetTimers(type);
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
@@ -496,6 +500,10 @@
cmpTimer.CancelTimer(this.timer);
delete this.timer;
+ let cmpResistance = Engine.QueryInterface(this.target, IID_Resistance);
+ if (cmpResistance)
+ cmpResistance.RemoveAttacker(this.entity);
+
delete this.target;
let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
@@ -539,10 +547,7 @@
this.PerformAttack(type, this.target);
if (!this.target)
- {
- this.StopAttacking("TargetInvalidated");
return;
- }
// We check the range after the attack to facilitate chasing.
if (!this.IsTargetInRange(this.target, type))
Index: ps/trunk/binaries/data/mods/public/simulation/components/Resistance.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Resistance.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/Resistance.js
@@ -73,6 +73,7 @@
Resistance.prototype.Init = function()
{
this.invulnerable = false;
+ this.attackers = new Set();
};
Resistance.prototype.IsInvulnerable = function()
@@ -80,6 +81,28 @@
return this.invulnerable;
};
+/**
+ * @param {number} attacker - The entity ID of the attacker to add.
+ * @return {boolean} - Whether the attacker was added sucessfully.
+ */
+Resistance.prototype.AddAttacker = function(attacker)
+{
+ if (this.attackers.has(attacker))
+ return false;
+
+ this.attackers.add(attacker);
+ return true;
+};
+
+/**
+ * @param {number} attacker - The entity ID of the attacker to remove.
+ * @return {boolean} - Whether the attacker was attacking us previously.
+ */
+Resistance.prototype.RemoveAttacker = function(attacker)
+{
+ return this.attackers.delete(attacker);
+};
+
Resistance.prototype.SetInvulnerability = function(invulnerability)
{
this.invulnerable = invulnerability;
@@ -151,4 +174,11 @@
return ret;
};
+Resistance.prototype.OnOwnershipChanged = function(msg)
+{
+ if (msg.to === INVALID_PLAYER)
+ for (let attacker of this.attackers)
+ Engine.QueryInterface(attacker, IID_Attack)?.StopAttacking("TargetInvalidated");
+};
+
Engine.RegisterComponentType(IID_Resistance, "Resistance", Resistance);
Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js
@@ -2156,9 +2156,6 @@
this.SetNextState("FINDINGNEWTARGET");
},
- // TODO: respond to target deaths immediately, rather than waiting
- // until the next Timer event
-
"Attacked": function(msg) {
if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) &&
this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture")
Index: ps/trunk/binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
+++ ps/trunk/binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
@@ -24,6 +24,7 @@
+