Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / door_manager.cpp
blob1b094ad7b9c5f6c0cb3247671fdad2cd95f0b5b7
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include <memory>
24 #include "door_manager.h"
25 #include "pacs_client.h"
26 #include "time_client.h"
27 #include "entity_cl.h"
28 #include "entities.h"
30 #include "nel/pacs/u_primitive_block.h"
31 #include "nel/pacs/u_move_container.h"
32 #include "nel/pacs/u_move_primitive.h"
33 #include "nel/pacs/u_global_position.h"
34 #include "nel/pacs/u_collision_desc.h"
36 using namespace NLMISC;
37 using namespace NLPACS;
38 using namespace NL3D;
40 // GLOBALS
42 CIGDoorAddedCallback IGDoorCallback;
44 CDoorManager *CDoorManager::_Instance = NULL;
46 extern CEntityManager EntitiesMngr;
48 uint32 CDoorManager::s_nextId = 0;
50 // ***************************************************************************
51 void CDoorManager::releaseInstance()
53 if( _Instance )
54 delete _Instance;
55 _Instance = NULL;
58 // ***************************************************************************
59 // SDoor
60 // ***************************************************************************
62 #define TIME_OPEN_CLOSE_DOOR 0.7f
63 #define SIZE_DOOR 3.0f
64 #define SIZE_DOOR_3PARTS_LR 1.0f
65 #define SIZE_DOOR_3PARTS_DN 1.5f
66 #define SIZE_DOOR_2PARTS_LR 1.0f
68 // ***************************************************************************
69 void CDoorManager::SDoor::anim()
71 switch(AnimType)
73 case Matis3Part:
74 case Matis3PartBourgeon:
76 if (Instances[0] != 0xFFFFFFFF) // Left Part
78 UInstance inst = InstanceGroup->getInstance(Instances[0]);
79 if (!inst.empty())
81 CMatrix mat = InstMat[0];
82 if (AnimType == Matis3Part)
83 mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR
84 + OCState*mat.getJ()*SIZE_DOOR_3PARTS_LR);
85 else
86 mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR
87 - OCState*mat.getI()*SIZE_DOOR_3PARTS_LR);
88 inst.setMatrix(mat);
92 if (Instances[1] != 0xFFFFFFFF) // Right Part
94 UInstance inst = InstanceGroup->getInstance(Instances[1]);
95 if (!inst.empty())
97 CMatrix mat = InstMat[1];
98 if (AnimType == Matis3Part)
99 mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR
100 - OCState*mat.getJ()*SIZE_DOOR_3PARTS_LR);
101 else
102 mat.setPos (mat.getPos()+ OCState*mat.getK()*SIZE_DOOR_3PARTS_LR
103 + OCState*mat.getI()*SIZE_DOOR_3PARTS_LR);
104 inst.setMatrix(mat);
108 if (Instances[2] != 0xFFFFFFFF) // Down Part
110 UInstance inst = InstanceGroup->getInstance(Instances[2]);
111 if (!inst.empty())
113 CMatrix mat = InstMat[2];
114 mat.setPos (mat.getPos() - OCState*mat.getK()*SIZE_DOOR_3PARTS_DN);
115 inst.setMatrix(mat);
119 break;
121 case Zorai2Part:
123 if (Instances[0] != 0xFFFFFFFF) // Left Part
125 UInstance inst = InstanceGroup->getInstance(Instances[0]);
126 if (!inst.empty())
128 CMatrix mat = InstMat[0];
129 mat.setPos (mat.getPos()- OCState*mat.getI()*SIZE_DOOR_2PARTS_LR);
130 inst.setMatrix(mat);
134 if (Instances[1] != 0xFFFFFFFF) // Right Part
136 UInstance inst = InstanceGroup->getInstance(Instances[1]);
137 if (!inst.empty())
139 CMatrix mat = InstMat[1];
140 mat.setPos (mat.getPos()+ OCState*mat.getI()*SIZE_DOOR_2PARTS_LR);
141 inst.setMatrix(mat);
145 break;
147 case Normal:
148 default:
150 for (uint i = 0; i < Instances.size(); ++i)
152 UInstance inst = InstanceGroup->getInstance(Instances[i]);
153 if (!inst.empty())
155 CMatrix mat = InstMat[i];
156 mat.setPos(mat.getPos()+OCState*mat.getK()*SIZE_DOOR);
157 inst.setMatrix(mat);
161 break;
165 // ***************************************************************************
166 bool CDoorManager::SDoor::open()
168 if (OCState >= 1.0f) return true;
170 if (!Opened)
172 InstanceGroup->setDynamicPortal (Name, true);
173 Opened = true;
176 OCState += DT / TIME_OPEN_CLOSE_DOOR;
177 if (OCState > 1.0f) OCState = 1.0f;
178 anim();
180 return false;
183 // ***************************************************************************
184 bool CDoorManager::SDoor::close()
186 if (OCState <= 0.0f) return true;
188 OCState -= DT / TIME_OPEN_CLOSE_DOOR;
189 if (OCState < 0.0f)
191 OCState = 0.0f;
192 if (Opened)
194 InstanceGroup->setDynamicPortal (Name, false);
195 Opened = false;
198 anim();
200 return false;
203 // ***************************************************************************
204 void CDoorManager::SDoor::entityCollide(CEntityCL *pE)
206 for (uint i = 0; i < Entities.size(); ++i)
207 if (Entities[i] == pE)
209 // The entity has moved in the trigger
210 EntitiesMoved[i] = 1;
211 return;
213 // New entity entered the trigger
214 Entities.push_back(pE);
215 EntitiesMoved.push_back(1);
218 // ***************************************************************************
219 void CDoorManager::SDoor::checkToClose()
221 for (sint i = 0; i < (sint)Entities.size(); ++i)
223 if (EntitiesMoved[i] == 0) // No trigger moved
225 if (Entities[i]->hasMoved()) // But the entity has moved (so not in the trigger)
227 // The entity has leaved the trigger
228 Entities.erase(Entities.begin()+i);
229 EntitiesMoved.erase(EntitiesMoved.begin()+i);
230 i--;
233 else // Trigger move ok lets reset for next update
235 EntitiesMoved[i] = EntitiesMoved[i] - 1;
239 if (Entities.empty())
240 close();
241 else
242 open();
246 // ***************************************************************************
247 // CDoorManager
248 // ***************************************************************************
250 // ***************************************************************************
251 std::string CDoorManager::transformName (uint /* index */, const std::string &/* instanceName */, const std::string &shapeName)
253 if (shapeName.rfind('.') == string::npos) return shapeName;
254 string sExt = toLowerAscii(shapeName.substr(shapeName.rfind('.')+1,shapeName.size()));
255 if (sExt != "pacs_prim") return shapeName;
256 return ""; // Do not load a pacs prim as a mesh...
259 // ***************************************************************************
260 void CDoorManager::loadedCallback (NL3D::UInstanceGroup *ig)
262 uint i;
263 // Check all instances and look if there are some pacs_prim to instanciate
264 uint numInstances = ig->getNumInstance();
265 for(uint k = 0; k < numInstances; ++k)
267 // Check if the shape name is a pacs_prim
268 string sShapeName = ig->getShapeName(k);
269 if (sShapeName.rfind('.') == string::npos) continue;
271 string sExt = toLowerAscii(sShapeName.substr(sShapeName.rfind('.')+1,sShapeName.size()));
272 if (sExt != "pacs_prim") continue;
274 // Check if the pacs_prim is a door detection
276 sShapeName = CPath::lookup(sShapeName,false);
277 if (!sShapeName.empty())
279 CUniquePtr<NLPACS::UPrimitiveBlock> pb(NLPACS::UPrimitiveBlock::createPrimitiveBlockFromFile(sShapeName));
280 NLPACS::UPrimitiveBlock *pPB = pb.release();
282 bool bDoorDetectPresent = false;
283 for (i = 0; i < pPB->getNbPrimitive(); ++i)
285 if ((pPB->getUserData(i) & 0xffff) == UserDataDoor)
287 bDoorDetectPresent = true;
288 break;
292 if (!bDoorDetectPresent)
294 delete pPB;
295 continue;
298 // Instanciate the pacs_prim and create a door structure associated
299 SDoor *pDoor = new SDoor;
300 pDoor->InstanceGroup = ig;
301 // compute orientation and position
302 NLMISC::CMatrix instanceMatrix;
303 ig->getInstanceMatrix(k, instanceMatrix);
304 NLMISC::CVector pos;
305 float angle;
306 NLMISC::CVector scale = ig->getInstanceScale(k);
307 NLPACS::UMoveContainer::getPACSCoordsFromMatrix(pos, angle, instanceMatrix);
308 PACS->addCollisionnablePrimitiveBlock(pPB, 0, 1, &pDoor->Prims, angle, pos, true, scale);
310 // Complete the user data of all the 'door primitives' with a pointer to the door structure
311 for (i = 0; i < pDoor->Prims.size(); ++i)
313 UMovePrimitive *pPrim = pDoor->Prims[i];
314 if ((pPrim->UserData&0xffff) == UserDataDoor)
316 // First byte is for type (2 for door 1 for ascensor)
317 pPrim->UserData |= ((uint64)pDoor->ID << 16);
321 // Link with all door 3d objects (depending on the structure of the door)
322 pDoor->Name = ig->getInstanceName(k);
323 pDoor->AnimType = SDoor::Normal;
325 if (strnicmp(pDoor->Name.c_str(),"ma_asc_3portes_bourgeons",24)==0)
327 pDoor->AnimType = SDoor::Matis3PartBourgeon;
328 pDoor->Instances.resize(3, 0xFFFFFFFF);
330 else if (strnicmp(pDoor->Name.c_str(),"ma_asc_3portes",14)==0)
332 pDoor->AnimType = SDoor::Matis3Part;
333 pDoor->Instances.resize(3, 0xFFFFFFFF);
335 else if (strnicmp(pDoor->Name.c_str(),"zo_asc_2portes",14)==0)
337 pDoor->AnimType = SDoor::Zorai2Part;
338 pDoor->Instances.resize(2, 0xFFFFFFFF);
341 for(i = 0; i < numInstances; ++i)
342 if (i != k)
344 string sInstName = ig->getInstanceName(i);
345 if (sInstName == pDoor->Name)
347 switch (pDoor->AnimType)
349 case SDoor::Matis3Part:
350 case SDoor::Matis3PartBourgeon:
352 string sDebug = ig->getShapeName(i);
353 sDebug = toLowerAscii(sDebug.substr(sDebug.rfind('_')+1,sDebug.size()));
354 if (sDebug == "gauche")
355 pDoor->Instances[0] = i;
356 else if (sDebug == "droite")
357 pDoor->Instances[1] = i;
358 else if (sDebug == "bas")
359 pDoor->Instances[2] = i;
361 break;
363 case SDoor::Zorai2Part:
365 string sDebug = ig->getShapeName(i);
366 sDebug = toLowerAscii(sDebug.substr(sDebug.rfind('_')+1,sDebug.size()));
367 if (sDebug == "gauche")
368 pDoor->Instances[0] = i;
369 else if (sDebug == "droite")
370 pDoor->Instances[1] = i;
372 break;
374 case SDoor::Normal:
375 default:
377 string sDebug = ig->getShapeName(i);
378 pDoor->Instances.push_back(i);
380 break;
385 bool bAllInit = true;
386 for (i = 0; i < pDoor->Instances.size(); ++i)
387 if (pDoor->Instances[i] == 0xFFFFFFFF)
388 bAllInit = false;
390 if (!bAllInit)
392 nlwarning("All the door part are not well initialized");
393 for (sint j = 0; j < (sint)pDoor->Prims.size(); ++j)
395 if (PACS != NULL)
396 PACS->removePrimitive(pDoor->Prims[j]);
397 else
398 nlwarning("PACS should not be NULL at this point");
400 delete pDoor;
402 else
404 // Get matrices
405 pDoor->InstMat.resize(pDoor->Instances.size());
406 for (i = 0; i < pDoor->Instances.size(); ++i)
408 CMatrix mat;
409 ig->getInstanceMatrix (pDoor->Instances[i], mat);
410 pDoor->InstMat[i] = mat;
413 // Close the door/portal by default
414 pDoor->Opened = false;
415 ig->setDynamicPortal(pDoor->Name, false);
417 // Add the door to the door manager
418 _Doors.push_back(pDoor);
419 ig->setTransformNameCallback(this);
425 // ***************************************************************************
426 void CDoorManager::addedCallback (NL3D::UInstanceGroup *ig)
428 sint i, j;
429 for (i = 0; i < (sint)_Doors.size(); ++i)
431 SDoor *pDoor = _Doors[i];
432 if (pDoor->InstanceGroup == ig)
434 for (j = 0; j < (sint)pDoor->Instances.size(); ++j)
436 UInstance inst = pDoor->InstanceGroup->getInstance(pDoor->Instances[j]);
437 if (!inst.empty())
439 inst.unfreezeHRC();
440 inst.setTransformMode(UInstance::DirectMatrix);
447 // ***************************************************************************
448 void CDoorManager::removedCallback (NL3D::UInstanceGroup *ig)
450 // Remove all doors corresponding to the instance group ig
451 sint i, j;
452 for (i = 0; i < (sint)_Doors.size(); ++i)
454 SDoor *pDoor = _Doors[i];
455 if (pDoor->InstanceGroup == ig)
457 // Remove Pacs
458 for (j = 0; j < (sint)pDoor->Prims.size(); ++j)
460 if (PACS != NULL)
461 PACS->removePrimitive(pDoor->Prims[j]);
462 else
463 nlwarning("PACS should not be NULL at this point");
465 delete pDoor;
466 _Doors.erase(_Doors.begin()+i);
467 i--;
472 // Copy triggers to be used in update
473 // ***************************************************************************
474 void CDoorManager::getPACSTriggers()
476 uint nNbTrig = PACS->getNumTriggerInfo();
477 for (uint i = 0; i < nNbTrig; ++i)
479 const UTriggerInfo &rTI = PACS->getTriggerInfo(i);
480 // Does one of the 2 objects is a door detection object
481 if (((rTI.Object0 & 0xffff) == UserDataDoor) || ((rTI.Object1 & 0xffff) == UserDataDoor))
483 uint64 nUserDataDoor = 0;
484 uint64 nUserDataEntity = 0;
485 if ((rTI.Object0 & 0xffff) == UserDataDoor)
487 nUserDataDoor = rTI.Object0;
488 nUserDataEntity = rTI.Object1;
491 if ((rTI.Object1 & 0xffff) == UserDataDoor)
493 nUserDataDoor = rTI.Object1;
494 nUserDataEntity = rTI.Object0;
497 if (rTI.CollisionType != UTriggerInfo::Inside)
498 continue;
500 // Retrieve the door pointer
501 SDoor *pDoor = NULL;
502 CEntityCL *pEntity = NULL;
504 uint32 doorId = ((nUserDataDoor >> 16) & 0xffffffff);
505 uint32 entityId = ((nUserDataEntity >> 16) & 0xffffffff);
507 for(uint i = 0; i < _Doors.size(); ++i)
509 pDoor = _Doors[i];
511 if (pDoor && pDoor->ID == doorId)
513 pEntity = EntitiesMngr.getEntityByCompressedIndex(entityId);
515 if (pEntity)
516 pDoor->entityCollide(pEntity);
518 break;
525 // ***************************************************************************
526 void CDoorManager::update ()
528 // Check all doors if we have to close
529 for (uint i = 0; i < _Doors.size(); ++i)
531 SDoor *pDoor = _Doors[i];
532 pDoor->checkToClose();