Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    64
  • comments
    28
  • views
    51281

Scripted components

Sign in to follow this  
Trefall

640 views

Over the summer, I integrated Lua scripting with my engine, and in that process also my component system. I'll try to write up how I did it in this journal entry, focusing on scripted components.

Overview
For the C++/Lua bridge, I used LuaPlus, as promoted by Mr. Mike in Game Coding Complete 3rd Ed. It gives the programmer a C++ layer on top of the Lua C API, which helps a lot when exposing functionality to Lua!

At the heart I have the ScriptManager. It's main functionality is to initialize the Lua API on the C++ side. We do this by creating a LuaStateOwner object in the ScriptManager header. From here we can start adding values into the globals table of the LuaState, and calling OpenLibs() on the LuaStateOwner instance is a good idea, to get all the common Lua functionality working in scripts! The ScriptManager will initialize all script wrappers of other manager classes in the engine that we want to expose to script. It also holds a doFile and doString function, that allows us to load a script file, and to execute a string of Lua script directly in C++.

Introducing my component system
My component system basically consist of an Entity that owns a collection of components and properties. Components can add properties to the entity, and keep references to these properties. Components can register listeners with properties, so that if some other component cause a property to change, we can "react" to that change.

Example of C++ component

Health::Health(Engine::Core::IManager *coreMgr, Engine::Object::IObject *go, const CL_String &name)
: Engine::Entity::Component(static_cast(go), static_cast(go), name), coreMgr(coreMgr), go(go)
{
alive_property = go->AddProperty<bool>("Alive", true);
health_property = go->AddProperty<float>("Health", 100.0f);
maxhealth_property = go->AddProperty<float>("MaxHealth", 100.0f);

slotHealthChanged = health_property.ValueChanged().connect(this, &Health::OnHealthChanged);
slotAliveChanged = alive_property.ValueChanged().connect(this, &Health::OnAliveChanged);
}

void Health::ExecuteEvent(const Engine::Com::IEvent &event, Engine::Player::IPlayer *player)
{
if(event.getType() == CL_String("TakeDamage"))
{
if(event.getArgType() == Engine::Com::TYPE_FLOAT)
{
const Engine::Com::Event<float, Engine::Object::IObject*> *e;
e = static_cast<const Engine::Com::Event<float, Engine::Object::IObject*>*>(&event);

float dmg = e->getArg();

CL_String msg = cl_format("%1 took %2 damage!", go->getName(), dmg);
coreMgr->getLogMgr()->log("Health::ExecuteEvent", msg, Engine::Log::L_DEBUG);

msg = cl_format("%1 HP: %2", go->getName(), health_property.Get() - dmg);
coreMgr->getLogMgr()->log("Health::ExecuteEvent", msg, Engine::Log::L_DEBUG);

health_property -= dmg;
}
}
else if(event.getType() == "Die")
{
if(event.getArgType() == Engine::Com::TYPE_OBJECT)
{
const Engine::Com::Event *e;
e = static_cast<const Engine::Com::Event*>(&event);

Engine::Object::IObject *arg = e->getArg();
if(arg != go)
return;

CL_String msg = cl_format("%1 died!", go->getName());
coreMgr->getLogMgr()->log("Health::ExecuteEvent", msg, Engine::Log::L_DEBUG);
}
}
}

void Health::OnHealthChanged(const float &oldValue, const float &newValue)
{
if(newValue <= 0.0f)
alive_property = false;
}

void Health::OnAliveChanged(const bool &oldValue, const bool &newValue)
{
//If no longer alive, kill it properly to make sure it stays dead!
if(!newValue)
{
go->executeEvent(Engine::Com::Event("Die", go, go), NULL);
go->kill();
}
}






Thoughts
Although this component system deals with decoupling component to component dependencies through the use of shared properties, the syntax involved in using it relies quite heavily on templates. The result is that we end up with a lot of text to code something as simple as a Health component. In addition, if I wanted to change something about the logic within the Health component, I'd have exit the game, recompile my code and restart it! The larger the project, the longer this process takes! In addition, you'd have to register every new component you make with the ComponentFactory! This is a core class in the engine, causing a rather deep recompilation of the game! This is where Lua scripted components come to the rescue!

Example of Lua Component

if(HealthComponent == nil) then
HealthComponent = {}
end

function HealthComponent:OnInit(go)
go:AddProperty("Alive", true)
go:AddProperty("Health", 100.0)
go:AddProperty("MaxHealth", 100.0)

