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
@@ -56,6 +56,16 @@
"" +
"";
+Attack.prototype.EffectsSchema =
+ "" +
+ "" +
+ "" + DamageTypes.BuildSchema("damage strength") + "" +
+ "" +
+ Attack.prototype.statusEffectsSchema +
+ "" +
+ "" +
+ Attack.prototype.bonusesSchema;
+
Attack.prototype.Schema =
"Controls the attack abilities and strengths of the unit." +
"" +
@@ -130,9 +140,7 @@
"" +
"" +
"" +
- "" +
- DamageTypes.BuildSchema("damage strength") +
- "" +
+ Attack.prototype.EffectsSchema +
"" +
"" +
"" +
@@ -140,7 +148,6 @@
"" + // TODO: it shouldn't be stretched
"" +
"" +
- Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
"" +
@@ -149,9 +156,7 @@
"" +
"" +
"" +
- "" +
- DamageTypes.BuildSchema("damage strength") +
- "" +
+ Attack.prototype.EffectsSchema +
"" +
"" +
""+
@@ -179,10 +184,7 @@
"" +
"" +
"" +
- "" +
- DamageTypes.BuildSchema("damage strength") +
- "" +
- Attack.prototype.bonusesSchema +
+ Attack.prototype.EffectsSchema +
"" +
"" +
"" +
@@ -217,8 +219,6 @@
"" +
"" +
"" +
- Attack.prototype.statusEffectsSchema +
- Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
"" +
@@ -227,12 +227,11 @@
"" +
"" +
"" +
- "" +
+ Attack.prototype.EffectsSchema +
"" +
"" + // TODO: it shouldn't be stretched
"" +
"" +
- Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
"" +
@@ -241,11 +240,8 @@
"" +
"" +
"" +
- "" +
- DamageTypes.BuildSchema("damage strength") +
- "" +
+ Attack.prototype.EffectsSchema +
"" + // TODO: how do these work?
- Attack.prototype.bonusesSchema +
Attack.prototype.preferredClassesSchema +
Attack.prototype.restrictedClassesSchema +
"" +
@@ -452,26 +448,35 @@
return { "prepare": prepare, "repeat": repeat };
};
-Attack.prototype.GetAttackStrengths = function(type)
+/**
+ * Returns a template-like object of attack effects.
+ */
+Attack.prototype.GetAttackTemplate = function(type, splash)
{
// Work out the attack values with technology effects
let template = this.template[type];
- let splash = "";
- if (!template)
- {
- template = this.template[type.split(".")[0]].Splash;
- splash = "/Splash";
- }
-
- let applyMods = damageType =>
- ApplyValueModificationsToEntity("Attack/" + type + splash + "/Damage/" + damageType, +(template.Damage[damageType] || 0), this.entity);
-
- if (type == "Capture")
- return { "value": ApplyValueModificationsToEntity("Attack/Capture/Value", +(template.Value || 0), this.entity) };
+ if (splash)
+ template = template.Splash;
let ret = {};
- for (let damageType of DamageTypes.GetTypes())
- ret[damageType] = applyMods(damageType);
+
+ if (template.Damage)
+ {
+ ret.Damage = {};
+ let applyMods = damageType =>
+ ApplyValueModificationsToEntity("Attack/" + type + splash ? "/Splash" : "" + "/Damage/" + damageType, +(template.Damage[damageType] || 0), this.entity);
+ for (let damageType of DamageTypes.GetTypes())
+ ret.Damage[damageType] = applyMods(damageType);
+ }
+ if (template.Capture)
+ ret.Capture = ApplyValueModificationsToEntity("Attack/Capture/Capture/Value", +(template.Capture || 0), this.entity);
+
+ if (template.StatusEffects)
+ ret.StatusEffects = template.StatusEffects;
+
+ let bonuses = this.GetBonusTemplate(type, splash);
+ if (bonuses)
+ ret.Bonuses = bonuses;
return ret;
};
@@ -482,7 +487,7 @@
return false;
let splash = {};
- splash.damage = this.GetAttackStrengths(type + ".Splash");
+ splash.template = this.GetAttackTemplate(type, true);
splash.friendlyFire = this.template[type].Splash.FriendlyFire != "false";
splash.shape = this.template[type].Splash.Shape;
return splash;
@@ -502,11 +507,11 @@
return { "max": max, "min": min, "elevationBonus": elevationBonus };
};
-Attack.prototype.GetBonusTemplate = function(type)
+Attack.prototype.GetBonusTemplate = function(type, splash)
{
let template = this.template[type];
- if (!template)
- template = this.template[type.split(".")[0]].Splash;
+ if (splash)
+ template = this.template[type].Splash;
return template.Bonuses || null;
};
@@ -604,11 +609,10 @@
"type": type,
"attacker": this.entity,
"target": target,
- "strengths": this.GetAttackStrengths(type),
+ "template": this.GetAttackTemplate(type),
"position": realTargetPosition,
"direction": missileDirection,
"projectileId": id,
- "bonus": this.GetBonusTemplate(type),
"isSplash": false,
"attackerOwner": attackerOwner,
"attackImpactSound": attackImpactSound,
@@ -620,48 +624,12 @@
data.radius = +this.template[type].Splash.Range;
data.shape = this.template[type].Splash.Shape;
data.isSplash = true;
- data.splashStrengths = this.GetAttackStrengths(type + ".Splash");
- data.splashBonus = this.GetBonusTemplate(type + ".Splash");
+ data.splashTemplate = this.GetAttackTemplate(type, true);
}
cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Damage, "MissileHit", timeToTarget * 1000 + +this.template[type].Delay, data);
}
- else if (type == "Capture")
- {
- if (attackerOwner == INVALID_PLAYER)
- return;
-
- let multiplier = GetDamageBonus(this.entity, target, type, this.GetBonusTemplate(type));
- let cmpHealth = Engine.QueryInterface(target, IID_Health);
- if (!cmpHealth || cmpHealth.GetHitpoints() == 0)
- return;
- multiplier *= cmpHealth.GetMaxHitpoints() / (0.1 * cmpHealth.GetMaxHitpoints() + 0.9 * cmpHealth.GetHitpoints());
-
- let cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
- if (!cmpCapturable || !cmpCapturable.CanCapture(attackerOwner))
- return;
-
- let strength = this.GetAttackStrengths("Capture").value * multiplier;
- if (cmpCapturable.Reduce(strength, attackerOwner) && IsOwnedByEnemyOfPlayer(attackerOwner, target))
- Engine.PostMessage(target, MT_Attacked, {
- "attacker": this.entity,
- "target": target,
- "type": type,
- "damage": strength,
- "attackerOwner": attackerOwner
- });
- }
else
- {
- // Melee attack - hurt the target immediately
- cmpDamage.CauseDamage({
- "strengths": this.GetAttackStrengths(type),
- "target": target,
- "attacker": this.entity,
- "multiplier": GetDamageBonus(this.entity, target, type, this.GetBonusTemplate(type)),
- "type": type,
- "attackerOwner": attackerOwner
- });
- }
+ cmpDamage.HandleAttackEffects(type, this.GetAttackTemplate(type), target, this.entity, attackerOwner);
};
/**
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
@@ -111,15 +111,14 @@
cmpSoundManager.PlaySoundGroupAtPosition(data.attackImpactSound, data.position);
// Do this first in case the direct hit kills the target
- if (data.isSplash)
+ if (data.splashTemplate)
{
this.CauseSplashDamage({
"attacker": data.attacker,
"origin": Vector2D.from3D(data.position),
"radius": data.radius,
"shape": data.shape,
- "strengths": data.splashStrengths,
- "splashBonus": data.splashBonus,
+ "splashTemplate": data.splashTemplate,
"direction": data.direction,
"playersToDamage": this.GetPlayersToDamage(data.attackerOwner, data.friendlyFire),
"type": data.type,
@@ -131,17 +130,11 @@
// Deal direct damage if we hit the main target
// and if the target has DamageReceiver (not the case for a mirage for example)
- let cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);
- if (cmpDamageReceiver && this.TestCollision(data.target, data.position, lateness))
+ if (this.TestCollision(data.target, data.position, lateness))
{
- data.multiplier = GetDamageBonus(data.attacker, data.target, data.type, 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);
-
+ this.HandleAttackEffects(data.type, data.template, data.target, data.attacker, data.attackerOwner);
return;
}
@@ -157,14 +150,8 @@
if (!this.TestCollision(ent, data.position, lateness))
continue;
- this.CauseDamage({
- "strengths": data.strengths,
- "target": ent,
- "attacker": data.attacker,
- "multiplier": GetDamageBonus(data.attacker, ent, data.type, data.bonus),
- "type": data.type,
- "attackerOwner": data.attackerOwner
- });
+ this.HandleAttackEffects(data.type, ent, data.target, data.attacker, data.attackerOwner);
+
cmpProjectileManager.RemoveProjectile(data.projectileId);
break;
}
@@ -222,19 +209,43 @@
warn("The " + data.shape + " splash damage shape is not implemented!");
}
- if (data.splashBonus)
- damageMultiplier *= GetDamageBonus(data.attacker, ent, data.type, data.splashBonus);
+ cmpDamage.HandleAttackEffects(data.type + ".Splash", data.splashTemplate, ent, data.attacker, data.attackerOwner);
+ }
+};
+
+Damage.prototype.HandleAttackEffects = function(name, attackTemplate, target, attacker, attackerOwner)
+{
+ if (attackTemplate.Damage)
+ {
+ let multiplier = 1;
+ if (attackTemplate.Bonuses)
+ multiplier = GetDamageBonus(attacker, target, name, attackTemplate.Bonuses);
- // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds....
this.CauseDamage({
- "strengths": data.strengths,
- "target": ent,
- "attacker": data.attacker,
- "multiplier": damageMultiplier,
- "type": data.type + ".Splash",
- "attackerOwner": data.attackerOwner
+ "type": name,
+ "target": target,
+ "attacker": attacker,
+ "attackerOwner": attackerOwner,
+ "strengths": attackTemplate.Damage,
+ "multiplier": multiplier
});
}
+
+ if (attackTemplate.Capture)
+ this.CauseCapture({
+ "type": name,
+ "target": target,
+ "attacker": attacker,
+ "attackerOwner": attackerOwner,
+ "template": attackTemplate
+ });
+
+ if (attackTemplate.StatusEffects)
+ {
+ let cmpStatusReceiver = Engine.QueryInterface(data.target, IID_StatusEffectsReceiver);
+ if (cmpStatusReceiver)
+ cmpStatusReceiver.InflictEffects(attackTemplate.StatusEffects);
+ }
};
/**
@@ -267,6 +278,32 @@
Engine.PostMessage(data.target, MT_Attacked, { "attacker": data.attacker, "target": data.target, "type": data.type, "damage": -targetState.change, "attackerOwner": data.attackerOwner });
};
+Damage.prototype.CauseCapture = function(data)
+{
+ if (data.attackerOwner == INVALID_PLAYER)
+ return;
+
+ let multiplier = GetDamageBonus(data.attacker, data.target, data.type, data.template.Bonuses);
+ let cmpHealth = Engine.QueryInterface(data.target, IID_Health);
+ if (!cmpHealth || cmpHealth.GetHitpoints() == 0)
+ return;
+ multiplier *= cmpHealth.GetMaxHitpoints() / (0.1 * cmpHealth.GetMaxHitpoints() + 0.9 * cmpHealth.GetHitpoints());
+
+ let cmpCapturable = Engine.QueryInterface(data.target, IID_Capturable);
+ if (!cmpCapturable || !cmpCapturable.CanCapture(data.attackerOwner))
+ return;
+
+ let strength = data.template.Capture * multiplier;
+ if (cmpCapturable.Reduce(strength, data.attackerOwner) && IsOwnedByEnemyOfPlayer(data.attackerOwner, data.target))
+ Engine.PostMessage(data.target, MT_Attacked, {
+ "attacker": data.attacker,
+ "target": data.target,
+ "type": data.type,
+ "damage": data.strength,
+ "attackerOwner": data.attackerOwner
+ });
+};
+
/**
* Gets entities near a give point for given players.
* @param {Vector2D} origin - The point to check around.
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -390,9 +390,9 @@
{
ret.attack[type] = {};
if (type == "Capture")
- ret.attack[type] = cmpAttack.GetAttackStrengths(type);
+ ret.attack[type] = cmpAttack.GetAttackTemplate(type);
else
- ret.attack[type].damage = cmpAttack.GetAttackStrengths(type);
+ ret.attack[type].damage = cmpAttack.GetAttackTemplate(type).Damage;
ret.attack[type].splash = cmpAttack.GetSplashDamage(type);
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
@@ -7,7 +7,7 @@
- 2
+ 2
4
1000
Field Palisade SiegeWall StoneWall
Index: binaries/data/mods/public/simulation/templates/template_unit_champion.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion.xml
@@ -2,7 +2,7 @@
- 5
+ 5
4
1000
Field Palisade SiegeWall StoneWall
Index: binaries/data/mods/public/simulation/templates/template_unit_hero.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero.xml
@@ -7,7 +7,7 @@
- 15
+ 15
4
1000
Field Palisade SiegeWall StoneWall
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
@@ -7,7 +7,7 @@
- 2
+ 2
4
1000
Field Palisade SiegeWall StoneWall