Ticket #4421: spread_alpha21.diff

File spread_alpha21.diff, 8.1 KB (added by elexis, 7 years ago)

For alpha 21 players only.

  • binaries/data/mods/public/globalscripts/Templates.js

     
    120120                    "hack": getAttackStat("Hack"),
    121121                    "pierce": getAttackStat("Pierce"),
    122122                    "crush": getAttackStat("Crush"),
    123123                    "minRange": getAttackStat("MinRange"),
    124124                    "maxRange": getAttackStat("MaxRange"),
    125                     "elevationBonus": getAttackStat("ElevationBonus")
     125                    "elevationBonus": getAttackStat("ElevationBonus"),
     126                    "spread": getAttackStat("Spread")
    126127                };
    127128                ret.attack[type].elevationAdaptedRange = Math.sqrt(ret.attack[type].maxRange *
    128129                    (2 * ret.attack[type].elevationBonus + ret.attack[type].maxRange));
    129130            }
    130131            ret.attack[type].repeatTime = getAttackStat("RepeatTime");
  • binaries/data/mods/public/gui/common/tooltips.js

     
    1515    "hack": translate("Hack"),
    1616    "pierce": translate("Pierce"),
    1717    "crush": translate("Crush"),
    1818};
    1919
     20const g_RangeTooltipString = {
     21    "relative": {
     22        "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(spread)s, %(rate)s"),
     23        "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s (%(relativeRange)s) %(rangeUnit)s, %(spread)s, %(rate)s"),
     24    },
     25    "non-relative": {
     26        "minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(minRange)s to %(maxRange)s %(rangeUnit)s, %(spread)s, %(rate)s"),
     27        "no-minRange": translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(maxRange)s %(rangeUnit)s, %(spread)s, %(rate)s"),
     28    }
     29};
     30
    2031function costIcon(resource)
    2132{
    2233    return '[icon="icon_' + resource + '"]';
    2334}
    2435
     
    130141            "damage": dmg[dmgType].toFixed(1),
    131142            "damageType": unitFont(g_DamageTypes[dmgType])
    132143        })).join(commaFont(translate(", ")));
    133144}
    134145
    135 // TODO: should also show minRange
    136146function getAttackTooltip(template)
    137147{
    138148    if (!template.attack)
    139149        return "";
    140150
    141     let attacks = [];
     151    let tooltips = [];
    142152    for (let type in template.attack)
    143153    {
    144154        if (type == "Slaughter")
    145155            continue; // Slaughter is used to kill animals, so do not show it.
    146156
     
    154164        });
    155165
    156166        let attackLabel = headerFont(g_AttackTypes[type]);
    157167        if (type == "Capture" || type != "Ranged")
    158168        {
    159             attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
     169            tooltips.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
    160170                "attackLabel": attackLabel,
    161171                "details":
    162172                    type == "Capture" ?
    163173                        template.attack.Capture.value :
    164174                        damageTypesToText(template.attack[type]),
    165175                "rate": rate
    166176            }));
    167177            continue;
    168178        }
    169179
     180        let minRange = Math.round(template.attack[type].minRange);
     181        let maxRange = Math.round(template.attack[type].maxRange);
    170182        let realRange = template.attack[type].elevationAdaptedRange;
    171         let range = Math.round(template.attack[type].maxRange);
    172         let relativeRange = realRange ? Math.round(realRange - range) : 0;
     183        let relativeRange = realRange ? Math.round(realRange - maxRange) : 0;
    173184
    174         let rangeString = relativeRange ?
    175             translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(rangeString)s (%(relative)s), %(rate)s") :
    176             translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(rangeString)s, %(rate)s");
     185        // Compare spread at a defined distance
     186        let spread = +(+template.attack[type].spread / maxRange * 100).toFixed(1);
    177187
    178         attacks.push(sprintf(rangeString, {
     188        tooltips.push(sprintf(g_RangeTooltipString[relativeRange ? "relative" : "non-relative"][minRange ? "minRange" : "no-minRange"], {
    179189            "attackLabel": attackLabel,
    180190            "damageTypes": damageTypesToText(template.attack[type]),
    181             "rangeLabel": translate("Range:"),
    182             "rangeString": sprintf(
    183                 translatePlural("%(range)s %(meters)s", "%(range)s %(meters)s", range), {
    184                     "range": range,
    185                     "meters": unitFont(translatePlural("meter", "meters", range))
    186                 }),
     191            "rangeLabel": headerFont(translate("Range:")),
     192            "minRange": minRange,
     193            "maxRange": maxRange,
     194            "relativeRange": relativeRange > 0 ? "+" + relativeRange : relativeRange,
     195            "rangeUnit":
     196                unitFont(minRange || relativeRange ?
     197                    // Translation: For example "0.5 to 1 meters", "1 (+1) meters" or "1 to 2 (+3) meters"
     198                    translate("meters") :
     199                    translatePlural("meter", "meters", maxRange)),
    187200            "rate": rate,
    188             "relative": relativeRange > 0 ? "+" + relativeRange : relativeRange,
     201            "spread": sprintf(translatePlural("%(label)s %(val)s %(unit)s", "%(label)s %(val)s %(unit)s", spread), {
     202                "label": headerFont(translate("Spread:")),
     203                "val": spread,
     204                "unit": unitFont(translatePlural("meter", "meters", spread))
     205            })
    189206        }));
    190207    }
    191     return attacks.join("\n");
     208    return tooltips.join("\n");
    192209}
    193210
    194211function getSplashDamageTooltip(template)
    195212{
    196213    if (!template.attack)
  • binaries/data/mods/public/simulation/components/Attack.js

     
    379379        "pierce": applyMods("Pierce"),
    380380        "crush": applyMods("Crush")
    381381    };
    382382};
    383383
     384Attack.prototype.GetSpread = function(type)
     385{
     386    if (type != "Ranged" || !this.template[type])
     387        return 0;
     388
     389    let spread = +this.template[type].Spread;
     390    return ApplyValueModificationsToEntity("Attack/" + type + "/Spread", spread, this.entity);
     391}
     392
    384393Attack.prototype.GetSplashDamage = function(type)
    385394{
    386395    if (!this.template[type].Splash)
    387396        return false;
    388397
     
    439448    return attackBonus;
    440449};
    441450
    442451// Returns a 2d random distribution scaled for a spread of scale 1.
    443452// The current implementation is a 2d gaussian with sigma = 1
    444 Attack.prototype.GetNormalDistribution = function(){
     453Attack.prototype.GetNormalDistribution = function()
     454{
    445455
    446456    // Use the Box-Muller transform to get a gaussian distribution
    447457    let a = Math.random();
    448458    let b = Math.random();
    449459
     
    474484
    475485        // Get some data about the entity
    476486        let horizSpeed = +this.template[type].ProjectileSpeed;
    477487        let gravity = 9.81; // this affects the shape of the curve; assume it's constant for now
    478488
    479         let spread = +this.template.Ranged.Spread;
    480         spread = ApplyValueModificationsToEntity("Attack/Ranged/Spread", spread, this.entity);
    481 
    482489        //horizSpeed /= 2; gravity /= 2; // slow it down for testing
    483490
    484491        let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    485492        if (!cmpPosition || !cmpPosition.IsInWorld())
    486493            return;
     
    510517
    511518        // Compute the real target point (based on spread and target speed)
    512519        let range = this.GetRange(type);
    513520        let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    514521        let elevationAdaptedMaxRange = cmpRangeManager.GetElevationAdaptedRange(selfPosition, cmpPosition.GetRotation(), range.max, range.elevationBonus, 0);
    515         let distanceModifiedSpread = spread * horizDistance/elevationAdaptedMaxRange;
     522        let distanceModifiedSpread = this.GetSpread(type) * horizDistance/elevationAdaptedMaxRange;
    516523
    517524        let randNorm = this.GetNormalDistribution();
    518525        let offsetX = randNorm[0] * distanceModifiedSpread * (1 + targetVelocity.length() / 20);
    519526        let offsetZ = randNorm[1] * distanceModifiedSpread * (1 + targetVelocity.length() / 20);
    520527
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    442442        if (types.length)
    443443            ret.attack = {};
    444444        for (let type of types)
    445445        {
    446446            ret.attack[type] = cmpAttack.GetAttackStrengths(type);
     447            ret.attack[type].spread = cmpAttack.GetSpread(type);
    447448            ret.attack[type].splash = cmpAttack.GetSplashDamage(type);
    448449
    449450            let range = cmpAttack.GetRange(type);
    450451            ret.attack[type].minRange = range.min;
    451452            ret.attack[type].maxRange = range.max;