go:AddHealthListener("HealthComponent:OnHealthChanged")
go:AddAliveListener("HealthComponent:OnAliveChanged")
end

function HealthComponent:OnEvent(go, event, playerId)
if(event.id == "TakeDamage") then
local dmg = event.arg
go:SetHealth(go:GetHealth() - dmg)
elseif(event.id == "Die") then
Log("HealthComponent:OnEvent", go.name .. " died!", LOG_INFO)
end
end

function HealthComponent:OnHealthChanged(node, oldValue, newValue)
if(newValue <= 0.0) then
node:SetAlive(false)
end
end

function HealthComponent:OnAliveChanged(node, oldValue, newValue)
if(newValue == false) then
node:SendEvent("Die")
node:Kill()
end
end

RegisterComponent("HealthComponent")






Thoughts
There's no doubt that the code here is a lot tighter and faster to write! Of course, there's always improvements one could wish for! But in the end this is a good blend of robustness and productivity! You can now register the component within the same script file as you declare the component, properties automatically detects the template type, and once a property is added, you may use its getter and setter with it's name, like GetHP() and SetHP(hp). The components are also stateless in script, so you can reload them with new functionality at any moment, and the game will adapt at runtime without deprecating any data!

Implementation
Following, I'll briefly describe the implementation of my solution.

Entity
On the C++ side we have the Entity. An Entity inherits from ComponentContainer. A ComponentContainer is like it says, a container of components. So, in order to allow us to make new components in Lua scripts, we need to write a C++ component that can handle the bridging for us. This way, the C++ engine and entity won't care that the component list is compiled of a variation of both C++ written components, and scripted components. This is definitely something we want, because although it can be very convenient to write components in script at runtime, now and then you have to move some of them into C++ in order to optimize!

