# Basic in-game building placement with new simulation system
This was SVN commit r7285.
This commit is contained in:
parent
0d9c9d646b
commit
953fb41c82
@ -9,3 +9,17 @@ TestScript1_AddEntity.prototype.GetX = function()
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "TestScript1_AddEntity", TestScript1_AddEntity);
|
||||
|
||||
|
||||
|
||||
function TestScript1_AddLocalEntity() {}
|
||||
|
||||
TestScript1_AddLocalEntity.prototype.GetX = function()
|
||||
{
|
||||
if (Engine.AddLocalEntity("bogus-template-name") !== 0)
|
||||
throw new Error("bogus AddLocalEntity failed");
|
||||
|
||||
return Engine.AddLocalEntity("test1");
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "TestScript1_AddLocalEntity", TestScript1_AddLocalEntity);
|
||||
|
@ -0,0 +1,8 @@
|
||||
function TestScript1_DestroyEntity() {}
|
||||
|
||||
TestScript1_DestroyEntity.prototype.GetX = function()
|
||||
{
|
||||
Engine.DestroyEntity(10);
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "TestScript1_DestroyEntity", TestScript1_DestroyEntity);
|
@ -10,6 +10,7 @@ HotloadA.prototype.GetX = function() {
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "HotloadA", HotloadA);
|
||||
|
||||
|
||||
function HotloadB() {}
|
||||
|
||||
HotloadB.prototype.Init = function() {
|
||||
@ -21,3 +22,12 @@ HotloadB.prototype.GetX = function() {
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "HotloadB", HotloadB);
|
||||
|
||||
|
||||
function HotloadC() {}
|
||||
|
||||
Engine.RegisterInterface("HotloadInterface");
|
||||
Engine.RegisterComponentType(IID_HotloadInterface, "HotloadC", HotloadC);
|
||||
|
||||
|
||||
Engine.RegisterGlobal("HotloadGlobal", 1);
|
||||
|
@ -9,3 +9,12 @@ HotloadA.prototype.GetX = function() {
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Test1, "HotloadA", HotloadA);
|
||||
|
||||
|
||||
function HotloadC() {}
|
||||
|
||||
Engine.RegisterInterface("HotloadInterface");
|
||||
Engine.RegisterComponentType(IID_HotloadInterface, "HotloadC", HotloadC);
|
||||
|
||||
|
||||
Engine.RegisterGlobal("HotloadGlobal", 2);
|
||||
|
@ -7,9 +7,12 @@ const SDL_BUTTON_RIGHT = 3;
|
||||
|
||||
var INPUT_NORMAL = 0;
|
||||
var INPUT_DRAGGING = 1;
|
||||
var INPUT_BUILDING_PLACEMENT = 2;
|
||||
|
||||
var inputState = INPUT_NORMAL;
|
||||
|
||||
var placementEntity = "";
|
||||
|
||||
function handleInputBeforeGui(ev)
|
||||
{
|
||||
return false;
|
||||
@ -67,6 +70,43 @@ function handleInputAfterGui(ev)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT_BUILDING_PLACEMENT:
|
||||
switch (ev.type)
|
||||
{
|
||||
case "mousemotion":
|
||||
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
|
||||
var angle = Math.PI;
|
||||
Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {"template": placementEntity, "x": target.x, "z": target.z, "angle": angle});
|
||||
|
||||
return false; // continue processing mouse motion
|
||||
|
||||
case "mousebuttondown":
|
||||
if (ev.button == SDL_BUTTON_LEFT)
|
||||
{
|
||||
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
|
||||
var angle = Math.PI;
|
||||
Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {"template": ""});
|
||||
Engine.PostNetworkCommand({"type": "construct", "template": placementEntity, "x": target.x, "z": target.z, "angle": angle});
|
||||
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
else if (ev.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {"template": ""});
|
||||
|
||||
inputState = INPUT_NORMAL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function testBuild(ent)
|
||||
{
|
||||
placementEntity = ent;
|
||||
inputState = INPUT_BUILDING_PLACEMENT;
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ function init()
|
||||
function onSimulationUpdate()
|
||||
{
|
||||
updateDebug();
|
||||
|
||||
updateBuildButton();
|
||||
}
|
||||
|
||||
function updateDebug()
|
||||
@ -20,3 +22,21 @@ function updateDebug()
|
||||
}
|
||||
debug.caption = text;
|
||||
}
|
||||
|
||||
function updateBuildButton()
|
||||
{
|
||||
var selection = getEntitySelection();
|
||||
if (selection.length)
|
||||
{
|
||||
var entity = Engine.GetEntityState(selection[0]);
|
||||
if (entity.buildEntities && entity.buildEntities.length)
|
||||
{
|
||||
var ent = entity.buildEntities[0];
|
||||
getGUIObjectByName("testBuild").caption = "Construct "+ent;
|
||||
getGUIObjectByName("testBuild").onpress = function() { testBuild(ent) };
|
||||
getGUIObjectByName("testBuild").hidden = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
getGUIObjectByName("testBuild").hidden = true;
|
||||
}
|
||||
|
@ -40,6 +40,15 @@
|
||||
this.hidden = !this.hidden;
|
||||
</action>
|
||||
|
||||
<object name="testBuild"
|
||||
type="button"
|
||||
style="wheatButton"
|
||||
size="64 100%-96 320 100%-64"
|
||||
hidden="true"
|
||||
>
|
||||
Construct thing
|
||||
</object>
|
||||
|
||||
<!-- Exit hotkey -->
|
||||
<object name="leave" hotkey="leave">
|
||||
<action on="Press"><![CDATA[
|
||||
|
19
binaries/data/mods/public/simulation/components/Builder.js
Normal file
19
binaries/data/mods/public/simulation/components/Builder.js
Normal file
@ -0,0 +1,19 @@
|
||||
function Builder() {}
|
||||
|
||||
Builder.prototype.Init = function()
|
||||
{
|
||||
};
|
||||
|
||||
Builder.prototype.GetEntitiesList = function()
|
||||
{
|
||||
var string = this.template.Entities._string;
|
||||
|
||||
// Replace the "{civ}" codes with this entity's civ ID
|
||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
||||
if (cmpIdentity)
|
||||
string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv());
|
||||
|
||||
return string.split(/\s+/);
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Builder, "Builder", Builder);
|
@ -6,7 +6,16 @@ Cost.prototype.Init = function()
|
||||
|
||||
Cost.prototype.GetPopCost = function()
|
||||
{
|
||||
if ('Population' in this.template)
|
||||
return +this.template.Population;
|
||||
return 0;
|
||||
};
|
||||
|
||||
Cost.prototype.GetPopBonus = function()
|
||||
{
|
||||
if ('PopulationBonus' in this.template)
|
||||
return +this.template.PopulationBonus;
|
||||
return 0;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Cost, "Cost", Cost);
|
||||
|
@ -1,24 +1,47 @@
|
||||
function GuiInterface() {}
|
||||
|
||||
GuiInterface.prototype.Init = function() {};
|
||||
GuiInterface.prototype.Init = function()
|
||||
{
|
||||
// TODO: need to not serialise this value
|
||||
this.placementEntity = undefined; // = undefined or [templateName, entityID]
|
||||
};
|
||||
|
||||
GuiInterface.prototype.GetSimulationState = function(player)
|
||||
{
|
||||
// print("GetSimulationState "+player+"\n");
|
||||
var ret = {
|
||||
players: []
|
||||
};
|
||||
|
||||
return { test: "simulation state" };
|
||||
var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
||||
var n = cmpPlayerMan.GetNumPlayers();
|
||||
for (var i = 0; i < n; ++i)
|
||||
{
|
||||
var playerEnt = cmpPlayerMan.GetPlayerByID(i);
|
||||
var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
|
||||
var player = {
|
||||
popCount: cmpPlayer.GetPopulationCount(),
|
||||
popLimit: cmpPlayer.GetPopulationLimit()
|
||||
};
|
||||
ret.players.push(player);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
{
|
||||
// print("GetEntityState "+player+" "+ent+"\n");
|
||||
|
||||
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
|
||||
|
||||
var ret = {
|
||||
position: cmpPosition.GetPosition()
|
||||
};
|
||||
|
||||
var cmpBuilder = Engine.QueryInterface(ent, IID_Builder);
|
||||
if (cmpBuilder)
|
||||
{
|
||||
ret.buildEntities = cmpBuilder.GetEntitiesList();
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
@ -28,4 +51,39 @@ GuiInterface.prototype.SetSelectionHighlight = function(ent, colour)
|
||||
cmpSelectable.SetSelectionHighlight(colour);
|
||||
};
|
||||
|
||||
GuiInterface.prototype.SetBuildingPlacementPreview = function(cmd)
|
||||
{
|
||||
if (!this.placementEntity || this.placementEntity[0] != cmd.template)
|
||||
{
|
||||
if (cmd.template == "")
|
||||
{
|
||||
if (this.placementEntity)
|
||||
Engine.DestroyEntity(this.placementEntity[1]);
|
||||
this.placementEntity = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.placementEntity = [cmd.template, Engine.AddLocalEntity("preview|" + cmd.template)];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.placementEntity)
|
||||
{
|
||||
var pos = Engine.QueryInterface(this.placementEntity[1], IID_Position);
|
||||
if (pos)
|
||||
{
|
||||
pos.JumpTo(cmd.x, cmd.z);
|
||||
pos.SetYRotation(cmd.angle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GuiInterface.prototype.ScriptCall = function(name, args)
|
||||
{
|
||||
if (name == "SetBuildingPlacementPreview")
|
||||
this.SetBuildingPlacementPreview(args);
|
||||
else
|
||||
throw new Error("Invalid GuiInterface Call name \""+name+"\"");
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_GuiInterface, "GuiInterface", GuiInterface);
|
||||
|
12
binaries/data/mods/public/simulation/components/Identity.js
Normal file
12
binaries/data/mods/public/simulation/components/Identity.js
Normal file
@ -0,0 +1,12 @@
|
||||
function Identity() {}
|
||||
|
||||
Identity.prototype.Init = function()
|
||||
{
|
||||
};
|
||||
|
||||
Identity.prototype.GetCiv = function()
|
||||
{
|
||||
return this.template.Civ;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);
|
@ -30,14 +30,20 @@ Player.prototype.OnGlobalOwnershipChanged = function(msg)
|
||||
{
|
||||
var cost = Engine.QueryInterface(msg.entity, IID_Cost);
|
||||
if (cost)
|
||||
{
|
||||
this.popCount -= cost.GetPopCost();
|
||||
this.popLimit += cost.GetPopBonus();
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.to == this.playerID)
|
||||
{
|
||||
var cost = Engine.QueryInterface(msg.entity, IID_Cost);
|
||||
if (cost)
|
||||
{
|
||||
this.popCount += cost.GetPopCost();
|
||||
this.popLimit -= cost.GetPopBonus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
Engine.RegisterInterface("Builder");
|
@ -0,0 +1 @@
|
||||
Engine.RegisterInterface("Identity");
|
@ -1,14 +1,37 @@
|
||||
Engine.LoadComponentScript("interfaces/Builder.js");
|
||||
Engine.LoadComponentScript("GuiInterface.js");
|
||||
|
||||
var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface");
|
||||
|
||||
TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), { test: "simulation state" });
|
||||
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
|
||||
GetNumPlayers: function() { return 2; },
|
||||
GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; }
|
||||
});
|
||||
|
||||
AddMock(100, IID_Player, {
|
||||
GetPopulationCount: function() { return 10; },
|
||||
GetPopulationLimit: function() { return 20; }
|
||||
});
|
||||
|
||||
AddMock(101, IID_Player, {
|
||||
GetPopulationCount: function() { return 40; },
|
||||
GetPopulationLimit: function() { return 30; }
|
||||
});
|
||||
|
||||
TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), { players: [{popCount:10, popLimit:20}, {popCount:40, popLimit:30}] });
|
||||
|
||||
|
||||
AddMock(10, IID_Position, {
|
||||
GetPosition: function() {
|
||||
return {x:1, y:2, z:3};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
AddMock(10, IID_Builder, {
|
||||
GetEntitiesList: function() {
|
||||
return ["test1", "test2"];
|
||||
}
|
||||
});
|
||||
|
||||
var state = cmp.GetEntityState(-1, 10);
|
||||
TS_ASSERT_UNEVAL_EQUALS(state, { position: {x:1, y:2, z:3} });
|
||||
TS_ASSERT_UNEVAL_EQUALS(state, { position: {x:1, y:2, z:3}, buildEntities: ["test1", "test2"] });
|
||||
|
@ -13,6 +13,7 @@ function ProcessCommand(player, cmd)
|
||||
pos.SetYRotation(pos.GetRotation().y + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case "walk":
|
||||
for each (var ent in cmd.entities)
|
||||
{
|
||||
@ -22,6 +23,21 @@ function ProcessCommand(player, cmd)
|
||||
motion.MoveToPoint(cmd.x, cmd.z);
|
||||
}
|
||||
break;
|
||||
|
||||
case "construct":
|
||||
// TODO: this should do all sorts of stuff with foundations and resource costs etc
|
||||
var ent = Engine.AddEntity(cmd.template);
|
||||
if (ent)
|
||||
{
|
||||
var pos = Engine.QueryInterface(ent, IID_Position);
|
||||
if (pos)
|
||||
{
|
||||
pos.JumpTo(cmd.x, cmd.z);
|
||||
pos.SetYRotation(cmd.angle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
print("Ignoring unrecognised command type '" + cmd.type + "'\n");
|
||||
}
|
||||
|
@ -1364,14 +1364,28 @@ void CGUI::Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile, std::set<
|
||||
if (! file.empty())
|
||||
{
|
||||
Paths.insert(file);
|
||||
try
|
||||
{
|
||||
g_ScriptingHost.RunScript(file, m_ScriptObject);
|
||||
}
|
||||
catch (PSERROR_Scripting& e)
|
||||
{
|
||||
LOGERROR(L"GUI: Error executing script %ls: %hs", file.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Execute inline scripts
|
||||
try
|
||||
{
|
||||
CStr code (Element.GetText());
|
||||
if (! code.empty())
|
||||
g_ScriptingHost.RunMemScript(code.c_str(), code.length(), "Some XML file", Element.GetLineNumber(), m_ScriptObject);
|
||||
}
|
||||
catch (PSERROR_Scripting& e)
|
||||
{
|
||||
LOGERROR(L"GUI: Error executing inline script: %hs", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile)
|
||||
{
|
||||
|
@ -146,6 +146,24 @@ CScriptVal GetEntityState(void* cbdata, entity_id_t ent)
|
||||
return CloneValueBetweenContexts(sim->GetScriptInterface().GetContext(), guiManager->GetScriptInterface().GetContext(), gui->GetEntityState(player, ent).get());
|
||||
}
|
||||
|
||||
CScriptVal GuiInterfaceCall(void* cbdata, std::string name, CScriptVal data)
|
||||
{
|
||||
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
|
||||
|
||||
if (!g_UseSimulation2 || !g_Game)
|
||||
return JSVAL_VOID;
|
||||
CSimulation2* sim = g_Game->GetSimulation2();
|
||||
debug_assert(sim);
|
||||
CmpPtr<ICmpGuiInterface> gui(*sim, SYSTEM_ENTITY);
|
||||
if (gui.null())
|
||||
return JSVAL_VOID;
|
||||
|
||||
JSContext* cxGui = guiManager->GetScriptInterface().GetContext();
|
||||
JSContext* cxSim = sim->GetScriptInterface().GetContext();
|
||||
CScriptVal ret = gui->ScriptCall(name, CloneValueBetweenContexts(cxGui, cxSim, data.get()));
|
||||
return CloneValueBetweenContexts(cxSim, cxGui, ret.get());
|
||||
}
|
||||
|
||||
void SetEntitySelectionHighlight(void* UNUSED(cbdata), entity_id_t ent, CColor color)
|
||||
{
|
||||
if (!g_UseSimulation2 || !g_Game)
|
||||
@ -211,6 +229,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<CScriptVal, &GetSimulationState>("GetSimulationState");
|
||||
scriptInterface.RegisterFunction<CScriptVal, entity_id_t, &GetEntityState>("GetEntityState");
|
||||
scriptInterface.RegisterFunction<void, entity_id_t, CColor, &SetEntitySelectionHighlight>("SetEntitySelectionHighlight");
|
||||
scriptInterface.RegisterFunction<CScriptVal, std::string, CScriptVal, &GuiInterfaceCall>("GuiInterfaceCall");
|
||||
|
||||
scriptInterface.RegisterFunction<void, CScriptVal, &PostNetworkCommand>("PostNetworkCommand");
|
||||
|
||||
|
@ -327,8 +327,20 @@ jsval ScriptInterface::GetGlobalObject()
|
||||
return OBJECT_TO_JSVAL(JS_GetGlobalObject(m->m_cx));
|
||||
}
|
||||
|
||||
bool ScriptInterface::SetGlobal_(const char* name, jsval value)
|
||||
bool ScriptInterface::SetGlobal_(const char* name, jsval value, bool replace)
|
||||
{
|
||||
if (!replace)
|
||||
{
|
||||
JSBool found;
|
||||
if (!JS_HasProperty(m->m_cx, m->m_glob, name, &found))
|
||||
return false;
|
||||
if (found)
|
||||
{
|
||||
JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSBool ok = JS_DefineProperty(m->m_cx, m->m_glob, name, value, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
|
||||
| JSPROP_PERMANENT);
|
||||
return ok ? true : false;
|
||||
|
@ -107,10 +107,12 @@ public:
|
||||
jsval GetGlobalObject();
|
||||
|
||||
/**
|
||||
* Set the named property on the global object
|
||||
* Set the named property on the global object.
|
||||
* If @p replace is true, an existing property will be overwritten; otherwise attempts
|
||||
* to set an already-defined value will fail.
|
||||
*/
|
||||
template<typename T>
|
||||
bool SetGlobal(const char* name, const T& value);
|
||||
bool SetGlobal(const char* name, const T& value, bool replace = false);
|
||||
|
||||
/**
|
||||
* Set the named property on the given object.
|
||||
@ -180,7 +182,7 @@ public:
|
||||
private:
|
||||
bool CallFunction_(jsval val, const char* name, std::vector<jsval>& args, jsval& ret);
|
||||
bool Eval_(const char* code, jsval& ret);
|
||||
bool SetGlobal_(const char* name, jsval value);
|
||||
bool SetGlobal_(const char* name, jsval value, bool replace);
|
||||
bool SetProperty_(jsval obj, const char* name, jsval value, bool readonly);
|
||||
bool GetProperty_(jsval obj, const char* name, jsval& value);
|
||||
static bool IsExceptionPending(JSContext* cx);
|
||||
@ -272,10 +274,10 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ScriptInterface::SetGlobal(const char* name, const T& value)
|
||||
bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
|
||||
{
|
||||
LOCAL_ROOT_SCOPE;
|
||||
return SetGlobal_(name, ToJSVal(GetContext(), value));
|
||||
return SetGlobal_(name, ToJSVal(GetContext(), value), replace);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -45,6 +45,11 @@ public:
|
||||
m_Script.Call<CScriptVal> ("SetSelectionHighlight", ent, color);
|
||||
// ignore return value
|
||||
}
|
||||
|
||||
virtual CScriptVal ScriptCall(std::string name, CScriptVal data)
|
||||
{
|
||||
return m_Script.Call<CScriptVal> ("ScriptCall", name, data);
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(GuiInterfaceScripted)
|
||||
|
@ -29,6 +29,12 @@ public:
|
||||
virtual CScriptVal GetEntityState(int player, entity_id_t ent) = 0;
|
||||
virtual void SetSelectionHighlight(entity_id_t ent, const CColor& color) = 0;
|
||||
|
||||
/**
|
||||
* Generic call function, for use by GUI scripts to talk to the GuiInterface script.
|
||||
*/
|
||||
virtual CScriptVal ScriptCall(std::string name, CScriptVal data) = 0;
|
||||
// TODO: some of the earlier functions should just use ScriptCall.
|
||||
|
||||
DECLARE_INTERFACE_TYPE(GuiInterface)
|
||||
};
|
||||
|
||||
|
@ -108,7 +108,9 @@ template<> jsval ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, CParamNode
|
||||
|
||||
// Prevent modifications to the object, so that it's safe to share between
|
||||
// components and to reconstruct on deserialization
|
||||
JS_SealObject(cx, obj, JS_TRUE);
|
||||
//JS_SealObject(cx, obj, JS_TRUE);
|
||||
// TODO: need to re-enable this when it works safely (e.g. it doesn't seal the
|
||||
// global object too (via the parent chain))
|
||||
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ CComponentManager::CComponentManager(const CSimContext& context, bool skipScript
|
||||
m_ScriptInterface.RegisterFunction<void, int, int, CScriptVal, CComponentManager::Script_PostMessage> ("PostMessage");
|
||||
m_ScriptInterface.RegisterFunction<void, int, CScriptVal, CComponentManager::Script_BroadcastMessage> ("BroadcastMessage");
|
||||
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddEntity> ("AddEntity");
|
||||
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddLocalEntity> ("AddLocalEntity");
|
||||
m_ScriptInterface.RegisterFunction<void, int, CComponentManager::Script_DestroyEntity> ("DestroyEntity");
|
||||
}
|
||||
|
||||
// Define MT_*, IID_* as script globals, and store their names
|
||||
@ -150,7 +152,7 @@ void CComponentManager::Script_RegisterComponentType(void* cbdata, int iid, std:
|
||||
|
||||
// We don't support changing the IID of a component type (it would require fiddling
|
||||
// around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity)
|
||||
if (ctPrevious.iid != ctWrapper.iid)
|
||||
if (ctPrevious.iid != iid)
|
||||
{
|
||||
// ...though it only matters if any components exist with this type
|
||||
if (!componentManager->m_ComponentsByTypeId[cid].empty())
|
||||
@ -236,6 +238,9 @@ void CComponentManager::Script_RegisterInterface(void* cbdata, std::string name)
|
||||
std::map<std::string, InterfaceId>::iterator it = componentManager->m_InterfaceIdsByName.find(name);
|
||||
if (it != componentManager->m_InterfaceIdsByName.end())
|
||||
{
|
||||
// Redefinitions are fine (and just get ignored) when hotloading; otherwise
|
||||
// they're probably unintentional and should be reported
|
||||
if (!componentManager->m_CurrentlyHotloading)
|
||||
componentManager->m_ScriptInterface.ReportError("Registering interface with already-registered name"); // TODO: report the actual name
|
||||
return;
|
||||
}
|
||||
@ -250,7 +255,9 @@ void CComponentManager::Script_RegisterGlobal(void* cbdata, std::string name, CS
|
||||
{
|
||||
CComponentManager* componentManager = static_cast<CComponentManager*> (cbdata);
|
||||
|
||||
componentManager->m_ScriptInterface.SetGlobal(name.c_str(), value);
|
||||
// Set the value, and accept duplicates only if hotloading (otherwise it's an error,
|
||||
// in order to detect accidental duplicate definitions of globals)
|
||||
componentManager->m_ScriptInterface.SetGlobal(name.c_str(), value, componentManager->m_CurrentlyHotloading);
|
||||
}
|
||||
|
||||
IComponent* CComponentManager::Script_QueryInterface(void* cbdata, int ent, int iid)
|
||||
@ -296,6 +303,25 @@ int CComponentManager::Script_AddEntity(void* cbdata, std::string templateName)
|
||||
return (int)ent;
|
||||
}
|
||||
|
||||
int CComponentManager::Script_AddLocalEntity(void* cbdata, std::string templateName)
|
||||
{
|
||||
CComponentManager* componentManager = static_cast<CComponentManager*> (cbdata);
|
||||
|
||||
std::wstring name(templateName.begin(), templateName.end());
|
||||
// TODO: should validate the string to make sure it doesn't contain scary characters
|
||||
// that will let it access non-component-template files
|
||||
|
||||
entity_id_t ent = componentManager->AddEntity(name, componentManager->AllocateNewLocalEntity());
|
||||
return (int)ent;
|
||||
}
|
||||
|
||||
void CComponentManager::Script_DestroyEntity(void* cbdata, int ent)
|
||||
{
|
||||
CComponentManager* componentManager = static_cast<CComponentManager*> (cbdata);
|
||||
|
||||
componentManager->DestroyComponentsSoon(ent);
|
||||
}
|
||||
|
||||
void CComponentManager::ResetState()
|
||||
{
|
||||
// Delete all IComponents
|
||||
|
@ -202,6 +202,8 @@ private:
|
||||
static void Script_PostMessage(void* cbdata, int ent, int mtid, CScriptVal data);
|
||||
static void Script_BroadcastMessage(void* cbdata, int mtid, CScriptVal data);
|
||||
static int Script_AddEntity(void* cbdata, std::string templateName);
|
||||
static int Script_AddLocalEntity(void* cbdata, std::string templateName);
|
||||
static void Script_DestroyEntity(void* cbdata, int ent);
|
||||
|
||||
void SendGlobalMessage(const CMessage& msg) const;
|
||||
|
||||
|
@ -390,6 +390,56 @@ public:
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest2*> (man.QueryInterface(ent2, IID_Test2))->GetX(), 12345);
|
||||
}
|
||||
|
||||
void test_script_AddLocalEntity()
|
||||
{
|
||||
CSimContext context;
|
||||
CComponentManager man(context);
|
||||
man.LoadComponentTypes();
|
||||
TS_ASSERT(man.LoadScript(L"simulation/components/test-addentity.js"));
|
||||
TS_ASSERT(man.LoadScript(L"simulation/components/addentity/test-addentity.js"));
|
||||
|
||||
entity_id_t ent1 = 1;
|
||||
entity_id_t ent2 = man.AllocateNewLocalEntity() + 2;
|
||||
CParamNode noParam;
|
||||
|
||||
TS_ASSERT(man.AddComponent(SYSTEM_ENTITY, CID_TemplateManager, noParam));
|
||||
|
||||
TS_ASSERT(man.AddComponent(ent1, man.LookupCID("TestScript1_AddLocalEntity"), noParam));
|
||||
|
||||
TS_ASSERT(man.QueryInterface(ent2, IID_Test1) == NULL);
|
||||
TS_ASSERT(man.QueryInterface(ent2, IID_Test2) == NULL);
|
||||
|
||||
{
|
||||
TestLogger logger; // ignore bogus-template warnings
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), (int)ent2);
|
||||
}
|
||||
|
||||
TS_ASSERT(man.QueryInterface(ent2, IID_Test1) != NULL);
|
||||
TS_ASSERT(man.QueryInterface(ent2, IID_Test2) != NULL);
|
||||
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), 999);
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest2*> (man.QueryInterface(ent2, IID_Test2))->GetX(), 12345);
|
||||
}
|
||||
|
||||
void test_script_DestroyEntity()
|
||||
{
|
||||
CSimContext context;
|
||||
CComponentManager man(context);
|
||||
man.LoadComponentTypes();
|
||||
TS_ASSERT(man.LoadScript(L"simulation/components/test-destroyentity.js"));
|
||||
|
||||
entity_id_t ent1 = 10;
|
||||
CParamNode noParam;
|
||||
|
||||
TS_ASSERT(man.AddComponent(ent1, man.LookupCID("TestScript1_DestroyEntity"), noParam));
|
||||
|
||||
TS_ASSERT(man.QueryInterface(ent1, IID_Test1) != NULL);
|
||||
static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX();
|
||||
TS_ASSERT(man.QueryInterface(ent1, IID_Test1) != NULL);
|
||||
man.FlushDestroyedComponents();
|
||||
TS_ASSERT(man.QueryInterface(ent1, IID_Test1) == NULL);
|
||||
}
|
||||
|
||||
void test_script_messages()
|
||||
{
|
||||
CSimContext context;
|
||||
@ -440,7 +490,7 @@ public:
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), 1+10+100+1000);
|
||||
}
|
||||
|
||||
void test_script_template_readonly()
|
||||
void TODO_test_script_template_readonly()
|
||||
{
|
||||
CSimContext context;
|
||||
CComponentManager man(context);
|
||||
@ -475,6 +525,7 @@ public:
|
||||
|
||||
man.AddComponent(ent1, man.LookupCID("HotloadA"), testParam);
|
||||
man.AddComponent(ent2, man.LookupCID("HotloadB"), testParam);
|
||||
man.AddComponent(ent2, man.LookupCID("HotloadC"), testParam);
|
||||
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 100);
|
||||
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), 200);
|
||||
|
Loading…
Reference in New Issue
Block a user