Changeset View
Standalone View
binaries/data/mods/public/simulation/components/Resistance.js
- This file was moved from binaries/data/mods/public/simulation/components/Armour.js.
function Armour() {} | function Resistance() {} | |||||||||
bb: I have noticed some sim components have been implemented in class syntax, however I have my… | ||||||||||
Armour.prototype.DamageResistanceSchema = "" + | /** | |||||||||
* Builds a RelaxRNG schema of possible attack effects. | ||||||||||
* ToDo: Resistance to StatusEffects. | ||||||||||
* | ||||||||||
Done Inline ActionsWhy the tab? bb: Why the tab? | ||||||||||
* @return {string} - RelaxNG schema string. | ||||||||||
*/ | ||||||||||
Resistance.prototype.BuildResistanceSchema = function() | ||||||||||
{ | ||||||||||
return "" + | ||||||||||
"<oneOrMore>" + | ||||||||||
"<choice>" + | ||||||||||
"<element name='Damage'>" + | ||||||||||
"<oneOrMore>" + | "<oneOrMore>" + | |||||||||
Done Inline ActionsShould not happen often, but one might not want to send a message if it didn't change? As done because it's likely out of scope. Stan: Should not happen often, but one might not want to send a message if it didn't change? As done… | ||||||||||
"<element a:help='Resistance against any number of damage types'>" + | "<element a:help='Resistance against any number of damage types affecting health.'>" + | |||||||||
"<anyName>" + | "<anyName/>" + | |||||||||
"<except><name>Foundation</name></except>" + | "<ref name='nonNegativeDecimal'/>" + | |||||||||
"</anyName>" + | "</element>" + | |||||||||
"</oneOrMore>" + | ||||||||||
"</element>" + | ||||||||||
"<element name='Capture' a:help='Resistance against Capture attacks.'>" + | ||||||||||
Done Inline Actionsperiod bb: period | ||||||||||
"<ref name='nonNegativeDecimal' />" + | "<ref name='nonNegativeDecimal'/>" + | |||||||||
"</element>" + | "</element>" + | |||||||||
"</choice>" + | ||||||||||
"</oneOrMore>"; | "</oneOrMore>"; | |||||||||
}; | ||||||||||
Armour.prototype.Schema = | Resistance.prototype.Schema = | |||||||||
"<a:help>Controls the damage resistance of the unit.</a:help>" + | "<a:help>Controls the damage resistance of the unit.</a:help>" + | |||||||||
"<a:example>" + | "<a:example>" + | |||||||||
"<Foundation>" + | ||||||||||
"<Damage>" + | ||||||||||
Done Inline ActionsSince strength can be inlined, applyMods is a oneliner which is used only once => double inline bb: Since `strength` can be inlined, `applyMods` is a oneliner which is used only once => double… | ||||||||||
"<Hack>10.0</Hack>" + | "<Hack>10.0</Hack>" + | |||||||||
"<Pierce>0.0</Pierce>" + | "<Pierce>0.0</Pierce>" + | |||||||||
"<Crush>5.0</Crush>" + | "<Crush>5.0</Crush>" + | |||||||||
"</Damage>" + | ||||||||||
"<Capture>10</Capture>" + | ||||||||||
"</Foundation>" + | ||||||||||
"<Entity>" + | ||||||||||
"<Damage>" + | ||||||||||
"<Poison>5</Poison>" + | ||||||||||
"</Damage>" + | ||||||||||
"</Entity>" + | ||||||||||
"</a:example>" + | "</a:example>" + | |||||||||
Armour.prototype.DamageResistanceSchema + | "<zeroOrMore>" + | |||||||||
"<optional>" + | "<choice>" + | |||||||||
"<element name='Foundation' a:help='Armour given to building foundations'>" + | "<element name='Foundation' a:help='Resistance of an unfinished structure (i.e. a foundation).'>" + | |||||||||
Done Inline ActionsSplit from attacking, make own schema here. Freagarach: Split from attacking, make own schema here. | ||||||||||
Armour.prototype.DamageResistanceSchema + | Resistance.prototype.BuildResistanceSchema() + | |||||||||
"</element>" + | "</element>" + | |||||||||
"</optional>"; | "<element name='Entity' a:help='Resistance of an entity.'>" + | |||||||||
Resistance.prototype.BuildResistanceSchema() + | ||||||||||
"</element>" + | ||||||||||
"</choice>" + | ||||||||||
Done Inline ActionsUnderstand why, you do it like this. But why do we need the loop here? Seems like we can access the correct form immediately bb: Understand why, you do it like this. But why do we need the loop here? Seems like we can access… | ||||||||||
"</zeroOrMore>"; | ||||||||||
Armour.prototype.Init = function() | Resistance.prototype.Init = function() | |||||||||
{ | { | |||||||||
this.invulnerable = false; | this.invulnerable = false; | |||||||||
}; | }; | |||||||||
Armour.prototype.IsInvulnerable = function() | Resistance.prototype.IsInvulnerable = function() | |||||||||
Done Inline Actions!#R@$@@!$@$%@ but that is probably just my opinion bb: !#R@$@@!$@$%@ but that is probably just my opinion | ||||||||||
{ | { | |||||||||
return this.invulnerable; | return this.invulnerable; | |||||||||
}; | }; | |||||||||
Armour.prototype.SetInvulnerability = function(invulnerability) | Resistance.prototype.SetInvulnerability = function(invulnerability) | |||||||||
{ | { | |||||||||
this.invulnerable = invulnerability; | this.invulnerable = invulnerability; | |||||||||
Engine.PostMessage(this.entity, MT_InvulnerabilityChanged, { "entity": this.entity, "invulnerability": invulnerability }); | Engine.PostMessage(this.entity, MT_InvulnerabilityChanged, { "entity": this.entity, "invulnerability": invulnerability }); | |||||||||
}; | }; | |||||||||
Done Inline ActionsI really fail to see the purpose of having several resistanceTypes, yes we should have foundation and 1 other, but more doesn't make sense right? We seem to simply add up all the values, so this doesn't give any new functionality in the template. (K you gain a very small amount of flexibility with techs and auras, but I don't think we need that) bb: I really fail to see the purpose of having several resistanceTypes, yes we should have… | ||||||||||
Not Done Inline ActionsI suppose these need to generalized somewhat too bb: I suppose these need to generalized somewhat too | ||||||||||
Not Done Inline ActionsEach receiving cmp could have their own implementation and one generalised one here? Freagarach: Each receiving cmp could have their own implementation and one generalised one here? | ||||||||||
Not Done Inline ActionsEach attackType should get their own here. A general one doesn't seem useful than anymore (since one can set all individually) bb: Each attackType should get their own here. A general one doesn't seem useful than anymore… | ||||||||||
Armour.prototype.GetArmourStrengths = function(effectType) | /** | |||||||||
{ | * Calculate the effective resistance of an entity to a particular effect. | |||||||||
// Work out the armour values with technology effects. | * ToDo: Support resistance against status effects. | |||||||||
let applyMods = (type, foundation) => { | * @param {string} effectType - The type of attack effect the resistance has to be calculated for (e.g. "Damage", "Capture"). | |||||||||
let strength; | * @return {object} - An object of the type { "Damage": { "Crush": 10, "Hack": 2 }, "Capture": 2 }. | |||||||||
Done Inline ActionsChange. Freagarach: Change. | ||||||||||
Done Inline Actions
bb: | ||||||||||
if (foundation) | */ | |||||||||
Resistance.prototype.GetEffectiveResistanceAgainst = function(effectType) | ||||||||||
{ | { | |||||||||
strength = +this.template.Foundation[type]; | let ret = {}; | |||||||||
type = "Foundation/" + type; | ||||||||||
} | ||||||||||
else | ||||||||||
strength = +this.template[type]; | ||||||||||
return ApplyValueModificationsToEntity("Armour/" + type, strength, this.entity); | let entityForm = Engine.QueryInterface(this.entity, IID_Foundation) ? "Foundation" : "Entity"; | |||||||||
}; | let template = this.GetResistanceOfForm(entityForm); | |||||||||
if (template[effectType]) | ||||||||||
ret[effectType] = template[effectType]; | ||||||||||
let foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation; | return ret; | |||||||||
}; | ||||||||||
/** | ||||||||||
* Get all separate resistances for showing in the GUI. | ||||||||||
* @return {Object} - All resistances ordered by type. | ||||||||||
*/ | ||||||||||
Resistance.prototype.GetFullResistance = function() | ||||||||||
{ | ||||||||||
let ret = {}; | let ret = {}; | |||||||||
for (let entityForm in this.template) | ||||||||||
ret[entityForm] = this.GetResistanceOfForm(entityForm); | ||||||||||
if (effectType != "Damage") | ||||||||||
return ret; | return ret; | |||||||||
}; | ||||||||||
for (let damageType in this.template) | /** | |||||||||
if (damageType != "Foundation") | * Get the resistance of a particular type, i.e. Foundation or Entity. | |||||||||
Done Inline ActionsI guess we should remove any reference that damagetypes may not be foundations bb: I guess we should remove any reference that damagetypes may not be foundations | ||||||||||
ret[damageType] = applyMods(damageType, foundation); | * @param {String} entityForm - The form of the entity to query. | |||||||||
Done Inline Actions
(yes, we should teach this to the linter) bb: (yes, we should teach this to the linter) | ||||||||||
* @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 = {}; | ||||||||||
Done Inline ActionsI always wonder if merging early continues is faster ? Stan: I always wonder if merging early continues is faster ? | ||||||||||
Done Inline ActionsLess readable. Freagarach: Less readable. | ||||||||||
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); | ||||||||||
Done Inline Actionsif !template continue ? Stan: if !template continue ? | ||||||||||
return ret; | return ret; | |||||||||
}; | }; | |||||||||
Engine.RegisterComponentType(IID_Resistance, "Armour", Armour); | Resistance.prototype.GetTotalAttackEffects = function(effectData, effectType, bonusMultiplier) | |||||||||
{ | ||||||||||
Done Inline Actionsperiod bb: period | ||||||||||
let total = 0; | ||||||||||
Done Inline Actionstab? bb: tab? | ||||||||||
let resistanceStrengths = this.GetEffectiveResistanceAgainst(effectType); | ||||||||||
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); | ||||||||||
Done Inline ActionsoneOrMore is correct over zeroOrMore since not having a damage note is preferred over zeroOrMore bb: oneOrMore is correct over zeroOrMore since not having a damage note is preferred over zeroOrMore | ||||||||||
Done Inline ActionsA foundation should have the same armour schema available (except that it can have a foundation). This is violated in the current proposed schema bb: A foundation should have the same armour schema available (except that it can have a… | ||||||||||
// Health dependancy. | ||||||||||
StanUnsubmitted Done Inline Actionsdependency Stan: dependency | ||||||||||
let cmpHealth = Engine.QueryInterface(this.entity, IID_Health); | ||||||||||
Done Inline Actionsspace? bb: space? | ||||||||||
if (cmpHealth) | ||||||||||
total /= 0.1 + 0.9 * cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints(); | ||||||||||
} | ||||||||||
else if (effectType == "StatusEffect") | ||||||||||
Done Inline ActionsNot implemented yet, so shouldn't be in the help yet bb: Not implemented yet, so shouldn't be in the help yet | ||||||||||
return effectData[effectType]; | ||||||||||
return total * bonusMultiplier; | ||||||||||
}; | ||||||||||
Resistance.prototype.HandleAttackEffects = function(attackType, attackData, attacker, attackerOwner, bonusMultiplier = 1) | ||||||||||
{ | ||||||||||
if (this.IsInvulnerable()) | ||||||||||
return; | ||||||||||
bonusMultiplier *= !attackData.Bonuses ? 1 : GetAttackBonus(attacker, this.entity, attackType, attackData.Bonuses); | ||||||||||
let targetState = {}; | ||||||||||
for (let effectType of g_EffectTypes) | ||||||||||
{ | ||||||||||
if (!attackData[effectType]) | ||||||||||
continue; | ||||||||||
let receiver = g_EffectReceiver[effectType]; | ||||||||||
let cmpReceiver = Engine.QueryInterface(this.entity, global[receiver.IID]); | ||||||||||
if (!cmpReceiver) | ||||||||||
continue; | ||||||||||
Object.assign(targetState, cmpReceiver[receiver.method](this.GetTotalAttackEffects(attackData, effectType, bonusMultiplier), attacker, attackerOwner)); | ||||||||||
} | ||||||||||
if (!Object.keys(targetState).length) | ||||||||||
return; | ||||||||||
if (targetState.killed) | ||||||||||
this.TargetKilled(attacker, this.entity, attackerOwner); | ||||||||||
Engine.PostMessage(this.entity, MT_Attacked, { | ||||||||||
"type": attackType, | ||||||||||
"target": this.entity, | ||||||||||
"attacker": attacker, | ||||||||||
"attackerOwner": attackerOwner, | ||||||||||
"damage": -(targetState.HPchange || 0), | ||||||||||
"capture": targetState.captureChange || 0, | ||||||||||
"statusEffects": targetState.inflictedStatuses || [], | ||||||||||
"fromStatusEffect": !!attackData.StatusEffect, | ||||||||||
}); | ||||||||||
// We do not want an entity to get XP from active Status Effects. | ||||||||||
if (!!attackData.StatusEffect) | ||||||||||
return; | ||||||||||
let cmpPromotion = Engine.QueryInterface(attacker, IID_Promotion); | ||||||||||
if (cmpPromotion && targetState.xp) | ||||||||||
cmpPromotion.IncreaseXp(targetState.xp); | ||||||||||
}; | ||||||||||
/** | ||||||||||
* Called when a unit kills something (another unit, building, animal etc). | ||||||||||
* @param {number} attacker - The entity id of the killer. | ||||||||||
* @param {number} target - The entity id of the target. | ||||||||||
* @param {number} attackerOwner - The player id of the attacker. | ||||||||||
*/ | ||||||||||
Resistance.prototype.TargetKilled = function(attacker, attackerOwner) | ||||||||||
{ | ||||||||||
let cmpAttackerOwnership = Engine.QueryInterface(attacker, IID_Ownership); | ||||||||||
let atkOwner = cmpAttackerOwnership && cmpAttackerOwnership.GetOwner() != INVALID_PLAYER ? cmpAttackerOwnership.GetOwner() : attackerOwner; | ||||||||||
// Add to killer statistics. | ||||||||||
let cmpKillerPlayerStatisticsTracker = QueryPlayerIDInterface(atkOwner, IID_StatisticsTracker); | ||||||||||
if (cmpKillerPlayerStatisticsTracker) | ||||||||||
cmpKillerPlayerStatisticsTracker.KilledEntity(this.entity); | ||||||||||
// Add to loser statistics. | ||||||||||
let cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); | ||||||||||
if (cmpTargetPlayerStatisticsTracker) | ||||||||||
cmpTargetPlayerStatisticsTracker.LostEntity(this.entity); | ||||||||||
// If killer can collect loot, let's try to collect it. | ||||||||||
let cmpLooter = Engine.QueryInterface(attacker, IID_Looter); | ||||||||||
if (cmpLooter) | ||||||||||
cmpLooter.Collect(this.entity); | ||||||||||
Done Inline ActionsSince this is just a health-thing, it can perhaps be moved thereto? Freagarach: Since this is just a health-thing, it can perhaps be moved thereto? | ||||||||||
Done Inline ActionsDidn't it use to be there? I think it's better in Health yeah. wraitii: Didn't it use to be there? I think it's better in Health yeah. | ||||||||||
Done Inline ActionsFreagarach: D2941. | ||||||||||
}; | ||||||||||
Engine.RegisterComponentType(IID_Resistance, "Resistance", Resistance); |
I have noticed some sim components have been implemented in class syntax, however I have my doubts about it. I certainly won't start rewriting stuff into class syntax.