Property
On the C++ side, we have a template-based implementation of properties. Properties hold reference counted data, and in addition, has a signal that will be invoked every time the data in the property change state. Anyone with access to the property can add a listener to this signal in the form of a function pointer. Since our components in Lua script will be stateless, it will be through properties that we are able to access state, the state of the entity that the component belong to. The script-side property should be a lot more streamlined and intuitive however, and simpler to use, than the robust C++ version.
- Automatic type definition when adding a property
- Getters and Setters use name of the property (GetHP() and SetHP(hp) for instance).
- Same for listeners (AddHPListener(listener)

Specific class implementations
Thus, we end up with the following class implementations:

LuaComponent implementation
LuaComponent.h

#pragma once

#include
#include
#include

namespace Engine
{
namespace Core { class CoreManager; }
namespace Player { class IPlayer; }
namespace Script { class WrapComponent; }

namespace Component
{
class LuaComponent : public Engine::Entity::Component
{
public:
virtual ~LuaComponent() {}

virtual void Update(double dt);
virtual void ExecuteCommand(const CL_String &command, Engine::Player::IPlayer *player);
virtual void ExecuteEvent(const Engine::Events::IEvent &event, Engine::Player::IPlayer *player);

static CL_String GetType() { return "Lua"; }
static Engine::Entity::Component* Create(Engine::Core::CoreManager *coreMgr, Engine::Entity::IEntity *entity, const T_String &name) { return new LuaComponent(coreMgr, entity, name); }

void initLuaExposure(Script::WrapComponent *wComp);

protected:
LuaComponent(Engine::Core::CoreManager *coreMgr, Engine::Entity::IEntity *entity, const T_String &name);

Engine::Core::CoreManager *coreMgr;
Script::WrapComponent *wComp;

bool hasInit, hasUpdate, hasCommand, hasEvent;
};
}}







LuaComponent.cpp

#include "LuaComponent.h"
#include "WrapComponent.h"
#include "WrapEntityManager.h"
#include "WrapIEvent.h"
#include "ScriptManager.h"
#include
#include
#include
#include
#include
#include
#include

#include

using namespace Engine;
using namespace Component;
using namespace LuaPlus;

LuaComponent::LuaComponent(Engine::Core::CoreManager *coreMgr, Engine::Entity::IEntity *entity, const CL_String &name)
: Engine::Entity::Component(entity, name), coreMgr(coreMgr)
{
wComp = NULL;
hasInit = false;
hasUpdate = false;
hasCommand = false;
hasEvent = false;
}

void LuaComponent::initLuaExposure(Script::WrapComponent *wComp)
{
this->wComp = wComp;

hasInit = false;
LuaObject lInit = wComp->getLComp().GetByName("OnInit");
if(lInit.IsFunction())
{
hasInit = true;
}

hasUpdate = false;
LuaObject lUpdate = wComp->getLComp().GetByName("OnUpdate");
if(lUpdate.IsFunction())
{
hasUpdate = true;
}

hasCommand = false;
LuaObject lCommand = wComp->getLComp().GetByName("OnCommand");
if(lCommand.IsFunction())
{
hasCommand = true;
}

hasEvent = false;
LuaObject lEvent = wComp->getLComp().GetByName("OnEvent");
if(lEvent.IsFunction())
{
hasEvent = true;
}

if(hasInit)
{
//Make sure that the entity exist in the global state's Entities table
LuaObject lEntity = coreMgr->getScriptMgr()->getWEntityMgr()->getLEntities().GetByIndex(entity->getId());
if(lEntity.IsNil())
{
CL_String err = cl_format("Entity of id %1 has not been exposed to Lua!", entity->getId());
coreMgr->getLogMgr()->log("LuaComponent:InitLuaExposure", err, Log::L_ERROR);
return;
}

coreMgr->getScriptMgr()->doString(cl_format("%1:OnInit(Entities[%2])", name, entity->getId()));
}
}

void LuaComponent::ExecuteCommand(const CL_String &command, Engine::Player::IPlayer *player)
{
if(hasCommand)
{
//Make sure that the entity exist in the global state's Entities table
LuaObject lEntity = coreMgr->getScriptMgr()->getWEntityMgr()->getLEntities().GetByIndex(entity->getId());
if(lEntity.IsNil())
{
CL_String err = cl_format("Entity of id %1 has not been exposed to Lua!", entity->getId());
coreMgr->getLogMgr()->log("LuaComponent:ExecuteCommand", err, Log::L_ERROR);
return;
}

if(player)
coreMgr->getScriptMgr()->doString(cl_format("%1:OnCommand(Entities[%2], '%3', %4)", name, entity->getId(), command, player->getId()));
else
coreMgr->getScriptMgr()->doString(cl_format("%1:OnCommand(Entities[%2], '%3', nil)", name, entity->getId(), command));
}
}

void LuaComponent::ExecuteEvent(const Engine::Events::IEvent &event, Engine::Player::IPlayer *player)
{
if(hasEvent)
{
//Make sure that the entity exist in the global state's Entities table
LuaObject lEntity = coreMgr->getScriptMgr()->getWEntityMgr()->getLEntities().GetByIndex(entity->getId());
if(lEntity.IsNil())
{
CL_String err = cl_format("Entity of id %1 has not been exposed to Lua!", entity->getId());
coreMgr->getLogMgr()->log("LuaComponent:ExecuteEvent", err, Log::L_ERROR);
return;
}

Script::WrapIEvent wEvent(coreMgr, &event);
int fail = wEvent.init();
if(fail)
return;

//Make sure that the event exist in the global state's Events table
LuaObject lEvent = wEvent.getLEvents().GetByName(event.getType());
if(lEvent.IsNil())
{
CL_String err = cl_format("Event of type %1 has not been exposed to Lua!", event.getType());
coreMgr->getLogMgr()->log("LuaComponent:ExecuteEvent", err, Log::L_ERROR);
return;
}

if(player)
coreMgr->getScriptMgr()->doString(cl_format("%1:OnEvent(Entities[%2], Events['%3'], %4)", name, entity->getId(), event.getType(), player->getId()));
else
coreMgr->getScriptMgr()->doString(cl_format("%1:OnEvent(Entities[%2], Events['%3'], nil)", name, entity->getId(), event.getType()));
}
}

void LuaComponent::Update(double dt)
{
if(hasUpdate)
{
//Make sure that the entity exist in the global state's Entities table
LuaObject lEntity = coreMgr->getScriptMgr()->getWEntityMgr()->getLEntities().GetByIndex(entity->getId());
if(lEntity.IsNil())
{
CL_String err = cl_format("Entity of id %1 has not been exposed to Lua!", entity->getId());
coreMgr->getLogMgr()->log("LuaComponent:Update", err, Log::L_ERROR);
return;
}

coreMgr->getScriptMgr()->doString(cl_format("%1:OnUpdate(Entities[%2], %3)", name, entity->getId(), dt));
}
}






WrapComponent implementation

WrapComponent.h

#pragma once

#include
#include

namespace Engine
{
namespace Core { class CoreManager; }
namespace Entity { class Component; }
namespace Script
{
class WrapIEntity;
class WrapComponentContainer;

class WrapComponent
{
public:
WrapComponent(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity, WrapComponentContainer *wCompContainer, Entity::Component *component);
~WrapComponent();

int init();

Entity::Component *getComp() const { return component; }
LuaPlus::LuaObject getLComp() const { return lComponent; }
WrapIEntity *getWEntity() const { return wEntity; }

private:
Core::CoreManager *coreMgr;
Script::WrapComponentContainer *wCompContainer;
Script::WrapIEntity *wEntity;

Entity::Component *component;
LuaPlus::LuaObject lComponent;
};

}
}






WrapComponent.cpp

#include "WrapComponent.h"
#include "WrapIEntity.h"
#include "WrapComponentContainer.h"
#include "LuaComponent.h"
#include "ScriptManager.h"
#include
#include
#include
#include

using namespace Engine;
using namespace Script;
using namespace LuaPlus;
using namespace Entity;

WrapComponent::WrapComponent(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity, WrapComponentContainer *wCompContainer, Entity::Component *component)
{
this->coreMgr = coreMgr;
this->wEntity = wEntity;
this->wCompContainer = wCompContainer;
this->component = component;
}

WrapComponent::~WrapComponent()
{
lComponent.AssignNil(coreMgr->getScriptMgr()->GetGlobalState()->Get());
}

int WrapComponent::init()
{
LuaObject globals = (*coreMgr->getScriptMgr()->GetGlobalState())->GetGlobals();

//Check if this is a C++ component, or a scripted Lua defined component
Component::LuaComponent *luaComp = dynamic_cast(component);
if(luaComp == NULL)
{
LuaObject &lComps = wCompContainer->getLComps();
lComponent = lComps.CreateTable(component->GetName().c_str());
lComponent.SetString("id", component->GetName().c_str());

{
LuaObject lMeta = lComponent.CreateTable("MetaTable");
lMeta.SetObject("__index", lMeta);

lComponent.SetLightUserData("__object", this);
lComponent.SetMetaTable(lMeta);
}
}
else
{
lComponent = globals.GetByName(component->GetName().c_str());
lComponent.SetString("id", component->GetName().c_str());
{
LuaObject lMeta = lComponent.CreateTable("MetaTable");
lMeta.SetObject("__index", lMeta);

lComponent.SetLightUserData("__object", this);
lComponent.SetMetaTable(lMeta);
}

luaComp->initLuaExposure(this);
}
return 0;
}






WrapComponentManager implementation
WrapComponentManager.h

#pragma once

#include
#include

namespace Engine
{
namespace Core { class CoreManager; }
namespace Script
{

class WrapComponentManager
{
public:
WrapComponentManager(Core::CoreManager *coreMgr);
~WrapComponentManager();

int init();

private:
void RegisterComponent(LuaPlus::LuaObject lName);

Core::CoreManager *coreMgr;
};

}
}






WrapComponentManager.cpp

#include "WrapComponentManager.h"
#include "WrapComponent.h"
#include "LuaComponent.h"
#include "ScriptManager.h"
#include
#include
#include
#include
#include
#include

using namespace Engine;
using namespace Script;
using namespace LuaPlus;

WrapComponentManager::WrapComponentManager(Core::CoreManager *coreMgr)
{
this->coreMgr = coreMgr;
}

WrapComponentManager::~WrapComponentManager()
{
}

int WrapComponentManager::init()
{
LuaObject globals = (*coreMgr->getScriptMgr()->GetGlobalState())->GetGlobals();
globals.RegisterDirect("RegisterComponent", *this, &WrapComponentManager::RegisterComponent);

//Load all scripts in Game/Components
coreMgr->getLogMgr()->log("WrapComponentManager:Init", "Loading scripted components!", Log::L_INFO);

std::vector scripts = coreMgr->getResMgr()->getFilesInDir("/Scripts/Components/");
for(unsigned int i = 0; i < scripts.size(); i++)
{
int fail = coreMgr->getScriptMgr()->doFile(cl_format("Components/%1", scripts));
if(fail)
{
CL_String err = cl_format("Failed to load component script %1", scripts);
coreMgr->getLogMgr()->log("WrapComponentManager:Init", err, Log::L_ERROR);
}
}
coreMgr->getLogMgr()->log("WrapComponentManager:Init", "Finsihed loading scripted components!", Log::L_INFO);

return 0;
}

void WrapComponentManager::RegisterComponent(LuaObject lName)
{
if(!lName.IsString())
{
CL_String name_type = lName.TypeName();

CL_String err = cl_format("Failed to register component, because the type of name was %1 when expecting String!", name_type);
coreMgr->getLogMgr()->log("WrapComponentManager:RegisterComponent", err, Log::L_ERROR);
return;
}

CL_String name = lName.ToString();
coreMgr->getEntityMgr()->getComponentFactory()->RegisterComponent(name, &Engine::Component::LuaComponent::Create);

coreMgr->getLogMgr()->log("WrapComponentManager:RegisterComponent", cl_format("Component: %1", name), Log::L_DEBUG);
return;
}






PropertyContainer implementation
WrapPropertyContainer.h

#pragma once

#include
#include
#include
#include
#include
#include
#include

namespace Engine
{
namespace Core { class CoreManager; }
namespace Script
{
class WrapIEntity;
class WrapIPlayer;
class WrapIProperty;
class WrapIRoom;

class WrapPropertyContainer
{
public:
WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity);
WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIPlayer *wPlayer);
WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIRoom *wRoom);
~WrapPropertyContainer();

int init();

LuaPlus::LuaObject &getLProps() { return lProperties; }

private:
void AddProperty(LuaPlus::LuaObject self, LuaPlus::LuaObject name, LuaPlus::LuaObject defValue);
LuaPlus::LuaObject HasProperty(LuaPlus::LuaObject self, LuaPlus::LuaObject name);

void add(const CL_String &name, Entity::IProperty *prop, WrapIProperty *wProp);

Core::CoreManager *coreMgr;
Script::WrapIEntity *wEntity;
Script::WrapIPlayer *wPlayer;
Script::WrapIRoom *wRoom;

LuaPlus::LuaObject lProperties;
std::vector wProperties;

void OnPropertyAdded(const Engine::Events::EngineEvent &event);
Engine::Events::EngineEventContainer engineEvents;
};

}
}






