diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js
index a9e46fc..30d36d1 100644
a
|
b
|
function GetTemplateDataHelper(template, player, auraTemplates, resources, damag
|
187 | 187 | ret.attack[type] = { |
188 | 188 | "minRange": getAttackStat("MinRange"), |
189 | 189 | "maxRange": getAttackStat("MaxRange"), |
190 | | "elevationBonus": getAttackStat("ElevationBonus") |
| 190 | "elevationBonus": getAttackStat("ElevationBonus"), |
| 191 | "spread": getAttackStat("Spread") |
191 | 192 | }; |
192 | 193 | for (let damageType of damageTypes.GetTypes()) |
193 | 194 | ret.attack[type][damageType] = getAttackStat(damageType); |
diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js
index e907c62..b6da3dc 100644
a
|
b
|
var g_SplashDamageTypes = {
|
24 | 24 | var g_RangeTooltipString = { |
25 | 25 | "relative": { |
26 | 26 | // Translation: For example: Ranged Attack: 12.0 Pierce, Range: 2 to 10 (+2) meters, Interval: 3 arrows / 2 seconds |
27 | | "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(rate)s"), |
| 27 | "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(rate)s, %(spread)s"), |
28 | 28 | // Translation: For example: Ranged Attack: 12.0 Pierce, Range: 10 (+2) meters, Interval: 3 arrows / 2 seconds |
29 | | "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(rate)s"), |
| 29 | "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(rate)s, %(spread)s"), |
30 | 30 | }, |
31 | 31 | "non-relative": { |
32 | 32 | // Translation: For example: Ranged Attack: 12.0 Pierce, Range: 2 to 10 meters, Interval: 3 arrows / 2 seconds |
33 | | "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s %(rangeUnit)s, %(rate)s"), |
| 33 | "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s %(rangeUnit)s, %(rate)s, %(spread)s"), |
34 | 34 | // Translation: For example: Ranged Attack: 12.0 Pierce, Range: 10 meters, Interval: 3 arrows / 2 seconds |
35 | | "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s %(rangeUnit)s, %(rate)s"), |
| 35 | "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s %(rangeUnit)s, %(rate)s, %(spread)s"), |
36 | 36 | } |
37 | 37 | }; |
38 | 38 | |
… |
… |
function getAttackTooltip(template)
|
284 | 284 | let maxRange = Math.round(template.attack[type].maxRange); |
285 | 285 | let realRange = template.attack[type].elevationAdaptedRange; |
286 | 286 | let relativeRange = realRange ? Math.round(realRange - maxRange) : 0; |
| 287 | let spread = +template.attack[type].spread; |
287 | 288 | |
288 | 289 | tooltips.push(sprintf(g_RangeTooltipString[relativeRange ? "relative" : "non-relative"][minRange ? "minRange" : "no-minRange"], { |
289 | 290 | "attackLabel": attackLabel, |
… |
… |
function getAttackTooltip(template)
|
298 | 299 | translate("meters") : |
299 | 300 | translatePlural("meter", "meters", maxRange)), |
300 | 301 | "rate": rate, |
| 302 | "spread": sprintf(translate("%(label)s %(val)s %(unit)s at %(range)s"), { |
| 303 | "label": headerFont(translate("Spread Hit Radius:")), |
| 304 | "val": spread, |
| 305 | "unit": unitFont(translatePlural("meter", "meters", spread)), |
| 306 | "range": sprintf("%(range)s %(unit)s", { "range": maxRange, "unit": translatePlural("meter", "meters", maxRange) }) |
| 307 | }) |
301 | 308 | })); |
302 | 309 | } |
303 | 310 | return tooltips.join("\n"); |
diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js
index b048152..69259a6 100644
a
|
b
|
Attack.prototype.GetAttackStrengths = function(type)
|
440 | 440 | return ret; |
441 | 441 | }; |
442 | 442 | |
| 443 | Attack.prototype.GetSpread = function(type) |
| 444 | { |
| 445 | if (type != "Ranged" || !this.template[type]) |
| 446 | return 0; |
| 447 | |
| 448 | let spread = +this.template[type].Spread; |
| 449 | return ApplyValueModificationsToEntity("Attack/" + type + "/Spread", spread, this.entity); |
| 450 | } |
| 451 | |
443 | 452 | Attack.prototype.GetSplashDamage = function(type) |
444 | 453 | { |
445 | 454 | if (!this.template[type].Splash) |
… |
… |
Attack.prototype.PerformAttack = function(type, target)
|
513 | 522 | let predictedPosition = (timeToTarget !== false) ? Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition) : targetPosition; |
514 | 523 | |
515 | 524 | // Add inaccuracy based on spread. |
516 | | let distanceModifiedSpread = ApplyValueModificationsToEntity("Attack/Ranged/Spread", +this.template.Ranged.Spread, this.entity) * |
517 | | predictedPosition.horizDistanceTo(selfPosition) / 100; |
| 525 | // let distanceModifiedSpread = ApplyValueModificationsToEntity("Attack/Ranged/Spread", +this.template.Ranged.Spread, this.entity) * |
| 526 | let distanceModifiedSpread = this.GetSpread(type) * predictedPosition.horizDistanceTo(selfPosition) / 100; |
518 | 527 | |
519 | 528 | let randNorm = randomNormal2D(); |
520 | 529 | let offsetX = randNorm[0] * distanceModifiedSpread; |
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index b0d8007..eb04f6a 100644
a
|
b
|
GuiInterface.prototype.GetEntityState = function(player, ent)
|
389 | 389 | for (let type of types) |
390 | 390 | { |
391 | 391 | ret.attack[type] = cmpAttack.GetAttackStrengths(type); |
| 392 | ret.attack[type].spread = cmpAttack.GetSpread(type); |
392 | 393 | ret.attack[type].splash = cmpAttack.GetSplashDamage(type); |
393 | 394 | |
394 | 395 | let range = cmpAttack.GetRange(type); |