Index: binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Attack.js
+++ binaries/data/mods/public/simulation/components/Attack.js
@@ -40,6 +40,16 @@
"" +
"";
+Attack.prototype.continuousDamageSchema =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
Attack.prototype.Schema =
"Controls the attack abilities and strengths of the unit." +
"" +
@@ -73,6 +83,10 @@
"800" +
"1600" +
"1000" +
+ "" +
+ "1000" +
+ "5" +
+ "" +
"" +
"" +
"Cavalry" +
@@ -114,6 +128,7 @@
"" + // TODO: it shouldn't be stretched
"" +
"" +
+ Attack.prototype.continuousDamageSchema +
Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
@@ -145,6 +160,10 @@
"" +
"" +
"" +
+ Attack.prototype.continuousDamageSchema +
+ Attack.prototype.bonusesSchema +
+ Attack.prototype.preferredClassesSchema +
+ Attack.prototype.restrictedClassesSchema +
"" +
"" +
"" +
@@ -477,6 +496,18 @@
return template.Bonuses || null;
};
+Attack.prototype.GetContinuousDamage = function(type)
+{
+ let interval = this.template[type].Continuous ? +this.template[type].Continuous.Interval : 0;
+ let numberOfIntervals = this.template[type].Continuous ? +this.template[type].Continuous.NumberOfIntervals : 0;
+ interval = ApplyValueModificationsToEntity(
+ "ContinuousDamage/" + type + "/Interval", interval, this.entity);
+ numberOfIntervals = ApplyValueModificationsToEntity(
+ "ContinuousDamage/" + type + "/NumberOfIntervals", numberOfIntervals, this.entity);
+
+ return { "interval": interval, "numberOfIntervals": numberOfIntervals};
+};
+
/**
* Attack the target entity. This should only be called after a successful range check,
* and should only be called after GetTimers().repeat msec has passed since the last
@@ -485,7 +516,8 @@
Attack.prototype.PerformAttack = function(type, target)
{
let attackerOwner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner();
- let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage);
+ let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage);
+ let continuousDamage = this.GetContinuousDamage(type);
// If this is a ranged attack, then launch a projectile
if (type == "Ranged")
@@ -577,7 +609,9 @@
"bonus": this.GetBonusTemplate(type),
"isSplash": false,
"attackerOwner": attackerOwner,
- "attackImpactSound": attackImpactSound
+ "attackImpactSound": attackImpactSound,
+ "interval": continuousDamage.interval,
+ "numberOfIntervals": continuousDamage.numberOfIntervals
};
if (this.template[type].Splash)
{
@@ -624,7 +658,9 @@
"attacker": this.entity,
"multiplier": GetDamageBonus(target, this.GetBonusTemplate(type)),
"type": type,
- "attackerOwner": attackerOwner
+ "attackerOwner": attackerOwner,
+ "interval": continuousDamage.interval,
+ "numberOfIntervals": continuousDamage.numberOfIntervals
});
}
};
Index: binaries/data/mods/public/simulation/components/Damage.js
===================================================================
--- binaries/data/mods/public/simulation/components/Damage.js
+++ binaries/data/mods/public/simulation/components/Damage.js
@@ -122,7 +122,9 @@
"direction": data.direction,
"playersToDamage": this.GetPlayersToDamage(data.attackerOwner, data.friendlyFire),
"type": data.type,
- "attackerOwner": data.attackerOwner
+ "attackerOwner": data.attackerOwner,
+ "interval": data.interval,
+ "numberOfIntervals": data.numberOfIntervals
});
}
@@ -157,7 +159,9 @@
"attacker": data.attacker,
"multiplier": GetDamageBonus(ent, data.bonus),
"type": data.type,
- "attackerOwner": data.attackerOwner
+ "attackerOwner": data.attackerOwner,
+ "interval": data.interval,
+ "numberOfIntervals": data.numberOfIntervals
});
cmpProjectileManager.RemoveProjectile(data.projectileId);
break;
@@ -226,7 +230,9 @@
"attacker": data.attacker,
"multiplier": damageMultiplier,
"type": data.type + ".Splash",
- "attackerOwner": data.attackerOwner
+ "attackerOwner": data.attackerOwner,
+ "interval": data.interval,
+ "numberOfIntervals": data.numberOfIntervals
});
}
};
@@ -240,6 +246,8 @@
* @param {number} data.multiplier - the damage multiplier.
* @param {string} data.type - the type of damage.
* @param {number} data.attackerOwner - the player id of the attacker.
+ * @param {number} data.interval - time between damages caused during continuous damage.
+ * @param {number} data.numberOfIntervals - remaining intervals during continuous damage.
*/
Damage.prototype.CauseDamage = function(data)
{
@@ -255,6 +263,12 @@
if (cmpPromotion && cmpLoot && cmpLoot.GetXp() > 0)
cmpPromotion.IncreaseXp(cmpLoot.GetXp() * -targetState.change / cmpHealth.GetMaxHitpoints());
+ if (data.numberOfIntervals && --data.numberOfIntervals > 0)
+ {
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Damage, "CauseDamage", +data.interval, data);
+ }
+
if (targetState.killed)
this.TargetKilled(data.attacker, data.target, data.attackerOwner);
Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Attack.js
+++ binaries/data/mods/public/simulation/components/tests/test_Attack.js
@@ -76,6 +76,10 @@
"Spread": 2,
"Gravity": 1
},
+ "Continuous": {
+ "Interval": 500,
+ "NumberOfIntervals": 4
+ },
"PreferredClasses": {
"_string": "Archer"
},
@@ -175,6 +179,11 @@
"friendlyFire": false,
"shape": "Circular"
});
+
+ TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetContinuousDamage("Ranged"), {
+ "interval": 500,
+ "numberOfIntervals": 4
+ });
});
for (let className of ["Infantry", "Cavalry"])
Index: binaries/data/mods/public/simulation/components/tests/test_Damage.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Damage.js
+++ binaries/data/mods/public/simulation/components/tests/test_Damage.js
@@ -548,3 +548,47 @@
}
Test_MissileHit();
+
+function Test_ContinuousDamage()
+{
+ ResetState();
+
+ let cmpDamage = ConstructComponent(SYSTEM_ENTITY, "Damage");
+ let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
+ let target = 60;
+ let damageTaken = false;
+ let data = {
+ "attacker": 50,
+ "target": target,
+ "type": "Melee",
+ "strengths": { "hack": 0, "pierce": 0, "crush": 1 },
+ "multiplier": 1.0,
+ "interval": 10000,
+ "numberOfIntervals": 3
+ }
+
+ AddMock(target, IID_DamageReceiver, {
+ "TakeDamage": (strengths, multiplier) => { damageTaken = true; return { "killed": false, "change": -multiplier * strengths.crush }; }
+ });
+
+ // damage scheduled: 0 sec, 10 sec, 20 sec
+ cmpDamage.CauseDamage(data);
+ TS_ASSERT(damageTaken); // 0 sec
+
+ damageTaken = false;
+ cmpTimer.OnUpdate({ turnLength: 9 });
+ TS_ASSERT(!damageTaken); // 9 sec
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT(damageTaken); // 10 sec
+
+ damageTaken = false;
+ cmpTimer.OnUpdate({ turnLength: 10 });
+ TS_ASSERT(damageTaken); // 20 sec
+
+ damageTaken = false;
+ cmpTimer.OnUpdate({ turnLength: 10 });
+ TS_ASSERT(!damageTaken); // 30 sec
+}
+
+Test_ContinuousDamage();
Index: binaries/data/mods/public/simulation/templates/units/athen_cavalry_javelinist_a.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/athen_cavalry_javelinist_a.xml
+++ binaries/data/mods/public/simulation/templates/units/athen_cavalry_javelinist_a.xml
@@ -1,5 +1,22 @@
+
+
+ 0
+ 18.0
+ 0
+ 28.0
+ 0.0
+ 62.5
+ 750
+ 1250
+ 3.0
+
+ 1000
+ 3
+
+
+
Advanced