WrapPropertyContainer.h

#include "WrapPropertyContainer.h"
#include "WrapIProperty.h"
#include "WrapIEntity.h"
#include "WrapIPlayer.h"
#include "WrapIRoom.h"
#include "ScriptManager.h"
#include
#include
#include
#include
#include
#include
#include

using namespace Engine;
using namespace Script;
using namespace LuaPlus;

WrapPropertyContainer::WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity)
: engineEvents(coreMgr->getEngineEventMgr())
{
this->coreMgr = coreMgr;
this->wEntity = wEntity;
this->wPlayer = NULL;
this->wRoom = NULL;
}

WrapPropertyContainer::WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIPlayer *wPlayer)
: engineEvents(coreMgr->getEngineEventMgr())
{
this->coreMgr = coreMgr;
this->wPlayer = wPlayer;
this->wEntity = NULL;
this->wRoom = NULL;
}

WrapPropertyContainer::WrapPropertyContainer(Core::CoreManager *coreMgr, Script::WrapIRoom *wRoom)
: engineEvents(coreMgr->getEngineEventMgr())
{
this->coreMgr = coreMgr;
this->wRoom = wRoom;
this->wPlayer = NULL;
this->wEntity = NULL;
}

WrapPropertyContainer::~WrapPropertyContainer()
{
for(unsigned int i = 0; i < wProperties.size(); i++)
{
WrapIProperty *wProp = wProperties;
delete wProp;
wProp = NULL;
}
wProperties.clear();
lProperties.AssignNil(coreMgr->getScriptMgr()->GetGlobalState()->Get());
}

