Rename Armour to Resistance and change the way it is processed.
- Renames Armour-node to Resistance. - Support resistance against Capture. - Puts resistance against effects in separate nodes. - Some cleaning. Differential Revision: D2229 Reviewed by: @bb (accepted), @wraitii. Comments by: @Stan, @Nescio. This was SVN commit r24001.
This commit is contained in:
parent
37d31a5e3d
commit
0f91c5ac61
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity filtered="">
|
||||
<AIProxy merge=""/>
|
||||
<Armour merge=""/>
|
||||
<BuildRestrictions merge=""/>
|
||||
<!-- Don't provide population bonuses yet (but still do take up population cost) -->
|
||||
<Cost merge="">
|
||||
@ -27,6 +26,7 @@
|
||||
<Position merge=""/>
|
||||
<RallyPoint merge=""/>
|
||||
<RallyPointRenderer merge=""/>
|
||||
<Resistance merge=""/>
|
||||
<Selectable merge=""/>
|
||||
<Sound merge=""/>
|
||||
<StatusBars merge=""/>
|
||||
|
@ -160,12 +160,23 @@ function GetTemplateDataHelper(template, player, auraTemplates, modifiers = {})
|
||||
|
||||
let ret = {};
|
||||
|
||||
if (template.Armour)
|
||||
if (template.Resistance)
|
||||
{
|
||||
ret.armour = {};
|
||||
for (let damageType in template.Armour)
|
||||
if (damageType != "Foundation")
|
||||
ret.armour[damageType] = getEntityValue("Armour/" + damageType);
|
||||
// Don't show Foundation resistance.
|
||||
ret.resistance = {};
|
||||
if (template.Resistance.Entity)
|
||||
{
|
||||
if (template.Resistance.Entity.Damage)
|
||||
{
|
||||
ret.resistance.Damage = {};
|
||||
for (let damageType in template.Resistance.Entity.Damage)
|
||||
ret.resistance.Damage[damageType] = getEntityValue("Resistance/Entity/Damage/" + damageType);
|
||||
}
|
||||
if (template.Resistance.Entity.Capture)
|
||||
ret.resistance.Capture = getEntityValue("Resistance/Entity/Capture");
|
||||
|
||||
// ToDo: Resistance against StatusEffects.
|
||||
}
|
||||
}
|
||||
|
||||
let getAttackEffects = (temp, path) => {
|
||||
|
@ -155,37 +155,76 @@ function getCurrentHealthTooltip(entState, label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an armor level into the actual reduction percentage
|
||||
* Converts an resistance level into the actual reduction percentage.
|
||||
*/
|
||||
function armorLevelToPercentageString(level)
|
||||
function resistanceLevelToPercentageString(level)
|
||||
{
|
||||
return sprintf(translate("%(percentage)s%%"), {
|
||||
"percentage": (100 - Math.round(Math.pow(0.9, level) * 100))
|
||||
});
|
||||
}
|
||||
|
||||
function getArmorTooltip(template)
|
||||
function getResistanceTooltip(template)
|
||||
{
|
||||
if (!template.armour)
|
||||
if (!template.resistance)
|
||||
return "";
|
||||
|
||||
let details = [];
|
||||
if (template.resistance.Damage)
|
||||
details.push(getDamageResistanceTooltip(template.resistance.Damage));
|
||||
|
||||
if (template.resistance.Capture)
|
||||
details.push(getCaptureResistanceTooltip(template.resistance.Capture));
|
||||
|
||||
// ToDo: Status effects resistance.
|
||||
|
||||
return sprintf(translate("%(label)s\n %(details)s"), {
|
||||
"label": headerFont(translate("Resistance:")),
|
||||
"details": details.join("\n ")
|
||||
});
|
||||
}
|
||||
|
||||
function getDamageResistanceTooltip(resistanceTypeTemplate)
|
||||
{
|
||||
if (!resistanceTypeTemplate)
|
||||
return "";
|
||||
|
||||
return sprintf(translate("%(label)s %(details)s"), {
|
||||
"label": headerFont(translate("Armor:")),
|
||||
"label": headerFont(translate("Damage Resistance:")),
|
||||
"details":
|
||||
g_DamageTypesMetadata.sort(Object.keys(template.armour)).map(
|
||||
dmgType => sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
|
||||
"damage": template.armour[dmgType].toFixed(1),
|
||||
g_DamageTypesMetadata.sort(Object.keys(resistanceTypeTemplate)).map(
|
||||
dmgType => sprintf(translate("%(damage)s %(damageType)s %(resistancePercentage)s"), {
|
||||
"damage": resistanceTypeTemplate[dmgType].toFixed(1),
|
||||
"damageType": unitFont(translateWithContext("damage type", g_DamageTypesMetadata.getName(dmgType))),
|
||||
"armorPercentage":
|
||||
"resistancePercentage":
|
||||
'[font="sans-10"]' +
|
||||
sprintf(translate("(%(armorPercentage)s)"), {
|
||||
"armorPercentage": armorLevelToPercentageString(template.armour[dmgType])
|
||||
sprintf(translate("(%(resistancePercentage)s)"), {
|
||||
"resistancePercentage": resistanceLevelToPercentageString(resistanceTypeTemplate[dmgType])
|
||||
}) + '[/font]'
|
||||
})
|
||||
).join(commaFont(translate(", ")))
|
||||
});
|
||||
}
|
||||
|
||||
function getCaptureResistanceTooltip(resistanceTypeTemplate)
|
||||
{
|
||||
if (!resistanceTypeTemplate)
|
||||
return "";
|
||||
return sprintf(translate("%(label)s %(details)s"), {
|
||||
"label": headerFont(translate("Capture Resistance:")),
|
||||
"details":
|
||||
sprintf(translate("%(damage)s %(damageType)s %(resistancePercentage)s"), {
|
||||
"damage": resistanceTypeTemplate.toFixed(1),
|
||||
"damageType": unitFont(translateWithContext("damage type", "Capture")),
|
||||
"resistancePercentage":
|
||||
'[font="sans-10"]' +
|
||||
sprintf(translate("(%(resistancePercentage)s)"), {
|
||||
"resistancePercentage": resistanceLevelToPercentageString(resistanceTypeTemplate)
|
||||
}) + '[/font]'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function attackRateDetails(interval, projectiles)
|
||||
{
|
||||
if (!interval)
|
||||
|
@ -55,7 +55,7 @@ ReferencePage.prototype.StatsFunctions = [
|
||||
getHealerTooltip,
|
||||
getAttackTooltip,
|
||||
getSplashDamageTooltip,
|
||||
getArmorTooltip,
|
||||
getResistanceTooltip,
|
||||
getGarrisonTooltip,
|
||||
getProjectilesTooltip,
|
||||
getSpeedTooltip,
|
||||
|
@ -76,9 +76,9 @@ class TemplateParser
|
||||
if (!parsed.upgrades)
|
||||
parsed.upgrades = [];
|
||||
|
||||
// Note: An assumption is made here that wall segments all have the same armor and auras
|
||||
// Note: An assumption is made here that wall segments all have the same resistance and auras
|
||||
let struct = this.getEntity(parsed.wallSet.templates.long, civCode);
|
||||
parsed.armour = struct.armour;
|
||||
parsed.resistance = struct.resistance;
|
||||
parsed.auras = struct.auras;
|
||||
|
||||
// For technology cost multiplier, we need to use the tower
|
||||
|
@ -86,7 +86,7 @@ PanelEntity.prototype.PortraitDirectory = "session/portraits/";
|
||||
PanelEntity.prototype.Tooltips = [
|
||||
getCurrentHealthTooltip,
|
||||
getAttackTooltip,
|
||||
getArmorTooltip,
|
||||
getResistanceTooltip,
|
||||
getEntityTooltip,
|
||||
getAurasTooltip
|
||||
];
|
||||
|
@ -311,11 +311,11 @@ function displaySingle(entState)
|
||||
showTemplateDetails(entState.template);
|
||||
};
|
||||
|
||||
Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = [
|
||||
Engine.GetGUIObjectByName("attackAndResistanceStats").tooltip = [
|
||||
getAttackTooltip,
|
||||
getSplashDamageTooltip,
|
||||
getHealerTooltip,
|
||||
getArmorTooltip,
|
||||
getResistanceTooltip,
|
||||
getGatherTooltip,
|
||||
getSpeedTooltip,
|
||||
getGarrisonTooltip,
|
||||
|
@ -980,7 +980,7 @@ g_SelectionPanels.Training = {
|
||||
getAttackTooltip,
|
||||
getSplashDamageTooltip,
|
||||
getHealerTooltip,
|
||||
getArmorTooltip,
|
||||
getResistanceTooltip,
|
||||
getGarrisonTooltip,
|
||||
getProjectilesTooltip,
|
||||
getSpeedTooltip
|
||||
|
@ -55,9 +55,9 @@
|
||||
</object>
|
||||
|
||||
<object size="0 63 100% 99" type="image" sprite="edgedPanelShader">
|
||||
<!-- Attack and Armor -->
|
||||
<object size="90 -2 126 34" name="attackAndArmorStats" type="image" sprite="stretched:session/icons/stances/defensive.png" tooltip_style="sessionToolTipInstantly">
|
||||
<translatableAttribute id="tooltip">Attack and Armor</translatableAttribute>
|
||||
<!-- Attack and Resistance -->
|
||||
<object size="90 -2 126 34" name="attackAndResistanceStats" type="image" sprite="stretched:session/icons/stances/defensive.png" tooltip_style="sessionToolTipInstantly">
|
||||
<translatableAttribute id="tooltip">Attack and Resistance</translatableAttribute>
|
||||
</object>
|
||||
|
||||
<!-- Resource carrying icon/counter -->
|
||||
@ -94,7 +94,7 @@
|
||||
</object>
|
||||
|
||||
|
||||
<!-- Names (this must come before the attack and armor icon to avoid clipping issues) -->
|
||||
<!-- Names (this must come before the attack and resistance icon to avoid clipping issues) -->
|
||||
<object size="2 96 100%-2 100%-36" name="statsArea" type="image" sprite="edgedPanelShader">
|
||||
|
||||
<!-- These images are used to clip off the top and bottom of the civ icon -->
|
||||
|
@ -196,17 +196,25 @@ m.Template = m.Class({
|
||||
|
||||
"getPopulationBonus": function() { return +this.get("Cost/PopulationBonus"); },
|
||||
|
||||
"armourStrengths": function() {
|
||||
let armourDamageTypes = this.get("Armour");
|
||||
if (!armourDamageTypes)
|
||||
"resistanceStrengths": function() {
|
||||
let resistanceTypes = this.get("Resistance");
|
||||
if (!resistanceTypes || !resistanceTypes.Entity)
|
||||
return undefined;
|
||||
|
||||
let armour = {};
|
||||
for (let damageType in armourDamageTypes)
|
||||
if (damageType != "Foundation")
|
||||
armour[damageType] = +armourDamageTypes[damageType];
|
||||
let resistance = {};
|
||||
if (resistanceTypes.Entity.Capture)
|
||||
resistance.Capture = +this.get("Resistance/Entity/Capture");
|
||||
|
||||
return armour;
|
||||
if (resistanceTypes.Entity.Damage)
|
||||
{
|
||||
resistance.Damage = {};
|
||||
for (let damageType in resistanceTypes.Entity.Damage)
|
||||
resistance.Damage[damageType] = +this.get("Resistance/Entity/Damage/" + damageType);
|
||||
}
|
||||
|
||||
// ToDo: Resistance to StatusEffects.
|
||||
|
||||
return resistance;
|
||||
},
|
||||
|
||||
"attackTypes": function() {
|
||||
@ -754,8 +762,8 @@ m.Entity = m.Class({
|
||||
return false;
|
||||
|
||||
let canCapture = allowCapture && this.canCapture(target);
|
||||
let armourStrengths = target.get("Armour");
|
||||
if (!armourStrengths)
|
||||
let health = target.get("Health");
|
||||
if (!health)
|
||||
return canCapture;
|
||||
|
||||
for (let type in attackTypes)
|
||||
|
@ -59,15 +59,19 @@ PETRA.getMaxStrength = function(ent, debugLevel, DamageTypeImportance, againstCl
|
||||
}
|
||||
}
|
||||
|
||||
let armourStrength = ent.armourStrengths();
|
||||
for (let str in armourStrength)
|
||||
{
|
||||
let val = parseFloat(armourStrength[str]);
|
||||
if (DamageTypeImportance[str])
|
||||
strength += DamageTypeImportance[str] * val / damageTypes.length;
|
||||
else if (debugLevel > 0)
|
||||
API3.warn("Petra: " + str + " unknown armourStrength in getMaxStrength (please add " + str + " to config.js).");
|
||||
}
|
||||
let resistanceStrength = ent.resistanceStrengths();
|
||||
|
||||
if (resistanceStrength.Damage)
|
||||
for (let str in resistanceStrength.Damage)
|
||||
{
|
||||
let val = +resistanceStrength.Damage[str];
|
||||
if (DamageTypeImportance[str])
|
||||
strength += DamageTypeImportance[str] * val / damageTypes.length;
|
||||
else if (debugLevel > 0)
|
||||
API3.warn("Petra: " + str + " unknown resistanceStrength in getMaxStrength (please add " + str + " to config.js).");
|
||||
}
|
||||
|
||||
// ToDo: Add support for StatusEffects and Capture.
|
||||
|
||||
return strength * ent.maxHitpoints() / 100.0;
|
||||
};
|
||||
|
@ -1,73 +0,0 @@
|
||||
function Armour() {}
|
||||
|
||||
Armour.prototype.DamageResistanceSchema = "" +
|
||||
"<oneOrMore>" +
|
||||
"<element a:help='Resistance against any number of damage types'>" +
|
||||
"<anyName>" +
|
||||
"<except><name>Foundation</name></except>" +
|
||||
"</anyName>" +
|
||||
"<ref name='nonNegativeDecimal' />" +
|
||||
"</element>" +
|
||||
"</oneOrMore>";
|
||||
|
||||
Armour.prototype.Schema =
|
||||
"<a:help>Controls the damage resistance of the unit.</a:help>" +
|
||||
"<a:example>" +
|
||||
"<Hack>10.0</Hack>" +
|
||||
"<Pierce>0.0</Pierce>" +
|
||||
"<Crush>5.0</Crush>" +
|
||||
"</a:example>" +
|
||||
Armour.prototype.DamageResistanceSchema +
|
||||
"<optional>" +
|
||||
"<element name='Foundation' a:help='Armour given to building foundations'>" +
|
||||
Armour.prototype.DamageResistanceSchema +
|
||||
"</element>" +
|
||||
"</optional>";
|
||||
|
||||
Armour.prototype.Init = function()
|
||||
{
|
||||
this.invulnerable = false;
|
||||
};
|
||||
|
||||
Armour.prototype.IsInvulnerable = function()
|
||||
{
|
||||
return this.invulnerable;
|
||||
};
|
||||
|
||||
Armour.prototype.SetInvulnerability = function(invulnerability)
|
||||
{
|
||||
this.invulnerable = invulnerability;
|
||||
Engine.PostMessage(this.entity, MT_InvulnerabilityChanged, { "entity": this.entity, "invulnerability": invulnerability });
|
||||
};
|
||||
|
||||
Armour.prototype.GetArmourStrengths = function(effectType)
|
||||
{
|
||||
// Work out the armour values with technology effects.
|
||||
let applyMods = (type, foundation) => {
|
||||
let strength;
|
||||
if (foundation)
|
||||
{
|
||||
strength = +this.template.Foundation[type];
|
||||
type = "Foundation/" + type;
|
||||
}
|
||||
else
|
||||
strength = +this.template[type];
|
||||
|
||||
return ApplyValueModificationsToEntity("Armour/" + type, strength, this.entity);
|
||||
};
|
||||
|
||||
let foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation;
|
||||
|
||||
let ret = {};
|
||||
|
||||
if (effectType != "Damage")
|
||||
return ret;
|
||||
|
||||
for (let damageType in this.template)
|
||||
if (damageType != "Foundation")
|
||||
ret[damageType] = applyMods(damageType, foundation);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Resistance, "Armour", Armour);
|
@ -556,7 +556,7 @@ Attack.prototype.PerformAttack = function(type, target)
|
||||
cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_DelayedDamage, "MissileHit", +this.template[type].Delay + timeToTarget * 1000, data);
|
||||
}
|
||||
else
|
||||
Attacking.HandleAttackEffects(type, this.GetAttackEffectsData(type), target, this.entity, attackerOwner);
|
||||
Attacking.HandleAttackEffects(target, type, this.GetAttackEffectsData(type), this.entity, attackerOwner);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -52,28 +52,19 @@ Capturable.prototype.SetCapturePoints = function(capturePointsArray)
|
||||
|
||||
/**
|
||||
* Compute the amount of capture points to be reduced and reduce them.
|
||||
* @param {number} effectData - Base number of capture points to be taken.
|
||||
* @param {number} amount - Number of capture points to be taken.
|
||||
* @param {number} captor - The entity capturing us.
|
||||
* @param {number} captorOwner - Owner of the captor.
|
||||
* @param {number} bonusMultiplier - Multiplier to be multiplied with effectData.
|
||||
* @return {Object} - Object of the form { "captureChange": number }, where number indicates the actual amount of capture points taken.
|
||||
*/
|
||||
Capturable.prototype.Capture = function(effectData, captor, captorOwner, bonusMultiplier)
|
||||
Capturable.prototype.Capture = function(amount, captor, captorOwner)
|
||||
{
|
||||
let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
|
||||
|
||||
let hitpoints = cmpHealth && cmpHealth.GetHitpoints();
|
||||
if (captorOwner == INVALID_PLAYER || !this.CanCapture(captorOwner) || !hitpoints)
|
||||
if (captorOwner == INVALID_PLAYER || !this.CanCapture(captorOwner))
|
||||
return {};
|
||||
|
||||
bonusMultiplier /= 0.1 + 0.9 * hitpoints / cmpHealth.GetMaxHitpoints();
|
||||
|
||||
let total = Attacking.GetTotalAttackEffects({ "Capture": effectData }, "Capture") * bonusMultiplier;
|
||||
|
||||
let change = this.Reduce(total, captorOwner);
|
||||
// TODO: implement loot
|
||||
|
||||
return { "captureChange": change };
|
||||
return { "captureChange": this.Reduce(amount, captorOwner) };
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -43,9 +43,8 @@ DelayedDamage.prototype.MissileHit = function(data, lateness)
|
||||
if (cmpSoundManager && data.attackImpactSound)
|
||||
cmpSoundManager.PlaySoundGroupAtPosition(data.attackImpactSound, data.position);
|
||||
|
||||
// Do this first in case the direct hit kills the target
|
||||
// Do this first in case the direct hit kills the target.
|
||||
if (data.splash)
|
||||
{
|
||||
Attacking.CauseDamageOverArea({
|
||||
"type": data.type,
|
||||
"attackData": data.splash.attackData,
|
||||
@ -57,17 +56,15 @@ DelayedDamage.prototype.MissileHit = function(data, lateness)
|
||||
"direction": data.direction,
|
||||
"friendlyFire": data.splash.friendlyFire
|
||||
});
|
||||
}
|
||||
|
||||
let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
|
||||
|
||||
// Deal direct damage if we hit the main target
|
||||
// and if the target has Resistance (not the case for a mirage for example)
|
||||
if (Attacking.TestCollision(data.target, data.position, lateness))
|
||||
// and we could handle the attack.
|
||||
if (Attacking.TestCollision(data.target, data.position, lateness) &&
|
||||
Attacking.HandleAttackEffects(data.target, data.type, data.attackData, data.attacker, data.attackerOwner))
|
||||
{
|
||||
cmpProjectileManager.RemoveProjectile(data.projectileId);
|
||||
|
||||
Attacking.HandleAttackEffects(data.type, data.attackData, data.target, data.attacker, data.attackerOwner);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -81,11 +78,10 @@ DelayedDamage.prototype.MissileHit = function(data, lateness)
|
||||
|
||||
for (let ent of ents)
|
||||
{
|
||||
if (!Attacking.TestCollision(ent, data.position, lateness))
|
||||
if (!Attacking.TestCollision(ent, data.position, lateness) ||
|
||||
!Attacking.HandleAttackEffects(ent, data.type, data.attackData, data.attacker, data.attackerOwner))
|
||||
continue;
|
||||
|
||||
Attacking.HandleAttackEffects(data.type, data.attackData, ent, data.attacker, data.attackerOwner);
|
||||
|
||||
cmpProjectileManager.RemoveProjectile(data.projectileId);
|
||||
break;
|
||||
}
|
||||
|
@ -454,9 +454,9 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
}
|
||||
}
|
||||
|
||||
let cmpArmour = Engine.QueryInterface(ent, IID_Resistance);
|
||||
if (cmpArmour)
|
||||
ret.armour = cmpArmour.GetArmourStrengths("Damage");
|
||||
let cmpResistance = Engine.QueryInterface(ent, IID_Resistance);
|
||||
if (cmpResistance)
|
||||
ret.resistance = cmpResistance.GetResistanceOfForm(cmpFoundation ? "Foundation" : "Entity");
|
||||
|
||||
let cmpBuildingAI = Engine.QueryInterface(ent, IID_BuildingAI);
|
||||
if (cmpBuildingAI)
|
||||
|
@ -178,22 +178,15 @@ Health.prototype.Kill = function()
|
||||
};
|
||||
|
||||
/**
|
||||
* Take damage according to the entity's resistance.
|
||||
* @param {Object} strengths - { "hack": number, "pierce": number, "crush": number } or something like that.
|
||||
* @param {number} bonusMultiplier - the damage multiplier.
|
||||
* Returns object of the form { "healthChange": -12 }.
|
||||
* @param {number} amount - The amount of damage to be taken.
|
||||
* @param {number} attacker - The entityID of the attacker.
|
||||
* @param {number} attackerOwner - The playerID of the owner of the attacker.
|
||||
*
|
||||
* @eturn {Object} - Object of the form { "healthChange": number }.
|
||||
*/
|
||||
Health.prototype.TakeDamage = function(effectData, attacker, attackerOwner, bonusMultiplier)
|
||||
Health.prototype.TakeDamage = function(amount, attacker, attackerOwner)
|
||||
{
|
||||
let cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance);
|
||||
|
||||
if (!this.hitpoints || cmpResistance && cmpResistance.IsInvulnerable())
|
||||
return { "healthChange": 0 };
|
||||
|
||||
let total = Attacking.GetTotalAttackEffects(effectData, "Damage", cmpResistance) * bonusMultiplier;
|
||||
|
||||
// Reduce health
|
||||
let change = this.Reduce(total);
|
||||
let change = this.Reduce(amount);
|
||||
|
||||
let cmpLoot = Engine.QueryInterface(this.entity, IID_Loot);
|
||||
if (cmpLoot && cmpLoot.GetXp() > 0 && change.healthChange < 0)
|
||||
|
128
binaries/data/mods/public/simulation/components/Resistance.js
Normal file
128
binaries/data/mods/public/simulation/components/Resistance.js
Normal file
@ -0,0 +1,128 @@
|
||||
function Resistance() {}
|
||||
|
||||
/**
|
||||
* Builds a RelaxRNG schema of possible attack effects.
|
||||
* ToDo: Resistance to StatusEffects.
|
||||
*
|
||||
* @return {string} - RelaxNG schema string.
|
||||
*/
|
||||
Resistance.prototype.BuildResistanceSchema = function()
|
||||
{
|
||||
return "" +
|
||||
"<oneOrMore>" +
|
||||
"<choice>" +
|
||||
"<element name='Damage'>" +
|
||||
"<oneOrMore>" +
|
||||
"<element a:help='Resistance against any number of damage types affecting health.'>" +
|
||||
"<anyName/>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
"</element>" +
|
||||
"</oneOrMore>" +
|
||||
"</element>" +
|
||||
"<element name='Capture' a:help='Resistance against Capture attacks.'>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
"</element>" +
|
||||
"</choice>" +
|
||||
"</oneOrMore>";
|
||||
};
|
||||
|
||||
Resistance.prototype.Schema =
|
||||
"<a:help>Controls the damage resistance of the unit.</a:help>" +
|
||||
"<a:example>" +
|
||||
"<Foundation>" +
|
||||
"<Damage>" +
|
||||
"<Hack>10.0</Hack>" +
|
||||
"<Pierce>0.0</Pierce>" +
|
||||
"<Crush>5.0</Crush>" +
|
||||
"</Damage>" +
|
||||
"<Capture>10</Capture>" +
|
||||
"</Foundation>" +
|
||||
"<Entity>" +
|
||||
"<Damage>" +
|
||||
"<Poison>5</Poison>" +
|
||||
"</Damage>" +
|
||||
"</Entity>" +
|
||||
"</a:example>" +
|
||||
"<zeroOrMore>" +
|
||||
"<choice>" +
|
||||
"<element name='Foundation' a:help='Resistance of an unfinished structure (i.e. a foundation).'>" +
|
||||
Resistance.prototype.BuildResistanceSchema() +
|
||||
"</element>" +
|
||||
"<element name='Entity' a:help='Resistance of an entity.'>" +
|
||||
Resistance.prototype.BuildResistanceSchema() +
|
||||
"</element>" +
|
||||
"</choice>" +
|
||||
"</zeroOrMore>";
|
||||
|
||||
Resistance.prototype.Init = function()
|
||||
{
|
||||
this.invulnerable = false;
|
||||
};
|
||||
|
||||
Resistance.prototype.IsInvulnerable = function()
|
||||
{
|
||||
return this.invulnerable;
|
||||
};
|
||||
|
||||
Resistance.prototype.SetInvulnerability = function(invulnerability)
|
||||
{
|
||||
this.invulnerable = invulnerability;
|
||||
Engine.PostMessage(this.entity, MT_InvulnerabilityChanged, { "entity": this.entity, "invulnerability": invulnerability });
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the effective resistance of an entity to a particular effect.
|
||||
* ToDo: Support resistance against status effects.
|
||||
* @param {string} effectType - The type of attack effect the resistance has to be calculated for (e.g. "Damage", "Capture").
|
||||
* @return {Object} - An object of the type { "Damage": { "Crush": number, "Hack": number }, "Capture": number }.
|
||||
*/
|
||||
Resistance.prototype.GetEffectiveResistanceAgainst = function(effectType)
|
||||
{
|
||||
let ret = {};
|
||||
|
||||
let template = this.GetResistanceOfForm(Engine.QueryInterface(this.entity, IID_Foundation) ? "Foundation" : "Entity");
|
||||
if (template[effectType])
|
||||
ret[effectType] = template[effectType];
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all separate resistances for showing in the GUI.
|
||||
* @return {Object} - All resistances ordered by type.
|
||||
*/
|
||||
Resistance.prototype.GetFullResistance = function()
|
||||
{
|
||||
let ret = {};
|
||||
for (let entityForm in this.template)
|
||||
ret[entityForm] = this.GetResistanceOfForm(entityForm);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the resistance of a particular type, i.e. Foundation or Entity.
|
||||
* @param {string} entityForm - The form of the entity to query.
|
||||
* @return {Object} - An object containing the resistances.
|
||||
*/
|
||||
Resistance.prototype.GetResistanceOfForm = function(entityForm)
|
||||
{
|
||||
let ret = {};
|
||||
let template = this.template[entityForm];
|
||||
if (!template)
|
||||
return ret;
|
||||
|
||||
if (template.Damage)
|
||||
{
|
||||
ret.Damage = {};
|
||||
for (let damageType in template.Damage)
|
||||
ret.Damage[damageType] = ApplyValueModificationsToEntity("Resistance/" + entityForm + "/Damage/" + damageType, +this.template[entityForm].Damage[damageType], this.entity);
|
||||
}
|
||||
|
||||
if (template.Capture)
|
||||
ret.Capture = ApplyValueModificationsToEntity("Resistance/" + entityForm + "/Capture", +this.template[entityForm].Capture, this.entity);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Resistance, "Resistance", Resistance);
|
@ -30,7 +30,7 @@ StatusEffectsReceiver.prototype.GetActiveStatuses = function()
|
||||
*
|
||||
* @return {Object} - The names of the status effects which were processed.
|
||||
*/
|
||||
StatusEffectsReceiver.prototype.ApplyStatus = function(effectData, attacker, attackerOwner, bonusMultiplier)
|
||||
StatusEffectsReceiver.prototype.ApplyStatus = function(effectData, attacker, attackerOwner)
|
||||
{
|
||||
let attackerData = { "entity": attacker, "owner": attackerOwner };
|
||||
for (let effect in effectData)
|
||||
|
@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Message of the form { "attacker": number, "target": number, "type": string, "damage": number, "attackerOwner": number }
|
||||
* sent from Attack component and by Damage component to the target entity, each time the target is attacked or damaged.
|
||||
*/
|
||||
Engine.RegisterMessageType("Attacked");
|
@ -4,3 +4,17 @@ Engine.RegisterInterface("Resistance");
|
||||
* Message of the form { "entity": entity, "invulnerability": true/false }
|
||||
*/
|
||||
Engine.RegisterMessageType("InvulnerabilityChanged");
|
||||
|
||||
/**
|
||||
* Message of the form {
|
||||
* "type": string,
|
||||
* "target": number,
|
||||
* "attacker": attacker,
|
||||
* "attackerOwner": number,
|
||||
* "damage": number,
|
||||
* "capture": number,
|
||||
* "statusEffects": Object[],
|
||||
* "fromStatusEffect": boolean }
|
||||
* sent from Attacking.js-helper to the target entity, each time the target is damaged.
|
||||
*/
|
||||
Engine.RegisterMessageType("Attacked");
|
||||
|
@ -1,7 +1,6 @@
|
||||
Engine.LoadHelperScript("Attacking.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadHelperScript("ValueModification.js");
|
||||
Engine.LoadComponentScript("interfaces/Attack.js");
|
||||
Engine.LoadComponentScript("interfaces/Auras.js");
|
||||
Engine.LoadComponentScript("interfaces/Capturable.js");
|
||||
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
|
||||
|
@ -1,17 +1,12 @@
|
||||
Engine.LoadHelperScript("Attacking.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadHelperScript("Sound.js");
|
||||
Engine.LoadHelperScript("ValueModification.js");
|
||||
Engine.LoadComponentScript("interfaces/Attack.js");
|
||||
Engine.LoadComponentScript("interfaces/AttackDetection.js");
|
||||
Engine.LoadComponentScript("interfaces/DelayedDamage.js");
|
||||
Engine.LoadComponentScript("interfaces/Resistance.js");
|
||||
Engine.LoadComponentScript("interfaces/Health.js");
|
||||
Engine.LoadComponentScript("interfaces/Loot.js");
|
||||
Engine.LoadComponentScript("interfaces/Player.js");
|
||||
Engine.LoadComponentScript("interfaces/Promotion.js");
|
||||
Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
|
||||
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
|
||||
Engine.LoadComponentScript("interfaces/Resistance.js");
|
||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||
Engine.LoadComponentScript("Attack.js");
|
||||
Engine.LoadComponentScript("DelayedDamage.js");
|
||||
@ -89,9 +84,9 @@ function Test_Generic()
|
||||
});
|
||||
|
||||
AddMock(target, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, bonusMultiplier) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
damageTaken = true;
|
||||
return { "healthChange": -bonusMultiplier * effectData.Crush };
|
||||
return { "healthChange": -amount };
|
||||
},
|
||||
});
|
||||
|
||||
@ -136,12 +131,12 @@ function Test_Generic()
|
||||
damageTaken = false;
|
||||
}
|
||||
|
||||
Attacking.HandleAttackEffects(data.type, data.attackData, data.target, data.attacker, data.attackerOwner);
|
||||
Attacking.HandleAttackEffects(target, data.type, data.attackData, data.attacker, data.attackerOwner);
|
||||
TestDamage();
|
||||
|
||||
data.type = "Ranged";
|
||||
type = data.type;
|
||||
Attacking.HandleAttackEffects(data.type, data.attackData, data.target, data.attacker, data.attackerOwner);
|
||||
Attacking.HandleAttackEffects(target, data.type, data.attackData, data.attacker, data.attackerOwner);
|
||||
TestDamage();
|
||||
|
||||
// Check for damage still being dealt if the attacker dies
|
||||
@ -214,26 +209,26 @@ function TestLinearSplashDamage()
|
||||
});
|
||||
|
||||
AddMock(60, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(60);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(2.2, -0.4));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(2.2, -0.4));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(61, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(61);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(0, 0));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(0, 0));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(62, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(62);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 0);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 0);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -246,10 +241,10 @@ function TestLinearSplashDamage()
|
||||
data.direction = new Vector3D(0.6, 747, 0.8);
|
||||
|
||||
AddMock(60, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(60);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(1, 2));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(1, 2));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -310,36 +305,36 @@ function TestCircularSplashDamage()
|
||||
});
|
||||
|
||||
AddMock(60, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(0));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(0));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(61, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(5));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(5));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(62, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100 * fallOff(1));
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, 100 * fallOff(1));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(63, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT(false);
|
||||
}
|
||||
});
|
||||
|
||||
let cmphealth = AddMock(64, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 0);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, 0);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmphealth, "TakeDamage");
|
||||
@ -408,10 +403,10 @@ function Test_MissileHit()
|
||||
});
|
||||
|
||||
AddMock(60, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(60);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 100);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -447,9 +442,9 @@ function Test_MissileHit()
|
||||
});
|
||||
|
||||
AddMock(60, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(false);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -465,10 +460,10 @@ function Test_MissileHit()
|
||||
});
|
||||
|
||||
AddMock(61, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(61);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 100);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 100);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -493,10 +488,10 @@ function Test_MissileHit()
|
||||
|
||||
let dealtDamage = 0;
|
||||
AddMock(61, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(61);
|
||||
dealtDamage += mult * (effectData.Hack + effectData.Pierce + effectData.Crush);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
dealtDamage += amount;
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -508,10 +503,10 @@ function Test_MissileHit()
|
||||
});
|
||||
|
||||
AddMock(62, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(62);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Hack + effectData.Pierce + effectData.Crush), 200 * 0.75);
|
||||
return { "healthChange": -mult * (effectData.Hack + effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 200 * 0.75);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -589,10 +584,10 @@ function Test_MissileHit()
|
||||
|
||||
dealtDamage = 0;
|
||||
AddMock(61, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(61);
|
||||
dealtDamage += mult * (effectData.Pierce + effectData.Crush);
|
||||
return { "healthChange": -mult * (effectData.Pierce + effectData.Crush) };
|
||||
dealtDamage += amount;
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
@ -604,10 +599,10 @@ function Test_MissileHit()
|
||||
});
|
||||
|
||||
AddMock(62, IID_Health, {
|
||||
"TakeDamage": (effectData, __, ___, mult) => {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
hitEnts.add(62);
|
||||
TS_ASSERT_EQUALS(mult * (effectData.Pierce + effectData.Crush), 200 * 0.75);
|
||||
return { "healtChange": -mult * (effectData.Pierce + effectData.Crush) };
|
||||
TS_ASSERT_EQUALS(amount, 200 * 0.75);
|
||||
return { "healtChange": -amount };
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
Engine.LoadHelperScript("ObstructionSnap.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadComponentScript("interfaces/Attack.js");
|
||||
Engine.LoadComponentScript("interfaces/AlertRaiser.js");
|
||||
Engine.LoadComponentScript("interfaces/Auras.js");
|
||||
Engine.LoadComponentScript("interfaces/Barter.js");
|
||||
|
@ -0,0 +1,250 @@
|
||||
Engine.LoadHelperScript("Attacking.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadHelperScript("ValueModification.js");
|
||||
Engine.LoadComponentScript("interfaces/Capturable.js");
|
||||
Engine.LoadComponentScript("interfaces/Foundation.js");
|
||||
Engine.LoadComponentScript("interfaces/Health.js");
|
||||
Engine.LoadComponentScript("interfaces/Looter.js");
|
||||
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
|
||||
Engine.LoadComponentScript("interfaces/PlayerManager.js");
|
||||
Engine.LoadComponentScript("interfaces/Promotion.js");
|
||||
Engine.LoadComponentScript("interfaces/Resistance.js");
|
||||
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
|
||||
Engine.LoadComponentScript("Resistance.js");
|
||||
|
||||
class testResistance
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.cmpResistance = null;
|
||||
this.PlayerID = 1;
|
||||
this.EnemyID = 2;
|
||||
this.EntityID = 3;
|
||||
this.AttackerID = 4;
|
||||
}
|
||||
|
||||
Reset(schema = {})
|
||||
{
|
||||
this.cmpResistance = ConstructComponent(this.EntityID, "Resistance", schema);
|
||||
DeleteMock(this.EntityID, IID_Health);
|
||||
DeleteMock(this.EntityID, IID_Capturable);
|
||||
DeleteMock(this.EntityID, IID_Identity);
|
||||
}
|
||||
|
||||
TestInvulnerability()
|
||||
{
|
||||
this.Reset();
|
||||
|
||||
let damage = 5;
|
||||
let attackData = { "Damage": { "Name": damage } };
|
||||
let attackType = "Test";
|
||||
|
||||
TS_ASSERT(!this.cmpResistance.IsInvulnerable());
|
||||
|
||||
let cmpHealth = AddMock(this.EntityID, IID_Health, {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmpHealth, "TakeDamage");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, attackType, attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
|
||||
this.cmpResistance.SetInvulnerability(true);
|
||||
|
||||
TS_ASSERT(this.cmpResistance.IsInvulnerable());
|
||||
Attacking.HandleAttackEffects(this.EntityID, attackType, attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
}
|
||||
|
||||
TestBonus()
|
||||
{
|
||||
this.Reset();
|
||||
|
||||
let damage = 5;
|
||||
let bonus = 2;
|
||||
let classes = "Entity";
|
||||
let attackData = {
|
||||
"Damage": { "Name": damage },
|
||||
"Bonuses": {
|
||||
"bonus": {
|
||||
"Classes": classes,
|
||||
"Multiplier": bonus
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddMock(this.EntityID, IID_Identity, {
|
||||
"GetClassesList": () => [classes],
|
||||
"GetCiv": () => "civ"
|
||||
});
|
||||
|
||||
let cmpHealth = AddMock(this.EntityID, IID_Health, {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * bonus);
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmpHealth, "TakeDamage");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, "Test", attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
}
|
||||
|
||||
TestDamageResistanceApplies()
|
||||
{
|
||||
let resistanceValue = 2;
|
||||
let damageType = "Name";
|
||||
this.Reset({
|
||||
"Entity": {
|
||||
"Damage": {
|
||||
[damageType]: resistanceValue
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let damage = 5;
|
||||
let attackData = {
|
||||
"Damage": { "Name": damage }
|
||||
};
|
||||
|
||||
let cmpHealth = AddMock(this.EntityID, IID_Health, {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * Math.pow(0.9, resistanceValue));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmpHealth, "TakeDamage");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, "Test", attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
}
|
||||
|
||||
TestCaptureResistanceApplies()
|
||||
{
|
||||
let resistanceValue = 2;
|
||||
this.Reset({
|
||||
"Entity": {
|
||||
"Capture": resistanceValue
|
||||
}
|
||||
});
|
||||
|
||||
let damage = 5;
|
||||
let attackData = {
|
||||
"Capture": damage
|
||||
};
|
||||
|
||||
let cmpCapturable = AddMock(this.EntityID, IID_Capturable, {
|
||||
"Capture": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * Math.pow(0.9, resistanceValue));
|
||||
return { "captureChange": amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmpCapturable, "Capture");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, "Test", attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
}
|
||||
|
||||
TestResistanceAndBonus()
|
||||
{
|
||||
let resistanceValue = 2;
|
||||
let damageType = "Name";
|
||||
this.Reset({
|
||||
"Entity": {
|
||||
"Damage": {
|
||||
[damageType]: resistanceValue
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let damage = 5;
|
||||
let bonus = 2;
|
||||
let classes = "Entity";
|
||||
let attackData = {
|
||||
"Damage": { "Name": damage },
|
||||
"Bonuses": {
|
||||
"bonus": {
|
||||
"Classes": classes,
|
||||
"Multiplier": bonus
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddMock(this.EntityID, IID_Identity, {
|
||||
"GetClassesList": () => [classes],
|
||||
"GetCiv": () => "civ"
|
||||
});
|
||||
|
||||
let cmpHealth = AddMock(this.EntityID, IID_Health, {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * bonus * Math.pow(0.9, resistanceValue));
|
||||
return { "healthChange": -amount };
|
||||
}
|
||||
});
|
||||
let spy = new Spy(cmpHealth, "TakeDamage");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, "Test", attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(spy._called, 1);
|
||||
}
|
||||
|
||||
TestMultipleEffects()
|
||||
{
|
||||
let captureResistanceValue = 2;
|
||||
this.Reset({
|
||||
"Entity": {
|
||||
"Capture": captureResistanceValue
|
||||
}
|
||||
});
|
||||
|
||||
let damage = 5;
|
||||
let bonus = 2;
|
||||
let classes = "Entity";
|
||||
let attackData = {
|
||||
"Damage": { "Name": damage },
|
||||
"Capture": damage,
|
||||
"Bonuses": {
|
||||
"bonus": {
|
||||
"Classes": classes,
|
||||
"Multiplier": bonus
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddMock(this.EntityID, IID_Identity, {
|
||||
"GetClassesList": () => [classes],
|
||||
"GetCiv": () => "civ"
|
||||
});
|
||||
|
||||
let cmpCapturable = AddMock(this.EntityID, IID_Capturable, {
|
||||
"Capture": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * bonus * Math.pow(0.9, captureResistanceValue));
|
||||
return { "captureChange": amount };
|
||||
}
|
||||
});
|
||||
let cmpHealth = AddMock(this.EntityID, IID_Health, {
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, damage * bonus);
|
||||
return { "healthChange": -amount };
|
||||
},
|
||||
"GetHitpoints": () => 1,
|
||||
"GetMaxHitpoints": () => 1
|
||||
});
|
||||
let healthSpy = new Spy(cmpHealth, "TakeDamage");
|
||||
let captureSpy = new Spy(cmpCapturable, "Capture");
|
||||
|
||||
Attacking.HandleAttackEffects(this.EntityID, "Test", attackData, this.AttackerID, this.EnemyID);
|
||||
TS_ASSERT_EQUALS(healthSpy._called, 1);
|
||||
TS_ASSERT_EQUALS(captureSpy._called, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let cmp = new testResistance();
|
||||
cmp.TestInvulnerability();
|
||||
cmp.TestBonus();
|
||||
cmp.TestDamageResistanceApplies();
|
||||
cmp.TestCaptureResistanceApplies();
|
||||
cmp.TestResistanceAndBonus();
|
||||
cmp.TestMultipleEffects();
|
@ -2,7 +2,6 @@ Engine.LoadHelperScript("FSM.js");
|
||||
Engine.LoadHelperScript("Entity.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadHelperScript("Sound.js");
|
||||
Engine.LoadComponentScript("interfaces/Attack.js");
|
||||
Engine.LoadComponentScript("interfaces/Auras.js");
|
||||
Engine.LoadComponentScript("interfaces/Builder.js");
|
||||
Engine.LoadComponentScript("interfaces/BuildingAI.js");
|
||||
|
@ -3,9 +3,9 @@
|
||||
"radius": 70,
|
||||
"affects": ["Soldier"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.1 },
|
||||
{ "value": "Attack/Melee/Damage/Pierce", "multiply": 1.1 },
|
||||
{ "value": "Attack/Melee/Damage/Crush", "multiply": 1.1 },
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "garrisonedUnits",
|
||||
"affects": ["Soldier"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 3 },
|
||||
{ "value": "Armour/Pierce", "add": 3 },
|
||||
{ "value": "Armour/Crush", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 3 },
|
||||
{ "value": "Vision/Range", "add": 20 }
|
||||
],
|
||||
"auraName": "Wall Protection",
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "global",
|
||||
"affects": ["Melee Cavalry"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Health/Max", "multiply": 1.1 }
|
||||
],
|
||||
"auraName": "Commander of Heavy Cavalry",
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "global",
|
||||
"affects": ["Human", "Siege"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 }
|
||||
],
|
||||
"auraName": "Founder and Defender of the Republic",
|
||||
"auraDescription": "Brutus was one of the key figures in the overthrow of the monarchy and the founding of the Roman Republic. Later, as consul he led a Roman army to victory against the Etruscan King Tarquinius who sought to retake the throne.\nHumans and Siege Engines +1 armor."
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "formation",
|
||||
"affects": ["Soldier"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 3 },
|
||||
{ "value": "Armour/Pierce", "add": 3 },
|
||||
{ "value": "Armour/Crush", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 3 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 3 },
|
||||
{ "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
|
||||
],
|
||||
"auraName": "Formation Reforms",
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "global",
|
||||
"affects": ["Soldier", "Siege"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
|
||||
],
|
||||
"auraName": "Guerrilla Chief",
|
||||
|
@ -3,9 +3,9 @@
|
||||
"radius": 50,
|
||||
"affects": ["Soldier"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 }
|
||||
],
|
||||
"auraDescription": "Soldiers +1 armor.",
|
||||
"auraName": "Battle Fervor",
|
||||
|
@ -3,9 +3,9 @@
|
||||
"radius": 60,
|
||||
"affects": ["Siege"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.2 },
|
||||
{ "value": "Attack/Melee/Damage/Pierce", "multiply": 1.2 },
|
||||
{ "value": "Attack/Melee/Damage/Crush", "multiply": 1.2 },
|
||||
|
@ -2,9 +2,9 @@
|
||||
"type": "global",
|
||||
"affects": ["Human", "Structure"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 }
|
||||
],
|
||||
"auraName": "Shield of Rome",
|
||||
"auraDescription": "Humans and Structures +1 armor."
|
||||
|
@ -3,9 +3,9 @@
|
||||
"radius": 45,
|
||||
"affects": ["Cavalry"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Armour/Crush", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 2 }
|
||||
],
|
||||
"auraName": "Ilarchès",
|
||||
"auraDescription": "Cavalry +2 armor."
|
||||
|
@ -3,9 +3,9 @@
|
||||
"radius": 60,
|
||||
"affects": ["Citizen Infantry Javelineer"],
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Attack/Ranged/Damage/Pierce", "multiply": 1.25 }
|
||||
],
|
||||
"auraName": "Helot Reforms",
|
||||
|
@ -15,8 +15,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Cavalry +1 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 }
|
||||
],
|
||||
"affects": ["Cavalry"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -9,8 +9,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Cavalry +1 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 }
|
||||
],
|
||||
"affects": ["Cavalry"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -16,8 +16,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Heroes +50 metal cost, +2 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Cost/Resources/metal", "add": 50 }
|
||||
],
|
||||
"affects": ["Hero"],
|
||||
|
@ -21,8 +21,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Infantry +1 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 }
|
||||
],
|
||||
"affects": ["Infantry"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -19,8 +19,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Infantry +1 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 }
|
||||
],
|
||||
"affects": ["Infantry"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -9,9 +9,9 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Ships +2 armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Armour/Crush", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 2 }
|
||||
],
|
||||
"affects": ["Ship"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -9,9 +9,9 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Ships +2 armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Armour/Crush", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 2 }
|
||||
],
|
||||
"affects": ["Ship"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -8,9 +8,9 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Ships +2 armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Armour/Crush", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 2 }
|
||||
],
|
||||
"affects": ["Ship"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -8,7 +8,7 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Siege Engines +2 hack armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 }
|
||||
],
|
||||
"affects": ["Siege"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -8,9 +8,9 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Towers +2 armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 },
|
||||
{ "value": "Armour/Crush", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 2 }
|
||||
],
|
||||
"affects": ["Tower"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -9,8 +9,8 @@
|
||||
"researchTime": 40,
|
||||
"tooltip": "Traders +2 hack and pierce armor.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 2 },
|
||||
{ "value": "Armour/Pierce", "add": 2 }
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 2 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 2 }
|
||||
],
|
||||
"affects": ["Trader"],
|
||||
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
|
||||
|
@ -3,9 +3,9 @@
|
||||
"icon": "upgrade_advanced.png",
|
||||
"tooltip": "Advanced and Elite units +20% training time, +1 armor, +10% health, +0.7 capture attack strength, +20% loot, and −30% gather speed; Healers +5 healing strength and +3 healing range; Melee units +20% attack damage; Ranged units +4 attack range and −10% spread.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Attack/Capture/Capture", "add": 0.7 },
|
||||
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.2, "affects": "Melee" },
|
||||
{ "value": "Attack/Melee/Damage/Pierce", "multiply": 1.2, "affects": "Melee" },
|
||||
|
@ -3,9 +3,9 @@
|
||||
"icon": "upgrade_elite.png",
|
||||
"tooltip": "Elite units +20% training time, +1 armor, +10% health, +0.8 capture attack strength, +20% loot, and −30% gather speed; Healers +5 healing strength and +3 healing range; Melee units +20% attack damage; Ranged units +4 attack range and −10% spread.",
|
||||
"modifications": [
|
||||
{ "value": "Armour/Hack", "add": 1 },
|
||||
{ "value": "Armour/Pierce", "add": 1 },
|
||||
{ "value": "Armour/Crush", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Hack", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Pierce", "add": 1 },
|
||||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 },
|
||||
{ "value": "Attack/Capture/Capture", "add": 0.8 },
|
||||
{ "value": "Attack/Melee/Damage/Hack", "multiply": 1.2, "affects": "Melee" },
|
||||
{ "value": "Attack/Melee/Damage/Pierce", "multiply": 1.2, "affects": "Melee" },
|
||||
|
@ -7,10 +7,7 @@ const DirectEffectsSchema =
|
||||
"<element name='Damage'>" +
|
||||
"<oneOrMore>" +
|
||||
"<element a:help='One or more elements describing damage types'>" +
|
||||
"<anyName>" +
|
||||
// Armour requires Foundation to not be a damage type.
|
||||
"<except><name>Foundation</name></except>" +
|
||||
"</anyName>" +
|
||||
"<anyName/>" +
|
||||
"<ref name='nonNegativeDecimal' />" +
|
||||
"</element>" +
|
||||
"</oneOrMore>" +
|
||||
@ -168,15 +165,41 @@ Attacking.prototype.GetStatusEffectsModifications = function(valueModifRoot, tem
|
||||
return modifiers;
|
||||
};
|
||||
|
||||
Attacking.prototype.GetTotalAttackEffects = function(effectData, effectType, cmpResistance)
|
||||
/**
|
||||
* Calculate the total effect taking bonus and resistance into account.
|
||||
*
|
||||
* @param {number} target - The target of the attack.
|
||||
* @param {Object} effectData - The effects calculate the effect for.
|
||||
* @param {string} effectType - The type of effect to apply (e.g. Damage, Capture or StatusEffect).
|
||||
* @param {number} bonusMultiplier - The factor to multiply the total effect with.
|
||||
* @param {Object} cmpResistance - Optionally the resistance component of the target.
|
||||
*
|
||||
* @return {number} - The total value of the effect.
|
||||
*/
|
||||
Attacking.prototype.GetTotalAttackEffects = function(target, effectData, effectType, bonusMultiplier, cmpResistance)
|
||||
{
|
||||
let total = 0;
|
||||
let armourStrengths = cmpResistance ? cmpResistance.GetArmourStrengths(effectType) : {};
|
||||
if (!cmpResistance)
|
||||
cmpResistance = Engine.QueryInterface(target, IID_Resistance);
|
||||
|
||||
for (let type in effectData)
|
||||
total += effectData[type] * Math.pow(0.9, armourStrengths[type] || 0);
|
||||
let resistanceStrengths = cmpResistance ? cmpResistance.GetEffectiveResistanceAgainst(effectType) : {};
|
||||
|
||||
return total;
|
||||
if (effectType == "Damage")
|
||||
for (let type in effectData.Damage)
|
||||
total += effectData.Damage[type] * Math.pow(0.9, resistanceStrengths.Damage ? resistanceStrengths.Damage[type] || 0 : 0);
|
||||
else if (effectType == "Capture")
|
||||
{
|
||||
total = effectData.Capture * Math.pow(0.9, resistanceStrengths.Capture || 0);
|
||||
|
||||
// If Health is lower we are more susceptible to capture attacks.
|
||||
let cmpHealth = Engine.QueryInterface(target, IID_Health);
|
||||
if (cmpHealth)
|
||||
total /= 0.1 + 0.9 * cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints();
|
||||
}
|
||||
else if (effectType == "StatusEffect")
|
||||
return effectData[effectType];
|
||||
|
||||
return total * bonusMultiplier;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -302,12 +325,27 @@ Attacking.prototype.CauseDamageOverArea = function(data)
|
||||
warn("The " + data.shape + " splash damage shape is not implemented!");
|
||||
}
|
||||
|
||||
this.HandleAttackEffects(data.type + ".Splash", data.attackData, ent, data.attacker, data.attackerOwner, damageMultiplier);
|
||||
this.HandleAttackEffects(ent, data.type + ".Splash", data.attackData, data.attacker, data.attackerOwner, damageMultiplier);
|
||||
}
|
||||
};
|
||||
|
||||
Attacking.prototype.HandleAttackEffects = function(attackType, attackData, target, attacker, attackerOwner, bonusMultiplier = 1)
|
||||
/**
|
||||
* Handle an attack peformed on an entity.
|
||||
*
|
||||
* @param {number} target - The targetted entityID.
|
||||
* @param {string} attackType - The type of attack that was performed (e.g. "Melee" or "Capture").
|
||||
* @param {Object} effectData - The effects use.
|
||||
* @param {number} attacker - The entityID that attacked us.
|
||||
* @param {number} attackerOwner - The playerID that owned the attacker when the attack was performed.
|
||||
* @param {number} bonusMultiplier - The factor to multiply the total effect with, defaults to 1.
|
||||
*
|
||||
* @return {boolean} - Whether we handled the attack.
|
||||
*/
|
||||
Attacking.prototype.HandleAttackEffects = function(target, attackType, attackData, attacker, attackerOwner, bonusMultiplier = 1)
|
||||
{
|
||||
let cmpResistance = Engine.QueryInterface(target, IID_Resistance);
|
||||
if (cmpResistance && cmpResistance.IsInvulnerable())
|
||||
return false;
|
||||
|
||||
bonusMultiplier *= !attackData.Bonuses ? 1 : this.GetAttackBonus(attacker, target, attackType, attackData.Bonuses);
|
||||
|
||||
let targetState = {};
|
||||
@ -321,10 +359,11 @@ Attacking.prototype.HandleAttackEffects = function(attackType, attackData, targe
|
||||
if (!cmpReceiver)
|
||||
continue;
|
||||
|
||||
Object.assign(targetState, cmpReceiver[receiver.method](attackData[effectType], attacker, attackerOwner, bonusMultiplier));
|
||||
Object.assign(targetState, cmpReceiver[receiver.method](this.GetTotalAttackEffects(target, attackData, effectType, bonusMultiplier, cmpResistance), attacker, attackerOwner));
|
||||
}
|
||||
|
||||
if (!Object.keys(targetState).length)
|
||||
return;
|
||||
return false;
|
||||
|
||||
Engine.PostMessage(target, MT_Attacked, {
|
||||
"type": attackType,
|
||||
@ -339,11 +378,13 @@ Attacking.prototype.HandleAttackEffects = function(attackType, attackData, targe
|
||||
|
||||
// We do not want an entity to get XP from active Status Effects.
|
||||
if (!!attackData.StatusEffect)
|
||||
return;
|
||||
return true;
|
||||
|
||||
let cmpPromotion = Engine.QueryInterface(attacker, IID_Promotion);
|
||||
if (cmpPromotion && targetState.xp)
|
||||
cmpPromotion.IncreaseXp(targetState.xp);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
36
binaries/data/mods/public/simulation/helpers/tests/test_Attacking.js
Executable file → Normal file
36
binaries/data/mods/public/simulation/helpers/tests/test_Attacking.js
Executable file → Normal file
@ -1,8 +1,8 @@
|
||||
Engine.LoadHelperScript("Attacking.js");
|
||||
Engine.LoadComponentScript("interfaces/Attack.js");
|
||||
Engine.LoadComponentScript("interfaces/Capturable.js");
|
||||
Engine.LoadComponentScript("interfaces/Health.js");
|
||||
Engine.LoadComponentScript("interfaces/Promotion.js");
|
||||
Engine.LoadComponentScript("interfaces/Resistance.js");
|
||||
|
||||
// Unit tests for the Attacking helper.
|
||||
// TODO: Some of it is tested in components/test_Damage.js, which should be spliced and moved.
|
||||
@ -13,8 +13,8 @@ class testHandleAttackEffects {
|
||||
this.TESTED_ENTITY_ID = 5;
|
||||
|
||||
this.attackData = {
|
||||
"Damage": "Uniquely Hashed Value",
|
||||
"Capture": "Something Else Entirely",
|
||||
"Damage": "1",
|
||||
"Capture": "2"
|
||||
};
|
||||
}
|
||||
|
||||
@ -24,13 +24,15 @@ class testHandleAttackEffects {
|
||||
testMultipleEffects() {
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Health, {
|
||||
"TakeDamage": x => { this.resultString += x; },
|
||||
"GetHitpoints": () => 1,
|
||||
"GetMaxHitpoints": () => 1,
|
||||
});
|
||||
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Capturable, {
|
||||
"Capture": x => { this.resultString += x; },
|
||||
});
|
||||
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Damage) !== -1);
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Capture) !== -1);
|
||||
@ -44,7 +46,7 @@ class testHandleAttackEffects {
|
||||
"Capture": x => { this.resultString += x; },
|
||||
});
|
||||
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Damage) === -1);
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Capture) !== -1);
|
||||
|
||||
@ -52,9 +54,11 @@ class testHandleAttackEffects {
|
||||
DeleteMock(this.TESTED_ENTITY_ID, IID_Capturable);
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Health, {
|
||||
"TakeDamage": x => { this.resultString += x; },
|
||||
"GetHitpoints": () => 1,
|
||||
"GetMaxHitpoints": () => 1,
|
||||
});
|
||||
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Damage) !== -1);
|
||||
TS_ASSERT(this.resultString.indexOf(this.attackData.Capture) === -1);
|
||||
}
|
||||
@ -64,22 +68,24 @@ class testHandleAttackEffects {
|
||||
*/
|
||||
testAttackedMessage() {
|
||||
Engine.PostMessage = () => TS_ASSERT(false);
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Capturable, {
|
||||
"Capture": () => ({ "captureChange": 0 }),
|
||||
});
|
||||
let count = 0;
|
||||
Engine.PostMessage = () => count++;
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
TS_ASSERT_EQUALS(count, 1);
|
||||
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Health, {
|
||||
"TakeDamage": () => ({ "healthChange": 0 }),
|
||||
"GetHitpoints": () => 1,
|
||||
"GetMaxHitpoints": () => 1,
|
||||
});
|
||||
count = 0;
|
||||
Engine.PostMessage = () => count++;
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER);
|
||||
TS_ASSERT_EQUALS(count, 1);
|
||||
}
|
||||
|
||||
@ -88,13 +94,19 @@ class testHandleAttackEffects {
|
||||
*/
|
||||
testBonusMultiplier() {
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Health, {
|
||||
"TakeDamage": (_, __, ___, mult) => { TS_ASSERT_EQUALS(mult, 2); },
|
||||
"TakeDamage": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, this.attackData.Damage * 2);
|
||||
},
|
||||
"GetHitpoints": () => 1,
|
||||
"GetMaxHitpoints": () => 1,
|
||||
});
|
||||
AddMock(this.TESTED_ENTITY_ID, IID_Capturable, {
|
||||
"Capture": (_, __, ___, mult) => { TS_ASSERT_EQUALS(mult, 2); },
|
||||
"Capture": (amount, __, ___) => {
|
||||
TS_ASSERT_EQUALS(amount, this.attackData.Capture * 2);
|
||||
},
|
||||
});
|
||||
|
||||
Attacking.HandleAttackEffects("Test", this.attackData, this.TESTED_ENTITY_ID, INVALID_ENTITY, INVALID_PLAYER, 2);
|
||||
Attacking.HandleAttackEffects(this.TESTED_ENTITY_ID, "Test", this.attackData, INVALID_ENTITY, INVALID_PLAYER, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_hero_infantry_swordsman">
|
||||
<Armour>
|
||||
<Crush>20</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Crush>20</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<MaxRange>7.0</MaxRange>
|
||||
|
@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_hero_infantry_swordsman">
|
||||
<Armour>
|
||||
<Crush>20</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Crush>20</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<MaxRange>7.0</MaxRange>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_fauna_hunt_aggressive">
|
||||
<Armour>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>4</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>4</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_fauna_hunt_defensive">
|
||||
<Armour>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>4</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>4</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Fence</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Fence</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity filtered="">
|
||||
<AIProxy merge=""/>
|
||||
<Armour merge=""/>
|
||||
<Resistance merge=""/>
|
||||
<AutoBuildable merge=""/>
|
||||
<BuildRestrictions merge=""/>
|
||||
<!-- Don't provide population bonuses yet (but still do take up population cost) -->
|
||||
|
@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_special">
|
||||
<Armour>
|
||||
<Pierce>35</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Pierce>35</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Auras datatype="tokens">
|
||||
structures/cart_super_dock_repair
|
||||
</Auras>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_special">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>2</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Damage>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_wall_gate">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral enemy</Territory>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_wall_long">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral enemy</Territory>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_wall_medium">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral enemy</Territory>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_wall_short">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral enemy</Territory>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_wall_tower">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>7</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral enemy</Territory>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,16 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity>
|
||||
<AIProxy/>
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>0</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>0</GarrisonArrowMultiplier>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Identity>
|
||||
<Classes datatype="tokens">ConquestCritical</Classes>
|
||||
<GenericName>Civic Structure</GenericName>
|
||||
|
@ -6,15 +6,21 @@
|
||||
<EndOfAlertRange>190</EndOfAlertRange>
|
||||
<SearchRange>100</SearchRange>
|
||||
</AlertRaiser>
|
||||
<Armour>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>15</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>15</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Damage>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>25</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>25</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Identity>
|
||||
<GenericName>Defensive Structure</GenericName>
|
||||
<VisibleClasses datatype="tokens">Defensive</VisibleClasses>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive">
|
||||
<Armour>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<PlacementType>land-shore</PlacementType>
|
||||
<Category>Wall</Category>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_tower">
|
||||
<Armour>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Damage>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_tower">
|
||||
<Armour>
|
||||
<Hack op="add">-5</Hack>
|
||||
<Pierce op="add">-5</Pierce>
|
||||
<Crush op="add">-2</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">-5</Hack>
|
||||
<Pierce op="add">-5</Pierce>
|
||||
<Crush op="add">-2</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Damage>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>5</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Identity>
|
||||
<GenericName>Economic Structure</GenericName>
|
||||
<VisibleClasses datatype="tokens">Economic</VisibleClasses>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>35</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Military</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,13 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_military">
|
||||
<Armour>
|
||||
<Pierce>30</Pierce>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Pierce>30</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Embassy</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_military">
|
||||
<Armour>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
<Crush op="add">3</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
<Crush op="add">3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Damage>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>20</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>1</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Resource</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_resource">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>40</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>40</Pierce>
|
||||
<Crush>5</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Field</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>20</Hack>
|
||||
<Pierce>30</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<BuildRestrictions>
|
||||
<Category>Special</Category>
|
||||
</BuildRestrictions>
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure">
|
||||
<Armour>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>3</Crush>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>15</Hack>
|
||||
<Pierce>25</Pierce>
|
||||
<Crush>3</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
<Foundation>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
<Damage>
|
||||
<Hack>2</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>2</Crush>
|
||||
</Damage>
|
||||
</Foundation>
|
||||
</Armour>
|
||||
</Resistance>
|
||||
<Auras datatype="tokens">
|
||||
structures/wonder_pop_1
|
||||
structures/wonder_pop_2
|
||||
|
@ -1,11 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity>
|
||||
<AIProxy/>
|
||||
<Armour>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>15</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>1</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>15</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Cost>
|
||||
<Population>1</Population>
|
||||
<PopulationBonus>0</PopulationBonus>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit">
|
||||
<Armour>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>15</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>1</Pierce>
|
||||
<Crush>15</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Capture>
|
||||
<Capture>2.5</Capture>
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_cavalry_melee">
|
||||
<Armour>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>2</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>3</Hack>
|
||||
<Pierce>2</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_cavalry_melee">
|
||||
<Armour>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>3</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>3</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_cavalry_melee">
|
||||
<Armour>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>2</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>4</Hack>
|
||||
<Pierce>2</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_champion">
|
||||
<Armour>
|
||||
<Hack>7</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>20</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>7</Hack>
|
||||
<Pierce>5</Pierce>
|
||||
<Crush>20</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Cost>
|
||||
<Population>1</Population>
|
||||
<BuildTime>30</BuildTime>
|
||||
|
@ -1,8 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_champion_cavalry">
|
||||
<Armour>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_champion_cavalry">
|
||||
<Armour>
|
||||
<Hack op="add">1</Hack>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">1</Hack>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,9 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_champion_cavalry">
|
||||
<Armour>
|
||||
<Hack op="add">1</Hack>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">1</Hack>
|
||||
<Pierce op="add">2</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Attack>
|
||||
<Melee>
|
||||
<Damage>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_unit_champion">
|
||||
<Armour>
|
||||
<Hack>10</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>25</Crush>
|
||||
</Armour>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack>10</Hack>
|
||||
<Pierce>10</Pierce>
|
||||
<Crush>25</Crush>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<Cost>
|
||||
<Population>3</Population>
|
||||
<BuildTime>30</BuildTime>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user