Ticket #1358: 1358-git.patch

File 1358-git.patch, 33.4 KB (added by historic_bruno, 12 years ago)
  • new file inaries/data/mods/public/globalscripts/Technologies.js

    From 379ae72627fd347e448545d05e2d7b051972aebb Mon Sep 17 00:00:00 2001
    From: Ben Brian <ben@wildfiregames.com>
    Date: Fri, 8 Jun 2012 16:57:35 -0400
    Subject: [PATCH 1/3] Extends engine to load all global scripts with new
     function (separate from replacing RNG). Calls that
     function for GUI, AI, map generator and simulation
     script contexts. Adds dummy folder to fix simulation
     tests.
    
    ---
     .../data/mods/public/globalscripts/Technologies.js |   24 +++++++++
     source/graphics/MapGenerator.cpp                   |    3 +-
     source/gui/GUIManager.cpp                          |    2 +
     source/scriptinterface/ScriptInterface.cpp         |   54 +++++++++++---------
     source/scriptinterface/ScriptInterface.h           |   11 +++-
     .../scriptinterface/tests/test_ScriptInterface.h   |    2 +-
     source/simulation2/components/CCmpAIManager.cpp    |    6 ++-
     source/simulation2/system/ComponentManager.cpp     |    3 +-
     8 files changed, 75 insertions(+), 30 deletions(-)
     create mode 100644 binaries/data/mods/_test.sim/globalscripts/.gitignore
     create mode 100644 binaries/data/mods/public/globalscripts/Technologies.js
    
    diff --git a/binaries/data/mods/_test.sim/globalscripts/.gitignore b/binaries/data/mods/_test.sim/globalscripts/.gitignore
    new file mode 100644
    index 0000000..e69de29
    diff --git a/binaries/data/mods/public/globalscripts/Technologies.js b/binaries/data/mods/public/globalscripts/Technologies.js
    new file mode 100644
    index 0000000..f1ee71a
    - +  
     1/**
     2 * This file contains shared logic for applying tech modifications
     3 * in GUI, AI, and simulation scripts.
     4 * As such it must be completely deterministic and can't have any global state,
     5 * but each context should do its own caching as needed.
     6 *
     7 * fullEntityTemplateData: object containing all raw entity template data
     8 * fullTechTemplateData: object containing all raw tech template data
     9 * currentResearchedTechs: array of objects, one for each player containing their
     10 *      currently researched techs (acquired from the simulation/TechManager)
     11 * playerID: the player which is testing this tech
     12 * entityTemplateName: name of the entity template to retrieve the property
     13 * propertyName: name of the tech property we're applying
     14 *
     15 */
     16function GetTechModifiedProperty(
     17    fullEntityTemplateData, fullTechTemplateData, currentResearchedTechs,
     18    playerID, entityTemplateName, propertyName)
     19{
     20
     21
     22
     23
     24}
  • source/graphics/MapGenerator.cpp

    diff --git a/source/graphics/MapGenerator.cpp b/source/graphics/MapGenerator.cpp
    index 518e98e..b804165 100644
    a b bool CMapGeneratorWorker::Run()  
    8888    m_ScriptInterface->SetCallbackData(static_cast<void*> (this));
    8989
    9090    // Replace RNG with a seeded deterministic function
    91     m_ScriptInterface->ReplaceNondeterministicFunctions(m_MapGenRNG);
     91    m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
     92    m_ScriptInterface->LoadGlobalScripts();
    9293
    9394    // Functions for RMS
    9495    m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
  • source/gui/GUIManager.cpp

    diff --git a/source/gui/GUIManager.cpp b/source/gui/GUIManager.cpp
    index 2c55661..bccfc0f 100644
    a b CGUIManager::CGUIManager(ScriptInterface& scriptInterface) :  
    5353{
    5454    ENSURE(ScriptInterface::GetCallbackData(scriptInterface.GetContext()) == NULL);
    5555    scriptInterface.SetCallbackData(this);
     56
     57    scriptInterface.LoadGlobalScripts();
    5658}
    5759
    5860CGUIManager::~CGUIManager()
  • source/scriptinterface/ScriptInterface.cpp

    diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp
    index 9a16a0d..b5b346f 100644
    a b void* ScriptInterface::GetCallbackData(JSContext* cx)  
    609609    return JS_GetContextPrivate(cx);
    610610}
    611611
    612 void ScriptInterface::ReplaceNondeterministicFunctions(boost::rand48& rng)
     612bool ScriptInterface::LoadGlobalScripts()
    613613{
    614     jsval math;
    615     if (!JS_GetProperty(m->m_cx, m->m_glob, "Math", &math) || !JSVAL_IS_OBJECT(math))
    616     {
    617         LOGERROR(L"ReplaceNondeterministicFunctions: failed to get Math");
    618         return;
    619     }
     614    // Ignore this failure in tests
     615    if (!g_VFS)
     616        return false;
    620617
    621     JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0,
    622             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
    623     if (!random)
     618    // Load and execute *.js in the global scripts directory
     619    VfsPaths pathnames;
     620    vfs::GetPathnames(g_VFS, L"globalscripts/", L"*.js", pathnames);
     621    for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
    624622    {
    625         LOGERROR(L"ReplaceNondeterministicFunctions: failed to replace Math.random");
    626         return;
     623        if (!LoadGlobalScriptFile(*it))
     624        {
     625            LOGERROR(L"LoadGlobalScripts: Failed to load script %ls", it->string().c_str());
     626            return false;
     627        }
    627628    }
    628     // Store the RNG in a slot which is sort-of-guaranteed to be unused by the JS engine
    629     JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng));
    630    
    631     // Load a script which replaces some of the system dependent trig functions
    632     VfsPath path("globalscripts/Math.js");
    633    
    634     // This function is called from tests without the VFS set up,
    635     // so silently fail in that case because every other option seems worse
    636     if (g_VFS && VfsFileExists(path))
     629
     630    return true;
     631}
     632
     633bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
     634{
     635    jsval math;
     636    if (JS_GetProperty(m->m_cx, m->m_glob, "Math", &math) && JSVAL_IS_OBJECT(math))
    637637    {
    638         if (!this->LoadGlobalScriptFile(path))
     638        JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0,
     639            JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
     640        if (random)
    639641        {
    640             LOGERROR(L"ReplaceNondeterministicFunctions: failed to load %ls", path.string().c_str());
    641             return;
     642            // Store the RNG in a slot which is sort-of-guaranteed to be unused by the JS engine
     643            if (JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng)))
     644                return true;
    642645        }
    643646    }
     647
     648    LOGERROR(L"ReplaceNondeterministicRNG: failed to replace Math.random");
     649    return false;
    644650}
    645651
    646652void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
  • source/scriptinterface/ScriptInterface.h

    diff --git a/source/scriptinterface/ScriptInterface.h b/source/scriptinterface/ScriptInterface.h
    index c0118d0..f80f671 100644
    a b public:  
    9898    JSContext* GetContext() const;
    9999    JSRuntime* GetRuntime() const;
    100100
    101     void ReplaceNondeterministicFunctions(boost::rand48& rng);
     101    /**
     102     * Load global scripts that most script contexts need,
     103     * located in the /globalscripts directory. VFS must be initialized.
     104     */
     105    bool LoadGlobalScripts();
     106
     107    /**
     108     * Replace the default JS random number geenrator with a seeded, network-sync'd one.
     109     */
     110    bool ReplaceNondeterministicRNG(boost::rand48& rng);
    102111
    103112    /**
    104113     * Call a constructor function, equivalent to JS "new ctor(arg)".
  • source/scriptinterface/tests/test_ScriptInterface.h

    diff --git a/source/scriptinterface/tests/test_ScriptInterface.h b/source/scriptinterface/tests/test_ScriptInterface.h
    index ff4ab3e..8168306 100644
    a b public:  
    116116        TS_ASSERT_DIFFERS(d1, d2);
    117117
    118118        boost::rand48 rng;
    119         script.ReplaceNondeterministicFunctions(rng);
     119        script.ReplaceNondeterministicRNG(rng);
    120120        rng.seed((u64)0);
    121121        TS_ASSERT(script.Eval("Math.random()", d1));
    122122        TS_ASSERT(script.Eval("Math.random()", d2));
  • source/simulation2/components/CCmpAIManager.cpp

    diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp
    index 72f9463..c2470c1 100644
    a b private:  
    8282        {
    8383            m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
    8484
    85             m_ScriptInterface.ReplaceNondeterministicFunctions(rng);
     85            m_ScriptInterface.ReplaceNondeterministicRNG(rng);
     86            m_ScriptInterface.LoadGlobalScripts();
    8687
    8788            m_ScriptInterface.RegisterFunction<void, std::wstring, CAIPlayer::IncludeModule>("IncludeModule");
    8889            m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIPlayer::PostCommand>("PostCommand");
    public:  
    269270        m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
    270271
    271272        // TODO: ought to seed the RNG (in a network-synchronised way) before we use it
    272         m_ScriptInterface.ReplaceNondeterministicFunctions(m_RNG);
     273        m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
     274        m_ScriptInterface.LoadGlobalScripts();
    273275    }
    274276
    275277    ~CAIWorker()
  • source/simulation2/system/ComponentManager.cpp

    diff --git a/source/simulation2/system/ComponentManager.cpp b/source/simulation2/system/ComponentManager.cpp
    index c98db5c..09dce48 100644
    a b CComponentManager::CComponentManager(CSimContext& context, bool skipScriptFuncti  
    6262    m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
    6363
    6464    // TODO: ought to seed the RNG (in a network-synchronised way) before we use it
    65     m_ScriptInterface.ReplaceNondeterministicFunctions(m_RNG);
     65    m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
     66    m_ScriptInterface.LoadGlobalScripts();
    6667
    6768    // For component script tests, the test system sets up its own scripted implementation of
    6869    // these functions, so we skip registering them here in those cases
  • binaries/data/mods/public/globalscripts/Technologies.js

    -- 
    1.7.10.msysgit.1
    
    
    From a0e0b16c5cd89e3ba85b9777deac29bb3fd8171b Mon Sep 17 00:00:00 2001
    From: historicbruno <bb51@evansville.edu>
    Date: Fri, 15 Jun 2012 21:08:07 -0400
    Subject: [PATCH 2/3] Implements global GetTechModifiedProperty function by
     reusing modifications data from TechnologyManager (as
     described at
     http://trac.wildfiregames.com/ticket/1358#comment:7).
     GuiInterface.GetTemplateData applies tech modifications
     directly to its returned data which is already cached
     by the UI which avoids needing a new separate caching
     mechanism. Fixes GUIInterface tests.
    
    ---
     .../data/mods/public/globalscripts/Technologies.js |   55 +++++++++++++----
     .../public/simulation/components/GuiInterface.js   |   58 +++++++++---------
     .../simulation/components/TechnologyManager.js     |   63 ++++----------------
     .../components/tests/test_GuiInterface.js          |    6 ++
     4 files changed, 92 insertions(+), 90 deletions(-)
    
    diff --git a/binaries/data/mods/public/globalscripts/Technologies.js b/binaries/data/mods/public/globalscripts/Technologies.js
    index f1ee71a..46104d3 100644
    a b  
    44 * As such it must be completely deterministic and can't have any global state,
    55 * but each context should do its own caching as needed.
    66 *
    7  * fullEntityTemplateData: object containing all raw entity template data
    8  * fullTechTemplateData: object containing all raw tech template data
    9  * currentResearchedTechs: array of objects, one for each player containing their
    10  *      currently researched techs (acquired from the simulation/TechManager)
    11  * playerID: the player which is testing this tech
    12  * entityTemplateName: name of the entity template to retrieve the property
    13  * propertyName: name of the tech property we're applying
    14  *
    157 */
    16 function GetTechModifiedProperty(
    17     fullEntityTemplateData, fullTechTemplateData, currentResearchedTechs,
    18     playerID, entityTemplateName, propertyName)
     8
     9function GetTechModifiedProperty(currentTechModifications, entityTemplateData, propertyName, propertyValue)
    1910{
     11    // Get all modifications to this value
     12    var modifications = currentTechModifications[propertyName];
     13    if (!modifications) // no modifications so return the original value
     14        return propertyValue;
     15
     16    // Get the classes which this entity template belongs to
     17    var rawClasses = entityTemplateData.Identity.Classes;
     18    var classes = (rawClasses && "_string" in rawClasses ? rawClasses._string.split(/\s+/) : []);
     19
     20    var retValue = propertyValue;
    2021
     22    for (var i in modifications)
     23    {
     24        var modification = modifications[i];
     25        var applies = false;
     26        // See if any of the lists of classes matches this entity
     27        for (var j in modification.affects)
     28        {
     29            var hasAllClasses = true;
     30            // Check each class in affects is present for the entity
     31            for (var k in modification.affects[j])
     32                hasAllClasses = hasAllClasses && (classes.indexOf(modification.affects[j][k]) !== -1);
    2133
     34            if (hasAllClasses)
     35            {
     36                applies = true;
     37                break;
     38            }
     39        }
    2240
     41        // We found a match, apply the modification
     42        if (applies)
     43        {
     44            // Nothing is cumulative so that ordering doesn't matter as much as possible
     45            if (modification.multiplier)
     46                retValue += (modification.multiplier - 1) * propertyValue;
     47            else if (modification.add)
     48                retValue += modification.add;
     49            else if (modification.replace !== undefined) // This will depend on ordering because there is no choice
     50                retValue = modification.replace;
     51            else
     52                warn("GetTechModifiedProperty: modification format not recognised (modifying " + propertyName + "): " + uneval(modification));
     53        }
     54    }
    2355
     56    return retValue;
    2457}
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
    index 4ce8c71..15a9514 100644
    a b GuiInterface.prototype.GetSimulationState = function(player)  
    5353        var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
    5454       
    5555        // Work out what phase we are in
    56         var cmpTechMan = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
     56        var cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
    5757        var phase = "";
    58         if (cmpTechMan.IsTechnologyResearched("phase_city"))
     58        if (cmpTechnologyManager.IsTechnologyResearched("phase_city"))
    5959            phase = "city";
    60         else if (cmpTechMan.IsTechnologyResearched("phase_town"))
     60        else if (cmpTechnologyManager.IsTechnologyResearched("phase_town"))
    6161            phase = "town";
    62         else if (cmpTechMan.IsTechnologyResearched("phase_village"))
     62        else if (cmpTechnologyManager.IsTechnologyResearched("phase_village"))
    6363            phase = "village";
    6464       
    6565        // store player ally/enemy data as arrays
    GuiInterface.prototype.GetSimulationState = function(player)  
    8585            "isAlly": allies,
    8686            "isEnemy": enemies,
    8787            "buildLimits": cmpPlayerBuildLimits.GetLimits(),
    88             "buildCounts": cmpPlayerBuildLimits.GetCounts()
     88            "buildCounts": cmpPlayerBuildLimits.GetCounts(),
     89            "techModifications": cmpTechnologyManager.GetTechModifications()
    8990        };
    9091        ret.players.push(playerData);
    9192    }
    GuiInterface.prototype.ClearRenamedEntities = function(player)  
    133134
    134135GuiInterface.prototype.GetEntityState = function(player, ent)
    135136{
    136     var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     137    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
    137138
    138139    // All units must have a template; if not then it's a nonexistent entity id
    139     var template = cmpTempMan.GetCurrentTemplateName(ent);
     140    var template = cmpTemplateManager.GetCurrentTemplateName(ent);
    140141    if (!template)
    141142        return null;
    142143
    GuiInterface.prototype.GetEntityState = function(player, ent)  
    312313
    313314GuiInterface.prototype.GetTemplateData = function(player, name)
    314315{
    315     var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
    316     var template = cmpTempMan.GetTemplate(name);
     316    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     317    var template = cmpTemplateManager.GetTemplate(name);
    317318
    318319    if (!template)
    319320        return null;
    320321
    321322    var ret = {};
    322323
     324    var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
     325    var techMods = cmpTechnologyManager.GetTechModifications();
     326
    323327    if (template.Armour)
    324328    {
    325329        ret.armour = {
    326             "hack": +template.Armour.Hack,
    327             "pierce": +template.Armour.Pierce,
    328             "crush": +template.Armour.Crush,
     330            "hack": GetTechModifiedProperty(techMods, template, "Armour/Hack", +template.Armour.Hack),
     331            "pierce": GetTechModifiedProperty(techMods, template, "Armour/Pierce", +template.Armour.Pierce),
     332            "crush": GetTechModifiedProperty(techMods, template, "Armour/Crush", +template.Armour.Crush),
    329333        };
    330334    }
    331335   
    GuiInterface.prototype.GetTemplateData = function(player, name)  
    335339        for (var type in template.Attack)
    336340        {
    337341            ret.attack[type] = {
    338                 "hack": (+template.Attack[type].Hack || 0),
    339                 "pierce": (+template.Attack[type].Pierce || 0),
    340                 "crush": (+template.Attack[type].Crush || 0),
     342                "hack": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Hack", +template.Attack[type].Hack || 0),
     343                "pierce": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Pierce", +template.Attack[type].Pierce || 0),
     344                "crush": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Crush", +template.Attack[type].Crush || 0),
    341345            };
    342346        }
    343347    }
    GuiInterface.prototype.GetTemplateData = function(player, name)  
    365369    if (template.Cost)
    366370    {
    367371        ret.cost = {};
    368         if (template.Cost.Resources.food) ret.cost.food = +template.Cost.Resources.food;
    369         if (template.Cost.Resources.wood) ret.cost.wood = +template.Cost.Resources.wood;
    370         if (template.Cost.Resources.stone) ret.cost.stone = +template.Cost.Resources.stone;
    371         if (template.Cost.Resources.metal) ret.cost.metal = +template.Cost.Resources.metal;
    372         if (template.Cost.Population) ret.cost.population = +template.Cost.Population;
    373         if (template.Cost.PopulationBonus) ret.cost.populationBonus = +template.Cost.PopulationBonus;
     372        if (template.Cost.Resources.food) ret.cost.food = GetTechModifiedProperty(techMods, template, "Cost/Resources/food", +template.Cost.Resources.food);
     373        if (template.Cost.Resources.wood) ret.cost.wood = GetTechModifiedProperty(techMods, template, "Cost/Resources/wood", +template.Cost.Resources.wood);
     374        if (template.Cost.Resources.stone) ret.cost.stone = GetTechModifiedProperty(techMods, template, "Cost/Resources/stone", +template.Cost.Resources.stone);
     375        if (template.Cost.Resources.metal) ret.cost.metal = GetTechModifiedProperty(techMods, template, "Cost/Resources/metal", +template.Cost.Resources.metal);
     376        if (template.Cost.Population) ret.cost.population = GetTechModifiedProperty(techMods, template, "Cost/Population", +template.Cost.Population);
     377        if (template.Cost.PopulationBonus) ret.cost.populationBonus = GetTechModifiedProperty(techMods, template, "Cost/PopulationBonus", +template.Cost.PopulationBonus);
    374378    }
    375379   
    376380    if (template.Footprint)
    GuiInterface.prototype.GetTechnologyData = function(player, name)  
    508512
    509513GuiInterface.prototype.IsTechnologyResearched = function(player, tech)
    510514{
    511     var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
     515    var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
    512516   
    513     if (!cmpTechMan)
     517    if (!cmpTechnologyManager)
    514518        return false;
    515519   
    516     return cmpTechMan.IsTechnologyResearched(tech);
     520    return cmpTechnologyManager.IsTechnologyResearched(tech);
    517521};
    518522
    519523// Checks whether the requirements for this technology have been met
    520524GuiInterface.prototype.CheckTechnologyRequirements = function(player, tech)
    521525{
    522     var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
     526    var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
    523527   
    524     if (!cmpTechMan)
     528    if (!cmpTechnologyManager)
    525529        return false;
    526530   
    527     return cmpTechMan.CanResearch(tech);
     531    return cmpTechnologyManager.CanResearch(tech);
    528532};
    529533
    530534GuiInterface.prototype.PushNotification = function(notification)
  • binaries/data/mods/public/simulation/components/TechnologyManager.js

    diff --git a/binaries/data/mods/public/simulation/components/TechnologyManager.js b/binaries/data/mods/public/simulation/components/TechnologyManager.js
    index c52705c..5312ce2 100644
    a b TechnologyManager.prototype.ApplyModifications = function(valueName, curValue, e  
    316316        this.modificationCache[valueName] = {};
    317317   
    318318    if (!this.modificationCache[valueName][ent])
    319         this.modificationCache[valueName][ent] = this.ApplyModificationsWorker(valueName, curValue, ent);
    320    
    321     return this.modificationCache[valueName][ent];
    322 }
    323 
    324 // The code to actually apply the modification
    325 TechnologyManager.prototype.ApplyModificationsWorker = function(valueName, curValue, ent)
    326 {
    327     // Get all modifications to this value
    328     var modifications = this.modifications[valueName];
    329     if (!modifications) // no modifications so return the original value
    330         return curValue;
    331    
    332     // Get the classes which this entity belongs to
    333     var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    334     var classes = cmpIdentity.GetClassesList();
    335    
    336     var retValue = curValue;
    337    
    338     for (var i in modifications)
    339319    {
    340         var modification = modifications[i];
    341         var applies = false;
    342         // See if any of the lists of classes matches this entity
    343         for (var j in modification.affects)
    344         {
    345             var hasAllClasses = true;
    346             // Check each class in affects is present for the entity
    347             for (var k in modification.affects[j])
    348                 hasAllClasses = hasAllClasses && (classes.indexOf(modification.affects[j][k]) !== -1);
    349            
    350             if (hasAllClasses)
    351             {
    352                 applies = true;
    353                 break;
    354             }
    355         }
    356        
    357         // We found a match, apply the modification
    358         if (applies)
    359         {
    360             // Nothing is cumulative so that ordering doesn't matter as much as possible
    361             if (modification.multiplier)
    362                 retValue += (modification.multiplier - 1) * curValue;
    363             else if (modification.add)
    364                 retValue += modification.add;
    365             else if (modification.replace !== undefined) // This will depend on ordering because there is no choice
    366                 retValue = modification.replace;
    367             else
    368                 warn("modification format not recognised (modifying " + valueName + "): " + uneval(modification));
    369         }
     320        var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     321        var templateName = cmpTemplateManager.GetCurrentTemplateName(ent);
     322        this.modificationCache[valueName][ent] = GetTechModifiedProperty(this.modifications, cmpTemplateManager.GetTemplate(templateName), valueName, curValue);
    370323    }
    371324   
    372     return retValue;
    373 };
     325    return this.modificationCache[valueName][ent];
     326}
    374327
    375328// Marks a technology as being currently researched
    376329TechnologyManager.prototype.StartedResearch = function (tech)
    TechnologyManager.prototype.IsInProgress = function(tech)  
    393346        return false;
    394347};
    395348
     349// Get helper data for tech modifications
     350TechnologyManager.prototype.GetTechModifications = function()
     351{
     352    return this.modifications;
     353};
     354
    396355Engine.RegisterComponentType(IID_TechnologyManager, "TechnologyManager", TechnologyManager);
  • binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js b/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
    index f72db6c..558a95b 100644
    a b AddMock(100, IID_BuildLimits, {  
    7373
    7474AddMock(100, IID_TechnologyManager, {
    7575    IsTechnologyResearched: function(tech) { return false; },
     76    GetTechModifications: function() { return {}; },
    7677});
    7778
    7879AddMock(100, IID_StatisticsTracker, {
    AddMock(101, IID_BuildLimits, {  
    123124
    124125AddMock(101, IID_TechnologyManager, {
    125126    IsTechnologyResearched: function(tech) { if (tech == "phase_village") return true; else return false; },
     127    GetTechModifications: function() { return {}; },
    126128});
    127129
    128130AddMock(101, IID_StatisticsTracker, {
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {  
    170172            isEnemy: [true, true, true],
    171173            buildLimits: {"Foo": 10},
    172174            buildCounts: {"Foo": 5},
     175            techModifications: {},
    173176        },
    174177        {
    175178            name: "Player 2",
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {  
    187190            isEnemy: [false, false, false],
    188191            buildLimits: {"Bar": 20},
    189192            buildCounts: {"Bar": 0},
     193            techModifications: {},
    190194        }
    191195    ],
    192196    circularMap: false,
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {  
    211215            isEnemy: [true, true, true],
    212216            buildLimits: {"Foo": 10},
    213217            buildCounts: {"Foo": 5},
     218            techModifications: {},
    214219            statistics: {
    215220                unitsTrained: 10,
    216221                unitsLost: 9,
    TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {  
    244249            isEnemy: [false, false, false],
    245250            buildLimits: {"Bar": 20},
    246251            buildCounts: {"Bar": 0},
     252            techModifications: {},
    247253            statistics: {
    248254                unitsTrained: 10,
    249255                unitsLost: 9,
  • binaries/data/mods/public/simulation/ai/common-api-v2/base.js

    -- 
    1.7.10.msysgit.1
    
    
    From 83a0ae5b04bd3f91ad5f2949410e3be447bfc019 Mon Sep 17 00:00:00 2001
    From: historicbruno <bb51@evansville.edu>
    Date: Fri, 15 Jun 2012 21:26:17 -0400
    Subject: [PATCH 3/3] Adds tech modifications support to AIs (common-api-v2)
     using the same tech modifications data as the UI via
     GuiInterface. Entity and EntityTemplates are
     constructed with this data to return accurate stats
     (existing objects will need to be updated somehow, in
     response to new research). Caching may need to be
     considered as well. Fixes AI serialization with
     slightly hacky solution.
    
    ---
     .../public/simulation/ai/common-api-v2/base.js     |   10 ++++++-
     .../public/simulation/ai/common-api-v2/entity.js   |   30 +++++++++++---------
     .../mods/public/simulation/ai/jubot/gamestate.js   |    3 +-
     .../mods/public/simulation/ai/qbot/gamestate.js    |    3 +-
     .../mods/public/simulation/components/AIProxy.js   |    2 ++
     5 files changed, 31 insertions(+), 17 deletions(-)
    
    diff --git a/binaries/data/mods/public/simulation/ai/common-api-v2/base.js b/binaries/data/mods/public/simulation/ai/common-api-v2/base.js
    index 93ad9da..2a71484 100644
    a b function BaseAI(settings)  
    99    Object.defineProperty(this, "_derivedTemplates", {value: {}, enumerable: false});
    1010
    1111    this._entityMetadata = {};
     12    this._techModifications = {};
    1213
    1314    this._entityCollections = [];
    1415    this._entityCollectionsByDynProp = {};
    BaseAI.prototype.Serialize = function()  
    2930    return {
    3031        _rawEntities: rawEntities,
    3132        _entityMetadata: this._entityMetadata,
     33        _techModifications: this._techModifications,
    3234    };
    3335
    3436    // TODO: ought to get the AI script subclass to serialize its own state
    BaseAI.prototype.Deserialize = function(data)  
    4042{
    4143    var rawEntities = data._rawEntities;
    4244    this._entityMetadata = data._entityMetadata;
    43     this._entities = {}
     45    this._entities = {};
     46    this._techModifications = data._techModifications;
    4447   
    4548    for (var id in rawEntities)
    4649    {
    BaseAI.prototype.GetTemplate = function(name)  
    8992
    9093BaseAI.prototype.HandleMessage = function(state)
    9194{
     95    // Update tech modifications data (used for constructing entities and templates)
     96    this._techModifications = state.players[this._player].techModifications;
     97
    9298    if (!this._entities)
    9399    {
    94100        // Do a (shallow) clone of all the initial entity properties (in order
    BaseAI.prototype.HandleMessage = function(state)  
    116122    this.templates = this._templates;
    117123    this.territoryMap = state.territoryMap;
    118124    this.timeElapsed = state.timeElapsed;
     125    this.techModifications = this._techModifications;
    119126
    120127    Engine.ProfileStop();
    121128
    BaseAI.prototype.HandleMessage = function(state)  
    131138    delete this.templates;
    132139    delete this.territoryMap;
    133140    delete this.timeElapsed;
     141    delete this.techModifications;
    134142};
    135143
    136144BaseAI.prototype.ApplyEntitiesDelta = function(state)
  • binaries/data/mods/public/simulation/ai/common-api-v2/entity.js

    diff --git a/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js b/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js
    index 7f8cb44..9e182ad 100644
    a b  
    11var EntityTemplate = Class({
    22
    3     _init: function(template)
     3    _init: function(template, techModifications)
    44    {
     5        this._techModifications = techModifications;
    56        this._template = template;
    67    },
    78
    var EntityTemplate = Class({  
    3435
    3536        var ret = {};
    3637        for (var type in this._template.Cost.Resources)
    37             ret[type] = +this._template.Cost.Resources[type];
     38            ret[type] = GetTechModifiedProperty(this._techModifications, this._template, "Cost/Resources/"+type, +this._template.Cost.Resources[type]);
    3839        return ret;
    3940    },
    4041
    var EntityTemplate = Class({  
    6970            return undefined;
    7071
    7172        return {
    72             hack: +this._template.Armour.Hack,
    73             pierce: +this._template.Armour.Pierce,
    74             crush: +this._template.Armour.Crush
     73            hack: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Hack", +this._template.Armour.Hack),
     74            pierce: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Pierce", +this._template.Armour.Pierce),
     75            crush: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Crush", +this._template.Armour.Crush)
    7576        };
    7677    },
    7778
    var EntityTemplate = Class({  
    9192            return undefined;
    9293
    9394        return {
    94             max: +this._template.Attack[type].MaxRange,
    95             min: +(this._template.Attack[type].MinRange || 0)
     95            max: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MaxRange", +this._template.Attack[type].MaxRange),
     96            min: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MinRange", +(this._template.Attack[type].MinRange || 0))
    9697        };
    9798    },
    9899
    var EntityTemplate = Class({  
    101102            return undefined;
    102103
    103104        return {
    104             hack: +(this._template.Attack[type].Hack || 0),
    105             pierce: +(this._template.Attack[type].Pierce || 0),
    106             crush: +(this._template.Attack[type].Crush || 0)
     105            hack: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Hack", +(this._template.Attack[type].Hack || 0)),
     106            pierce: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Pierce", +(this._template.Attack[type].Pierce || 0)),
     107            crush: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Crush", +(this._template.Attack[type].Crush || 0))
    107108        };
    108109    },
    109110   
    var EntityTemplate = Class({  
    112113            return undefined;
    113114
    114115        return {
    115             prepare: +(this._template.Attack[type].PrepareTime || 0),
    116             repeat: +(this._template.Attack[type].RepeatTime || 1000)
     116            prepare: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/PrepareTime", +(this._template.Attack[type].PrepareTime || 0)),
     117            repeat: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/RepeatTime", +(this._template.Attack[type].RepeatTime || 1000))
    117118        };
    118119    },
    119120
    var EntityTemplate = Class({  
    152153        if (!this._template.ResourceGatherer)
    153154            return undefined;
    154155        var ret = {};
     156        var baseSpeed = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/BaseSpeed", +this._template.ResourceGatherer.BaseSpeed);
    155157        for (var r in this._template.ResourceGatherer.Rates)
    156             ret[r] = this._template.ResourceGatherer.Rates[r] * this._template.ResourceGatherer.BaseSpeed;
     158            ret[r] = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/Rates/"+r, +this._template.ResourceGatherer.Rates[r]) * baseSpeed;
    157159        return ret;
    158160    },
    159161
    var Entity = Class({  
    229231
    230232    _init: function(baseAI, entity)
    231233    {
    232         this._super.call(this, baseAI.GetTemplate(entity.template));
     234        this._super.call(this, baseAI.GetTemplate(entity.template), baseAI._techModifications);
    233235
    234236        this._ai = baseAI;
    235237        this._templateName = entity.template;
  • binaries/data/mods/public/simulation/ai/jubot/gamestate.js

    diff --git a/binaries/data/mods/public/simulation/ai/jubot/gamestate.js b/binaries/data/mods/public/simulation/ai/jubot/gamestate.js
    index d7ae01c..5fc910a 100644
    a b var GameState = Class({  
    1414        this.entities = ai.entities;
    1515        this.player = ai.player;
    1616        this.playerData = ai.playerData;
     17        this.techModifications = ai.techModifications;
    1718   
    1819        if (!this.ai._gameStateStore){
    1920            this.ai._gameStateStore = {};
    var GameState = Class({  
    3031    {
    3132        if (!this.templates[type])
    3233            return null;
    33         return new EntityTemplate(this.templates[type]);
     34        return new EntityTemplate(this.templates[type], this.techModifications);
    3435    },
    3536
    3637    applyCiv: function(str)
  • binaries/data/mods/public/simulation/ai/qbot/gamestate.js

    diff --git a/binaries/data/mods/public/simulation/ai/qbot/gamestate.js b/binaries/data/mods/public/simulation/ai/qbot/gamestate.js
    index cd49dee..e9e14d7 100644
    a b var GameState = function(ai) {  
    1212    this.player = ai.player;
    1313    this.playerData = ai.playerData;
    1414    this.buildingsBuilt = 0;
     15    this.techModifications = ai.techModifications;
    1516   
    1617    if (!this.ai._gameStateStore){
    1718        this.ai._gameStateStore = {};
    GameState.prototype.getTemplate = function(type) {  
    4142        return null;
    4243    }
    4344   
    44     return new EntityTemplate(this.templates[type]);
     45    return new EntityTemplate(this.templates[type], this.techModifications);
    4546};
    4647
    4748GameState.prototype.applyCiv = function(str) {
  • binaries/data/mods/public/simulation/components/AIProxy.js

    diff --git a/binaries/data/mods/public/simulation/components/AIProxy.js b/binaries/data/mods/public/simulation/components/AIProxy.js
    index aa73f4f..7e0bc4a 100644
    a b AIProxy.prototype.OnFoundationProgressChanged = function(msg)  
    155155
    156156// TODO: event handlers for all the other things
    157157
     158// TODO: research - could updates tech modifications data in the Entity/EntityTemplates
     159
    158160AIProxy.prototype.GetFullRepresentation = function()
    159161{
    160162    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);