int WrapPropertyContainer::init()
{
LuaObject globals = (*coreMgr->getScriptMgr()->GetGlobalState())->GetGlobals();

if(wEntity)
{
lProperties = wEntity->getLEntity().CreateTable("Properties");

Engine::Entity::IEntity *entity = wEntity->getEntity();
std::map &properties = entity->GetProperties();
std::map::iterator propIt = properties.begin();
for(; propIt != properties.end(); ++propIt)
{
WrapIProperty *wProp = new WrapIProperty(coreMgr, wEntity, this, propIt->second);
int fail = wProp->init();
if(fail)
{
delete wProp;
wProp = NULL;

CL_String msg = cl_format("Failed to initialize Property Wrapper for property %1", propIt->first);
coreMgr->getLogMgr()->log("WrapPropertyContainer::Init", msg, Log::L_ERROR);
continue;
}
wProperties.push_back(wProp);
}

LuaObject lMeta = wEntity->getLMeta();
lMeta.RegisterDirect("AddProperty", *this, &WrapPropertyContainer::AddProperty);
lMeta.RegisterDirect("HasProperty", *this, &WrapPropertyContainer::HasProperty);
}
else if(wPlayer)
{
lProperties = wPlayer->getLPlayer().CreateTable("Properties");

Engine::Player::IPlayer *player = wPlayer->getPlayer();
std::map &properties = player->GetProperties();
std::map::iterator propIt = properties.begin();
for(; propIt != properties.end(); ++propIt)
{
WrapIProperty *wProp = new WrapIProperty(coreMgr, wPlayer, this, propIt->second);
int fail = wProp->init();
if(fail)
{
delete wProp;
wProp = NULL;

CL_String msg = cl_format("Failed to initialize Property Wrapper for property %1", propIt->first);
coreMgr->getLogMgr()->log("WrapPropertyContainer::Init", msg, Log::L_ERROR);
continue;
}
wProperties.push_back(wProp);
}

LuaObject lMeta = wPlayer->getLMeta();
lMeta.RegisterDirect("AddProperty", *this, &WrapPropertyContainer::AddProperty);
lMeta.RegisterDirect("HasProperty", *this, &WrapPropertyContainer::HasProperty);
}
else if(wRoom)
{
lProperties = wRoom->getLRoom().CreateTable("Properties");

Engine::Room::IRoom *room = wRoom->getRoom();
std::map &properties = room->GetProperties();
std::map::iterator propIt = properties.begin();
for(; propIt != properties.end(); ++propIt)
{
WrapIProperty *wProp = new WrapIProperty(coreMgr, wRoom, this, propIt->second);
int fail = wProp->init();
if(fail)
{
delete wProp;
wProp = NULL;

CL_String msg = cl_format("Failed to initialize Property Wrapper for property %1", propIt->first);
coreMgr->getLogMgr()->log("WrapPropertyContainer::Init", msg, Log::L_ERROR);
continue;
}
wProperties.push_back(wProp);
}

LuaObject lMeta = wRoom->getLMeta();
lMeta.RegisterDirect("AddProperty", *this, &WrapPropertyContainer::AddProperty);
lMeta.RegisterDirect("HasProperty", *this, &WrapPropertyContainer::HasProperty);
}

engineEvents.Connect("PropertyAdded", this, &WrapPropertyContainer::OnPropertyAdded);

return 0;
}

