Ticket #4082: globalauras.diff

File globalauras.diff, 15.6 KB (added by fatherbushido, 3 years ago)
  • binaries/data/mods/public/simulation/components/AuraManager.js

    AuraManager.prototype.Init = function() 
    77{
    88    this.modificationsCache = new Map();
    99    this.modifications = new Map();
    1010    this.templateModificationsCache = new Map();
    1111    this.templateModifications = new Map();
     12
     13    this.globalAuraGivers = [];
     14};
     15
     16
     17AuraManager.prototype.RegisterGlobalAuraGiver = function(ent)
     18{
     19    if (this.globalAuraGivers.indexOf(ent) == -1)
     20        this.globalAuraGivers.push(ent);
     21};
     22
     23AuraManager.prototype.UnregisterGlobalAuraGiver = function(ent)
     24{
     25    let idx = this.globalAuraGivers.indexOf(ent);
     26    if (idx != -1)
     27        this.globalAuraGivers.splice(idx, 1);
    1228};
    1329
    1430AuraManager.prototype.ensureExists = function(name, value, id, key, defaultData)
    1531{
    1632    var cacheName = name + "Cache";
    AuraManager.prototype.ApplyTemplateModif 
    228244        }
    229245    }
    230246    return value * multiply + add;
    231247};
    232248
     249AuraManager.prototype.OnGlobalOwnershipChanged = function(msg)
     250{
     251    for (let ent of this.globalAuraGivers)
     252    {
     253        let cmpAuras = Engine.QueryInterface(ent, IID_Auras);
     254        if (cmpAuras)
     255            cmpAuras.RegisterGlobalOwnershipChanged(msg);
     256    }
     257};
     258
    233259Engine.RegisterSystemComponentType(IID_AuraManager, "AuraManager", AuraManager);
  • binaries/data/mods/public/simulation/components/Auras.js

    Auras.prototype.GetAffectedEntities = fu 
    5858    return this[name].targetUnits;
    5959};
    6060
    6161Auras.prototype.GetRange = function(name)
    6262{
    63     if (!this.IsRangeAura(name))
    64         return undefined;
    65     if (this.IsGlobalAura(name))
    66         return -1; // -1 is infinite range
    67     return +this.auras[name].radius;
     63    if (this.IsRangeAura(name))
     64        return +this.auras[name].radius;
     65    return undefined;
    6866};
    6967
    7068Auras.prototype.GetClasses = function(name)
    7169{
    7270    return this.auras[name].affects;
    Auras.prototype.IsGarrisonedUnitsAura = 
    152150    return this.GetType(name) == "garrisonedUnits";
    153151};
    154152
    155153Auras.prototype.IsRangeAura = function(name)
    156154{
    157     // A global aura is also treated as a range aura with infinite range.
    158     return ["range", "global"].indexOf(this.GetType(name)) != -1;
     155    return this.GetType(name) == "range";
    159156};
    160157
    161158Auras.prototype.IsGlobalAura = function(name)
    162159{
    163160    return this.GetType(name) == "global";
    Auras.prototype.Clean = function() 
    202199        var affectedPlayers = this.GetAffectedPlayers(name);
    203200
    204201        if (!affectedPlayers.length)
    205202            continue;
    206203
    207         if (!this.IsRangeAura(name))
     204        if (this.IsGlobalAura(name))
    208205        {
    209             this.ApplyBonus(name, targetUnitsClone[name]);
     206            for (let player of affectedPlayers)
     207            {
     208                this.ApplyTemplateBonus(name, affectedPlayers);
     209                // When only Player class affected, we do not need a rangeQuery as applicable only to player entity
     210                // and templates TODO maybe add a new type "player"
     211                if (this.GetClasses(name).length == 1 && this.GetClasses(name)[0] == "Player")
     212                {
     213                    let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     214                    let playerEnts = affectedPlayers.map(player => cmpPlayerManager.GetPlayerByID(player));
     215                    this.ApplyBonus(name, playerEnts);
     216                }
     217                else
     218                    this.ApplyBonus(name, cmpRangeManager.GetEntitiesByPlayer(player));
     219            }
    210220            continue;
    211221        }
    212222
    213         // When only Player class affected, we do not need a rangeQuery as applicable only to player entity
    214         // and templates TODO maybe add a new type "player"
    215         if (this.IsGlobalAura(name) && this.GetClasses(name).length == 1 && this.GetClasses(name)[0] == "Player")
     223        if (!this.IsRangeAura(name))
    216224        {
    217             this.ApplyTemplateBonus(name, affectedPlayers);
    218             let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    219             let playerEnts = affectedPlayers.map(player => cmpPlayerManager.GetPlayerByID(player));
    220             this.ApplyBonus(name, playerEnts);
     225            this.ApplyBonus(name, targetUnitsClone[name]);
    221226            continue;
    222227        }
    223228
    224229        this[name].rangeQuery = cmpRangeManager.CreateActiveQuery(
    225230            this.entity,
    Auras.prototype.Clean = function() 
    228233            affectedPlayers,
    229234            IID_Identity,
    230235            cmpRangeManager.GetEntityFlagMask("normal")
    231236        );
    232237        cmpRangeManager.EnableActiveQuery(this[name].rangeQuery);
    233 
    234         if (this.IsGlobalAura(name))
    235         {
    236             this.ApplyTemplateBonus(name, affectedPlayers);
    237 
    238             // Add self to your own query for consistency with templates.
    239             this.OnRangeUpdate({
    240                 "tag": this[name].rangeQuery,
    241                 "added": [this.entity],
    242                 "removed": []
    243             });
    244         }
    245238    }
    246239};
    247240
    248241Auras.prototype.GiveMembersWithValidClass = function(auraName, entityList)
    249242{
    Auras.prototype.OnGarrisonedUnitsChanged 
    272265        this.ApplyBonus(name, msg.added);
    273266        this.RemoveBonus(name, msg.removed);
    274267    }
    275268};
    276269
     270Auras.prototype.RegisterGlobalOwnershipChanged = function(msg)
     271{
     272    let auraNames = this.GetAuraNames().filter(n => this.IsGlobalAura(n));
     273    for (let name of auraNames)
     274    {
     275        let affectedPlayers = this.GetAffectedPlayers(name);
     276        let wasApplied = affectedPlayers.indexOf(msg.from) != -1;
     277        let willBeApplied = affectedPlayers.indexOf(msg.to) != -1;
     278        if (wasApplied && !willBeApplied)
     279            this.RemoveBonus(name, [msg.entity]);
     280        if (willBeApplied && !wasApplied)
     281            this.ApplyBonus(name, [msg.entity]);
     282    }
     283};
     284
    277285Auras.prototype.ApplyFormationBonus = function(memberList)
    278286{
    279287    var auraNames = this.GetAuraNames().filter(n => this.IsFormationAura(n));
    280288    for (let name of auraNames)
    281289        this.ApplyBonus(name, memberList);
    Auras.prototype.ApplyTemplateBonus = fun 
    297305        return;
    298306    var modifications = this.GetModifications(name);
    299307    var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
    300308    var classes = this.GetClasses(name);
    301309
     310    cmpAuraManager.RegisterGlobalAuraGiver(this.entity);
     311
    302312    for (let mod of modifications)
    303313        for (let player of players)
    304314            cmpAuraManager.ApplyTemplateBonus(mod.value, player, classes, mod, this.GetModifierIdentifier(name));
    305315};
    306316
    Auras.prototype.RemoveTemplateBonus = fu 
    323333    if (!this[name].isApplied)
    324334        return;
    325335    if (!this.IsGlobalAura(name))
    326336        return;
    327337
    328     var modifications = this.GetModifications(name);
    329338    var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager);
     339    cmpAuraManager.UnregisterGlobalAuraGiver(this.entity);
     340
     341    var modifications = this.GetModifications(name);
    330342    var classes = this.GetClasses(name);
    331343    var players = this.GetAffectedPlayers(name);
    332344
    333345    for (let mod of modifications)
    334346        for (let player of players)
  • binaries/data/mods/public/simulation/components/tests/test_Auras.js

    Engine.LoadComponentScript("interfaces/A 
    44Engine.LoadComponentScript("interfaces/AuraManager.js");
    55Engine.LoadComponentScript("interfaces/TechnologyManager.js");
    66Engine.LoadComponentScript("Auras.js");
    77Engine.LoadComponentScript("AuraManager.js");
    88
    9 let playerID = 1;
    10 let playerEnt = 10;
    11 let auraEnt = 20;
     9let playerID = [0, 1, 2];
     10let playerEnt = [10, 11, 12];
     11let giverEnt = 20;
    1212let targetEnt = 30;
    1313let auraRange = 40;
    1414let template = { "Identity" : { "Classes" : { "_string" : "CorrectClass OtherClass" } } };
    1515
    1616function testAuras(name, test_function)
    1717{
    1818    ResetState();
    1919
    2020    AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
    21         "GetPlayerByID": () => playerEnt,
    22         "GetNumPlayers": () => 2,
     21        "GetPlayerByID": idx => playerEnt[idx],
     22        "GetNumPlayers": () => 3,
    2323    });
    2424
    2525    AddMock(SYSTEM_ENTITY, IID_RangeManager, {
    2626        "CreateActiveQuery": (ent, minRange, maxRange, players, iid, flags) => 1,
    2727        "EnableActiveQuery": id => {},
    2828        "ResetActiveQuery": id => {},
    2929        "DisableActiveQuery": id => {},
    3030        "DestroyActiveQuery": id => {},
    3131        "GetEntityFlagMask": identifier => {},
     32        "GetEntitiesByPlayer": id => [30, 31, 32]
    3233    });
    3334
    3435    AddMock(SYSTEM_ENTITY, IID_DataTemplateManager, {
    3536        "GetAuraTemplate": (name) => {
    3637            let template = {
    3738                "type": name,
     39                "affectedPlayers": ["Ally"],
    3840                "affects": ["CorrectClass"],
    3941                "modifications": [{ "value": "Component/Value", "add": 10 }],
    4042                "auraName": "name",
    4143                "auraDescription": "description"
    4244            };
    function testAuras(name, test_function) 
    4446                template.radius = auraRange;
    4547            return template;
    4648        }
    4749    });
    4850
    49     AddMock(playerEnt, IID_Player, {
     51    AddMock(playerEnt[1], IID_Player, {
    5052        "IsAlly": id => id == 1,
    5153        "IsEnemy": id => id != 1,
    52         "GetPlayerID": () => 1,
     54        "GetPlayerID": () => playerID[1],
    5355    });
    5456
    5557    AddMock(targetEnt, IID_Identity, {
    5658        "GetClassesList": () => ["CorrectClass", "OtherClass"],
    5759    });
    5860
    59     AddMock(auraEnt, IID_Position, {
    60         "GetPosition2D": () => new Vector2D(),
     61    AddMock(giverEnt, IID_Position, {
     62        "GetPosition2D": () => new Vector2D()
    6163    });
    6264
    6365    AddMock(targetEnt, IID_Position, {
    64         "GetPosition2D": () => new Vector2D(),
     66        "GetPosition2D": () => new Vector2D()
    6567    });
    6668
    67     AddMock(auraEnt, IID_Ownership, {
    68         "GetOwner": () => 1,
     69    if (playerEnt.indexOf(giverEnt) == -1)
     70    {
     71        AddMock(giverEnt, IID_Ownership, {
     72            "GetOwner": () => playerID[1]
     73        });
     74    }
     75
     76    AddMock(targetEnt, IID_Ownership, {
     77        "GetOwner": () => playerID[1]
    6978    });
    7079
    7180    ConstructComponent(SYSTEM_ENTITY, "AuraManager", {});
    72     let cmpAuras = ConstructComponent(auraEnt, "Auras", { "_string": name });
     81    let cmpAuras = ConstructComponent(giverEnt, "Auras", { "_string": name });
    7382    test_function(name, cmpAuras);
    7483}
    7584
    7685testAuras("global", (name, cmpAuras) => {
    77     cmpAuras.OnRangeUpdate({ "tag": 1, "added": [targetEnt], "removed": [] });
    78     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 11);
    79     TS_ASSERT_EQUALS(ApplyValueModificationsToTemplate("Component/Value", 1, playerID, template), 11);
     86    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
     87    TS_ASSERT_EQUALS(ApplyValueModificationsToTemplate("Component/Value", 5, playerID[1], template), 15);
     88});
     89
     90giverEnt = 11;
     91testAuras("global", (name, cmpAuras) => {
     92    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
     93    TS_ASSERT_EQUALS(ApplyValueModificationsToTemplate("Component/Value", 5, playerID[1], template), 15);
    8094});
    8195
     96// Test the case when the aura giver is a player entity.
     97giverEnt = 20;
    8298testAuras("range", (name, cmpAuras) => {
    8399    cmpAuras.OnRangeUpdate({ "tag": 1, "added": [targetEnt], "removed": [] });
    84     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 11);
    85     TS_ASSERT_EQUALS(ApplyValueModificationsToTemplate("Component/Value", 1, playerID, template), 1);
     100    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
     101    TS_ASSERT_EQUALS(ApplyValueModificationsToTemplate("Component/Value", 5, playerID[1], template), 5);
    86102    cmpAuras.OnRangeUpdate({ "tag": 1, "added": [], "removed": [targetEnt] });
    87     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 1);
     103    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 5);
    88104});
    89105
    90106testAuras("garrisonedUnits", (name, cmpAuras) => {
    91107    cmpAuras.OnGarrisonedUnitsChanged({ "added" : [targetEnt], "removed": [] });
    92     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 11);
     108    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
    93109    cmpAuras.OnGarrisonedUnitsChanged({ "added" : [], "removed": [targetEnt] });
    94     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 1);
     110    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 5);
    95111});
    96112
    97113testAuras("garrison", (name, cmpAuras) => {
    98114    TS_ASSERT_EQUALS(cmpAuras.HasGarrisonAura(), true);
    99115    cmpAuras.ApplyGarrisonBonus(targetEnt);
    100     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 11);
     116    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
    101117    cmpAuras.RemoveGarrisonBonus(targetEnt);
    102118    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 1);
    103119});
    104120
    105121testAuras("formation", (name, cmpAuras) => {
    106122    TS_ASSERT_EQUALS(cmpAuras.HasFormationAura(), true);
    107123    cmpAuras.ApplyFormationBonus([targetEnt]);
    108     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 11);
     124    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
    109125    cmpAuras.RemoveFormationBonus([targetEnt]);
    110     TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 1, targetEnt), 1);
     126    TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 5);
    111127});
  • binaries/data/mods/public/simulation/data/auras/maur_hero_ashoka.json

     
    55        { "value": "ProductionQueue/TechCostMultiplier/wood", "multiply": 0.5 },
    66        { "value": "ProductionQueue/TechCostMultiplier/food", "multiply": 0.5 },
    77        { "value": "ProductionQueue/TechCostMultiplier/metal", "multiply": 0.5 },
    88        { "value": "ProductionQueue/TechCostMultiplier/stone", "multiply": 0.5 },
    99        { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.5 }
     10        { "value": "Cost/BuildTime", "multiply": 0.5 },
     11        { "value": "Cost/Resources/stone", "multiply": 0.5 },
     12        { "value": "Cost/Resources/wood", "multiply": 0.5 }
    1013    ],
    11     "auraDescription": "All player temple technologies -50% cost and -50% research time.",
     14    "auraDescription": "Temples and temple technologies -50% cost, in resources and time.",
    1215    "auraName": "Buddhism Aura",
    1316    "overlayIcon": "art/textures/ui/session/auras/build_bonus.png"
    1417}
  • binaries/data/mods/public/simulation/data/auras/teambonuses/maur_player_teambonus.json

     
     1{
     2    "type": "global",
     3    "affects": ["Temple"],
     4    "affectedPlayers": ["ExclusiveMutualAlly"],
     5    "modifications": [
     6        { "value": "ProductionQueue/TechCostMultiplier/wood", "multiply": 0.5 },
     7        { "value": "ProductionQueue/TechCostMultiplier/food", "multiply": 0.5 },
     8        { "value": "ProductionQueue/TechCostMultiplier/metal", "multiply": 0.5 },
     9        { "value": "ProductionQueue/TechCostMultiplier/stone", "multiply": 0.5 },
     10        { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.5 },
     11        { "value": "Cost/BuildTime", "multiply": 0.5 },
     12        { "value": "Cost/Resources/stone", "multiply": 0.5 },
     13        { "value": "Cost/Resources/wood", "multiply": 0.5 }
     14    ],
     15    "auraName": "Ashoka's Religious Support",
     16    "auraDescription": "Allied temples and temple technologies -50% cost, in resources and time."
     17}
  • binaries/data/mods/public/simulation/templates/special/player_maur.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity parent="special/player">
     3  <Auras datatype="tokens">teambonuses/maur_player_teambonus</Auras>
     4</Entity>