cyphesis/server/EntityFactory.cpp
Al Riddoch 68ab5f7fc8 2004-09-20 Al Riddoch <alriddoch@zepler.org>
* physics/Vector3D.h, physics/Vector3D.cpp, modules/Location.h,
	  modules/Location.cpp: Convert to using WFMath::Point instead of
	  WFMath::Vector for position.

	* physics/Collision.h, physics/Collision.cpp: Update collision code
	  to use new pos type.

	* rulesets/Py_Point3D.h, rulesets/Py_Point3D.cpp: New python
	  wrappers for WFMath::Point.

	* rulesets/Py_Location.cpp, rulesets/Py_Vector3D.cpp,
	  rulesets/Python_API.cpp:
	  Update python wrappers to take into account new pos type.
	  Ensure that only apropriate functionality is available for
	  point and vector.

	* rulesets/Character.cpp, rulesets/Entity.cpp, rulesets/Entity.h,
	  rulesets/Line.cpp, rulesets/Movement.cpp, rulesets/Movement.h,
	  rulesets/Pedestrian.cpp, rulesets/Pedestrian.h,
	  rulesets/Plant.cpp, rulesets/Thing.cpp, rulesets/World.cpp,
	  server/EntityFactory.cpp, server/WorldRouter.cpp,
	  server/WorldRouter.h: Update the rest of the code for new pos
	  type, converting all places where entity parenting is changed
	  to use WFMath toParentCoords() and toLocalCoords() methods,
	  so that orientation is handled correctly.

	* tests/transformtest.cpp: New test to ensure that toParentCoords()
	  and toLocalCoords() perform as expected.

	* tests/Creatortest.cpp, tests/Locationtest.cpp, tests/Worldtest.cpp,
	  tests/collisiontest.cpp, tests/emergencetest.cpp:
	  Update the rest of the tests.
2004-09-20 10:16:04 +00:00

297 lines
11 KiB
C++