void WrapPropertyContainer::AddProperty(LuaObject self, LuaObject lName, LuaObject defValue)
{
if(!self.IsTable())
{
CL_String self_type = self.TypeName();

CL_String msg = cl_format("Failed to add property, because the type of self was %1 when expecting Table", self_type);
coreMgr->getLogMgr()->log("WrapPropertyContainer::AddProperty", msg, Log::L_ERROR);
return;
}

if(!lName.IsString())
{
CL_String name_type = lName.TypeName();

CL_String msg = cl_format("Failed to add property, because the type of name was %1 when expecting String", name_type);
coreMgr->getLogMgr()->log("WrapPropertyContainer::AddProperty", msg, Log::L_ERROR);
return;
}

CL_String name = lName.ToString();

Entity::IEntity *entity = NULL;
Player::IPlayer *player = NULL;
Room::IRoom *room = NULL;
if(wEntity)
entity = wEntity->getEntity();
else if(wPlayer)
player = wPlayer->getPlayer();
else if(wRoom)
room = wRoom->getRoom();

if(defValue.IsBoolean())
{
bool val = defValue.GetBoolean();
if(entity)
entity->AddProperty<bool>(name, val);
else if(player)
player->AddProperty<bool>(name, val);
else if(room)
room->AddProperty<bool>(name, val);
}
else if(defValue.IsNumber())
{
float val = (float)defValue.ToNumber();
if(entity)
entity->AddProperty<float>(name, val);
else if(player)
player->AddProperty<float>(name, val);
else if(room)
room->AddProperty<float>(name, val);
}
else if(defValue.IsInteger())
{
int val = defValue.ToInteger();
if(entity)
entity->AddProperty<int>(name, val);
else if(player)
player->AddProperty<int>(name, val);
else if(room)
room->AddProperty<int>(name, val);
}
else if(defValue.IsString())
{
CL_String val = defValue.ToString();
if(entity)
entity->AddProperty(name, val);
else if(player)
player->AddProperty(name, val);
else if(room)
room->AddProperty(name, val);
}
else if(defValue.IsTable())
{
bool hasX = false;
bool hasY = false;
bool hasZ = false;
bool hasW = false;

LuaObject xObj = defValue.GetByName("x");
if(xObj.IsNumber())
hasX = true;

LuaObject yObj = defValue.GetByName("y");
if(yObj.IsNumber())
hasY = true;

LuaObject zObj = defValue.GetByName("z");
if(zObj.IsNumber())
hasZ = true;

LuaObject wObj = defValue.GetByName("w");
if(wObj.IsNumber())
hasW = true;

if(hasX && hasY && hasZ && hasW)
{
CL_Vec4f val = CL_Vec4f((float)xObj.ToNumber(),(float)yObj.ToNumber(),(float)zObj.ToNumber(),(float)wObj.ToNumber());
if(entity)
entity->AddProperty(name, val);
else if(player)
player->AddProperty(name, val);
else if(room)
room->AddProperty(name, val);
}
else if(hasX && hasY && hasZ)
{
CL_Vec3f val = CL_Vec3f((float)xObj.ToNumber(),(float)yObj.ToNumber(),(float)zObj.ToNumber());
if(entity)
entity->AddProperty(name, val);
else if(player)
player->AddProperty(name, val);
else if(room)
room->AddProperty(name, val);
}
else if(hasX && hasY)
{
CL_Vec2f val = CL_Vec2f((float)xObj.ToNumber(),(float)yObj.ToNumber());
if(entity)
entity->AddProperty(name, val);
else if(player)
player->AddProperty(name, val);
else if(room)
room->AddProperty(name, val);
}
}
return;
}

