// 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.size() == 0)
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())
{
std::auto_ptr 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();
}
}