Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/components/Attack.js
Show First 20 Lines • Show All 426 Lines • ▼ Show 20 Lines | Attack.prototype.GetRange = function(type) | ||||
min = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity); | min = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity); | ||||
let elevationBonus = +(this.template[type].ElevationBonus || 0); | let elevationBonus = +(this.template[type].ElevationBonus || 0); | ||||
elevationBonus = ApplyValueModificationsToEntity("Attack/" + type + "/ElevationBonus", elevationBonus, this.entity); | elevationBonus = ApplyValueModificationsToEntity("Attack/" + type + "/ElevationBonus", elevationBonus, this.entity); | ||||
return { "max": max, "min": min, "elevationBonus": elevationBonus }; | return { "max": max, "min": min, "elevationBonus": elevationBonus }; | ||||
}; | }; | ||||
// Calculate the attack damage multiplier against a target | Attack.prototype.GetBonusTemplate = function(type) | ||||
Attack.prototype.GetAttackBonus = function(type, target) | |||||
{ | { | ||||
let attackBonus = 1; | |||||
let template = this.template[type]; | let template = this.template[type]; | ||||
if (!template) | if (!template) | ||||
template = this.template[type.split(".")[0]].Splash; | template = this.template[type.split(".")[0]].Splash; | ||||
if (template.Bonuses) | if (template.Bonuses) | ||||
{ | return clone(template.Bonuses); | ||||
let cmpIdentity = Engine.QueryInterface(target, IID_Identity); | return null; | ||||
if (!cmpIdentity) | |||||
return 1; | |||||
// Multiply the bonuses for all matching classes | |||||
for (let key in template.Bonuses) | |||||
{ | |||||
let bonus = template.Bonuses[key]; | |||||
if (bonus.Civ && bonus.Civ !== cmpIdentity.GetCiv()) | |||||
continue; | |||||
if (bonus.Classes && bonus.Classes.split(/\s+/).some(cls => !cmpIdentity.HasClass(cls))) | |||||
continue; | |||||
attackBonus *= bonus.Multiplier; | |||||
} | |||||
} | |||||
return attackBonus; | |||||
}; | }; | ||||
/** | /** | ||||
* Attack the target entity. This should only be called after a successful range check, | * Attack the target entity. This should only be called after a successful range check, | ||||
* and should only be called after GetTimers().repeat msec has passed since the last | * and should only be called after GetTimers().repeat msec has passed since the last | ||||
* call to PerformAttack. | * call to PerformAttack. | ||||
*/ | */ | ||||
Attack.prototype.PerformAttack = function(type, target) | Attack.prototype.PerformAttack = function(type, target) | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | if (type == "Ranged") | ||||
let data = { | let data = { | ||||
"type": type, | "type": type, | ||||
"attacker": this.entity, | "attacker": this.entity, | ||||
"target": target, | "target": target, | ||||
"strengths": this.GetAttackStrengths(type), | "strengths": this.GetAttackStrengths(type), | ||||
"position": realTargetPosition, | "position": realTargetPosition, | ||||
"direction": missileDirection, | "direction": missileDirection, | ||||
"projectileId": id, | "projectileId": id, | ||||
"multiplier": this.GetAttackBonus(type, target), | "bonus": this.GetBonusTemplate(type), | ||||
"isSplash": false, | "isSplash": false, | ||||
"attackerOwner": attackerOwner | "attackerOwner": attackerOwner | ||||
}; | }; | ||||
if (this.template.Ranged.Splash) | if (this.template.Ranged.Splash) | ||||
{ | { | ||||
data.friendlyFire = this.template.Ranged.Splash.FriendlyFire != "false"; | data.friendlyFire = this.template.Ranged.Splash.FriendlyFire != "false"; | ||||
data.radius = +this.template.Ranged.Splash.Range; | data.radius = +this.template.Ranged.Splash.Range; | ||||
data.shape = this.template.Ranged.Splash.Shape; | data.shape = this.template.Ranged.Splash.Shape; | ||||
data.isSplash = true; | data.isSplash = true; | ||||
data.splashStrengths = this.GetAttackStrengths(type+".Splash"); | data.splashStrengths = this.GetAttackStrengths(type + ".Splash"); | ||||
data.splashBonus = this.GetBonusTemplate(type + ".Splash"); | |||||
} | } | ||||
cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Damage, "MissileHit", timeToTarget * 1000, data); | cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Damage, "MissileHit", timeToTarget * 1000, data); | ||||
} | } | ||||
else if (type == "Capture") | else if (type == "Capture") | ||||
{ | { | ||||
if (attackerOwner == -1) | if (attackerOwner == -1) | ||||
return; | return; | ||||
let multiplier = this.GetAttackBonus(type, target); | let multiplier = GetDamageBonus(target, this.GetBonusTemplate(type)); | ||||
let cmpHealth = Engine.QueryInterface(target, IID_Health); | let cmpHealth = Engine.QueryInterface(target, IID_Health); | ||||
if (!cmpHealth || cmpHealth.GetHitpoints() == 0) | if (!cmpHealth || cmpHealth.GetHitpoints() == 0) | ||||
return; | return; | ||||
multiplier *= cmpHealth.GetMaxHitpoints() / (0.1 * cmpHealth.GetMaxHitpoints() + 0.9 * cmpHealth.GetHitpoints()); | multiplier *= cmpHealth.GetMaxHitpoints() / (0.1 * cmpHealth.GetMaxHitpoints() + 0.9 * cmpHealth.GetHitpoints()); | ||||
let cmpCapturable = Engine.QueryInterface(target, IID_Capturable); | let cmpCapturable = Engine.QueryInterface(target, IID_Capturable); | ||||
if (!cmpCapturable || !cmpCapturable.CanCapture(attackerOwner)) | if (!cmpCapturable || !cmpCapturable.CanCapture(attackerOwner)) | ||||
return; | return; | ||||
Show All 10 Lines | Attack.prototype.PerformAttack = function(type, target) | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Melee attack - hurt the target immediately | // Melee attack - hurt the target immediately | ||||
cmpDamage.CauseDamage({ | cmpDamage.CauseDamage({ | ||||
"strengths": this.GetAttackStrengths(type), | "strengths": this.GetAttackStrengths(type), | ||||
"target": target, | "target": target, | ||||
"attacker": this.entity, | "attacker": this.entity, | ||||
"multiplier": this.GetAttackBonus(type, target), | "multiplier": GetDamageBonus(target, this.GetBonusTemplate(type)), | ||||
"type": type, | "type": type, | ||||
"attackerOwner": attackerOwner | "attackerOwner": attackerOwner | ||||
}); | }); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Get the predicted time of collision between a projectile (or a chaser) | * Get the predicted time of collision between a projectile (or a chaser) | ||||
Show All 18 Lines | Attack.prototype.PredictTimeToTarget = function(selfPosition, horizSpeed, targetPosition, targetVelocity) | ||||
if (c == 0) | if (c == 0) | ||||
return 0; | return 0; | ||||
let disc = b * b - a * c; | let disc = b * b - a * c; | ||||
if (a < 0 || b < 0 && disc >= 0) | if (a < 0 || b < 0 && disc >= 0) | ||||
return c / (Math.sqrt(disc) - b); | return c / (Math.sqrt(disc) - b); | ||||
return false; | return false; | ||||
} | }; | ||||
Attack.prototype.OnValueModification = function(msg) | Attack.prototype.OnValueModification = function(msg) | ||||
{ | { | ||||
if (msg.component != "Attack") | if (msg.component != "Attack") | ||||
return; | return; | ||||
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); | ||||
if (!cmpUnitAI) | if (!cmpUnitAI) | ||||
return; | return; | ||||
if (this.GetAttackTypes().some(type => | if (this.GetAttackTypes().some(type => | ||||
msg.valueNames.indexOf("Attack/" + type + "/MaxRange") != -1)) | msg.valueNames.indexOf("Attack/" + type + "/MaxRange") != -1)) | ||||
cmpUnitAI.UpdateRangeQueries(); | cmpUnitAI.UpdateRangeQueries(); | ||||
}; | }; | ||||
Engine.RegisterComponentType(IID_Attack, "Attack", Attack); | Engine.RegisterComponentType(IID_Attack, "Attack", Attack); |
Wildfire Games · Phabricator