mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 06:13:55 -07:00
Applies tech modifications to template data returned by GuiInterface. Extends engine to load arbitrary global scripts, separates this from RNG replacement. Refs #1193. Loads global scripts for most script contexts for consistency. Adds simulation tests for global scripts. This was SVN commit r12056.
This commit is contained in:
parent
db943341a8
commit
99d04e93bb
14 changed files with 195 additions and 111 deletions
|
|
@ -0,0 +1,4 @@
|
|||
function GlobalSubtractionHelper(a, b)
|
||||
{
|
||||
return a-b;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
function TestScript1_GlobalHelper() {}
|
||||
|
||||
TestScript1_GlobalHelper.prototype.GetX = function()
|
||||
{
|
||||
return GlobalSubtractionHelper(1, -1);
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "TestScript1_GlobalHelper", TestScript1_GlobalHelper);
|
||||
67
binaries/data/mods/public/globalscripts/Technologies.js
Normal file
67
binaries/data/mods/public/globalscripts/Technologies.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* This file contains shared logic for applying tech modifications in GUI, AI,
|
||||
* and simulation scripts. As such it must be fully deterministic and not store
|
||||
* any global state, but each context should do its own caching as needed.
|
||||
* Also it cannot directly access the simulation and requires data passed to it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns modified property value if at least one tech modification is found
|
||||
* applicable to the given entity template; else, returns its original value.
|
||||
*
|
||||
* currentTechModifications: mapping of property names to modification arrays,
|
||||
* retrieved from the intended player's TechnologyManager.
|
||||
* entityTemplateData: raw entity template object.
|
||||
* propertyName: name of the tech modification to apply.
|
||||
* propertyValue: original value of property to be modified.
|
||||
*/
|
||||
function GetTechModifiedProperty(currentTechModifications, entityTemplateData, propertyName, propertyValue)
|
||||
{
|
||||
// Get all modifications to this value
|
||||
var modifications = currentTechModifications[propertyName];
|
||||
if (!modifications) // no modifications so return the original value
|
||||
return propertyValue;
|
||||
|
||||
// TODO: will we ever need the full template?
|
||||
// Get the classes which this entity template belongs to
|
||||
var rawClasses = entityTemplateData.Identity.Classes;
|
||||
var classes = (rawClasses && "_string" in rawClasses ? rawClasses._string.split(/\s+/) : []);
|
||||
|
||||
var retValue = propertyValue;
|
||||
|
||||
for (var i in modifications)
|
||||
{
|
||||
var modification = modifications[i];
|
||||
var applies = false;
|
||||
// See if any of the lists of classes matches this entity
|
||||
for (var j in modification.affects)
|
||||
{
|
||||
var hasAllClasses = true;
|
||||
// Check each class in affects is present for the entity
|
||||
for (var k in modification.affects[j])
|
||||
hasAllClasses = hasAllClasses && (classes.indexOf(modification.affects[j][k]) !== -1);
|
||||
|
||||
if (hasAllClasses)
|
||||
{
|
||||
applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We found a match, apply the modification
|
||||
if (applies)
|
||||
{
|
||||
// Nothing is cumulative so that ordering doesn't matter as much as possible
|
||||
if (modification.multiplier)
|
||||
retValue += (modification.multiplier - 1) * propertyValue;
|
||||
else if (modification.add)
|
||||
retValue += modification.add;
|
||||
else if (modification.replace !== undefined) // This will depend on ordering because there is no choice
|
||||
retValue = modification.replace;
|
||||
else
|
||||
warn("GetTechModifiedProperty: modification format not recognised (modifying " + propertyName + "): " + uneval(modification));
|
||||
}
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
|
@ -53,13 +53,13 @@ GuiInterface.prototype.GetSimulationState = function(player)
|
|||
var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
|
||||
|
||||
// Work out what phase we are in
|
||||
var cmpTechMan = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
|
||||
var cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
|
||||
var phase = "";
|
||||
if (cmpTechMan.IsTechnologyResearched("phase_city"))
|
||||
if (cmpTechnologyManager.IsTechnologyResearched("phase_city"))
|
||||
phase = "city";
|
||||
else if (cmpTechMan.IsTechnologyResearched("phase_town"))
|
||||
else if (cmpTechnologyManager.IsTechnologyResearched("phase_town"))
|
||||
phase = "town";
|
||||
else if (cmpTechMan.IsTechnologyResearched("phase_village"))
|
||||
else if (cmpTechnologyManager.IsTechnologyResearched("phase_village"))
|
||||
phase = "village";
|
||||
|
||||
// store player ally/enemy data as arrays
|
||||
|
|
@ -85,7 +85,8 @@ GuiInterface.prototype.GetSimulationState = function(player)
|
|||
"isAlly": allies,
|
||||
"isEnemy": enemies,
|
||||
"buildLimits": cmpPlayerBuildLimits.GetLimits(),
|
||||
"buildCounts": cmpPlayerBuildLimits.GetCounts()
|
||||
"buildCounts": cmpPlayerBuildLimits.GetCounts(),
|
||||
"techModifications": cmpTechnologyManager.GetTechModifications()
|
||||
};
|
||||
ret.players.push(playerData);
|
||||
}
|
||||
|
|
@ -133,10 +134,10 @@ GuiInterface.prototype.ClearRenamedEntities = function(player)
|
|||
|
||||
GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
{
|
||||
var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
|
||||
// All units must have a template; if not then it's a nonexistent entity id
|
||||
var template = cmpTempMan.GetCurrentTemplateName(ent);
|
||||
var template = cmpTemplateManager.GetCurrentTemplateName(ent);
|
||||
if (!template)
|
||||
return null;
|
||||
|
||||
|
|
@ -312,20 +313,23 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
|||
|
||||
GuiInterface.prototype.GetTemplateData = function(player, name)
|
||||
{
|
||||
var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTempMan.GetTemplate(name);
|
||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTemplateManager.GetTemplate(name);
|
||||
|
||||
if (!template)
|
||||
return null;
|
||||
|
||||
var ret = {};
|
||||
|
||||
var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
var techMods = cmpTechnologyManager.GetTechModifications();
|
||||
|
||||
if (template.Armour)
|
||||
{
|
||||
ret.armour = {
|
||||
"hack": +template.Armour.Hack,
|
||||
"pierce": +template.Armour.Pierce,
|
||||
"crush": +template.Armour.Crush,
|
||||
"hack": GetTechModifiedProperty(techMods, template, "Armour/Hack", +template.Armour.Hack),
|
||||
"pierce": GetTechModifiedProperty(techMods, template, "Armour/Pierce", +template.Armour.Pierce),
|
||||
"crush": GetTechModifiedProperty(techMods, template, "Armour/Crush", +template.Armour.Crush),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -335,9 +339,9 @@ GuiInterface.prototype.GetTemplateData = function(player, name)
|
|||
for (var type in template.Attack)
|
||||
{
|
||||
ret.attack[type] = {
|
||||
"hack": (+template.Attack[type].Hack || 0),
|
||||
"pierce": (+template.Attack[type].Pierce || 0),
|
||||
"crush": (+template.Attack[type].Crush || 0),
|
||||
"hack": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Hack", +template.Attack[type].Hack || 0),
|
||||
"pierce": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Pierce", +template.Attack[type].Pierce || 0),
|
||||
"crush": GetTechModifiedProperty(techMods, template, "Attack/"+type+"/Crush", +template.Attack[type].Crush || 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -365,12 +369,12 @@ GuiInterface.prototype.GetTemplateData = function(player, name)
|
|||
if (template.Cost)
|
||||
{
|
||||
ret.cost = {};
|
||||
if (template.Cost.Resources.food) ret.cost.food = +template.Cost.Resources.food;
|
||||
if (template.Cost.Resources.wood) ret.cost.wood = +template.Cost.Resources.wood;
|
||||
if (template.Cost.Resources.stone) ret.cost.stone = +template.Cost.Resources.stone;
|
||||
if (template.Cost.Resources.metal) ret.cost.metal = +template.Cost.Resources.metal;
|
||||
if (template.Cost.Population) ret.cost.population = +template.Cost.Population;
|
||||
if (template.Cost.PopulationBonus) ret.cost.populationBonus = +template.Cost.PopulationBonus;
|
||||
if (template.Cost.Resources.food) ret.cost.food = GetTechModifiedProperty(techMods, template, "Cost/Resources/food", +template.Cost.Resources.food);
|
||||
if (template.Cost.Resources.wood) ret.cost.wood = GetTechModifiedProperty(techMods, template, "Cost/Resources/wood", +template.Cost.Resources.wood);
|
||||
if (template.Cost.Resources.stone) ret.cost.stone = GetTechModifiedProperty(techMods, template, "Cost/Resources/stone", +template.Cost.Resources.stone);
|
||||
if (template.Cost.Resources.metal) ret.cost.metal = GetTechModifiedProperty(techMods, template, "Cost/Resources/metal", +template.Cost.Resources.metal);
|
||||
if (template.Cost.Population) ret.cost.population = GetTechModifiedProperty(techMods, template, "Cost/Population", +template.Cost.Population);
|
||||
if (template.Cost.PopulationBonus) ret.cost.populationBonus = GetTechModifiedProperty(techMods, template, "Cost/PopulationBonus", +template.Cost.PopulationBonus);
|
||||
}
|
||||
|
||||
if (template.Footprint)
|
||||
|
|
@ -508,23 +512,23 @@ GuiInterface.prototype.GetTechnologyData = function(player, name)
|
|||
|
||||
GuiInterface.prototype.IsTechnologyResearched = function(player, tech)
|
||||
{
|
||||
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
|
||||
if (!cmpTechMan)
|
||||
if (!cmpTechnologyManager)
|
||||
return false;
|
||||
|
||||
return cmpTechMan.IsTechnologyResearched(tech);
|
||||
return cmpTechnologyManager.IsTechnologyResearched(tech);
|
||||
};
|
||||
|
||||
// Checks whether the requirements for this technology have been met
|
||||
GuiInterface.prototype.CheckTechnologyRequirements = function(player, tech)
|
||||
{
|
||||
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
var cmpTechnologyManager = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
|
||||
if (!cmpTechMan)
|
||||
if (!cmpTechnologyManager)
|
||||
return false;
|
||||
|
||||
return cmpTechMan.CanResearch(tech);
|
||||
return cmpTechnologyManager.CanResearch(tech);
|
||||
};
|
||||
|
||||
GuiInterface.prototype.PushNotification = function(notification)
|
||||
|
|
|
|||
|
|
@ -316,62 +316,15 @@ TechnologyManager.prototype.ApplyModifications = function(valueName, curValue, e
|
|||
this.modificationCache[valueName] = {};
|
||||
|
||||
if (!this.modificationCache[valueName][ent])
|
||||
this.modificationCache[valueName][ent] = this.ApplyModificationsWorker(valueName, curValue, ent);
|
||||
{
|
||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var templateName = cmpTemplateManager.GetCurrentTemplateName(ent);
|
||||
this.modificationCache[valueName][ent] = GetTechModifiedProperty(this.modifications, cmpTemplateManager.GetTemplate(templateName), valueName, curValue);
|
||||
}
|
||||
|
||||
return this.modificationCache[valueName][ent];
|
||||
}
|
||||
|
||||
// The code to actually apply the modification
|
||||
TechnologyManager.prototype.ApplyModificationsWorker = function(valueName, curValue, ent)
|
||||
{
|
||||
// Get all modifications to this value
|
||||
var modifications = this.modifications[valueName];
|
||||
if (!modifications) // no modifications so return the original value
|
||||
return curValue;
|
||||
|
||||
// Get the classes which this entity belongs to
|
||||
var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
var classes = cmpIdentity.GetClassesList();
|
||||
|
||||
var retValue = curValue;
|
||||
|
||||
for (var i in modifications)
|
||||
{
|
||||
var modification = modifications[i];
|
||||
var applies = false;
|
||||
// See if any of the lists of classes matches this entity
|
||||
for (var j in modification.affects)
|
||||
{
|
||||
var hasAllClasses = true;
|
||||
// Check each class in affects is present for the entity
|
||||
for (var k in modification.affects[j])
|
||||
hasAllClasses = hasAllClasses && (classes.indexOf(modification.affects[j][k]) !== -1);
|
||||
|
||||
if (hasAllClasses)
|
||||
{
|
||||
applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We found a match, apply the modification
|
||||
if (applies)
|
||||
{
|
||||
// Nothing is cumulative so that ordering doesn't matter as much as possible
|
||||
if (modification.multiplier)
|
||||
retValue += (modification.multiplier - 1) * curValue;
|
||||
else if (modification.add)
|
||||
retValue += modification.add;
|
||||
else if (modification.replace !== undefined) // This will depend on ordering because there is no choice
|
||||
retValue = modification.replace;
|
||||
else
|
||||
warn("modification format not recognised (modifying " + valueName + "): " + uneval(modification));
|
||||
}
|
||||
}
|
||||
|
||||
return retValue;
|
||||
};
|
||||
|
||||
// Marks a technology as being currently researched
|
||||
TechnologyManager.prototype.StartedResearch = function (tech)
|
||||
{
|
||||
|
|
@ -393,4 +346,10 @@ TechnologyManager.prototype.IsInProgress = function(tech)
|
|||
return false;
|
||||
};
|
||||
|
||||
// Get helper data for tech modifications
|
||||
TechnologyManager.prototype.GetTechModifications = function()
|
||||
{
|
||||
return this.modifications;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_TechnologyManager, "TechnologyManager", TechnologyManager);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ AddMock(100, IID_BuildLimits, {
|
|||
|
||||
AddMock(100, IID_TechnologyManager, {
|
||||
IsTechnologyResearched: function(tech) { return false; },
|
||||
GetTechModifications: function() { return {}; },
|
||||
});
|
||||
|
||||
AddMock(100, IID_StatisticsTracker, {
|
||||
|
|
@ -123,6 +124,7 @@ AddMock(101, IID_BuildLimits, {
|
|||
|
||||
AddMock(101, IID_TechnologyManager, {
|
||||
IsTechnologyResearched: function(tech) { if (tech == "phase_village") return true; else return false; },
|
||||
GetTechModifications: function() { return {}; },
|
||||
});
|
||||
|
||||
AddMock(101, IID_StatisticsTracker, {
|
||||
|
|
@ -170,6 +172,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
|
|||
isEnemy: [true, true, true],
|
||||
buildLimits: {"Foo": 10},
|
||||
buildCounts: {"Foo": 5},
|
||||
techModifications: {},
|
||||
},
|
||||
{
|
||||
name: "Player 2",
|
||||
|
|
@ -187,6 +190,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
|
|||
isEnemy: [false, false, false],
|
||||
buildLimits: {"Bar": 20},
|
||||
buildCounts: {"Bar": 0},
|
||||
techModifications: {},
|
||||
}
|
||||
],
|
||||
circularMap: false,
|
||||
|
|
@ -211,6 +215,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
|
|||
isEnemy: [true, true, true],
|
||||
buildLimits: {"Foo": 10},
|
||||
buildCounts: {"Foo": 5},
|
||||
techModifications: {},
|
||||
statistics: {
|
||||
unitsTrained: 10,
|
||||
unitsLost: 9,
|
||||
|
|
@ -244,6 +249,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
|
|||
isEnemy: [false, false, false],
|
||||
buildLimits: {"Bar": 20},
|
||||
buildCounts: {"Bar": 0},
|
||||
techModifications: {},
|
||||
statistics: {
|
||||
unitsTrained: 10,
|
||||
unitsLost: 9,
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ bool CMapGeneratorWorker::Run()
|
|||
m_ScriptInterface->SetCallbackData(static_cast<void*> (this));
|
||||
|
||||
// Replace RNG with a seeded deterministic function
|
||||
m_ScriptInterface->ReplaceNondeterministicFunctions(m_MapGenRNG);
|
||||
m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
|
||||
m_ScriptInterface->LoadGlobalScripts();
|
||||
|
||||
// Functions for RMS
|
||||
m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ CGUIManager::CGUIManager(ScriptInterface& scriptInterface) :
|
|||
{
|
||||
ENSURE(ScriptInterface::GetCallbackData(scriptInterface.GetContext()) == NULL);
|
||||
scriptInterface.SetCallbackData(this);
|
||||
|
||||
scriptInterface.LoadGlobalScripts();
|
||||
}
|
||||
|
||||
CGUIManager::~CGUIManager()
|
||||
|
|
|
|||
|
|
@ -609,38 +609,44 @@ void* ScriptInterface::GetCallbackData(JSContext* cx)
|
|||
return JS_GetContextPrivate(cx);
|
||||
}
|
||||
|
||||
void ScriptInterface::ReplaceNondeterministicFunctions(boost::rand48& rng)
|
||||
bool ScriptInterface::LoadGlobalScripts()
|
||||
{
|
||||
jsval math;
|
||||
if (!JS_GetProperty(m->m_cx, m->m_glob, "Math", &math) || !JSVAL_IS_OBJECT(math))
|
||||
{
|
||||
LOGERROR(L"ReplaceNondeterministicFunctions: failed to get Math");
|
||||
return;
|
||||
}
|
||||
// Ignore this failure in tests
|
||||
if (!g_VFS)
|
||||
return false;
|
||||
|
||||
JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0,
|
||||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
if (!random)
|
||||
// Load and execute *.js in the global scripts directory
|
||||
VfsPaths pathnames;
|
||||
vfs::GetPathnames(g_VFS, L"globalscripts/", L"*.js", pathnames);
|
||||
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
|
||||
{
|
||||
LOGERROR(L"ReplaceNondeterministicFunctions: failed to replace Math.random");
|
||||
return;
|
||||
}
|
||||
// Store the RNG in a slot which is sort-of-guaranteed to be unused by the JS engine
|
||||
JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng));
|
||||
|
||||
// Load a script which replaces some of the system dependent trig functions
|
||||
VfsPath path("globalscripts/Math.js");
|
||||
|
||||
// This function is called from tests without the VFS set up,
|
||||
// so silently fail in that case because every other option seems worse
|
||||
if (g_VFS && VfsFileExists(path))
|
||||
{
|
||||
if (!this->LoadGlobalScriptFile(path))
|
||||
if (!LoadGlobalScriptFile(*it))
|
||||
{
|
||||
LOGERROR(L"ReplaceNondeterministicFunctions: failed to load %ls", path.string().c_str());
|
||||
return;
|
||||
LOGERROR(L"LoadGlobalScripts: Failed to load script %ls", it->string().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
|
||||
{
|
||||
jsval math;
|
||||
if (JS_GetProperty(m->m_cx, m->m_glob, "Math", &math) && JSVAL_IS_OBJECT(math))
|
||||
{
|
||||
JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0,
|
||||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
if (random)
|
||||
{
|
||||
// Store the RNG in a slot which is sort-of-guaranteed to be unused by the JS engine
|
||||
if (JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGERROR(L"ReplaceNondeterministicRNG: failed to replace Math.random");
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
|
||||
|
|
|
|||
|
|
@ -98,7 +98,16 @@ public:
|
|||
JSContext* GetContext() const;
|
||||
JSRuntime* GetRuntime() const;
|
||||
|
||||
void ReplaceNondeterministicFunctions(boost::rand48& rng);
|
||||
/**
|
||||
* Load global scripts that most script contexts need,
|
||||
* located in the /globalscripts directory. VFS must be initialized.
|
||||
*/
|
||||
bool LoadGlobalScripts();
|
||||
|
||||
/**
|
||||
* Replace the default JS random number geenrator with a seeded, network-sync'd one.
|
||||
*/
|
||||
bool ReplaceNondeterministicRNG(boost::rand48& rng);
|
||||
|
||||
/**
|
||||
* Call a constructor function, equivalent to JS "new ctor(arg)".
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ public:
|
|||
TS_ASSERT_DIFFERS(d1, d2);
|
||||
|
||||
boost::rand48 rng;
|
||||
script.ReplaceNondeterministicFunctions(rng);
|
||||
script.ReplaceNondeterministicRNG(rng);
|
||||
rng.seed((u64)0);
|
||||
TS_ASSERT(script.Eval("Math.random()", d1));
|
||||
TS_ASSERT(script.Eval("Math.random()", d2));
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ private:
|
|||
{
|
||||
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
|
||||
|
||||
m_ScriptInterface.ReplaceNondeterministicFunctions(rng);
|
||||
m_ScriptInterface.ReplaceNondeterministicRNG(rng);
|
||||
m_ScriptInterface.LoadGlobalScripts();
|
||||
|
||||
m_ScriptInterface.RegisterFunction<void, std::wstring, CAIPlayer::IncludeModule>("IncludeModule");
|
||||
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIPlayer::PostCommand>("PostCommand");
|
||||
|
|
@ -269,7 +270,8 @@ public:
|
|||
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
|
||||
|
||||
// TODO: ought to seed the RNG (in a network-synchronised way) before we use it
|
||||
m_ScriptInterface.ReplaceNondeterministicFunctions(m_RNG);
|
||||
m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
|
||||
m_ScriptInterface.LoadGlobalScripts();
|
||||
}
|
||||
|
||||
~CAIWorker()
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ CComponentManager::CComponentManager(CSimContext& context, bool skipScriptFuncti
|
|||
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
|
||||
|
||||
// TODO: ought to seed the RNG (in a network-synchronised way) before we use it
|
||||
m_ScriptInterface.ReplaceNondeterministicFunctions(m_RNG);
|
||||
m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
|
||||
m_ScriptInterface.LoadGlobalScripts();
|
||||
|
||||
// For component script tests, the test system sets up its own scripted implementation of
|
||||
// these functions, so we skip registering them here in those cases
|
||||
|
|
|
|||
|
|
@ -287,6 +287,21 @@ public:
|
|||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 3);
|
||||
}
|
||||
|
||||
void test_script_global_helper()
|
||||
{
|
||||
CSimContext context;
|
||||
CComponentManager man(context);
|
||||
man.LoadComponentTypes();
|
||||
TS_ASSERT(man.LoadScript(L"simulation/components/test-global-helper.js"));
|
||||
|
||||
entity_id_t ent1 = 1;
|
||||
CParamNode noParam;
|
||||
|
||||
man.AddComponent(ent1, man.LookupCID("TestScript1_GlobalHelper"), noParam);
|
||||
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 2);
|
||||
}
|
||||
|
||||
void test_script_interface()
|
||||
{
|
||||
CSimContext context;
|
||||
|
|
|
|||
Loading…
Reference in a new issue