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
@@ -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: ps/trunk/binaries/data/mods/public/simulation/components/Damage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Damage.js
+++ ps/trunk/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,11 @@
data.multiplier = GetDamageBonus(data.target, data.bonus);
this.CauseDamage(data);
cmpProjectileManager.RemoveProjectile(data.projectileId);
+
+ let cmpStatusReceiver = Engine.QueryInterface(data.target, IID_StatusEffectsReceiver);
+ if (cmpStatusReceiver && data.statusEffects)
+ cmpStatusReceiver.InflictEffects(data.statusEffects);
+
return;
}
@@ -236,7 +242,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: ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js
@@ -0,0 +1,69 @@
+function StatusEffectsReceiver() {}
+
+StatusEffectsReceiver.prototype.Init = function()
+{
+ this.activeStatusEffects = {};
+};
+
+StatusEffectsReceiver.prototype.InflictEffects = function(statusEffects)
+{
+ for (let effect in statusEffects)
+ this.InflictEffect(effect, statusEffects[effect]);
+};
+
+StatusEffectsReceiver.prototype.InflictEffect = function(statusName, data)
+{
+ if (this.activeStatusEffects[statusName])
+ return;
+
+ this.activeStatusEffects[statusName] = {};
+ let status = this.activeStatusEffects[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_StatusEffectsReceiver, "ExecuteEffect", 0, +status.interval, statusName);
+};
+
+StatusEffectsReceiver.prototype.RemoveEffect = function(statusName) {
+ if (!this.activeStatusEffects[statusName])
+ return;
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.activeStatusEffects[statusName].timer);
+ this.activeStatusEffects[statusName] = undefined;
+};
+
+StatusEffectsReceiver.prototype.ExecuteEffect = function(statusName, lateness)
+{
+ let status = this.activeStatusEffects[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.RemoveEffect(statusName);
+};
+
+Engine.RegisterComponentType(IID_StatusEffectsReceiver, "StatusEffectsReceiver", StatusEffectsReceiver);
Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/StatusEffectsReceiver.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/StatusEffectsReceiver.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/StatusEffectsReceiver.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("StatusEffectsReceiver");
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
+++ ps/trunk/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/StatusEffectsReceiver.js");
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("Attack.js");
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js
@@ -0,0 +1,119 @@
+Engine.LoadComponentScript("interfaces/Damage.js");
+Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("StatusEffectsReceiver.js");
+Engine.LoadComponentScript("Timer.js");
+
+var target = 42;
+var cmpStatusReceiver;
+var cmpTimer;
+var dealtDamage;
+
+function setup()
+{
+ cmpStatusReceiver = ConstructComponent(target, "StatusEffectsReceiver");
+ cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
+ dealtDamage = 0;
+}
+
+function testInflictEffects()
+{
+ setup();
+ let statusName = "Burn";
+ AddMock(SYSTEM_ENTITY, IID_Damage, {
+ "CauseDamage": (data) => { dealtDamage += data.strengths[statusName]; }
+ });
+
+ // damage scheduled: 0, 10, 20 sec
+ cmpStatusReceiver.InflictEffects({
+ [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
+}
+
+testInflictEffects();
+
+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
+ cmpStatusReceiver.InflictEffects({
+ "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 testRemoveEffect()
+{
+ setup();
+ let statusName = "Poison";
+ AddMock(SYSTEM_ENTITY, IID_Damage, {
+ "CauseDamage": (data) => { dealtDamage += data.strengths[statusName]; }
+ });
+
+ // damage scheduled: 0, 10, 20 sec
+ cmpStatusReceiver.InflictEffects({
+ [statusName]: {
+ "Duration": 20000,
+ "Interval": 10000,
+ "Damage": 1
+ }
+ });
+
+ cmpTimer.OnUpdate({ "turnLength": 1 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 1 sec
+
+ cmpStatusReceiver.RemoveEffect(statusName);
+
+ cmpTimer.OnUpdate({ "turnLength": 10 });
+ TS_ASSERT_EQUALS(dealtDamage, 1); // 11 sec
+}
+
+testRemoveEffect();
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -141,6 +141,7 @@
0.6
12.0
+
20
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit.xml
@@ -111,6 +111,7 @@
0.333
5.0
+
aggressive
12.0