// This file may be redistributed and modified only under the terms of
// the GNU General Public License (See COPYING for details).
// Copyright (C) 2000,2001 Alistair Riddoch
#include "EntityFactory.h"
#include "PersistantThingFactory.h"
#include "Persistance.h"
#include "Persistor.h"
#include "Player.h"
#include "rulesets/Thing.h"
#include "rulesets/MindFactory.h"
#include "rulesets/Character.h"
#include "rulesets/Creator.h"
#include "rulesets/Plant.h"
#include "rulesets/Food.h"
#include "rulesets/Stackable.h"
#include "rulesets/Structure.h"
#include "rulesets/Line.h"
#include "rulesets/Area.h"
#include "rulesets/World.h"
#include "rulesets/Python_API.h"
#include "common/debug.h"
#include "common/globals.h"
#include "common/const.h"
#include "common/inheritance.h"
#include "common/random.h"
#include <Atlas/Message/Element.h>
#include <Atlas/Objects/Operation/Login.h>
#include <Atlas/Objects/Entity/GameEntity.h>
static const bool debug_flag = false;
// This is a template which requires debug flag to be declared.
#include "rulesets/Entity_getLocation.h"
EntityFactory * EntityFactory::m_instance = NULL;
EntityFactory::EntityFactory(BaseWorld & w) : m_world(w),
m_eft(new PersistantThingFactory<Entity>())
{
installFactory("game_entity", "world", new PersistantThingFactory<World>());
PersistantThingFactory<Thing> * tft = new PersistantThingFactory<Thing>();
installFactory("game_entity", "thing", tft);
installFactory("thing", "feature", tft->duplicateFactory());
installFactory("feature", "line", new PersistantThingFactory<Line>());
installFactory("feature", "area", new PersistantThingFactory<Area>());
installFactory("thing", "character",
new PersistantThingFactory<Character>());
installFactory("character", "creator",
new PersistantThingFactory<Creator>());
installFactory("thing", "plant", new PersistantThingFactory<Plant>());
installFactory("thing", "food", new PersistantThingFactory<Food>());
installFactory("thing", "stackable",
new PersistantThingFactory<Stackable>());
installFactory("thing", "structure",
new PersistantThingFactory<Structure>());
}
void EntityFactory::initWorld()
{
FactoryDict::const_iterator I = m_factories.find("world");
if (I == m_factories.end()) {
log(CRITICAL, "No world factory");
return;
}
PersistantThingFactory<World> * wft = dynamic_cast<PersistantThingFactory<World> *>(I->second);
if (wft == 0) {
log(CRITICAL, "Its not a world factory");
return;
}
wft->m_p.persist((World&)m_world.m_gameWorld);
}
Entity * EntityFactory::newEntity(const std::string & id,
const std::string & type,
const MapType & attributes)
{
debug(std::cout << "EntityFactor::newEntity()" << std::endl << std::flush;);
Entity * thing = 0;
FactoryDict::const_iterator I = m_factories.find(type);
PersistorBase * pc = 0;
if (I == m_factories.end()) {
return 0;
}
FactoryBase * factory = I->second;
if (consts::enable_persistence) {
thing = factory->newPersistantThing(id, &pc);
} else {
thing = factory->newThing(id);
}
debug( std::cout << "[" << type << " " << thing->getName() << "]"
<< std::endl << std::flush;);
thing->setType(type);
// Sort out python object
if ((factory->m_language == "python") && (!factory->m_script.empty())) {
debug(std::cout << "Class " << type << " has a python class"
<< std::endl << std::flush;);
Create_PyEntity(thing, factory->m_script, type);
}
// Read the defaults
thing->merge(factory->m_attributes);
// And then override with the values provided for this entity.
thing->merge(attributes);
// Get location from entity, if it is present
// The default attributes cannot contain info on location
if (thing->getLocation(attributes, m_world.getObjects())) {
// If no info was provided, put the entity in the game world
thing->m_location.m_loc = &m_world.m_gameWorld;
}
if (!thing->m_location.m_pos.isValid()) {
// If no position coords were provided, put it somewhere near origin
thing->m_location.m_pos = Point3D(uniform(-8,8), uniform(-8,8), 0);
}
if (pc != 0) {
pc->persist();
thing->clearUpdateFlags();
}
delete pc;
return thing;
}
void EntityFactory::flushFactories()
{
FactoryDict::const_iterator I = m_factories.begin();
for (; I != m_factories.end(); I++) {
delete I->second;
}
delete m_eft;
m_eft = NULL;
}
void EntityFactory::installRule(const std::string & className,
const MapType & classDesc)
{
MapType::const_iterator J = classDesc.find("parents");
if (J == classDesc.end()) {
std::string msg = std::string("Rule \"") + className
+ "\" has no parents. Skipping.";
log(ERROR, msg.c_str());
return;
}
if (!J->second.isList()) {
std::string msg = std::string("Rule \"") + className
+ "\" has malformed parents. Skipping.";
log(ERROR, msg.c_str());
return;
}
const ListType & parents = J->second.asList();
if (parents.empty()) {
std::string msg = std::string("Rule \"") + className
+ "\" has empty parents. Skipping.";
log(ERROR, msg.c_str());
return;
}
const Element & p1 = parents.front();
if (!p1.isString() || p1.asString().empty()) {
std::string msg = std::string("Rule \"") + className
+ "\" has malformed first parent. Skipping.";
log(ERROR, msg.c_str());
return;
}
const std::string & parent = p1.asString();
// Get the new factory for this rule
FactoryBase * f = getNewFactory(parent);
if (f == 0) {
debug(std::cout << "Rule \"" << className
<< "\" has non existant parent \"" << parent
<< "\". Waiting." << std::endl << std::flush;);
m_waitingRules.insert(make_pair(parent, make_pair(className, classDesc)));
return;
}
// Establish whether this rule has an associated script, and
// if so, use it.
J = classDesc.find("script");
if ((J != classDesc.end()) && (J->second.isMap())) {
const MapType & script = J->second.asMap();
J = script.find("name");
if ((J != script.end()) && (J->second.isString())) {
f->m_script = J->second.asString();
J = script.find("language");
if ((J != script.end()) && (J->second.isString())) {
f->m_language = J->second.asString();
}
}
}
// Establish whether this rule has an associated mind rule,
// and handle it.
J = classDesc.find("mind");
if ((J != classDesc.end()) && (J->second.isMap())) {
const MapType & script = J->second.asMap();
J = script.find("name");
if ((J != script.end()) && (J->second.isString())) {
const std::string mindType = J->second.asString();
// language is unused. might need it one day
// J = script.find("language");
// if ((J != script.end()) && (J->second.isString())) {
// const std::string & mindLang = J->second.asString();
// }
MindFactory::instance()->addMindType(className, mindType);
}
}
// Store the default attribute for entities create by this rule.
J = classDesc.find("attributes");
if ((J != classDesc.end()) && (J->second.isMap())) {
const MapType & attrs = J->second.asMap();
MapType::const_iterator K = attrs.begin();
for (; K != attrs.end(); ++K) {
if (!K->second.isMap()) {
log(ERROR, "Attribute description in rule is not a map.");
continue;
}
const MapType & attr = K->second.asMap();
MapType::const_iterator L = attr.find("default");
if (L != attr.end()) {
f->m_attributes[K->first] = L->second;
}
}
}
// Check whether it should be available to players.
J = classDesc.find("playable");
if ((J != classDesc.end()) && (J->second.isInt())) {
Player::playableTypes.insert(className);
}
debug(std::cout << "INSTALLING " << className << ":" << parent
<< "{" << f->m_script << "." << f->m_language << "}"
<< std::endl << std::flush;);
// Install the factory in place.
installFactory(parent, className, f);
RuleWaitList::iterator I = m_waitingRules.lower_bound(className);
for (; I != m_waitingRules.upper_bound(className); ++I) {
const std::string & wClassName = I->second.first;
const MapType & wClassDesc = I->second.second;
debug(std::cout << "WAITING rule " << wClassName
<< " now ready" << std::endl << std::flush;);
installRule(wClassName, wClassDesc);
}
m_waitingRules.erase(className);
}
void EntityFactory::installRules()
{
MapType ruleTable;
Persistance * p = Persistance::instance();
p->getRules(ruleTable);
MapType::const_iterator I = ruleTable.begin();
for(; I != ruleTable.end(); ++I) {
const std::string & className = I->first;
const MapType & classDesc = I->second.asMap();
installRule(className, classDesc);
}
// Report on the non-cleared rules.
// Perhaps we can keep them too?
// m_waitingRules.clear();
RuleWaitList::const_iterator J = m_waitingRules.begin();
for(; J != m_waitingRules.end(); ++J) {
const std::string & wParentName = J->first;
std::string msg = std::string("Rule \"") + J->first
+ "\" with parent \"" + wParentName
+ "\" is an orphan.";
log(ERROR, msg.c_str());
}
}
void EntityFactory::installFactory(const std::string & parent,
const std::string & className,
FactoryBase * factory)
{
assert(factory != 0);
m_factories[className] = factory;
Inheritance & i = Inheritance::instance();
Atlas::Objects::Root * r = new Atlas::Objects::Entity::GameEntity(Atlas::Objects::Entity::GameEntity::Class());
r->setId(className);
r->setParents(ListType(1, parent));
i.addChild(r);
}
FactoryBase * EntityFactory::getNewFactory(const std::string & parent)
{
FactoryDict::const_iterator I = m_factories.find(parent);
if (I == m_factories.end()) {
return 0;
}
return I->second->duplicateFactory();
}