Vector<WorldCoordinates>* PathFinderManager::findPathFromCellToCell(const WorldCoordinates& pointA, const WorldCoordinates& pointB) {
	CellObject* ourCell = pointA.getCell();
	CellObject* targetCell = pointB.getCell();

	if (ourCell != targetCell)
		return findPathFromCellToDifferentCell(pointA, pointB);

	int ourCellID = ourCell->getCellNumber();

	ManagedReference<BuildingObject*> building = cast<BuildingObject*>( ourCell->getParent().get().get());

	SharedObjectTemplate* templateObject = building->getObjectTemplate();

	if (templateObject == NULL)
		return NULL;

	PortalLayout* portalLayout = templateObject->getPortalLayout();

	if (portalLayout == NULL)
		return NULL;

	FloorMesh* floorMesh1 = portalLayout->getFloorMesh(ourCellID);
	PathGraph* pathGraph1 = floorMesh1->getPathGraph();

	Vector<WorldCoordinates>* path = new Vector<WorldCoordinates>(5, 1);
	path->add(pointA); // adding source

	//info("same cell... trying to calculate triangle path", true);

	Vector<Triangle*>* trianglePath = NULL;

	//info("searching floorMesh for cellID " + String::valueOf(ourCellID), true);

	int res = getFloorPath(pointA.getPoint(), pointB.getPoint(), floorMesh1, trianglePath);

	if (res == -1) { //points in the same triangle
		path->add(pointB);

		return path;
	}

	if (trianglePath == NULL) { // returning NULL, no path found
		//error("path NULL");
		delete path;

		return findPathFromCellToDifferentCell(pointA, pointB);
	} else {
		//info("path found", true);

		addTriangleNodeEdges(pointA.getPoint(), pointB.getPoint(), trianglePath, path, ourCell);

		delete trianglePath;

		path->add(pointB); //adding destination

		return path;
	}

	return path;
}
AABBTree* CollisionManager::getAABBTree(SceneObject* scno, int collisionBlockFlags) {
	SharedObjectTemplate* templateObject = scno->getObjectTemplate();

	if (templateObject == NULL)
		return NULL;

	if (!(templateObject->getCollisionActionBlockFlags() & collisionBlockFlags))
		return NULL;

	PortalLayout* portalLayout = templateObject->getPortalLayout();
	MeshAppearanceTemplate* mesh = NULL;

	if (portalLayout != NULL) {
		mesh = portalLayout->getMeshAppearanceTemplate(0);
	} else {
		AppearanceTemplate* appTemplate = templateObject->getAppearanceTemplate();

		if (appTemplate == NULL)
			return NULL;

		mesh = dynamic_cast<MeshAppearanceTemplate*>(appTemplate->getFirstMesh());
	}

	if (mesh == NULL)
		return NULL;

	return mesh->getAABBTree();
}
short PetManagerImplementation::checkMountEligibility(PetControlDevice* petControlDevice, float height) {
	ManagedReference<TangibleObject*> controlledObject = petControlDevice->getControlledObject();
	if (controlledObject == NULL || !controlledObject->isAiAgent())
		return PetManager::INVALIDCREATURE;

	ManagedReference<AiAgent*> pet = cast<AiAgent*>(controlledObject.get());
	if( pet == NULL )
		return PetManager::INVALIDCREATURE;

	//Check if the pet's species is able to be trained as a mount
	if (!pet->hasSlotDescriptor("rider"))
		return PetManager::INVALIDCREATURE;

	SharedObjectTemplate* objectTemplate = petControlDevice->getObjectTemplate();
	if (objectTemplate == NULL)
		return PetManager::INVALIDCREATURE;

	short result;

	if (height == -1)
		result = isValidMountScale(objectTemplate->getAppearanceFilename(), 1, pet->getHeight());
	else
		result = isValidMountScale(objectTemplate->getAppearanceFilename(), 1, height);

	return result;
}
bool PlanetManagerImplementation::isInObjectsNoBuildZone(float x, float y, float extraMargin) {
	SortedVector<ManagedReference<QuadTreeEntry* > > closeObjects;

	Vector3 targetPos(x, y, zone->getHeight(x, y));

	zone->getInRangeObjects(x, y, 512, &closeObjects, true);

	for (int i = 0; i < closeObjects.size(); ++i) {
		SceneObject* obj = cast<SceneObject*>(closeObjects.get(i).get());

		SharedObjectTemplate* objectTemplate = obj->getObjectTemplate();

		if (objectTemplate != NULL) {
			float radius = objectTemplate->getNoBuildRadius();

			// Only check objects with an actual NoBuildRadius
			if (radius > 0) {
				// Add margin to check
				radius += extraMargin;

				Vector3 objWorldPos = obj->getWorldPosition();

				if (objWorldPos.squaredDistanceTo(targetPos) < radius * radius) {
					return true;
				}
			}
		}
	}

	return false;
}
bool CollisionManager::checkLineOfSightWorldToCell(const Vector3& rayOrigin, const Vector3& rayEnd, float distance, CellObject* cellObject) {
	ManagedReference<SceneObject*> building = cellObject->getParent();

	if (building == NULL)
		return true;

	SharedObjectTemplate* objectTemplate = building->getObjectTemplate();
	PortalLayout* portalLayout = objectTemplate->getPortalLayout();

	if (portalLayout == NULL)
		return true;

	Ray ray = convertToModelSpace(rayOrigin, rayEnd, building);

	if (cellObject->getCellNumber() >= portalLayout->getAppearanceTemplatesSize())
		return true;

	MeshAppearanceTemplate* app = portalLayout->getMeshAppearanceTemplate(cellObject->getCellNumber());

	AABBTree* aabbTree = app->getAABBTree();

	if (aabbTree == NULL)
		return true;

	float intersectionDistance;
	Triangle* triangle = NULL;

	if (aabbTree->intersects(ray, distance, intersectionDistance, triangle, true))
		return false;

	return true;
}
bool CollisionManager::checkLineOfSightInParentCell(SceneObject* object, Vector3& endPoint) {
	ManagedReference<SceneObject*> parent = object->getParent();

	if (parent == NULL || !parent->isCellObject())
		return true;

	CellObject* cell = cast<CellObject*>( parent.get());

	SharedObjectTemplate* objectTemplate = parent->getRootParent().get()->getObjectTemplate();
	PortalLayout* portalLayout = objectTemplate->getPortalLayout();
	MeshAppearanceTemplate* appearanceMesh = NULL;

	if (portalLayout == NULL)
		return true;

	try {
		appearanceMesh = portalLayout->getMeshAppearanceTemplate(cell->getCellNumber());
	} catch (Exception& e) {
		return true;
	}

	if (appearanceMesh == NULL) {
		//info("null appearance mesh ");
		return true;
	}

	AABBTree* aabbTree = appearanceMesh->getAABBTree();

	if (aabbTree == NULL)
		return true;

	//switching Y<->Z, adding 0.1 to account floor
	Vector3 startPoint = object->getPosition();
	startPoint.set(startPoint.getX(), startPoint.getY(), startPoint.getZ() + 0.1f);

	endPoint.set(endPoint.getX(), endPoint.getY(), endPoint.getZ() + 0.1f);

	Vector3 dir = endPoint - startPoint;
	dir.normalize();

	float distance = endPoint.distanceTo(startPoint);
	float intersectionDistance;

	Ray ray(startPoint, dir);

	Triangle* triangle = NULL;

	//nothing in the middle
	if (aabbTree->intersects(ray, distance, intersectionDistance, triangle, true))
		return false;

	Ray ray2(endPoint, Vector3(0, -1, 0));

	//check if we are in the cell with dir (0, -1, 0)
	if (!aabbTree->intersects(ray2, 64000.f, intersectionDistance, triangle, true))
		return false;

	return true;
}
Vector<float>* CollisionManager::getCellFloorCollision(float x, float y, CellObject* cellObject) {
	Vector<float>* collisions = NULL;

	ManagedReference<SceneObject*> rootObject = cellObject->getRootParent();

	if (rootObject == NULL)
		return NULL;

	SharedObjectTemplate* templateObject = rootObject->getObjectTemplate();

	if (templateObject == NULL)
		return NULL;

	PortalLayout* portalLayout = templateObject->getPortalLayout();

	if (portalLayout == NULL)
		return NULL;

	FloorMesh* mesh = portalLayout->getFloorMesh(cellObject->getCellNumber());

	if (mesh == NULL)
		return NULL;

	AABBTree* tree = mesh->getAABBTree();

	if (tree == NULL)
		return NULL;

	Vector3 rayStart(x, 16384.f, y);
	Vector3 rayEnd(x, -16384.f, y);

	Vector3 norm = rayEnd - rayStart;
	norm.normalize();

	Ray ray(rayStart, norm);

	SortedVector<IntersectionResult> results(3, 2);

	tree->intersects(ray, 16384 * 2, results);

	if (results.size() == 0)
		return NULL;

	collisions = new Vector<float>(results.size(), 1);

	for (int i = 0; i < results.size(); ++i) {
		float floorHeight = 16384 - results.get(i).getIntersectionDistance();

		collisions->add(floorHeight);
	}

	return collisions;
}
示例#8
0
String TemplateManager::getTemplateFile(uint32 key) {
	SharedObjectTemplate* templateData = templateCRCMap->get(key);

	if (templateData == NULL) {
		String ascii = clientTemplateCRCMap->get(key);

		if (ascii.isEmpty())
			throw Exception("TemplateManager::getTemplateFile exception unknown template key 0x" + String::hexvalueOf((int)key));
		else
			return ascii;
	}

	return templateData->getFullTemplateString();
}
示例#9
0
TEST_F(LuaMobileTest, LuaLootGroupsTest) {

	// Verify loot group map loaded
	ASSERT_EQ(LootGroupMap::ERROR_CODE, 0);

	// Test Loot Items
	HashTableIterator<String, Reference<LootItemTemplate*> > itemIter = lootGroupMap->itemTemplates.iterator();
	while (itemIter.hasNext()) {

		LootItemTemplate* lootItemTemplate = itemIter.next();
		String itemTemplateName( lootItemTemplate->getTemplateName().toCharArray() );

		// Make sure that no loot items have the same name as a loot group
		EXPECT_FALSE( lootGroupMap->lootGroupExists(itemTemplateName) ) << "Loot item " << std::string(itemTemplateName.toCharArray()) << " has the same name as a loot group.";

		// Verify that directObjectTemplate is valid
		String directObjectTemplate = lootItemTemplate->getDirectObjectTemplate();
		SharedObjectTemplate* templateObject = templateManager->getTemplate(directObjectTemplate.hashCode());
		EXPECT_TRUE( templateObject != NULL && templateObject->isSharedTangibleObjectTemplate() ) << "directObjectTemplate is invalid in loot item " << std::string(itemTemplateName.toCharArray());
	}

	// Test Loot Groups
	HashTableIterator<String, Reference<LootGroupTemplate*> > iter = lootGroupMap->groupTemplates.iterator();
	while (iter.hasNext()) {

		LootGroupTemplate* lootGroupTemplate = iter.next();
		String groupTemplateName( lootGroupTemplate->getTemplateName().toCharArray() );

		// Verify that group is not empty
		EXPECT_TRUE( lootGroupTemplate->getLootGroupEntryForRoll(-1).length() > 0 ) << "No entries in loot group: " << std::string(groupTemplateName.toCharArray());

		// Check loot group to make sure their chances total correctly
		EXPECT_GT( lootGroupTemplate->getLootGroupEntryForRoll(10000000).length(), 0 ) << "Item total chance is less than 10000000: " << std::string(groupTemplateName.toCharArray());
		EXPECT_EQ( lootGroupTemplate->getLootGroupEntryForRoll(10000001).length(), 0 ) << "Item total chance is greater than 10000000: " << std::string(groupTemplateName.toCharArray());

		// Check that all loot group entries are valid
		for( int i = 0; i < lootGroupTemplate->size(); i++ ){

			Vector<String> parentGroups;
			parentGroups.add(groupTemplateName);

			String entryName = lootGroupTemplate->getLootGroupEntryAt(i);

			checkLootGroupEntryRecursive(entryName, &parentGroups);
		}

	}

}
float PetManagerImplementation::getMountedRunSpeed(CreatureObject* mount) {
	if (!mount->isMount())
		return mount->getRunSpeed();

	ManagedReference<PetControlDevice*> pcd = mount->getControlDevice().get().castTo<PetControlDevice*>();

	if (pcd != NULL) {
		SharedObjectTemplate* objectTemplate = pcd->getObjectTemplate();

		if (objectTemplate != NULL) {
			MountSpeedData* mountSpeedData = getMountSpeedData(objectTemplate->getAppearanceFilename());

			if (mountSpeedData != NULL)
				return mountSpeedData->getRunSpeed();
		}
	}

	return mount->getRunSpeed();
}
FloorMesh* PathFinderManager::getFloorMesh(CellObject* cell) {
    ManagedReference<BuildingObject*> building1 = (cell->getParent().get().castTo<BuildingObject*>());

    SharedObjectTemplate* templateObject = building1->getObjectTemplate();

    if (templateObject == NULL) {
    	return NULL;
    }

    PortalLayout* portalLayout = templateObject->getPortalLayout();

    if (portalLayout == NULL) {
    	return NULL;
    }

    FloorMesh* floorMesh1 = portalLayout->getFloorMesh(cell->getCellNumber());

    return floorMesh1;
}
示例#12
0
SceneObject* ObjectManager::loadObjectFromTemplate(uint32 objectCRC) {
	Locker _locker(this);

	SceneObject* object = NULL;

	try {
		SharedObjectTemplate* templateData = templateManager->getTemplate(objectCRC);

		if (templateData == NULL) {
			error("trying to create object with unknown objectcrc 0x" + String::hexvalueOf((int)objectCRC));
			return NULL;
		}

		uint32 gameObjectType = templateData->getGameObjectType();

		object = objectFactory.createObject(gameObjectType);

		if (object == NULL) {
			error("creating object unknown gameObjectType " + String::valueOf(gameObjectType));
			return NULL;
		}

		databaseManager->addTemporaryObject(object);

		object->setServerObjectCRC(objectCRC);
		object->loadTemplateData(templateData);

	} catch (Exception& e) {
		error("exception caught in SceneObject* ObjectManager::loadObjectFromTemplate(uint32 objectCRC)");
		error(e.getMessage());

		e.printStackTrace();
	} catch (...) {
		error("unreported exception caught in SceneObject* ObjectManager::loadObjectFromTemplate(uint32 objectCRC)");

		throw;
	}

	return object;
}
bool CollisionManager::checkLineOfSightInBuilding(SceneObject* object1, SceneObject* object2, SceneObject* building) {
	SharedObjectTemplate* objectTemplate = building->getObjectTemplate();
	PortalLayout* portalLayout = objectTemplate->getPortalLayout();

	if (portalLayout == NULL)
		return true;

	//we are in model space... in cells
	Vector3 rayOrigin = object1->getPosition();
	rayOrigin.set(rayOrigin.getX(), rayOrigin.getY(), rayOrigin.getZ() + 1.f);

	Vector3 rayEnd = object2->getPosition();
	rayEnd.set(rayEnd.getX(), rayEnd.getY(), rayEnd.getZ() + 1.f);

	Vector3 direction(Vector3(rayEnd - rayOrigin));
	direction.normalize();

	float distance = rayEnd.distanceTo(rayOrigin);
	float intersectionDistance;

	Ray ray(rayOrigin, direction);
	Triangle* triangle = NULL;

	// we check interior cells
	for (int i = 1; i < portalLayout->getAppearanceTemplatesSize(); ++i) {
		MeshAppearanceTemplate* app = portalLayout->getMeshAppearanceTemplate(i);

		AABBTree* aabbTree = app->getAABBTree();

		if (aabbTree == NULL)
			continue;

		if (aabbTree->intersects(ray, distance, intersectionDistance, triangle, true))
			return false;
	}

	return true;
}
bool PlanetManagerImplementation::isInObjectsNoBuildZone(float x, float y, float extraMargin) {
	SortedVector<QuadTreeEntry*> closeObjects;

	Vector3 targetPos(x, y, zone->getHeight(x, y));

	zone->getInRangeObjects(x, y, 512, &closeObjects, true);

	for (int i = 0; i < closeObjects.size(); ++i) {
		SceneObject* obj = static_cast<SceneObject*>(closeObjects.get(i));

		SharedObjectTemplate* objectTemplate = obj->getObjectTemplate();

		if (objectTemplate != NULL) {
			float radius = objectTemplate->getNoBuildRadius();

			// Only check objects with an actual NoBuildRadius
			if (radius > 0) {
				// Add margin to check
				radius += extraMargin;

				Vector3 objWorldPos = obj->getWorldPosition();

				if (objWorldPos.squaredDistanceTo(targetPos) < radius * radius) {
					return true;
				}
			}

			// Check if it's within a structure's footprint
			if (objectTemplate->isSharedStructureObjectTemplate()) {
				if (StructureManager::instance()->isInStructureFootprint(cast<StructureObject*>(obj), x, y, extraMargin)) {
					return true;
				}
			}
		}
	}

	return false;
}
示例#15
0
void TemplateManager::addTemplate(uint32 key, const String& fullName, LuaObject* templateData) {
	uint32 templateType = templateData->getIntField("templateType");
	String clientTemplateFile = templateData->getStringField("clientTemplateFileName");

	SharedObjectTemplate* templateObject = templateFactory.createObject(templateType);

	if (templateObject == NULL) {
		error("error creating template from lua with templateType 0x" + String::hexvalueOf((int)templateType));

		return;
	}

	//info("loading " + fullName, true);

	String fileName = fullName.subString(fullName.lastIndexOf('/') + 1, fullName.lastIndexOf('.'));

	templateObject->setFullTemplateString(fullName);
	templateObject->setTemplateFileName(fileName);

	if (!clientTemplateFile.isEmpty()) {
		IffStream* iffStream = openIffFile(clientTemplateFile);

		if (iffStream != NULL) {
			templateObject->readObject(iffStream);

			delete iffStream;
		}
	}

	templateObject->readObject(templateData);

	if (!clientTemplateFile.isEmpty())
		templateObject->addDerivedFile(clientTemplateFile);

	info("loaded " + fullName);

	if (templateCRCMap->put(key, templateObject) != NULL) {
		//error("duplicate template for " + fullName);
	}
}
Vector<WorldCoordinates>* PathFinderManager::findPathFromWorldToCell(const WorldCoordinates& pointA, const WorldCoordinates& pointB, Zone *zone) {
	CellObject* targetCell = pointB.getCell();

	if (targetCell == NULL)
		return NULL;

	ManagedReference<BuildingObject*> building = dynamic_cast<BuildingObject*>(targetCell->getParent().get().get());

	if (building == NULL) {
		error("building == NULL in PathFinderManager::findPathFromWorldToCell");
		return NULL;
	}

	SharedObjectTemplate* templateObject = building->getObjectTemplate();

	if (templateObject == NULL)
		return NULL;

	PortalLayout* portalLayout = templateObject->getPortalLayout();

	if (portalLayout == NULL)
		return NULL;

	//find nearest entrance
	FloorMesh* exteriorFloorMesh = portalLayout->getFloorMesh(0); // get outside layout

	if (exteriorFloorMesh == NULL)
		return NULL;

	PathGraph* exteriorPathGraph = exteriorFloorMesh->getPathGraph();

	FloorMesh* targetFloorMesh = portalLayout->getFloorMesh(targetCell->getCellNumber());
	PathGraph* targetPathGraph = targetFloorMesh->getPathGraph();

	Vector<WorldCoordinates>* path = new Vector<WorldCoordinates>(5, 1);
	path->add(pointA);

	Vector3 transformedPosition = transformToModelSpace(pointA.getPoint(), building);

	PathNode* nearestEntranceNode = exteriorPathGraph->findNearestNode(transformedPosition);

	if (nearestEntranceNode == NULL) {
		error("NULL entrance node for building " + templateObject->getFullTemplateString());
		delete path;
		return NULL;
	}
	//PathNode* nearestTargetNode = targetPathGraph->findNearestNode(pointB.getPoint());
	TriangleNode* nearestTargetNodeTriangle = CollisionManager::getTriangle(pointB.getPoint(), targetFloorMesh);

	if (nearestTargetNodeTriangle == NULL) {
		delete path;
		return NULL;
	}

	PathNode* nearestTargetNode = CollisionManager::findNearestPathNode(nearestTargetNodeTriangle, targetFloorMesh, pointB.getPoint());//targetPathGraph->findNearestNode(pointB.getPoint());

	if (nearestTargetNode == NULL) {
		delete path;
		return NULL;
	}

	/*if (nearestEntranceNode == nearestTargetNode)
		info("nearestEntranceNode == nearestTargetNode", true);*/

	//find graph from outside to appropriate cell
	Vector<PathNode*>* pathToCell = portalLayout->getPath(nearestEntranceNode, nearestTargetNode);

	if (pathToCell == NULL) {
		error("pathToCell = portalLayout->getPath(nearestEntranceNode, nearestTargetNode); == NULL");
		delete path;
		return NULL;
	}

	for (int i = 0; i < pathToCell->size(); ++i) {
		PathNode* pathNode = pathToCell->get(i);
		PathGraph* pathGraph = pathNode->getPathGraph();

		FloorMesh* floorMesh = pathGraph->getFloorMesh();

		int cellID = floorMesh->getCellID();

		//info("cellID:" + String::valueOf(cellID), true);

		if (cellID == 0) { // we are still outside
			WorldCoordinates coord(pathNode->getPosition(), targetCell);

			path->add(WorldCoordinates(coord.getWorldPosition(), NULL));
		} else { // we are inside the building
			CellObject* pathCell = building->getCell(cellID);

			path->add(WorldCoordinates(pathNode->getPosition(), pathCell));

			if (i == pathToCell->size() - 1)
				if (pathCell != targetCell) {
					error("final cell not target cell");
				}
		}
	}

	delete pathToCell;
	pathToCell = NULL;

	// path from cell path node to destination point
	Vector<Triangle*>* trianglePath = NULL;

	int res = getFloorPath(path->get(path->size() - 1).getPoint(), pointB.getPoint(), targetFloorMesh, trianglePath);

	if (res != -1 && trianglePath != NULL)
		addTriangleNodeEdges(path->get(path->size() - 1).getPoint(), pointB.getPoint(), trianglePath, path, targetCell);

	if (trianglePath != NULL)
		delete trianglePath;

	path->add(pointB);

	return path;
}
示例#17
0
TEST_F(LuaMobileTest, LuaMobileTemplatesTest) {
	CreatureTemplateManager::DEBUG_MODE = 1;

	// Verify that all mobiles load
	ASSERT_EQ(CreatureTemplateManager::instance()->loadTemplates(), 0);

	// Verify loot group map loaded
	ASSERT_EQ(LootGroupMap::ERROR_CODE, 0);

	// Verify factions load
	FactionManager::instance()->loadData();
	ASSERT_FALSE(FactionManager::instance()->getFactionMap()->isEmpty());

	// Load Templates
	ASSERT_TRUE( TemplateManager::instance() != NULL );
	if( TemplateManager::instance()->loadedTemplatesCount == 0 ){
		TemplateManager::instance()->loadLuaTemplates();
		ASSERT_EQ(TemplateManager::ERROR_CODE, 0);
	}
	// verify DNA manager loads
	DnaManager::instance()->loadSampleData();
	ASSERT_TRUE( DnaManager::instance() != NULL);


	// Test Creature Templates
	HashTableIterator<uint32, Reference<CreatureTemplate*> > creatureIterator = CreatureTemplateManager::instance()->iterator();
	while (creatureIterator.hasNext()) {
		CreatureTemplate* creature = creatureIterator.next();
		std::string templateName( creature->getTemplateName().toCharArray() );
		//Verify non-empty objectName is a valid string
		String objName = creature->getObjectName();
		if (!objName.isEmpty()) {
			std::string name = objName.toCharArray();
			EXPECT_TRUE( mobNames.contains(objName) ) << "Mobile " << templateName << " has invalid objectName: "  << name;
		}

		// Check configured templates
		Vector<String> objTemps = creature->getTemplates();
		EXPECT_FALSE( objTemps.isEmpty() ) << "Mobile " << templateName << " does not have any templates configured";
		int objectType = 0;
		for( int j=0; j< objTemps.size(); j++ ){
			SharedObjectTemplate* templateData = templateManager->getTemplate(objTemps.get(j).hashCode());
			std::string objName = objTemps.get(j).toCharArray();
			EXPECT_TRUE( templateData != NULL ) << "Mobile " << templateName << " has invalid template configured: " << objName;

			// Check Template Genetics math to find invalid mobs
			if (templateData != NULL) {
				SharedCreatureObjectTemplate* creoData = dynamic_cast<SharedCreatureObjectTemplate*> (templateData);
				if (creoData != NULL) {
				}
			}

			if (objectType == 0) {
				objectType = templateData->getGameObjectType();
			}
		}
		// Verify that control device template is valid
		String controlDeviceTemplate = creature->getControlDeviceTemplate();
		if (!controlDeviceTemplate.isEmpty()) {
			SharedObjectTemplate* controlDeviceTemplateData = templateManager->getTemplate(controlDeviceTemplate.hashCode());
			EXPECT_TRUE( controlDeviceTemplateData != NULL ) << "Control device template " << controlDeviceTemplate.toCharArray() << " from " << templateName << " does not exist.";
			EXPECT_TRUE( controlDeviceTemplate.beginsWith("object/intangible/pet/") ) << "Control device template " << controlDeviceTemplate.toCharArray() << " from " << templateName << " is not a pet/droid control device template.";
		}

		// Verify that faction is valid
		String faction = creature->getFaction();
		if (!faction.isEmpty()) {
			EXPECT_TRUE( FactionManager::instance()->isFaction(faction) ) << "Faction, " << faction.toCharArray() << ", from mobile template " << templateName << " does not exist.";
		}

		// Verify level
		int level = creature->getLevel();
		EXPECT_TRUE( level > 0 ) << "Level is not a positive value on mobile: " << templateName;

		// Verify hit chance
		float hitChance = creature->getChanceHit();
		EXPECT_TRUE( hitChance > 0 ) << "ChanceHit is not a positive value on mobile: " << templateName;

		// Verify xp
		int xp = creature->getBaseXp();
		EXPECT_TRUE( xp >= 0 ) << "Xp has a negative value on mobile: " << templateName;

		// Verify damage
		int minDamage = creature->getDamageMin();
		int maxDamage = creature->getDamageMax();
		EXPECT_TRUE( minDamage > 0 ) << "Min damage is not a positive value on mobile: " << templateName;
		EXPECT_TRUE( maxDamage >= minDamage ) << "Max damage is lower than min damage on mobile: " << templateName;

		// Verify HAM
		int minHam = creature->getBaseHAM();
		int maxHam = creature->getBaseHAMmax();
		EXPECT_TRUE( minHam > 0 ) << "Base ham is not a positive value on mobile: " << templateName;
		EXPECT_TRUE( maxHam >= minHam ) << "Base ham max is lower than base ham on mobile: " << templateName;

		// Verify armor
		int armor = creature->getArmor();
		EXPECT_TRUE( armor >= 0 && armor <= 3 ) << "Armor is not a valid value on mobile: " << templateName;

		// Verify resists
		float kinetic = creature->getKinetic();
		EXPECT_TRUE( kinetic >= -1 && kinetic <= 200 ) << "Kinetic resist is not a valid value on mobile: " << templateName;
		float energy = creature->getEnergy();
		EXPECT_TRUE( energy >= -1 && energy <= 200 ) << "Energy resist is not a valid value on mobile: " << templateName;
		float electricity = creature->getElectricity();
		EXPECT_TRUE( electricity >= -1 && electricity <= 200 ) << "Electricity resist is not a valid value on mobile: " << templateName;
		float stun = creature->getStun();
		EXPECT_TRUE( stun >= -1 && stun <= 200 ) << "Stun resist is not a valid value on mobile: " << templateName;
		float blast = creature->getBlast();
		EXPECT_TRUE( blast >= -1 && blast <= 200 ) << "Blast resist is not a valid value on mobile: " << templateName;
		float heat = creature->getHeat();
		EXPECT_TRUE( heat >= -1 && heat <= 200 ) << "Heat resist is not a valid value on mobile: " << templateName;
		float cold = creature->getCold();
		EXPECT_TRUE( cold >= -1 && cold <= 200 ) << "Cold resist is not a valid value on mobile: " << templateName;
		float acid = creature->getAcid();
		EXPECT_TRUE( acid >= -1 && acid <= 200 ) << "Acid resist is not a valid value on mobile: " << templateName;
		float lightSaber = creature->getLightSaber();
		EXPECT_TRUE( lightSaber >= -1 && lightSaber <= 200 ) << "LightSaber resist is not a valid value on mobile: " << templateName;

		// Verify creature resources
		String meat = creature->getMeatType();
		float meatMax = creature->getMeatMax();
		if (!meat.isEmpty()) {
			String meatResources = "meat_domesticated,meat_wild,meat_herbivore,meat_carnivore,meat_reptilian,meat_avian,meat_insect";
			StringTokenizer tokenizer(meatResources);
			tokenizer.setDelimeter(",");
			bool match = false;
			String token;
			while (tokenizer.hasMoreTokens()) {
				tokenizer.getStringToken(token);
				if (meat == token)
					match = true;
			}
			EXPECT_TRUE( match ) << "Meat type on mobile " << templateName << " is not a valid meat resource";
			EXPECT_TRUE( meatMax > 0 ) << "Meat amount on mobile " << templateName << " is zero.";
		} else {
			EXPECT_TRUE( meatMax == 0 ) << "MeatAmount is not zero yet has no type defined on mobile " << templateName;
		}

		String hide = creature->getHideType();
		float hideMax = creature->getHideMax();
		if (!hide.isEmpty()) {
			String hideResources = "hide_bristley,hide_leathery,hide_scaley,hide_wooly";
			StringTokenizer tokenizer(hideResources);
			tokenizer.setDelimeter(",");
			bool match = false;
			String token;
			while (tokenizer.hasMoreTokens()) {
				tokenizer.getStringToken(token);
				if (hide == token)
					match = true;
			}
			EXPECT_TRUE( match ) << "Hide type on mobile " << templateName << " is not a valid hide resource";
			EXPECT_TRUE( hideMax > 0 ) << "Hide amount on mobile " << templateName << " is zero.";
		} else {
			EXPECT_TRUE( hideMax == 0 ) << "HideAmount is not zero yet has no type defined on mobile " << templateName;
		}

		String bone = creature->getBoneType();
		float boneMax = creature->getBoneMax();
		if (!bone.isEmpty()) {
			String boneResources = "bone_avian,bone_mammal";
			StringTokenizer tokenizer(boneResources);
			tokenizer.setDelimeter(",");
			bool match = false;
			String token;
			while (tokenizer.hasMoreTokens()) {
				tokenizer.getStringToken(token);
				if (bone == token)
					match = true;
			}
			EXPECT_TRUE( match ) << "Bone type on mobile " << templateName << " is not a valid bone resource";
			EXPECT_TRUE( boneMax > 0 ) << "Bone amount on mobile " << templateName << " is zero.";
		} else {
			EXPECT_TRUE( boneMax == 0 ) << "BoneAmount is not zero yet has no type defined on mobile " << templateName;
		}

		String milk = creature->getMilkType();
		float milkMax = creature->getMilk();
		if (!milk.isEmpty()) {
			String milkResources = "milk_domesticated,milk_wild";
			StringTokenizer tokenizer(milkResources);
			tokenizer.setDelimeter(",");
			bool match = false;
			String token;
			while (tokenizer.hasMoreTokens()) {
				tokenizer.getStringToken(token);
				if (milk == token)
					match = true;
			}
			EXPECT_TRUE( match ) << "Milk type on mobile " << templateName << " is not a valid milk resource";
			EXPECT_TRUE( milkMax > 0 ) << "Milk amount on mobile " << templateName << " is zero.";
		} else {
			EXPECT_TRUE( milkMax == 0 ) << "Milk is not zero yet has no type defined on mobile " << templateName;
		}

		// Verify taming chance
		float tamingChance = creature->getTame();
		EXPECT_TRUE( tamingChance >= 0 && tamingChance <= 1 ) << "Taming chance is not a valid value on mobile: " << templateName;

		// Verify diet on creatures
		if (boneMax > 0 || hideMax > 0 || meatMax > 0 || milkMax > 0 || tamingChance > 0) {
			uint32 diet = creature->getDiet();
			EXPECT_TRUE( diet != 0 ) << "Diet is NONE on creature type mobile " << templateName;
		}

		// Verify scale
		float scale = creature->getScale();
		EXPECT_TRUE( scale > 0 ) << "Scale is not a positive value on mobile: " << templateName;

		// Verify PACK mobs have a social group
		uint32 creatureBitmask = creature->getCreatureBitmask();
		String socialGroup = creature->getSocialGroup();
		if (creatureBitmask & CreatureFlag::PACK) {
			EXPECT_FALSE( socialGroup.isEmpty() ) << "Social group is empty on pack mobile: " << templateName;
		}

		// Verify loot group percentages
		LootGroupCollection* groupCollection = creature->getLootGroups();
		if( groupCollection->count() > 0 ){


			for( int i = 0; i < groupCollection->count(); i++ ){

				LootGroupCollectionEntry* collectionEntry = groupCollection->get(i);
				LootGroups* groups = collectionEntry->getLootGroups();
				if( groups->count() > 0){

					int totalChance = 0;
					for( int j = 0; j < groups->count(); j++ ){

						LootGroupEntry* lootGroup = groups->get(j);
						totalChance += lootGroup->getLootChance();

						// Verify loot group is configured correctly
						LootGroupTemplate* foundGroup = lootGroupMap->getLootGroupTemplate( lootGroup->getLootGroupName() );
						std::string groupName( lootGroup->getLootGroupName().toCharArray() );
						EXPECT_TRUE( foundGroup != NULL ) << "Loot group " << groupName << " from " << templateName << " was not found in LootGroupMap";

					}

					EXPECT_EQ( 10000000, totalChance ) << "Loot groups total chance is incorrect " << templateName;
				}
			}
		}

		// Verify weapon groups exist
		Vector<String> weapons = creature->getWeapons();
		for (int i = 0; i < weapons.size(); i++) {
			String weaponGroup = weapons.get(i);
			std::string groupName( weaponGroup.toCharArray() );
			Vector<String> group = CreatureTemplateManager::instance()->getWeapons(weaponGroup);
			EXPECT_TRUE( group.size() > 0 ) << "Weapon group " << groupName << " from " << templateName << " was not found in weaponMap";
		}

		// Verify conversation template exist, and the mob has converse option bit
		uint32 convoTemplate = creature->getConversationTemplate();
		uint32 optionsBitmask = creature->getOptionsBitmask();
		if (convoTemplate != 0) {
			ConversationTemplate* convoTemp = CreatureTemplateManager::instance()->getConversationTemplate(convoTemplate);
			EXPECT_TRUE( convoTemp != NULL ) << "Conversation template from " << templateName << " was not found.";
			EXPECT_TRUE( optionsBitmask & OptionBitmask::CONVERSE ) << templateName << " has a convo template but not the CONVERSE options bit.";
		}
		// Verify that mobs with converse option bit have a convo template
		if (optionsBitmask & OptionBitmask::CONVERSE) {
			EXPECT_TRUE( convoTemplate != 0 ) << templateName << " has the CONVERSE options bit but not a convo template.";
		}

		// Verify that outfits exist
		String outfit = creature->getOutfit();
		if (!outfit.isEmpty()) {
			MobileOutfitGroup* outfitGroup = CreatureTemplateManager::instance()->getMobileOutfitGroup(outfit);
			EXPECT_TRUE( outfitGroup != NULL ) << "Outfit group " << outfit.toCharArray() << " from " << templateName << " was not found.";
		}

		// Verify attacks are valid commands
		CreatureAttackMap* cam = creature->getAttacks();
		for (int i = 0; i < cam->size(); i++) {
			String commandName = cam->getCommand(i);

			EXPECT_TRUE( commandName.isEmpty() || commandConfigManager->contains(commandName) ) << "Attack: " << commandName.toCharArray() << " is not a valid command in mobile template: " << templateName;
		}

		// Very attackable npcs
		uint32 pvpBitmask = creature->getPvpBitmask();
		if ((pvpBitmask & CreatureFlag::ATTACKABLE) && objectType == 1025) {
			// Verify attackable npcs have attacks
			EXPECT_TRUE( cam->size() > 0 ) << "Attackable npc " << templateName << " does not have attacks.";
		}
	}

	// Test Lair Templates
	HashTableIterator<uint32, Reference<LairTemplate*> > lairIterator = CreatureTemplateManager::instance()->lairTemplateIterator();
	while (lairIterator.hasNext()) {
		LairTemplate* lair = lairIterator.next();
		std::string templateName( lair->getName().toCharArray() );

		// Verify that mobiles exist and that their weighting is positive
		VectorMap<String, int>* mobiles = lair->getMobiles();
		for (int i = 0; i < mobiles->size(); i++) {
			int weighting = mobiles->elementAt(i).getValue();
			String mobile = mobiles->elementAt(i).getKey();
			std::string mobName = mobile.toCharArray();
			EXPECT_TRUE( CreatureTemplateManager::instance()->getTemplate(mobile) != NULL ) << "Mobile " << mobName << " in lair template " << templateName << " does not exist";
			EXPECT_TRUE( weighting > 0 ) << "Mobile " << mobName << " in lair template " << templateName << " has a non positive weighting";
		}

		// Verify that boss mobiles exist and that their count is positive
		VectorMap<String, int>* bossMobiles = lair->getBossMobiles();
		for (int i = 0; i < bossMobiles->size(); i++) {
			int count = bossMobiles->elementAt(i).getValue();
			String bossMob = bossMobiles->elementAt(i).getKey();
			std::string bossName = bossMob.toCharArray();
			EXPECT_TRUE( CreatureTemplateManager::instance()->getTemplate(bossMob) != NULL ) << "Boss mobile " << bossName << " in lair template " << templateName << " does not exist";
			EXPECT_TRUE( count > 0 ) << "Boss mobile " << bossName << " in lair template " << templateName << " has a non positive spawn count";
		}

		// Verify spawn limit is positive
		int limit = lair->getSpawnLimit();
		EXPECT_TRUE( limit > 0 ) << "Spawn limit in lair template " << templateName << " is not positive";

		// Verify any configured buildings exist
		int buildingCount = 0;
		for(int i=0; i<=4; i++){

			Vector<String>* buildings = lair->getBuildings( i );
			if( buildings == NULL )
				continue;

			buildingCount += buildings->size();

			for( int j=0; j < buildings->size(); j++ ){
				String buildingTemplate = buildings->get(j);
				std::string buildingStr = buildingTemplate.toCharArray();
				SharedObjectTemplate* templateObject = templateManager->getTemplate(buildingTemplate.hashCode());
				EXPECT_TRUE( templateObject != NULL && templateObject->isSharedTangibleObjectTemplate() ) << "Building template " << buildingStr << " in lair template " << templateName << " does not exist";
				if( lair->getBuildingType() == LairTemplate::LAIR ){
					EXPECT_TRUE( buildingTemplate.beginsWith( "object/tangible/lair/") ) << "Building template " << buildingStr << " in lair template " << templateName << " is not a child of object/tangible/lair/";
				}
				if( lair->getBuildingType() == LairTemplate::THEATER ){
					EXPECT_TRUE( buildingTemplate.beginsWith( "object/building/poi/") ) << "Building template " << buildingStr << " in lair template " << templateName << " is not a child of object/building/poi/";
				}
			}
		}

		// Verify mission buildings exist and are lairs
		String missionBuilding = lair->getMissionBuilding(10);
		if (!missionBuilding.isEmpty()) {
			std::string buildingStr = missionBuilding.toCharArray();
			SharedObjectTemplate* templateObject = templateManager->getTemplate(missionBuilding.hashCode());
			EXPECT_TRUE( templateObject != NULL && templateObject->isSharedTangibleObjectTemplate() ) << "Mission building template " << buildingStr << " in lair template " << templateName << " does not exist";
			EXPECT_TRUE( missionBuilding.beginsWith( "object/tangible/lair/") ) << "Mission building template " << buildingStr << " in lair template " << templateName << " is not a child of object/tangible/lair/";
		}

		if( lair->getBuildingType() == LairTemplate::THEATER ){
			EXPECT_TRUE( buildingCount > 0 ) << "There are no buildings configured in theater type lair template " << templateName;
		}
		if( lair->getBuildingType() == LairTemplate::NONE ){
			EXPECT_TRUE( buildingCount == 0 ) << "There are buildings configured in 'none' type lair template " << templateName;
		}
		if( lair->getBuildingType() == LairTemplate::LAIR ){
			EXPECT_TRUE( buildingCount > 0 ) << "There are no buildings configured in lair type lair template " << templateName;
		}

	}

	// Test Spawn Groups
	HashTableIterator<uint32, Reference<SpawnGroup*> > spawnIterator = CreatureTemplateManager::instance()->spawnGroupIterator();
	while (spawnIterator.hasNext()) {
		SpawnGroup* group = spawnIterator.next();
		std::string templateName( group->getTemplateName().toCharArray() );

		Vector<String> lairTemplates;

		// Verify spawn list
		Vector<Reference<LairSpawn*> >* spawnList = group->getSpawnList();
		for (int i = 0; i < spawnList->size(); i++) {
			LairSpawn* spawn = spawnList->get(i);
			std::string lairName( spawn->getLairTemplateName().toCharArray() );

			// Verify lair template exists and isn't duplicated in the group
			String lairTemplateName = spawn->getLairTemplateName();
			Reference<LairTemplate*> lairTemplate = CreatureTemplateManager::instance()->getLairTemplate(lairTemplateName.hashCode());
			EXPECT_TRUE( lairTemplate != NULL ) << "Lair template " << lairName << " in spawn group " << templateName << " does not exist.";
			EXPECT_FALSE( lairTemplates.contains(lairTemplateName) ) << "Lair template " << lairName << " is duplicated in spawn group " << templateName;
			lairTemplates.add(lairTemplateName);

			// Verify spawn limit is at least -1
			float spawnLimit = spawn->getSpawnLimit();
			EXPECT_TRUE( spawnLimit >= -1 ) << "SpawnLimit for lairTemplate " << lairName << " in spawn group " << templateName << " is less than -1.";

			// Verify difficulties
			int minDiff = spawn->getMinDifficulty();
			int maxDiff = spawn->getMaxDifficulty();
			EXPECT_TRUE( minDiff > 0 ) << "MinDifficulty for lairTemplate " << lairName << " in spawn group " << templateName << " is not positive.";
			EXPECT_TRUE( maxDiff >= minDiff ) << "MaxDifficulty for lairTemplate " << lairName << " in spawn group " << templateName << " is less than min difficulty.";

			// Verify number to spawn is not negative
			int numberToSpawn = spawn->getNumberToSpawn();
			EXPECT_TRUE( numberToSpawn >= 0 ) << "NumberToSpawn for lairTemplate " << lairName << " in spawn group " << templateName << " is negative.";

			// Verify weighting is positive
			int weighting = spawn->getWeighting();
			EXPECT_TRUE( weighting > 0 ) << "Weighting for lairTemplate " << lairName << " in spawn group " << templateName << " is not positive.";

			// Verify size is at least 1
			float size = spawn->getSize();
			EXPECT_TRUE( size >= 1 ) << "Size for lairTemplate " << lairName << " in spawn group " << templateName << " is less than 1.";
		}
	}

	// Test Destroy Mission Spawn Groups
	HashTableIterator<uint32, Reference<SpawnGroup*> > missionIterator = CreatureTemplateManager::instance()->destroyMissionGroupIterator();
	while (missionIterator.hasNext()) {
		SpawnGroup* group = missionIterator.next();
		std::string templateName( group->getTemplateName().toCharArray() );

		Vector<String> lairTemplates;

		// Verify spawn list
		Vector<Reference<LairSpawn*> >* spawnList = group->getSpawnList();
		for (int i = 0; i < spawnList->size(); i++) {
			LairSpawn* spawn = spawnList->get(i);
			std::string lairName( spawn->getLairTemplateName().toCharArray() );

			// Verify lair template exists
			String lairTemplateName = spawn->getLairTemplateName();
			Reference<LairTemplate*> lairTemplate = CreatureTemplateManager::instance()->getLairTemplate(lairTemplateName.hashCode());
			EXPECT_TRUE( lairTemplate != NULL ) << "Lair template " << lairName << " in destroy mission spawn group " << templateName << " does not exist.";
			EXPECT_FALSE( lairTemplates.contains(lairTemplateName) ) << "Lair template " << lairName << " is duplicated in destroy mission spawn group " << templateName;
			lairTemplates.add(lairTemplateName);

			if (lairTemplate != NULL) {
				// Verify that lair template has a valid mission building or is of type LAIR
				String missionBuilding = lairTemplate->getMissionBuilding(10);
				if (!missionBuilding.isEmpty()) {
					std::string buildingStr = missionBuilding.toCharArray();
					SharedObjectTemplate* templateObject = templateManager->getTemplate(missionBuilding.hashCode());
					EXPECT_TRUE( templateObject != NULL && templateObject->isSharedTangibleObjectTemplate() ) << "Mission building template " << buildingStr << " in lair template " << lairName << ", part of destroy mission group " << templateName << " does not exist";
					EXPECT_TRUE( missionBuilding.beginsWith( "object/tangible/lair/") ) << "Mission building template " << buildingStr << " in lair template " << lairName << ", part of destroy mission group " << templateName << " is not a child of object/tangible/lair/";
				} else {
					EXPECT_TRUE( lairTemplate->getBuildingType() == LairTemplate::LAIR ) << "Lair template " << lairName << ", part of destroy mission group " << templateName << " is not of type LAIR";
				}
			}

			// Verify difficulties
			int minDiff = spawn->getMinDifficulty();
			int maxDiff = spawn->getMaxDifficulty();
			EXPECT_TRUE( minDiff > 0 ) << "MinDifficulty for lairTemplate " << lairName << " in destroy mission spawn group " << templateName << " is not positive.";
			EXPECT_TRUE( maxDiff >= minDiff ) << "MaxDifficulty for lairTemplate " << lairName << " in destroy mission spawn group " << templateName << " is less than min difficulty.";

			// Verify size is at least 1
			float size = spawn->getSize();
			EXPECT_TRUE( size >= 1 ) << "Size for lairTemplate " << lairName << " in destroy mission spawn group " << templateName << " is less than 1.";
		}
	}
}
Vector<WorldCoordinates>* PathFinderManager::findPathFromCellToWorld(const WorldCoordinates& pointA, const WorldCoordinates& pointB, Zone *zone) {
	Vector<WorldCoordinates>* path = new Vector<WorldCoordinates>(5, 1);

	if (path == NULL)
		return NULL;

	path->add(pointA);

	CellObject* ourCell = pointA.getCell();
	ManagedReference<BuildingObject*> building = cast<BuildingObject*>( ourCell->getParent().get().get());
	int ourCellID = ourCell->getCellNumber();
	SharedObjectTemplate* templateObject = ourCell->getParent().get()->getObjectTemplate();

	if (templateObject == NULL) {
		delete path;
		return NULL;
	}

	PortalLayout* portalLayout = templateObject->getPortalLayout();

	if (portalLayout == NULL) {
		delete path;
		return NULL;
	}

	FloorMesh* sourceFloorMesh = portalLayout->getFloorMesh(ourCellID);

	if (sourceFloorMesh == NULL) {
		delete path;
		return NULL;
	}

	PathGraph* sourcePathGraph = sourceFloorMesh->getPathGraph();

	if (sourcePathGraph == NULL) {
		delete path;
		return NULL;
	}

	FloorMesh* exteriorFloorMesh = portalLayout->getFloorMesh(0);

	if (exteriorFloorMesh == NULL) {
		delete path;
		return NULL;
	}

	PathGraph* exteriorPathGraph = exteriorFloorMesh->getPathGraph();

	if (exteriorPathGraph == NULL) {
		delete path;
		return NULL;
	}

	// we need to move world position into model space
	Vector3 transformedPosition = transformToModelSpace(pointB.getPoint(), building);

	//find exit node in our cell
	//PathNode* exitNode = sourcePathGraph->findNearestNode(pointA.getPoint());
	TriangleNode* nearestTargetNodeTriangle = CollisionManager::getTriangle(pointA.getPoint(), sourceFloorMesh);

	if (nearestTargetNodeTriangle == NULL) {
		delete path;
		return NULL;
	}

	PathNode* exitNode = CollisionManager::findNearestPathNode(nearestTargetNodeTriangle, sourceFloorMesh, transformedPosition);//targetPathGraph->findNearestNode(pointB.getPoint());

	if (exitNode == NULL) {
		delete path;
		return NULL;
	}

	//find exterior node
	PathNode* exteriorNode = exteriorPathGraph->findNearestGlobalNode(transformedPosition);

	if (exteriorNode == NULL) {
		delete path;
		return NULL;
	}

	//find path to the exit
	Vector<PathNode*>* exitPath = portalLayout->getPath(exitNode, exteriorNode);

	if (exitPath == NULL) {
		error("exitPath == NULL");
		delete path;
		return NULL;
	}

	//find triangle path to exitNode
	Vector<Triangle*>* trianglePath = NULL;

	int res = getFloorPath(pointA.getPoint(), exitNode->getPosition(), sourceFloorMesh, trianglePath);

	if (res != -1 && trianglePath != NULL)
		addTriangleNodeEdges(pointA.getPoint(), exitNode->getPosition(), trianglePath, path, ourCell);

	if (trianglePath != NULL)
		delete trianglePath;

	path->add(WorldCoordinates(exitNode->getPosition(), ourCell));

	//populate cell traversing
	for (int i = 0; i < exitPath->size(); ++i) {
		PathNode* pathNode = exitPath->get(i);
		PathGraph* pathGraph = pathNode->getPathGraph();

		FloorMesh* floorMesh = pathGraph->getFloorMesh();

		int cellID = floorMesh->getCellID();

		if (cellID == 0) { // we are outside
			WorldCoordinates coord(pathNode->getPosition(), ourCell);

			path->add(WorldCoordinates(coord.getWorldPosition(), NULL));
		} else { // we are inside the building
			CellObject* pathCell = building->getCell(cellID);

			path->add(WorldCoordinates(pathNode->getPosition(), pathCell));
		}
	}

	delete exitPath;
	exitPath = NULL;
	
	if (path->size()) {
		Vector<WorldCoordinates>* newPath = findPathFromWorldToWorld(path->get(path->size()-1), pointB, zone);
		if (newPath) {
			path->addAll(*newPath);
			delete newPath;
		}
	} else
		path->add(pointB);

	return path;
}
Vector<WorldCoordinates>* PathFinderManager::findPathFromCellToDifferentCell(const WorldCoordinates& pointA, const WorldCoordinates& pointB) {
	//info ("findPathFromCellToDifferentCell", true);

	CellObject* ourCell = pointA.getCell();
	CellObject* targetCell = pointB.getCell();

	int ourCellID = ourCell->getCellNumber();
	int targetCellID = targetCell->getCellNumber();

	ManagedReference<BuildingObject*> building1 = cast<BuildingObject*>( ourCell->getParent().get().get());
	ManagedReference<BuildingObject*> building2 = cast<BuildingObject*>( targetCell->getParent().get().get());

	if (building1 != building2) // TODO: implement path finding between 2 buildings
		return NULL;

	SharedObjectTemplate* templateObject = building1->getObjectTemplate();

	if (templateObject == NULL)
		return NULL;

	PortalLayout* portalLayout = templateObject->getPortalLayout();

	if (portalLayout == NULL)
		return NULL;

	FloorMesh* floorMesh1 = portalLayout->getFloorMesh(ourCellID);
	FloorMesh* floorMesh2 = portalLayout->getFloorMesh(targetCellID);

	if (floorMesh2->getCellID() != targetCellID)
		error("floorMes2 cellID != targetCellID");

	//info("targetCellID:" + String::valueOf(targetCellID), true);

	PathGraph* pathGraph1 = floorMesh1->getPathGraph();
	PathGraph* pathGraph2 = floorMesh2->getPathGraph();

	Vector<WorldCoordinates>* path = new Vector<WorldCoordinates>(5, 1);
	path->add(pointA); // adding source

	//PathNode* source = pathGraph1->findNearestNode(pointA.getPoint());
	TriangleNode* nearestSourceNodeTriangle = CollisionManager::getTriangle(pointA.getPoint(), floorMesh1);

	if (nearestSourceNodeTriangle == NULL) {
		delete path;
		return NULL;
	}

	PathNode* source = CollisionManager::findNearestPathNode(nearestSourceNodeTriangle, floorMesh1, pointB.getPoint());//targetPathGraph->findNearestNode(pointB.getPoint());

	if (source == NULL) {
		delete path;
		return NULL;
	}

	//PathNode* target = pathGraph2->findNearestNode(pointB.getPoint());
	TriangleNode* nearestTargetNodeTriangle = CollisionManager::getTriangle(pointB.getPoint(), floorMesh2);

	if (nearestTargetNodeTriangle == NULL) {
		delete path;
		return NULL;
	}

	PathNode* target = CollisionManager::findNearestPathNode(nearestTargetNodeTriangle, floorMesh2, pointB.getPoint());//targetPathGraph->findNearestNode(pointB.getPoint());

	if (target == NULL) {
		delete path;
		return NULL;
	}

	Vector<PathNode*>* nodes = portalLayout->getPath(source, target);

	if (nodes == NULL) {
		StringBuffer str;
		str << "Could not find path from node: " << source->getID()
				<< " to node: " << target->getID() << " in building: "
				<< templateObject->getFullTemplateString();

		log(str.toString());

		delete path;
		return NULL;
	}

	// FIXME (dannuic): Sometimes nodes only have one entry.... why?
	if (nodes->size() == 1) {
		error("Only one node");

		delete path;
		return NULL;
	}

	// path from our position to path node
	Vector<Triangle*>* trianglePath = NULL;

	int res = getFloorPath(pointA.getPoint(), nodes->get(1)->getPosition(), floorMesh1, trianglePath);

	if (res != -1 && trianglePath != NULL)
		addTriangleNodeEdges(pointA.getPoint(), nodes->get(1)->getPosition(), trianglePath, path, ourCell);

	if (trianglePath != NULL) {
		delete trianglePath;
		trianglePath = NULL;
	}

	path->add(WorldCoordinates(source->getPosition(), ourCell));

	//traversing cells
	for (int i = 1; i < nodes->size(); ++i) {
		PathNode* pathNode = nodes->get(i);
		PathGraph* pathGraph = pathNode->getPathGraph();

		FloorMesh* floorMesh = pathGraph->getFloorMesh();

		int cellID = floorMesh->getCellID();

		if (cellID == 0) {
			//info("cellID == 0", true);
			WorldCoordinates coord(pathNode->getPosition(), ourCell);

			path->add(WorldCoordinates(coord.getWorldPosition(), NULL));
		} else {
			CellObject* pathCell = building1->getCell(cellID);

			WorldCoordinates coord(pathNode->getPosition(), pathCell);

			path->add(coord);

			//info("cellID:" + String::valueOf(cellID), true);

			if (i == nodes->size() - 1) {
				if (pathNode != target) {
					StringBuffer msg;
					msg << "pathNode != target pathNode: " << pathNode->getID() << " target:" << target->getID();
					error(msg.toString());
				}

				if (pathCell != targetCell) {
					error("final cell not target cell");
				}
			}
		}
	}

	delete nodes;
	nodes = NULL;

	// path from cell entrance to destination point
	trianglePath = NULL;

	res = getFloorPath(path->get(path->size() - 1).getPoint(), pointB.getPoint(), floorMesh2, trianglePath);

	if (res != -1 && trianglePath != NULL)
		addTriangleNodeEdges(path->get(path->size() - 1).getPoint(), pointB.getPoint(), trianglePath, path, targetCell);

	if (trianglePath != NULL)
		delete trianglePath;

	path->add(pointB);

	return path;
}
int EventPerkDeedImplementation::handleObjectMenuSelect(CreatureObject* player, byte selectedID) {

	if (selectedID == 20) {
		if (generated) {
			return 1;
		}

		Zone* zone = player->getZone();

		if (zone == NULL) {
			return 1;
		}

		PlanetManager* planetManager = zone->getPlanetManager();
		if (planetManager == NULL) {
			return 1;
		}

		EventPerkDeedTemplate* deedTemplate = cast<EventPerkDeedTemplate*>(getObjectTemplate());
		if (deedTemplate == NULL) {
			return 1;
		}

		if (zone->getZoneName().contains("space_")) {
			player->sendSystemMessage("@event_perk:not_in_space"); // You may not deploy a Rental in space. Return to the ground first.
			return 1;
		}

		if (!deedTemplate->isAllowedZone(zone->getZoneName())) {
			player->sendSystemMessage("@event_perk:not_on_this_planet"); // You cannot deploy this rental on this planet. Examine the deed to determine the intended planet for this rental.
			return 1;
		}

		if (!isASubChildOf(player)) {
			player->sendSystemMessage("@event_perk:from_inventory_only"); // This rental must be in your inventory in order to be deployed.
			return 1;
		}

		if (player->getParent() != NULL) {
			player->sendSystemMessage("@event_perk:not_inside"); // You cannot deploy a Rental indoors. You must move outside.
			return 1;
		}

		if (player->isInCombat()) {
			player->sendSystemMessage("@event_perk:not_in_combat"); // You cannot deploy a Rental while in combat.
			return 1;
		}

		if (player->isSwimming()) {
			player->sendSystemMessage("@event_perk:not_while_swimming"); // You cannot deploy a Rental while swimming.
			return 1;
		}

		ManagedReference<CityRegion*> city = player->getCityRegion().get();

		if (city != NULL) {
			if (city->isClientRegion()) {
				player->sendSystemMessage("@event_perk:not_in_municipal_zone"); // You may not place a Rental in a municipal zone.
				return 1;
			}

			if (city->isZoningEnabled() && !city->hasZoningRights(player->getObjectID())) {
				player->sendSystemMessage("@event_perk:no_zoning_rights"); // You must have zoning rights to place a Rental in this city.
				return 1;
			}
		}

		int x = player->getWorldPositionX();
		int y = player->getWorldPositionY();
		int nearbyPerks = 0;

		TerrainManager* terrainManager = planetManager->getTerrainManager();
		if ( terrainManager == NULL || terrainManager->getHighestHeightDifference(x - 10, y - 10, x + 10, y + 10) > 15.0) {
			player->sendSystemMessage("@event_perk:bad_area"); // This rental could not be deployed due to the surrounding terrain. Please move to another area and try again.
			return 1;
		}

		SortedVector<ManagedReference<QuadTreeEntry* > >* closeObjects = player->getCloseObjects();

		if (closeObjects == NULL) {
			error("Player has NULL closeObjectsVector in EventPerkDeedImplementation::handleObjectMenuSelect");
			return 1;
		}

		for (int i = 0; i < closeObjects->size(); ++i) {
			SceneObject* obj = cast<SceneObject*>(closeObjects->get(i).get());

			if (obj == NULL) {
				continue;
			}

			SharedObjectTemplate* objectTemplate = obj->getObjectTemplate();
			if (objectTemplate == NULL) {
				continue;
			}

			float radius = objectTemplate->getNoBuildRadius();

			if (obj->isLairObject() && player->isInRange(obj, radius)) {
				player->sendSystemMessage("@event_perk:too_close_lair"); // You cannot place a Rental this close to a lair.
				return 1;
			}

			if (obj->isCampStructure() && player->isInRange(obj, radius)) {
				player->sendSystemMessage("@event_perk:too_close_camp"); // You cannot place a Rental this close to a camp.
				return 1;
			}

			if (radius > 0 && player->isInRange(obj, radius)) {
				player->sendSystemMessage("@event_perk:too_close_something"); // You are too close to an object to deploy your Rental here. Move away from it.
				return 1;
			}

			if (objectTemplate->isSharedStructureObjectTemplate()) {
				if (StructureManager::instance()->isInStructureFootprint(cast<StructureObject*>(obj), x, y, 0)) {
					player->sendSystemMessage("@event_perk:too_close_building"); // You may not place a Rental this close to a building.
					return 1;
				}
			}

			if (obj->isEventPerk() && player->isInRange(obj, 32) && ++nearbyPerks > 2) {
				player->sendSystemMessage("@event_perk:too_many_perks"); // There are too many Rentals already deployed in this area. Please move to another location.
				return 1;
			}
		}

		SortedVector<ManagedReference<ActiveArea* > > activeAreas;
		zone->getInRangeActiveAreas(x, y, &activeAreas, true);

		for (int i = 0; i < activeAreas.size(); ++i) {
			ActiveArea* area = activeAreas.get(i);

			if (area->isNoBuildArea()) {
				player->sendSystemMessage("@event_perk:too_close_something"); // You are too close to an object to deploy your Rental here. Move away from it.
				return 1;
			}
		}

		if (planetManager->isInRangeWithPoi(x, y, 150)) {
			player->sendSystemMessage("@event_perk:too_close_something"); // You are too close to an object to deploy your Rental here. Move away from it.
			return 1;
		}

		if (perkType != EventPerkDeedTemplate::STATIC) {
			player->sendSystemMessage("This type of event perk deed is not functional yet.");
			return 1;
		}

		ManagedReference<TangibleObject*> object = generatedObject.get();

		if (object == NULL) {
			object = (server->getZoneServer()->createObject(generatedObjectTemplate.hashCode(), "playerstructures", 1)).castTo<TangibleObject*>();

			if (object == NULL) {
				player->sendSystemMessage("Error generating object. Wrong generatedObjectTemplate or is not a tangible object.");
				return 1;
			}

			generatedObject = object;
		}

		EventPerkDataComponent* data = cast<EventPerkDataComponent*>(object->getDataObjectComponent()->get());

		if (data == NULL) {
			player->sendSystemMessage("Error: no dataObjectComponent.");
			object->destroyObjectFromDatabase();
			return 1;
		}

		data->setDeed(_this.get());

		object->initializePosition(player->getPositionX(), player->getPositionZ(), player->getPositionY());
		object->setDirection(Math::deg2rad(player->getDirectionAngle()));
		zone->transferObject(object, -1, true);

		generated = true;
		destroyObjectFromWorld(true);

		return 0;
	}

	return DeedImplementation::handleObjectMenuSelect(player, selectedID);
}
float CollisionManager::getWorldFloorCollision(float x, float y, Zone* zone, bool testWater) {
	SortedVector<ManagedReference<QuadTreeEntry*> > closeObjects;
	zone->getInRangeObjects(x, y, 128, &closeObjects, true);

	PlanetManager* planetManager = zone->getPlanetManager();

	if (planetManager == NULL)
		return 0.f;

	float height = 0;

	TerrainManager* terrainManager = planetManager->getTerrainManager();

	//need to include exclude affectors in the terrain calcs
	height = terrainManager->getHeight(x, y);

	Vector3 rayStart(x, 16384.f, y);
	Vector3 rayEnd(x, -16384.f, y);

	Triangle* triangle = NULL;

	if (testWater) {
		float waterHeight;

		if (terrainManager->getWaterHeight(x, y, waterHeight))
			if (waterHeight > height)
				height = waterHeight;
	}

	float intersectionDistance;

	for (int i = 0; i < closeObjects.size(); ++i) {
		BuildingObject* building = dynamic_cast<BuildingObject*>(closeObjects.get(i).get());

		if (building == NULL)
			continue;

		//building->getObjectTemplate()->get

		SharedObjectTemplate* templateObject = building->getObjectTemplate();

		if (templateObject == NULL)
			continue;

		PortalLayout* portalLayout = templateObject->getPortalLayout();

		if (portalLayout == NULL)
			continue;

		if (portalLayout->getFloorMeshNumber() == 0)
			continue;

		//find nearest entrance
		FloorMesh* exteriorFloorMesh = portalLayout->getFloorMesh(0); // get outside layout
		AABBTree* aabbTree = exteriorFloorMesh->getAABBTree();

		if (aabbTree == NULL)
			continue;

		Ray ray = convertToModelSpace(rayStart, rayEnd, building);

		if (aabbTree->intersects(ray, 16384 * 2, intersectionDistance, triangle, true)) {
			float floorHeight = 16384 - intersectionDistance;

			if (floorHeight > height)
				height = floorHeight;
		}
	}

	return height;
}