// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include #include "door_manager.h" #include "pacs_client.h" #include "time_client.h" #include "entity_cl.h" #include "entities.h" #include "nel/pacs/u_primitive_block.h" #include "nel/pacs/u_move_container.h" #include "nel/pacs/u_move_primitive.h" #include "nel/pacs/u_global_position.h" #include "nel/pacs/u_collision_desc.h" using namespace NLMISC; using namespace NLPACS; using namespace NL3D; // GLOBALS CIGDoorAddedCallback IGDoorCallback; CDoorManager *CDoorManager::_Instance = NULL; extern CEntityManager EntitiesMngr; uint32 CDoorManager::s_nextId = 0; // *************************************************************************** void CDoorManager::releaseInstance() { if( _Instance ) delete _Instance; _Instance = NULL; } // *************************************************************************** // SDoor // *************************************************************************** #define TIME_OPEN_CLOSE_DOOR 0.7f #define SIZE_DOOR 3.0f #define SIZE_DOOR_3PARTS_LR 1.0f #define SIZE_DOOR_3PARTS_DN 1.5f #define SIZE_DOOR_2PARTS_LR 1.0f // *************************************************************************** void CDoorManager::SDoor::anim() { switch(AnimType) { case Matis3Part: case Matis3PartBourgeon: { if (Instances[0] != 0xFFFFFFFF) // Left Part { UInstance inst = InstanceGroup->getInstance(Instances[0]); if (!inst.empty()) { CMatrix mat = InstMat[0]; if (AnimType == Matis3Part) mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR + OCState*mat.getJ()*SIZE_DOOR_3PARTS_LR); else mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR - OCState*mat.getI()*SIZE_DOOR_3PARTS_LR); inst.setMatrix(mat); } } if (Instances[1] != 0xFFFFFFFF) // Right Part { UInstance inst = InstanceGroup->getInstance(Instances[1]); if (!inst.empty()) { CMatrix mat = InstMat[1]; if (AnimType == Matis3Part) mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR - OCState*mat.getJ()*SIZE_DOOR_3PARTS_LR); else mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR + OCState*mat.getI()*SIZE_DOOR_3PARTS_LR); inst.setMatrix(mat); } } if (Instances[2] != 0xFFFFFFFF) // Down Part { UInstance inst = InstanceGroup->getInstance(Instances[2]); if (!inst.empty()) { CMatrix mat = InstMat[2]; mat.setPos (mat.getPos() - OCState*mat.getK()*SIZE_DOOR_3PARTS_DN); inst.setMatrix(mat); } } } break; case Zorai2Part: { if (Instances[0] != 0xFFFFFFFF) // Left Part { UInstance inst = InstanceGroup->getInstance(Instances[0]); if (!inst.empty()) { CMatrix mat = InstMat[0]; mat.setPos (mat.getPos()- OCState*mat.getI()*SIZE_DOOR_2PARTS_LR); inst.setMatrix(mat); } } if (Instances[1] != 0xFFFFFFFF) // Right Part { UInstance inst = InstanceGroup->getInstance(Instances[1]); if (!inst.empty()) { CMatrix mat = InstMat[1]; mat.setPos (mat.getPos()+ OCState*mat.getI()*SIZE_DOOR_2PARTS_LR); inst.setMatrix(mat); } } } break; case Normal: default: { for (uint i = 0; i < Instances.size(); ++i) { UInstance inst = InstanceGroup->getInstance(Instances[i]); if (!inst.empty()) { CMatrix mat = InstMat[i]; mat.setPos(mat.getPos()+OCState*mat.getK()*SIZE_DOOR); inst.setMatrix(mat); } } } break; } } // *************************************************************************** bool CDoorManager::SDoor::open() { if (OCState >= 1.0f) return true; if (!Opened) { InstanceGroup->setDynamicPortal (Name, true); Opened = true; } OCState += DT / TIME_OPEN_CLOSE_DOOR; if (OCState > 1.0f) OCState = 1.0f; anim(); return false; } // *************************************************************************** bool CDoorManager::SDoor::close() { if (OCState <= 0.0f) return true; OCState -= DT / TIME_OPEN_CLOSE_DOOR; if (OCState < 0.0f) { OCState = 0.0f; if (Opened) { InstanceGroup->setDynamicPortal (Name, false); Opened = false; } } anim(); return false; } // *************************************************************************** void CDoorManager::SDoor::entityCollide(CEntityCL *pE) { for (uint i = 0; i < Entities.size(); ++i) if (Entities[i] == pE) { // The entity has moved in the trigger EntitiesMoved[i] = 1; return; } // New entity entered the trigger Entities.push_back(pE); EntitiesMoved.push_back(1); } // *************************************************************************** void CDoorManager::SDoor::checkToClose() { for (sint i = 0; i < (sint)Entities.size(); ++i) { if (EntitiesMoved[i] == 0) // No trigger moved { if (Entities[i]->hasMoved()) // But the entity has moved (so not in the trigger) { // The entity has leaved the trigger Entities.erase(Entities.begin()+i); EntitiesMoved.erase(EntitiesMoved.begin()+i); i--; } } else // Trigger move ok lets reset for next update { EntitiesMoved[i] = EntitiesMoved[i] - 1; } } if (Entities.empty()) close(); else open(); } // *************************************************************************** // CDoorManager // *************************************************************************** // *************************************************************************** std::string CDoorManager::transformName (uint /* index */, const std::string &/* instanceName */, const std::string &shapeName) { if (shapeName.rfind('.') == string::npos) return shapeName; string sExt = toLower(shapeName.substr(shapeName.rfind('.')+1,shapeName.size())); if (sExt != "pacs_prim") return shapeName; return ""; // Do not load a pacs prim as a mesh... } // *************************************************************************** void CDoorManager::loadedCallback (NL3D::UInstanceGroup *ig) { uint i; // Check all instances and look if there are some pacs_prim to instanciate uint numInstances = ig->getNumInstance(); for(uint k = 0; k < numInstances; ++k) { // Check if the shape name is a pacs_prim string sShapeName = ig->getShapeName(k); if (sShapeName.rfind('.') == string::npos) continue; string sExt = toLower(sShapeName.substr(sShapeName.rfind('.')+1,sShapeName.size())); if (sExt != "pacs_prim") continue; // Check if the pacs_prim is a door detection sShapeName = CPath::lookup(sShapeName,false); if (!sShapeName.empty()) { CUniquePtr pb(NLPACS::UPrimitiveBlock::createPrimitiveBlockFromFile(sShapeName)); NLPACS::UPrimitiveBlock *pPB = pb.release(); bool bDoorDetectPresent = false; for (i = 0; i < pPB->getNbPrimitive(); ++i) { if ((pPB->getUserData(i) & 0xffff) == UserDataDoor) { bDoorDetectPresent = true; break; } } if (!bDoorDetectPresent) { delete pPB; continue; } // Instanciate the pacs_prim and create a door structure associated SDoor *pDoor = new SDoor; pDoor->InstanceGroup = ig; // compute orientation and position NLMISC::CMatrix instanceMatrix; ig->getInstanceMatrix(k, instanceMatrix); NLMISC::CVector pos; float angle; NLMISC::CVector scale = ig->getInstanceScale(k); NLPACS::UMoveContainer::getPACSCoordsFromMatrix(pos, angle, instanceMatrix); PACS->addCollisionnablePrimitiveBlock(pPB, 0, 1, &pDoor->Prims, angle, pos, true, scale); // Complete the user data of all the 'door primitives' with a pointer to the door structure for (i = 0; i < pDoor->Prims.size(); ++i) { UMovePrimitive *pPrim = pDoor->Prims[i]; if ((pPrim->UserData&0xffff) == UserDataDoor) { // First byte is for type (2 for door 1 for ascensor) pPrim->UserData |= ((uint64)pDoor->ID << 16); } } // Link with all door 3d objects (depending on the structure of the door) pDoor->Name = ig->getInstanceName(k); pDoor->AnimType = SDoor::Normal; if (strnicmp(pDoor->Name.c_str(),"ma_asc_3portes_bourgeons",24)==0) { pDoor->AnimType = SDoor::Matis3PartBourgeon; pDoor->Instances.resize(3, 0xFFFFFFFF); } else if (strnicmp(pDoor->Name.c_str(),"ma_asc_3portes",14)==0) { pDoor->AnimType = SDoor::Matis3Part; pDoor->Instances.resize(3, 0xFFFFFFFF); } else if (strnicmp(pDoor->Name.c_str(),"zo_asc_2portes",14)==0) { pDoor->AnimType = SDoor::Zorai2Part; pDoor->Instances.resize(2, 0xFFFFFFFF); } for(i = 0; i < numInstances; ++i) if (i != k) { string sInstName = ig->getInstanceName(i); if (sInstName == pDoor->Name) { switch (pDoor->AnimType) { case SDoor::Matis3Part: case SDoor::Matis3PartBourgeon: { string sDebug = ig->getShapeName(i); sDebug = toLower(sDebug.substr(sDebug.rfind('_')+1,sDebug.size())); if (sDebug == "gauche") pDoor->Instances[0] = i; else if (sDebug == "droite") pDoor->Instances[1] = i; else if (sDebug == "bas") pDoor->Instances[2] = i; } break; case SDoor::Zorai2Part: { string sDebug = ig->getShapeName(i); sDebug = toLower(sDebug.substr(sDebug.rfind('_')+1,sDebug.size())); if (sDebug == "gauche") pDoor->Instances[0] = i; else if (sDebug == "droite") pDoor->Instances[1] = i; } break; case SDoor::Normal: default: { string sDebug = ig->getShapeName(i); pDoor->Instances.push_back(i); } break; } } } bool bAllInit = true; for (i = 0; i < pDoor->Instances.size(); ++i) if (pDoor->Instances[i] == 0xFFFFFFFF) bAllInit = false; if (!bAllInit) { nlwarning("All the door part are not well initialized"); for (sint j = 0; j < (sint)pDoor->Prims.size(); ++j) { if (PACS != NULL) PACS->removePrimitive(pDoor->Prims[j]); else nlwarning("PACS should not be NULL at this point"); } delete pDoor; } else { // Get matrices pDoor->InstMat.resize(pDoor->Instances.size()); for (i = 0; i < pDoor->Instances.size(); ++i) { CMatrix mat; ig->getInstanceMatrix (pDoor->Instances[i], mat); pDoor->InstMat[i] = mat; } // Close the door/portal by default pDoor->Opened = false; ig->setDynamicPortal(pDoor->Name, false); // Add the door to the door manager _Doors.push_back(pDoor); ig->setTransformNameCallback(this); } } } } // *************************************************************************** void CDoorManager::addedCallback (NL3D::UInstanceGroup *ig) { sint i, j; for (i = 0; i < (sint)_Doors.size(); ++i) { SDoor *pDoor = _Doors[i]; if (pDoor->InstanceGroup == ig) { for (j = 0; j < (sint)pDoor->Instances.size(); ++j) { UInstance inst = pDoor->InstanceGroup->getInstance(pDoor->Instances[j]); if (!inst.empty()) { inst.unfreezeHRC(); inst.setTransformMode(UInstance::DirectMatrix); } } } } } // *************************************************************************** void CDoorManager::removedCallback (NL3D::UInstanceGroup *ig) { // Remove all doors corresponding to the instance group ig sint i, j; for (i = 0; i < (sint)_Doors.size(); ++i) { SDoor *pDoor = _Doors[i]; if (pDoor->InstanceGroup == ig) { // Remove Pacs for (j = 0; j < (sint)pDoor->Prims.size(); ++j) { if (PACS != NULL) PACS->removePrimitive(pDoor->Prims[j]); else nlwarning("PACS should not be NULL at this point"); } delete pDoor; _Doors.erase(_Doors.begin()+i); i--; } } } // Copy triggers to be used in update // *************************************************************************** void CDoorManager::getPACSTriggers() { uint nNbTrig = PACS->getNumTriggerInfo(); for (uint i = 0; i < nNbTrig; ++i) { const UTriggerInfo &rTI = PACS->getTriggerInfo(i); // Does one of the 2 objects is a door detection object if (((rTI.Object0 & 0xffff) == UserDataDoor) || ((rTI.Object1 & 0xffff) == UserDataDoor)) { uint64 nUserDataDoor = 0; uint64 nUserDataEntity = 0; if ((rTI.Object0 & 0xffff) == UserDataDoor) { nUserDataDoor = rTI.Object0; nUserDataEntity = rTI.Object1; } if ((rTI.Object1 & 0xffff) == UserDataDoor) { nUserDataDoor = rTI.Object1; nUserDataEntity = rTI.Object0; } if (rTI.CollisionType != UTriggerInfo::Inside) continue; // Retrieve the door pointer SDoor *pDoor = NULL; CEntityCL *pEntity = NULL; uint32 doorId = ((nUserDataDoor >> 16) & 0xffffffff); uint32 entityId = ((nUserDataEntity >> 16) & 0xffffffff); for(uint i = 0; i < _Doors.size(); ++i) { pDoor = _Doors[i]; if (pDoor && pDoor->ID == doorId) { pEntity = EntitiesMngr.getEntityByCompressedIndex(entityId); if (pEntity) pDoor->entityCollide(pEntity); break; } } } } } // *************************************************************************** void CDoorManager::update () { // Check all doors if we have to close for (uint i = 0; i < _Doors.size(); ++i) { SDoor *pDoor = _Doors[i]; pDoor->checkToClose(); } }