LuaPlus::LuaObject WrapPropertyContainer::HasProperty(LuaPlus::LuaObject self, LuaPlus::LuaObject name)
{
if(!self.IsTable())
{
CL_String msg = cl_format("Self was not a table (it's a %1)", self.TypeName());
coreMgr->getLogMgr()->log("WrapPropertyContainer::HasProperty", msg, Log::L_ERROR);
return LuaObject(coreMgr->getScriptMgr()->GetGlobalState()->Get());
}

if(!name.IsString())
{
CL_String msg = cl_format("Name was not a string (it's a %1)", name.TypeName());
coreMgr->getLogMgr()->log("WrapPropertyContainer::HasProperty", msg, Log::L_ERROR);
return LuaObject(coreMgr->getScriptMgr()->GetGlobalState()->Get());
}

bool retVal = false;
if(wEntity)
retVal = wEntity->getEntity()->HasProperty(name.ToString());
else if(wPlayer)
retVal = wPlayer->getPlayer()->HasProperty(name.ToString());
else if(wRoom)
retVal = wRoom->getRoom()->HasProperty(name.ToString());

LuaObject lRetVal;
lRetVal.AssignBoolean(coreMgr->getScriptMgr()->GetGlobalState()->Get(), retVal);
return lRetVal;
}

void WrapPropertyContainer::add(const CL_String &name, Entity::IProperty *prop, WrapIProperty *wProp)
{
if(prop == NULL)
{
CL_String msg = cl_format("Failed to add property %1, because no property was returned", name);
coreMgr->getLogMgr()->log("WrapPropertyContainer::add", msg, Log::L_ERROR);
return;
}
if(wEntity)
wProp = new WrapIProperty(coreMgr, wEntity, this, prop);
else if(wPlayer)
wProp = new WrapIProperty(coreMgr, wPlayer, this, prop);
else if(wRoom)
wProp = new WrapIProperty(coreMgr, wRoom, this, prop);

int fail = wProp->init();
if(fail)
{
delete wProp;
wProp = NULL;

CL_String msg = cl_format("Failed to initialize property wrapper for property %1", prop->GetName());
coreMgr->getLogMgr()->log("WrapPropertyContainer::add", msg, Log::L_ERROR);
return;
}
wProperties.push_back(wProp);
}

void WrapPropertyContainer::OnPropertyAdded(const Engine::Events::EngineEvent &event)
{
if(event.getArgument(1).IsEntity() && wEntity)
{
Engine::Entity::IEntity *entity = event.getArgument(1).ToEntity();
if(entity->getId() != wEntity->getEntity()->getId())
return;
}
else if(event.getArgument(1).IsIPlayer() && wPlayer)
{
Engine::Player::IPlayer *player = event.getArgument(1).ToIPlayer();
if(player->getId() != wPlayer->getPlayer()->getId())
return;
}
else if(event.getArgument(1).IsIRoom() && wRoom)
{
Engine::Room::IRoom *room = event.getArgument(1).ToIRoom();
if(room->getId() != wRoom->getRoom()->getId())
return;
}
else
{
}

Engine::Entity::IProperty *prop = event.getArgument(0).ToProperty();
WrapIProperty *wProp = NULL;
add(prop->GetName(), prop, wProp);
}






