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
@@ -2,6 +2,22 @@
var g_AttackTypes = ["Melee", "Ranged", "Capture"];
+Attack.prototype.statusEffectsSchema =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
Attack.prototype.bonusesSchema =
"" +
"" +
@@ -187,6 +203,7 @@
"" +
"" +
"" +
+ Attack.prototype.statusEffectsSchema +
Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
@@ -577,7 +594,8 @@
"bonus": this.GetBonusTemplate(type),
"isSplash": false,
"attackerOwner": attackerOwner,
- "attackImpactSound": attackImpactSound
+ "attackImpactSound": attackImpactSound,
+ "statusEffects": this.template[type].StatusEffects
};
if (this.template[type].Splash)
{
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
@@ -93,6 +93,7 @@
* @param {Vector3D} data.direction - the unit vector defining the direction.
* @param {Object} data.bonus - the attack bonus template from the attacker.
* @param {string} data.attackImpactSound - the name of the sound emited on impact.
+ * @param {Object} data.statusEffects - status effects eg. poisoning, burning etc.
* ***When splash damage***
* @param {boolean} data.friendlyFire - a flag indicating if allied entities are also damaged.
* @param {number} data.radius - the radius of the splash damage.
@@ -136,6 +137,12 @@
data.multiplier = GetDamageBonus(data.target, data.bonus);
this.CauseDamage(data);
cmpProjectileManager.RemoveProjectile(data.projectileId);
+
+ // Do the status change, eg. poisoning, burning etc.
+ let cmpStatus = Engine.QueryInterface(data.target, IID_Status);
+ if (cmpStatus && data.statusEffects)
+ cmpStatus.StartEffects(data.statusEffects);
+
return;
}
@@ -236,7 +243,7 @@
* @param {Object} data - the data passed by the caller.
* @param {Object} data.strengths - data in the form of { 'hack': number, 'pierce': number, 'crush': number }.
* @param {number} data.target - the entity id of the target.
- * @param {number} data.attacker - the entity id og the attacker.
+ * @param {number} data.attacker - the entity id of the attacker.
* @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.
Index: binaries/data/mods/public/simulation/components/Status.js
===================================================================
--- binaries/data/mods/public/simulation/components/Status.js
+++ binaries/data/mods/public/simulation/components/Status.js
@@ -0,0 +1,69 @@
+function Status() {}
+
+Status.prototype.Init = function()
+{
+ this.currentStatuses = {};
+};
+
+Status.prototype.StartEffects = function(statusEffects)
+{
+ for (let effect in statusEffects)
+ this.StartEffect(effect, statusEffects[effect]);
+};
+
+Status.prototype.StartEffect = function(statusName, data)
+{
+ if (this.currentStatuses[statusName])
+ return;
+
+ this.currentStatuses[statusName] = {};
+ let status = this.currentStatuses[statusName];
+ status.duration = +data.Duration;
+ status.interval = +data.Interval;
+ status.damage = +data.Damage;
+ status.timeElapsed = 0;
+ status.firstTime = true;
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ status.timer = cmpTimer.SetInterval(this.entity, IID_Status, "ExecuteEffect", 0, +status.interval, statusName);
+};
+
+Status.prototype.StopEffect = function(statusName) {
+ if (!this.currentStatuses[statusName])
+ return;
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.currentStatuses[statusName].timer);
+ this.currentStatuses[statusName] = undefined;
+};
+
+Status.prototype.ExecuteEffect = function(statusName, lateness)
+{
+ let status = this.currentStatuses[statusName];
+ if (!status)
+ return;
+
+ if (status.firstTime)
+ {
+ status.firstTime = false;
+ status.timeElapsed += lateness;
+ }
+ else
+ status.timeElapsed += status.interval + lateness;
+
+ let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage);
+
+ cmpDamage.CauseDamage({
+ "strengths": { [statusName]: status.damage },
+ "target": this.entity,
+ "attacker": -1,
+ "multiplier": 1,
+ "type": statusName,
+ "attackerOwner": -1
+ });
+
+ if (status.timeElapsed >= status.duration)
+ this.StopEffect(statusName);
+};
+
+Engine.RegisterComponentType(IID_Status, "Status", Status);
Index: binaries/data/mods/public/simulation/components/interfaces/Status.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Status.js
+++ binaries/data/mods/public/simulation/components/interfaces/Status.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("Status");
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
@@ -12,6 +12,7 @@
Engine.LoadComponentScript("interfaces/Loot.js");
Engine.LoadComponentScript("interfaces/Player.js");
Engine.LoadComponentScript("interfaces/Promotion.js");
+Engine.LoadComponentScript("interfaces/Status.js");
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("Attack.js");
Index: binaries/data/mods/public/simulation/components/tests/test_Status.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Status.js
+++ binaries/data/mods/public/simulation/components/tests/test_Status.js
@@ -0,0 +1,116 @@
+Engine.LoadComponentScript("interfaces/Damage.js");
+Engine.LoadComponentScript("interfaces/Status.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+
+Engine.LoadComponentScript("Status.js");
+Engine.LoadComponentScript("Timer.js");
+
+let target = 42;
+let cmpStatus;
+let cmpTimer;
+let dealtDamage;
+
+function setup()
+{
+ cmpStatus = ConstructComponent(target, "Status");
+ cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
+ dealtDamage = 0;
+};
+
+function testStartEffects()
+{
+ setup();
+ let statusName = "Burn";
+ AddMock(SYSTEM_ENTITY, IID_Damage, {
+ "CauseDamage": (data) => { dealtDamage += data.strengths[statusName]; }
+ });
+
+ // damage scheduled: 0, 10, 20 sec
+ cmpStatus.StartEffects({[statusName]: {
+ "Duration": 20000,
+ "Interval": 10000,
+ "Damage": 1
+ }});
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 1 sec
+
+ cmpTimer.OnUpdate({ turnLength: 8 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 9 sec
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 2); // 10 sec
+
+ cmpTimer.OnUpdate({ turnLength: 10 });
+ TS_ASSERT_EQUALS(dealtDamage, 3); // 20 sec
+
+ cmpTimer.OnUpdate({ turnLength: 10 });
+ TS_ASSERT_EQUALS(dealtDamage, 3); // 30 sec
+};
+
+testStartEffects();
+
+function testMultipleEffects()
+{
+ setup();
+ AddMock(SYSTEM_ENTITY, IID_Damage, {
+ "CauseDamage": (data) => {
+ if (data.strengths["Burn"]) dealtDamage += data.strengths["Burn"];
+ if (data.strengths["Poison"]) dealtDamage += data.strengths["Poison"];
+ }
+ });
+
+ // damage scheduled: 0, 1, 2, 10 sec
+ cmpStatus.StartEffects({
+ "Burn": {
+ "Duration": 20000,
+ "Interval": 10000,
+ "Damage": 10
+ },
+ "Poison": {
+ "Duration": 3000,
+ "Interval": 1000,
+ "Damage": 1
+ }
+ });
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 12); // 1 sec
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 13); // 2 sec
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 13); // 3 sec
+
+ cmpTimer.OnUpdate({ turnLength: 7 });
+ TS_ASSERT_EQUALS(dealtDamage, 23); // 10 sec
+};
+
+testMultipleEffects();
+
+function testStopEffect()
+{
+ setup();
+ let statusName = "Poison";
+ AddMock(SYSTEM_ENTITY, IID_Damage, {
+ "CauseDamage": (data) => { dealtDamage += data.strengths[statusName]; }
+ });
+
+ // damage scheduled: 0, 10, 20 sec
+ cmpStatus.StartEffects({[statusName]: {
+ "Duration": 20000,
+ "Interval": 10000,
+ "Damage": 1
+ }});
+
+ cmpTimer.OnUpdate({ turnLength: 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 1 sec
+
+ cmpStatus.StopEffect(statusName);
+
+ cmpTimer.OnUpdate({ turnLength: 10 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 11 sec
+};
+
+testStopEffect();
Index: binaries/data/mods/public/simulation/templates/template_structure.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure.xml
+++ binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -1,5 +1,6 @@
+
1
Index: binaries/data/mods/public/simulation/templates/template_unit.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit.xml
+++ binaries/data/mods/public/simulation/templates/template_unit.xml
@@ -1,5 +1,6 @@
+
1
Index: binaries/data/mods/public/simulation/templates/units/iber_champion_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/iber_champion_cavalry.xml
+++ binaries/data/mods/public/simulation/templates/units/iber_champion_cavalry.xml
@@ -4,6 +4,13 @@
5
15
+
+
+ 50000
+ 1000
+ 1
+
+