WrapIProperty implementation

WrapIProperty.h

#pragma once

#include
#include

namespace Engine
{
namespace Core { class CoreManager; }
namespace Entity { class IProperty; }
namespace Script
{
class WrapIEntity;
class WrapIPlayer;
class WrapIRoom;
class WrapPropertyContainer;

class WrapIProperty
{
public:
WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity, WrapPropertyContainer *wPropContainer, Entity::IProperty *property);
WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIPlayer *wPlayer, WrapPropertyContainer *wPropContainer, Entity::IProperty *property);
WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIRoom *wRoom, WrapPropertyContainer *wPropContainer, Entity::IProperty *property);
~WrapIProperty();

int init();

Entity::IProperty *getProp() const { return property; }
LuaPlus::LuaObject getLProp() const { return lProperty; }

private:
LuaPlus::LuaObject Get(LuaPlus::LuaObject self);
void Set(LuaPlus::LuaObject self, LuaPlus::LuaObject value);
void AddListener(LuaPlus::LuaObject self, LuaPlus::LuaObject listener);

void initPropertyListener();

void OnPropertyChangedBool(const bool &oldValue, const bool &newValue);
void OnPropertyChangedVec2f(const CL_Vec2f &oldValue, const CL_Vec2f &newValue);
void OnPropertyChangedVec3f(const CL_Vec3f &oldValue, const CL_Vec3f &newValue);
void OnPropertyChangedVec4f(const CL_Vec4f &oldValue, const CL_Vec4f &newValue);
void OnPropertyChangedString(const CL_String &oldValue, const CL_String &newValue);
void OnPropertyChangedDouble(const double &oldValue, const double &newValue);
void OnPropertyChangedFloat(const float &oldValue, const float &newValue);
void OnPropertyChangedInt(const int &oldValue, const int &newValue);
void OnPropertyChangedUInt(const unsigned int &oldValue, const unsigned int &newValue);

template<class T>
void OnPropertyChanged(const T &oldValue, const T &newValue);

Core::CoreManager *coreMgr;
Script::WrapPropertyContainer *wPropContainer;
Script::WrapIEntity *wEntity;
Script::WrapIPlayer *wPlayer;
Script::WrapIRoom *wRoom;

CL_String name;

Entity::IProperty *property;
LuaPlus::LuaObject lProperty;

std::vector listeners;

CL_Slot slotPropertyChanged;
};

}
}






WrapIProperty.cpp

#include "WrapIProperty.h"
#include "WrapIEntity.h"
#include "WrapEntityManager.h"
#include "WrapIPlayer.h"
#include "WrapPlayerManager.h"
#include "WrapIRoom.h"
#include "WrapRoomManager.h"
#include "WrapPropertyContainer.h"
#include "ScriptManager.h"
#include
#include
#include
#include
#include
#include
#include
#include

using namespace Engine;
using namespace Script;
using namespace LuaPlus;
using namespace Entity;

WrapIProperty::WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIEntity *wEntity, WrapPropertyContainer *wPropContainer, Entity::IProperty *property)
{
this->coreMgr = coreMgr;
this->wEntity = wEntity;
this->wPlayer = NULL;
this->wRoom = NULL;
this->wPropContainer = wPropContainer;
this->property = property;
name = property->GetName();
}

WrapIProperty::WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIPlayer *wPlayer, WrapPropertyContainer *wPropContainer, Entity::IProperty *property)
{
this->coreMgr = coreMgr;
this->wEntity = NULL;
this->wPlayer = wPlayer;
this->wRoom = NULL;
this->wPropContainer = wPropContainer;
this->property = property;
name = property->GetName();
}

WrapIProperty::WrapIProperty(Core::CoreManager *coreMgr, Script::WrapIRoom *wRoom, WrapPropertyContainer *wPropContainer, Entity::IProperty *property)
{
this->coreMgr = coreMgr;
this->wEntity = NULL;
this->wPlayer = NULL;
this->wRoom = wRoom;
this->wPropContainer = wPropContainer;
this->property = property;
name = property->GetName();
}

WrapIProperty::~WrapIProperty()
{
lProperty.AssignNil(coreMgr->getScriptMgr()->GetGlobalState()->Get());
}

int WrapIProp
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!