Fix css style order when using external css files
[ryzomcore.git] / ryzom / client / src / entities.cpp
blob7743cb02e6c0cd1fe3c4261667bb96fa9cc91145
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /////////////
25 // INCLUDE //
26 /////////////
27 #include "stdpch.h"
28 // Client
29 #include "entities.h"
30 #include "entity_cl.h"
31 #include "fx_cl.h"
32 #include "forage_source_cl.h"
33 #include "item_cl.h"
34 #include "pacs_client.h"
35 #include "time_client.h"
36 #include "view.h"
37 #include "user_entity.h"
38 #include "sheet_manager.h"
39 #include "motion/user_controls.h"
40 #include "net_manager.h"
41 #include "debug_client.h"
42 #include "ingame_database_manager.h"
43 #include "interface_v3/interface_manager.h"
44 #include "door_manager.h"
45 #include "projectile_manager.h"
46 #include "client_chat_manager.h"
47 #include "interface_v3/people_interraction.h"
48 #include "interface_v3/bar_manager.h"
49 #include "interface_v3/group_compas.h"
50 #include "misc.h"
51 // 3D
52 #include "nel/3d/quad_tree.h"
53 // Interface 3D
54 #include "nel/3d/u_driver.h"
55 #include "nel/3d/u_scene.h"
56 #include "nel/3d/u_camera.h"
57 #include "nel/3d/u_text_context.h"
58 #include "nel/3d/u_material.h"
59 // Misc
60 #include "nel/misc/stream.h"
61 #include "nel/misc/common.h"
62 // Game share
63 #include "game_share/mission_desc.h"
64 #include "game_share/inventories.h"
65 // PACS
66 #include "nel/pacs/u_collision_desc.h"
67 // UI
68 #include "interface_v3/group_compas.h"
70 #include "player_r2_cl.h"
71 #include "r2/editor.h"
74 ///////////
75 // USING //
76 ///////////
77 using namespace NLMISC;
78 using namespace NL3D;
79 using namespace NLPACS;
80 using namespace std;
82 #ifdef DEBUG_NEW
83 #define new DEBUG_NEW
84 #endif
86 ////////////
87 // EXTERN //
88 ////////////
89 extern UDriver *Driver;
90 extern UScene *Scene;
91 extern UTextContext *TextContext;
92 extern UCamera MainCam;
93 extern CLFECOMMON::TCLEntityId SlotUnderCursor;
96 ////////////
97 // GLOBAL //
98 ////////////
99 CEntityManager EntitiesMngr;
101 // Hierarchical timer
102 H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update )
103 H_AUTO_DECL ( RZ_Client_Update_Post_Render )
104 H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update_Apply_Motion )
105 H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update_Count )
109 /////////////
110 // METHODS //
111 /////////////
112 //---------//
113 // PRIVATE //
114 //---------//
116 // ***************************************************************************
117 class CMissionTargetObserver : public ICDBNode::IPropertyObserver
119 public :
121 // From ICDBNode::IPropertyObserver
122 virtual void update(ICDBNode* node )
124 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
125 if (leaf)
127 // Get the target
128 uint32 oldTarget = leaf->getOldValue32();
129 uint32 target = leaf->getValue32();
131 // Scan all entities
132 CEntityCL *entity = NULL;
133 if (oldTarget)
134 entity = EntitiesMngr.getEntityByName(oldTarget);
135 if (entity)
136 entity->updateMissionTarget();
138 entity = NULL;
139 if (target)
140 entity = EntitiesMngr.getEntityByName(target);
141 if (entity)
142 entity->updateMissionTarget();
144 CInterfaceManager *im = CInterfaceManager::getInstance();
145 CGroupCompas *gc = dynamic_cast<CGroupCompas *>(CWidgetManager::getInstance()->getElementFromId("ui:interface:compass"));
146 // if new target title is not NULL, then show the compass and make it blink to indicate new location
147 // please note that the first time the player login, a target has not been saved in his config file, so
148 // we permit the first (and only one) mission that is received to become the new compass direction (chiang the strong ...)
149 if (!IngameDbMngr.initInProgress() || (gc && !gc->isSavedTargetValid()))
151 if (target)
153 _PendingMissionTitle.push_back(leaf);
158 // When a mission name has been retrieved, update the compass to point it
159 void update()
161 std::list<CCDBNodeLeaf *>::iterator it = _PendingMissionTitle.begin();
162 while (it != _PendingMissionTitle.end())
164 std::list<CCDBNodeLeaf *>::iterator tmpIt = it;
165 ++ it;
166 CCDBNodeLeaf *leaf = *tmpIt;
168 // If the previous title is not empty we probably have to clear the compass
169 if (leaf->getOldValue32() != 0)
171 CInterfaceManager *pIM = CInterfaceManager::getInstance();
172 CGroupCompas *pGC = dynamic_cast<CGroupCompas*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:compass"));
173 if (pGC == NULL)
175 nlwarning("Can't retrieve compass group");
176 return;
178 CCompassTarget ct = pGC->getTarget();
180 STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance();
181 string oldName;
182 if (!pSMC->getDynString(leaf->getOldValue32(), oldName))
184 nlwarning("Can't get compass target name");
185 return;
188 if (ct.Name == oldName)
190 CCompassTarget north;
191 pGC->setTarget(north);
195 // see if mission name has been retrieved
196 if ((*tmpIt)->getValue32() == 0)
198 _PendingMissionTitle.erase(tmpIt);
200 else
202 // TODO : maybe the following code could be include in CGroupMap::checkCoords, but it is not called when the map is not visible...
203 STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance();
204 string name;
205 if (pSMC->getDynString((*tmpIt)->getValue32(), name))
207 // if (_AlreadyReceived.count(name) == 0)
208 // {
209 // _AlreadyReceived.insert(name);
210 CInterfaceManager *im = CInterfaceManager::getInstance();
211 CGroupCompas *gc = dynamic_cast<CGroupCompas *>(CWidgetManager::getInstance()->getElementFromId("ui:interface:compass"));
212 if (!gc)
214 nlwarning("Can't retrieve compass group");
216 else
218 CCompassTarget ct;
219 CCDBNodeLeaf *leaf = *tmpIt;
220 CCDBNodeBranch *parent = leaf->getParent();
221 if (parent)
223 CCDBNodeLeaf *x = dynamic_cast<CCDBNodeLeaf *>(parent->getNode(ICDBNode::CTextId("X")));
224 CCDBNodeLeaf *y = dynamic_cast<CCDBNodeLeaf *>(parent->getNode(ICDBNode::CTextId("Y")));
225 if (x && y)
227 CSmartPtr<CNamedEntityPositionState> tracker = new CNamedEntityPositionState;
228 tracker->build(*tmpIt, x, y);
229 ct.setPositionState(tracker);
230 ct.Name = name;
231 // make the compass appear and blink
232 gc->setActive(true);
233 gc->setTarget(ct);
234 gc->blink();
235 gc->enableBlink(2);
236 CWidgetManager::getInstance()->setTopWindow(gc);
240 // }
241 _PendingMissionTitle.erase(tmpIt);
247 private:
248 std::list<CCDBNodeLeaf *> _PendingMissionTitle;
249 // std::set<std::string> _AlreadyReceived;
252 //-----------------------------------------------
253 CMissionTargetObserver MissionTargetObserver;
257 // ***************************************************************************
258 class CTeamUIDObserver : public ICDBNode::IPropertyObserver
260 public :
262 // From ICDBNode::IPropertyObserver
263 virtual void update(ICDBNode* node )
265 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
266 if (leaf)
268 // Get the uid
269 CLFECOMMON::TClientDataSetIndex oldEntityId = leaf->getOldValue32();
270 CLFECOMMON::TClientDataSetIndex entityId = leaf->getValue32();
272 // Scan all entities.
273 // check if removed from team
274 CEntityCL *entity = EntitiesMngr.getEntityByCompressedIndex(oldEntityId);
275 if (entity)
276 entity->updateIsInTeam();
278 // check if added in team
279 entity = EntitiesMngr.getEntityByCompressedIndex(entityId);
280 if (entity)
281 entity->updateIsInTeam();
286 //-----------------------------------------------
288 CTeamUIDObserver TeamUIDObserver;
291 // ***************************************************************************
292 class CTeamPresentObserver : public ICDBNode::IPropertyObserver
294 public :
296 // From ICDBNode::IPropertyObserver
297 virtual void update(ICDBNode* node )
299 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
300 if (leaf)
302 // Must get the NAME leaf
303 CCDBNodeBranch *parent= leaf->getParent();
304 if(parent)
306 leaf= dynamic_cast<CCDBNodeLeaf*>(parent->getNode(ICDBNode::CTextId("UID"), false));
307 // Get the name id
308 CLFECOMMON::TClientDataSetIndex entityId = CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
309 if(leaf)
310 entityId= leaf->getValue32();
312 // Scan all entities.
313 // check if added/removed in team
314 CEntityCL *entity= EntitiesMngr.getEntityByCompressedIndex(entityId);
315 if (entity)
316 entity->updateIsInTeam();
322 //-----------------------------------------------
324 CTeamPresentObserver TeamPresentObserver;
327 // ***************************************************************************
328 class CAnimalUIDObserver : public ICDBNode::IPropertyObserver
330 public :
332 // From ICDBNode::IPropertyObserver
333 virtual void update(ICDBNode* node )
335 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
336 if (leaf)
338 // Get the uid
339 CLFECOMMON::TClientDataSetIndex oldEntityId = leaf->getOldValue32();
340 CLFECOMMON::TClientDataSetIndex entityId = leaf->getValue32();
342 // Scan all entities.
343 // check if removed from animal list
344 CEntityCL *entity = EntitiesMngr.getEntityByCompressedIndex(oldEntityId);
345 if (entity)
346 entity->updateIsUserAnimal();
348 // check if added in animal list
349 entity = EntitiesMngr.getEntityByCompressedIndex(entityId);
350 if (entity)
351 entity->updateIsUserAnimal();
356 //-----------------------------------------------
358 CAnimalUIDObserver AnimalUIDObserver;
361 // ***************************************************************************
362 class CAnimalStatusObserver : public ICDBNode::IPropertyObserver
364 public :
366 // From ICDBNode::IPropertyObserver
367 virtual void update(ICDBNode* node )
369 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
370 if (leaf)
372 // Must get the NAME leaf
373 CCDBNodeBranch *parent= leaf->getParent();
374 if(parent)
376 leaf= dynamic_cast<CCDBNodeLeaf*>(parent->getNode(ICDBNode::CTextId("UID"), false));
377 // Get the name id
378 CLFECOMMON::TClientDataSetIndex entityId = CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
379 if(leaf)
380 entityId= leaf->getValue32();
382 // Scan all entities.
383 // check if added/removed in animal list
384 CEntityCL *entity= EntitiesMngr.getEntityByCompressedIndex(entityId);
385 if (entity)
386 entity->updateIsUserAnimal();
392 //-----------------------------------------------
394 CAnimalStatusObserver AnimalStatusObserver;
397 // ***************************************************************************
398 //--------//
399 // PUBLIC //
400 //--------//
401 //-----------------------------------------------
402 // CEntityManager :
403 // Constructor.
404 //-----------------------------------------------
405 CEntityManager::CEntityManager()
407 _NbMaxEntity = 0;
408 _EntitiesAllocated = 0;
409 _NbUser = 0;
410 _NbPlayer = 0;
411 _NbChar = 0;
412 _LastEntityUnderPos= NULL;
413 _LastRemovedInstance = -1;
414 }// CEntityManager //
416 //-----------------------------------------------
417 // ~CEntityManager :
418 // Destructor.
419 //-----------------------------------------------
420 CEntityManager::~CEntityManager()
422 release();
423 }// ~CEntityManager //
426 //-----------------------------------------------
427 // initialize :
429 //-----------------------------------------------
430 void CEntityManager::initialize(uint nbMaxEntity)
432 // Set the maximum number of entities.
433 _NbMaxEntity = nbMaxEntity;
434 // if
435 if(_NbMaxEntity)
437 _Entities.resize(_NbMaxEntity, 0);
438 _EntityGroundFXHandle.resize(_NbMaxEntity);
441 ICDBNode::CTextId textId;
443 // Add an observer on the mission database
444 CInterfaceManager *pIM = CInterfaceManager::getInstance();
445 uint i,j;
446 for (i=0; i<MAX_NUM_MISSIONS; i++)
447 for (j=0; j<MAX_NUM_MISSION_TARGETS; j++)
449 std::string text = toString("SERVER:MISSIONS:%d:TARGET%d:TITLE", i, j);
450 textId = ICDBNode::CTextId(text);
451 NLGUI::CDBManager::getInstance()->getDB()->addObserver(&MissionTargetObserver, textId );
452 _MissionTargetTitleDB[i][j] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
453 nlassert(_MissionTargetTitleDB[i][j]);
456 // Add an Observer to the Team database
457 for (i=0; i<MaxNumPeopleInTeam; i++)
459 std::string text = toString(TEAM_DB_PATH ":%d:UID", i);
460 textId = ICDBNode::CTextId(text);
461 NLGUI::CDBManager::getInstance()->getDB()->addObserver(&TeamUIDObserver, textId );
462 _GroupMemberUidDB[i] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
463 nlassert(_GroupMemberUidDB[i]);
465 text = toString(TEAM_DB_PATH ":%d:NAME", i);
466 textId = ICDBNode::CTextId(text);
467 NLGUI::CDBManager::getInstance()->getDB()->addObserver(&TeamPresentObserver, textId );
468 _GroupMemberNameDB[i] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
469 nlassert(_GroupMemberNameDB[i]);
472 // Add an Observer to the Animal database
473 for (i=0; i<MAX_INVENTORY_ANIMAL; i++)
475 std::string text = toString("SERVER:PACK_ANIMAL:BEAST%d:UID", i);
476 textId = ICDBNode::CTextId(text);
477 NLGUI::CDBManager::getInstance()->getDB()->addObserver(&AnimalUIDObserver, textId);
478 _BeastUidDB[i] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
479 nlassert(_BeastUidDB[i]);
481 text = toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS", i);
482 textId = ICDBNode::CTextId(text);
483 NLGUI::CDBManager::getInstance()->getDB()->addObserver(&AnimalStatusObserver, textId);
484 _BeastStatusDB[i] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
485 nlassert(_BeastStatusDB[i]);
487 text = toString("SERVER:PACK_ANIMAL:BEAST%d:TYPE", i);
488 _BeastTypeDB[i] = NLGUI::CDBManager::getInstance()->getDbProp(text, false);
489 nlassert(_BeastTypeDB[i]);
492 }// initialize //
495 //-----------------------------------------------
496 // release :
497 // Free the class and all the components.
498 //-----------------------------------------------
499 void CEntityManager::release()
501 _LastEntityUnderPos= NULL;
503 // Remove all entities.
504 for(uint i=0; i<_Entities.size(); ++i)
506 if(_Entities[i])
508 // remove from fx manager
509 if (_Entities[i]->supportGroundFX())
511 _GroundFXManager.remove(_EntityGroundFXHandle[i]);
513 delete _Entities[i];
514 _Entities[i] = 0;
518 UserEntity = NULL;
520 // Clear the list.
521 _Entities.clear();
523 // Clean the backuped list.
524 _BackupedChanges.clear();
526 _GroundFXManager.reset();
527 }// release //
529 //-----------------------------------------------
530 void CEntityManager::reinit()
532 release();
533 initialize(_NbMaxEntity);
536 CShapeInstanceReference CEntityManager::createInstance(const string& shape, const CVector &pos, const string& text, const string& url, bool haveCollisions, uint16 inIgZone, sint32& idx)
538 idx = -1;
539 CShapeInstanceReference nullinstref(UInstance(), string(""), string(""));
540 if (!Scene) return nullinstref;
542 UInstance instance = Scene->createInstance(shape);
544 if(!instance.empty())
546 UMovePrimitive *primitive = NULL;
548 if (PACS && haveCollisions)
550 primitive = PACS->addCollisionablePrimitive(dynamicWI, 1);
551 primitive->setDontSnapToGround(false);
554 // Put instance in last deleted position if found
555 if (_LastRemovedInstance != -1)
557 idx = _LastRemovedInstance;
558 _ShapeInstances[idx].Instance = instance;
559 _ShapeInstances[idx].Primitive = primitive;
560 _ShapeInstances[idx].ContextText = text;
561 _ShapeInstances[idx].ContextURL = url;
562 _ShapeInstances[idx].BboxActive = !text.empty() || !url.empty();
563 _ShapeInstances[idx].Deleted = false;
564 _ShapeInstances[idx].InIGZone = inIgZone > 0;
566 _LastRemovedInstance = _ShapeInstances[idx].LastDeleted;
567 _ShapeInstances[idx].LastDeleted = -1;
568 TIGZoneShapes::iterator it = _IgZoneShapes.find(inIgZone);
569 if (it == _IgZoneShapes.end())
571 vector<uint32> shapes;
572 shapes.push_back(idx);
573 _IgZoneShapes.insert(make_pair(inIgZone, shapes));
575 else
577 vector<uint32> &shapes = (*it).second;
578 shapes.push_back(idx);
580 return _ShapeInstances[idx];
582 else
584 CShapeInstanceReference instref = CShapeInstanceReference(instance, text, url, !text.empty() || !url.empty(), inIgZone > 0);
585 instref.Primitive = primitive;
586 idx = _ShapeInstances.size();
587 _ShapeInstances.push_back(instref);
588 TIGZoneShapes::iterator it = _IgZoneShapes.find(inIgZone);
589 if (it == _IgZoneShapes.end())
591 vector<uint32> shapes;
592 shapes.push_back(idx);
593 _IgZoneShapes.insert(make_pair(inIgZone, shapes));
595 else
597 vector<uint32> &shapes = (*it).second;
598 shapes.push_back(idx);
600 return instref;
603 return nullinstref;
606 bool CEntityManager::deleteInstance(uint32 idx)
608 if (!Scene || idx >= _ShapeInstances.size())
609 return false;
611 if (!_ShapeInstances[idx].Instance.empty())
612 Scene->deleteInstance(_ShapeInstances[idx].Instance);
613 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
614 if (primitive)
615 PACS->removePrimitive(primitive);
617 if (!_ShapeInstances[idx].Deleted)
619 _ShapeInstances[idx].Primitive = NULL;
620 _ShapeInstances[idx].Deleted = true;
621 _ShapeInstances[idx].LastDeleted = _LastRemovedInstance;
622 _LastRemovedInstance = idx;
625 return true;
628 void CEntityManager::removeInstancesInIgZone(uint16 igZone)
630 if (!Scene)
631 return;
633 TIGZoneShapes::iterator it = _IgZoneShapes.find(igZone);
634 if (it != _IgZoneShapes.end())
636 vector<uint32> &shapes = (*it).second;
637 for (uint i = 0; i < shapes.size(); i++)
638 deleteInstance(shapes[i]);
639 _IgZoneShapes.erase(it);
644 CVector CEntityManager::getInstancePos(uint32 idx)
646 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
647 return CVector(0,0,0);
649 UInstance instance = _ShapeInstances[idx].Instance;
650 if(instance.empty())
651 return CVector(0,0,0);
653 return instance.getPos();
656 bool CEntityManager::setInstancePos(uint32 idx, CVector pos)
658 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
659 return false;
661 UInstance instance = _ShapeInstances[idx].Instance;
662 if(instance.empty())
663 return false;
665 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
666 if (primitive)
668 primitive->setGlobalPosition(_ShapeInstances[idx].PrimRelativePos + pos, dynamicWI);
671 instance.setPos(pos);
672 return true;
675 CVector CEntityManager::getInstanceRot(uint32 idx)
677 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
678 return CVector(0,0,0);
680 UInstance instance = _ShapeInstances[idx].Instance;
681 if(instance.empty())
682 return CVector(0,0,0);
684 return instance.getRotEuler();
687 bool CEntityManager::setInstanceRot(uint32 idx, CVector rot)
689 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
690 return false;
692 UInstance instance = _ShapeInstances[idx].Instance;
693 if(instance.empty())
694 return false;
696 instance.setRotEuler(rot);
698 return true;
701 CVector CEntityManager::getInstanceScale(uint32 idx)
703 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
704 return CVector(0,0,0);
706 UInstance instance = _ShapeInstances[idx].Instance;
707 if(instance.empty())
708 return CVector(0,0,0);
710 return instance.getScale();
713 CVector CEntityManager::getInstanceColPos(uint32 idx)
715 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
716 return CVector(0,0,0);
718 return _ShapeInstances[idx].PrimRelativePos;
721 CVector CEntityManager::getInstanceColScale(uint32 idx)
723 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
724 return CVector(0,0,0);
726 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
727 if (!primitive)
728 return CVector(0,0,0);
730 float width, depth;
731 primitive->getSize(width, depth);
732 float height = primitive->getHeight();
734 return CVector(width, depth, height);
737 double CEntityManager::getInstanceColOrient(uint32 idx)
739 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
740 return 0.f;
742 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
743 if (!primitive)
744 return 0.f;
746 return primitive->getOrientation(dynamicWI);
749 CVector CEntityManager::getInstanceBBoxMin(uint32 idx)
751 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
752 return CVector(0,0,0);
754 UInstance instance = _ShapeInstances[idx].Instance;
755 if (instance.empty())
756 return CVector(0,0,0);
758 NLMISC::CAABBox bbox;
759 _ShapeInstances[idx].Instance.getShapeAABBox(bbox);
761 CVector bbox_min;
763 if (bbox.getCenter() == CVector::Null)
764 bbox_min = CVector(-0.5f, -0.5f, -0.5f);
765 else
766 bbox_min = bbox.getMin();
768 bbox_min.x *= _ShapeInstances[idx].Instance.getScale().x;
769 bbox_min.y *= _ShapeInstances[idx].Instance.getScale().y;
770 bbox_min.z *= _ShapeInstances[idx].Instance.getScale().z;
772 return bbox_min+_ShapeInstances[idx].Instance.getPos();
775 CVector CEntityManager::getInstanceBBoxMax(uint32 idx)
777 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
778 return CVector(0,0,0);
780 UInstance instance = _ShapeInstances[idx].Instance;
781 if(instance.empty())
782 return CVector(0,0,0);
784 NLMISC::CAABBox bbox;
785 _ShapeInstances[idx].Instance.getShapeAABBox(bbox);
787 CVector bbox_max;
789 if (bbox.getCenter() == CVector::Null)
790 bbox_max = CVector(-0.5f, -0.5f, -0.5f);
791 else
792 bbox_max = bbox.getMax();
794 bbox_max.x *= _ShapeInstances[idx].Instance.getScale().x;
795 bbox_max.y *= _ShapeInstances[idx].Instance.getScale().y;
796 bbox_max.z *= _ShapeInstances[idx].Instance.getScale().z;
798 return bbox_max+_ShapeInstances[idx].Instance.getPos();
801 bool CEntityManager::removeInstances()
803 if (!Scene) return false;
805 for(uint i=0; i<_ShapeInstances.size(); ++i)
807 if (!_ShapeInstances[i].InIGZone)
808 deleteInstance(i);
810 return true;
813 bool CEntityManager::setupInstance(uint32 idx, const vector<string> &keys, const vector<string> &values)
815 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
816 return false;
818 UInstance instance = _ShapeInstances[idx].Instance;
819 if(instance.empty())
820 return false;
822 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
824 for (uint32 i=0; i < keys.size(); i++)
826 string param = keys[i];
827 if (param == "transparency")
829 uint t;
830 if (fromString(values[i], t))
832 t = max(0, min((int)t, 255));
833 makeInstanceTransparent(instance, t, t == 255);
836 else if (param == "colorize")
838 if (values[i] == "0")
840 for(uint j=0;j<instance.getNumMaterials();j++)
842 instance.getMaterial(j).setShininess( 10.0f );
843 instance.getMaterial(j).setEmissive(CRGBA(255,255,255,255));
844 instance.getMaterial(j).setAmbient(CRGBA(0,0,0,255));
845 instance.getMaterial(j).setDiffuse(CRGBA(255,255,255,255));
848 else
850 CRGBA c;
851 if( fromString( values[i], c ) )
853 for(uint j=0;j<instance.getNumMaterials();j++)
855 instance.getMaterial(j).setShininess( 1000.0f );
856 instance.getMaterial(j).setEmissive(c);
857 instance.getMaterial(j).setAmbient(c);
858 instance.getMaterial(j).setDiffuse(c);
863 else if (param == "texture")
865 if (!values[i].empty())
867 for(uint j=0;j<instance.getNumMaterials();j++)
869 sint numStages = instance.getMaterial(j).getLastTextureStage() + 1;
870 for(sint l = 0; l < numStages; l++)
872 if (instance.getMaterial(j).isTextureFile((uint) l))
873 instance.getMaterial(j).setTextureFileName(values[i], (uint) l);
878 else if (param == "skeleton")
880 // TODO
882 else if (param == "context")
884 _ShapeInstances[idx].ContextText = values[i];
886 else if (param == "url")
888 _ShapeInstances[idx].ContextURL = values[i];
890 else if (param == "move x" || param == "move y" || param == "move z")
892 float v;
893 CVector pos = getInstancePos(idx);
895 if (getRelativeFloatFromString(values[i], v))
897 updateVector(param, pos, v, true);
899 else
901 updateVector(param, pos, v, false);
903 setInstancePos(idx, pos);
905 else if (param == "rot x" || param == "rot y" || param == "rot z")
908 float v;
909 CVector rot = getInstanceRot(idx);
911 if (getRelativeFloatFromString(values[i], v))
913 updateVector(param, rot, v, true);
915 else
917 updateVector(param, rot, v, false);
919 setInstanceRot(idx, rot);
921 else if (param == "scale x" || param == "scale y" || param == "scale z")
923 float v;
924 CVector scale = instance.getScale();
926 if (getRelativeFloatFromString(values[i], v))
928 updateVector(param, scale, v, true);
930 else
932 updateVector(param, scale, v, false);
934 instance.setScale(scale);
937 // Primitive colissions setups
939 if (!primitive) continue;
941 if (param == "col size x" || param == "col size y" || param == "col size z")
943 float width, depth;
944 primitive->getSize(width, depth);
945 float height = primitive->getHeight();
947 CVector size = CVector(width, depth, height);
948 float v;
949 if (getRelativeFloatFromString(values[i], v))
951 updateVector(param, size, v, true);
953 else
955 updateVector(param, size, v, false);
957 primitive->setSize(size.x, size.y);
958 primitive->setHeight(size.z);
960 else if (param == "col pos x" || param == "col pos y" || param == "col pos z")
962 CVector pos = instance.getPos();
963 float v;
965 if (getRelativeFloatFromString(values[i], v))
967 updateVector(param, _ShapeInstances[idx].PrimRelativePos, v, false);
969 else
971 if (param == "col pos x")
972 _ShapeInstances[idx].PrimRelativePos.x = v - pos.x;
973 if (param == "col pos y")
974 _ShapeInstances[idx].PrimRelativePos.y = v - pos.y;
975 if (param == "col pos z")
976 _ShapeInstances[idx].PrimRelativePos.z = v - pos.z;
978 primitive->setGlobalPosition(pos + _ShapeInstances[idx].PrimRelativePos, dynamicWI);
980 else if (param == "col orientation")
982 double orient = primitive->getOrientation(dynamicWI);
983 double v = 0.f;
985 if (values[i].empty())
986 continue;
988 if (values[i][0] == '+')
990 fromString(values[i].substr(1), v);
991 orient += v;
993 else
995 fromString(values[i], v);
996 orient = v;
999 primitive->setOrientation(orient, dynamicWI);
1001 else if (param == "col mask player")
1003 bool active;
1004 fromString(values[i], active);
1005 UMovePrimitive::TCollisionMask mask=primitive->getCollisionMask();
1006 if (active)
1007 primitive->setCollisionMask(mask|MaskColPlayer);
1008 else
1009 primitive->setCollisionMask(mask&~MaskColPlayer);
1011 else if (param == "col mask door")
1013 bool active;
1014 fromString(values[i], active);
1015 UMovePrimitive::TCollisionMask mask=primitive->getCollisionMask();
1016 if (active)
1017 primitive->setCollisionMask(mask|MaskColDoor);
1018 else
1019 primitive->setCollisionMask(mask&~MaskColDoor);
1021 else if (param == "col obstacle")
1023 bool active;
1024 fromString(values[i], active);
1025 primitive->setObstacle(active);
1027 else if (param == "col obstacle")
1033 return true;
1037 CShapeInstanceReference CEntityManager::getShapeInstanceUnderPos(float x, float y, sint32 &idx)
1039 CShapeInstanceReference selectedInstance(UInstance(), string(""), string(""));
1040 _LastInstanceUnderPos= NULL;
1041 idx = -1;
1043 // If not initialised, return
1044 if (_ShapeInstances.empty())
1045 return selectedInstance;
1047 // build the ray
1048 CMatrix camMatrix = MainCam.getMatrix();
1049 CFrustum camFrust = MainCam.getFrustum();
1050 CViewport viewport = Driver->getViewport();
1052 // Get the Ray made by the mouse.
1053 CVector pos, dir;
1054 viewport.getRayWithPoint(x, y, pos, dir, camMatrix, camFrust);
1055 // Normalize the direction.
1056 dir.normalize();
1058 // **** Get instances with box intersecting the ray.
1059 float bestDist = 255;
1060 for(uint i=0; i<_ShapeInstances.size(); i++)
1062 if (!_ShapeInstances[i].Deleted && _ShapeInstances[i].BboxActive)
1064 H_AUTO(RZ_Client_GEUP_box_intersect)
1066 // if intersect the bbox
1067 NLMISC::CAABBox bbox;
1068 //= _ShapeInstances[i].SelectionBox;
1069 if(!_ShapeInstances[i].Instance.empty())
1071 _ShapeInstances[i].Instance.getShapeAABBox(bbox);
1072 CVector bbox_min;
1073 CVector bbox_max;
1075 if (bbox.getCenter() == CVector::Null)
1077 bbox_min = CVector(-0.5f, -0.5f, -0.5f);
1078 bbox_max = CVector(-0.5f, -0.5f, -0.5f);
1080 else
1082 bbox_min = bbox.getMin();
1083 bbox_max = bbox.getMax();
1086 bbox_min.x *= _ShapeInstances[i].Instance.getScale().x;
1087 bbox_min.y *= _ShapeInstances[i].Instance.getScale().y;
1088 bbox_min.z *= _ShapeInstances[i].Instance.getScale().z;
1090 bbox_max.x *= _ShapeInstances[i].Instance.getScale().x;
1091 bbox_max.y *= _ShapeInstances[i].Instance.getScale().y;
1092 bbox_max.z *= _ShapeInstances[i].Instance.getScale().z;
1095 bbox.setMinMax(bbox_min+_ShapeInstances[i].Instance.getPos(), bbox_max+_ShapeInstances[i].Instance.getPos());
1097 if(bbox.intersect(pos, pos+dir*100.0f))
1099 float dist = (bbox.getCenter()-pos).norm();
1100 if (dist < bestDist)
1102 selectedInstance = _ShapeInstances[i];
1103 bestDist = dist;
1104 idx = (sint32)i;
1110 return selectedInstance;
1114 //-----------------------------------------------
1115 // Create an entity according to the slot and the form.
1116 // \param uint slot : slot for the entity.
1117 // \param uint32 form : form to create the entity.
1118 // \param TClientDataSetIndex : persitent id while the entity is connected.
1119 // \return CEntityCL * : pointer on the new entity.
1120 //-----------------------------------------------
1121 CEntityCL *CEntityManager::create(uint slot, uint32 form, const TNewEntityInfo& newEntityInfo)
1123 // DEBUG
1124 if(verboseVP(NULL, form))
1125 nlinfo("(%05d,%03d) EM:create: slot '%u': %s", sint32(T1%100000), NetMngr.getCurrentServerTick(), slot, CSheetId(form).toString().c_str());
1126 // Check parameter : slot.
1127 if(slot >= _NbMaxEntity)
1129 nlwarning("EM:create: Cannot create the entity, the slot '%u' is invalid.", slot);
1130 return 0;
1132 else
1134 // Slot 0 is for the user and so should be allocated only once (at beginning of main loop).
1135 if( slot == 0 && _Entities[0] )
1137 if (newEntityInfo.DataSetIndex != CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
1139 // Store the dataSetId received
1140 _Entities[0]->dataSetId(newEntityInfo.DataSetIndex);
1142 // Store the alias (although there should not be one for the slot 0!)
1143 _Entities[0]->npcAlias(newEntityInfo.Alias);
1144 return 0;
1148 // Remove the old one (except the user).
1149 if(_Entities[slot])
1151 nlwarning("EM:create: There is already an entity in the slot '%u' ! Old entity will be removed.", slot);
1152 // remove from ground fx manager
1153 // TODO : test if entity has ground fxs
1154 if (_Entities[slot]->supportGroundFX())
1156 _GroundFXManager.remove(_EntityGroundFXHandle[slot]);
1158 delete _Entities[slot];
1159 _Entities[slot] = 0;
1162 // Check parameter : form.
1163 CEntitySheet *entitySheet = SheetMngr.get((CSheetId)form);
1164 if(entitySheet == 0)
1166 nlwarning("EM:create: Attempt on create an entity with a bad form number %d (%s) for the slot '%d' trying to compute the default one.", form, ((CSheetId)form).toString().c_str(), slot);
1167 CSheetId defaultEntity;
1168 if(defaultEntity.buildSheetId(ClientCfg.DefaultEntity)==false)
1170 nlwarning("EM:create: The default entity (%s) is not in the sheetid.bin.", ClientCfg.DefaultEntity.c_str());
1171 return 0;
1173 entitySheet = SheetMngr.get(defaultEntity);
1174 if(entitySheet == 0)
1176 nlwarning("EM:create: The default entity (%s) is not in the sheet manager.", ClientCfg.DefaultEntity.c_str());
1177 return 0;
1181 // Create the entity according to the type.
1184 switch(entitySheet->type())
1186 case CEntitySheet::RACE_STATS:
1187 case CEntitySheet::CHAR:
1188 if (slot == 0)
1190 nlassert (UserEntity == NULL);
1191 UserEntity = new CUserEntity;
1192 _Entities[slot] = UserEntity;
1194 else
1196 _Entities[slot] = new CPlayerCL;
1198 break;
1200 case CEntitySheet::FAUNA:
1202 CCharacterSheet *sheet = NLMISC::safe_cast<CCharacterSheet *>(entitySheet);
1203 if (!sheet->R2Npc) _Entities[slot] = new CCharacterCL;
1204 else _Entities[slot] = new CPlayerR2CL;
1206 break;
1207 case CEntitySheet::FLORA:
1208 _Entities[slot] = new CCharacterCL;
1209 break;
1211 case CEntitySheet::FX:
1212 _Entities[slot] = new CFxCL;
1213 break;
1215 case CEntitySheet::ITEM:
1216 _Entities[slot] = new CItemCL;
1217 break;
1219 case CEntitySheet::FORAGE_SOURCE:
1220 _Entities[slot] = new CForageSourceCL;
1221 break;
1223 default:
1224 pushDebugStr(NLMISC::toString("Unknown Form Type '%d' -> entity not created.", entitySheet->type()));
1225 break;
1228 // If the entity has been right created.
1229 if(_Entities[slot])
1231 // Set the sheet Id.
1232 _Entities[slot]->sheetId((CSheetId)form);
1233 // Set the slot.
1234 _Entities[slot]->slot(slot);
1235 // Set the DataSet Index. AFTER slot(), so bar manager is correctly init
1236 _Entities[slot]->dataSetId(newEntityInfo.DataSetIndex);
1237 // Set the Mission Giver Alias
1238 _Entities[slot]->npcAlias(newEntityInfo.Alias);
1239 // Build the entity from a sheet.
1240 if(_Entities[slot]->build(entitySheet))
1242 // Apply properties backuped;
1243 applyBackupedProperties(slot);
1244 // register to the ground fx manager
1245 if(_Entities[slot]->supportGroundFX())
1247 _EntityGroundFXHandle[slot] = _GroundFXManager.add(_Entities[slot]);
1250 // Entity is not valid -> REMOVE IT
1251 else
1253 // Everyone except the User
1254 if(slot != 0)
1256 nlwarning("EM:%d: Cannot build the Entity -> REMOVE IT", slot);
1257 delete _Entities[slot];
1258 _Entities[slot] = 0;
1260 // The User
1261 else
1262 nlerror("EM: Cannot build the User");
1265 // Entity Not Allocated
1266 else
1267 pushDebugStr(NLMISC::toString("Cannot Allocated the Entity in the slot '%d'.", slot));
1268 // Log problems about the entity creation.
1269 flushDebugStack(NLMISC::toString("Create Entity in slot '%d' with Form '%s' :", slot, ((CSheetId)form).toString().c_str()));
1270 // Return a pointer on the entity created.
1271 return _Entities[slot];
1272 }// create //
1274 //-----------------------------------------------
1275 // remove :
1276 // Delete an entity.
1277 // \todo GUIGUI : rename into free.
1278 // \todo GUIGUI : finish the function.
1279 //-----------------------------------------------
1280 bool CEntityManager::remove(uint slot, bool warning)
1282 // DEBUG
1283 if(verboseVP(NULL))
1284 nlinfo("EM:remove: slot '%u'.", slot);
1285 // Check parameter : slot.
1286 if(slot >= _NbMaxEntity)
1288 nlwarning("CEntityManager::remove : Attempt on delete a bad slot (slot %d)", slot);
1289 return false;
1292 // Do not delete the user.
1293 if(slot == 0)
1295 nlwarning("CEntityManager::remove : Cannot remove the entity in the slot 0 (user slot).");
1296 return false;
1299 // Slot not allocated.
1300 if(_Entities[slot] == 0)
1302 if(warning)
1304 nlwarning("CEntityManager::remove : Attempt on delete the slot '%d' that is not allocated.", slot);
1305 return false;
1309 // Remove the entity from others target.
1310 for(uint i=0; i<_Entities.size(); ++i)
1312 // This entity is not allocated.
1313 if(_Entities[i] == 0)
1314 continue;
1316 // Inform about the slot of the entity that will be removed.
1317 _Entities[i]->slotRemoved(slot);
1320 // remove ground fx
1321 if(_Entities[slot] != 0)
1323 if (_Entities[slot]->supportGroundFX())
1325 _GroundFXManager.remove(_EntityGroundFXHandle[slot]);
1329 // notify the projectile manager that entity has been removed
1330 CProjectileManager::getInstance().entityRemoved(slot);
1332 // notify the Bar Manager
1333 if(_Entities[slot])
1334 CBarManager::getInstance()->delEntity(_Entities[slot]->slot());
1336 // previous UnderPos?
1337 if(_LastEntityUnderPos==_Entities[slot])
1338 _LastEntityUnderPos= NULL;
1340 // Free the slot.
1341 delete _Entities[slot];
1342 _Entities[slot] = 0;
1344 // Done.
1345 return true;
1346 }// remove //
1349 //-----------------------------------------------
1350 // removeCollision :
1351 // Remove the collision for all entities.
1352 //-----------------------------------------------
1353 void CEntityManager::removeCollision()
1355 const uint nbEntities = (uint)_Entities.size();
1356 for(uint i=0; i<nbEntities; ++i)
1358 // Is the entity allocated.
1359 if(_Entities[i] == 0)
1360 continue;
1362 // Remove the entity primitive.
1363 _Entities[i]->removePrimitive();
1365 // Remove the collision entity.
1366 _Entities[i]->removeCollisionEntity();
1368 }// removeCollision //
1370 //-----------------------------------------------
1371 // reloadAnims :
1372 // Re-load animations (remove and load).
1373 //-----------------------------------------------
1374 void CEntityManager::reloadAnims()
1376 for(uint i=0; i<_Entities.size(); ++i)
1378 if(_Entities[i])
1380 // Get a reference on the current entity.
1381 CEntityCL &entity = *(_Entities[i]);
1382 // Change the playlist
1383 entity.buildPlaylist();
1386 }// reloadAnims //
1389 //-----------------------------------------------
1390 // entity :
1391 // Get a pointer on an entity according to the asked slot.
1392 // \param uint slot : the asked slot.
1393 // \return CEntityCL * : pointer on the entity or 0.
1394 //-----------------------------------------------
1395 CEntityCL *CEntityManager::entity(uint slot)
1397 // Return 0 if the slot is the INVALID_SLOT
1398 if(slot==CLFECOMMON::INVALID_SLOT)
1399 return 0;
1400 // Check parameter : slot.
1401 if(slot >= _Entities.size())
1403 nlwarning("EM:entity: slot '%u' is invalid.", slot);
1404 if(ClientCfg.Check)
1405 nlstop;
1406 return 0;
1408 // Return the entity pointer.
1409 return _Entities[slot];
1410 }// entity //
1412 //-----------------------------------------------
1413 // entitiesNearDoors :
1414 // Return if there is an entity near a door.
1415 // \param float openingDist : near is when you are under the 'openingDist'.
1416 // \param const CVector& posDoor1 : first door position.
1417 // \param const CVector& posDoor2 : second door position.
1418 // \return bool ; 'true' if any entity is near one of the door.
1419 //-----------------------------------------------
1420 bool CEntityManager::entitiesNearDoors(float openingDist, const CVector& posDoor1, const CVector& posDoor2)
1422 for(uint i=0; i<_NbMaxEntity; ++i)
1424 // Is the entity allocated.
1425 if(_Entities[i] == 0)
1426 continue;
1428 // Get a reference on the current entity.
1429 CEntityCL &entity = *(_Entities[i]);
1431 // If the entity is close enough from the door -> return true.
1432 if( ((entity.pos() - posDoor1).sqrnorm() < openingDist)
1433 || ((entity.pos() - posDoor2).sqrnorm() < openingDist) )
1434 return true;
1437 // No Entity near the door.
1438 return false;
1439 }// entitiesNearDoors //
1441 //-----------------------------------------------
1442 // getEntityListForSelection
1443 //-----------------------------------------------
1444 void CEntityManager::getEntityListForSelection(std::vector<CEntityCL*> &entities, uint flags)
1446 // According to the view (first or third person), the user can or cannot be selected.
1447 entities.clear();
1448 uint firstEntity = (flags&CEntityFilterFlag::NotUser)?1:0;
1449 for(uint i=firstEntity; i<_NbMaxEntity; ++i)
1451 // Is the entity allocated and not user mount.
1452 if(_Entities[i] == 0 || i==UserEntity->mount())
1453 continue;
1454 // If entity unselectable, skip
1455 if(!_Entities[i]->properties().selectable())
1456 continue;
1458 // Apply each filter
1459 if ( (flags&CEntityFilterFlag::Friend) && !_Entities[i]->isFriend() )
1460 continue;
1461 if ( (flags&CEntityFilterFlag::Enemy) && !_Entities[i]->isEnemy() )
1462 continue;
1463 if ( (flags&CEntityFilterFlag::Alive) && _Entities[i]->isReallyDead() )
1464 continue;
1465 if ( (flags&CEntityFilterFlag::Dead) && !_Entities[i]->isReallyDead() )
1466 continue;
1467 if ( (flags&CEntityFilterFlag::Player) && !_Entities[i]->isPlayer() )
1468 continue;
1469 if ( (flags&CEntityFilterFlag::NonPlayer) && _Entities[i]->isPlayer() )
1470 continue;
1472 // Insert every entity in the valid list.
1473 entities.push_back(_Entities[i]);
1477 //-----------------------------------------------
1478 // getEntityUnderPos :
1479 // Get the entity under the (2d) position. Return NULL if not entity under this position.
1480 //-----------------------------------------------
1481 struct CSortEntity
1483 CEntityCL *Entity;
1484 float Depth;
1486 bool operator<(const CSortEntity &o) const
1488 return Depth<o.Depth;
1491 CEntityCL *CEntityManager::getEntityUnderPos(float x, float y, float distSelection, bool &isPlayerUnderCursor)
1493 H_AUTO (RZ_Client_getEntityUnderPos )
1494 uint i;
1496 // valid only if bbox still intersect
1497 CEntityCL *precEntityUnderPos= _LastEntityUnderPos;
1498 bool precEntityUnderPosValid= false;
1500 // reset result
1501 isPlayerUnderCursor= false;
1502 _LastEntityUnderPos= NULL;
1504 // If not initialised, return
1505 if (_Entities.empty())
1506 return NULL;
1509 // **** list of valid entities to test
1510 static vector<CEntityCL*> validEntities;
1511 uint filterFlags= CEntityFilterFlag::NoFilter;
1512 getEntityListForSelection(validEntities, filterFlags);
1514 // build the ray
1515 CMatrix camMatrix = MainCam.getMatrix();
1516 CFrustum camFrust = MainCam.getFrustum();
1517 CViewport viewport = Driver->getViewport();
1519 // Get the Ray made by the mouse.
1520 CVector pos, dir;
1521 viewport.getRayWithPoint(x, y, pos, dir, camMatrix, camFrust);
1522 // Normalize the direction.
1523 dir.normalize();
1526 // **** Get entities with box intersecting the ray.
1527 static vector<CSortEntity> intersectedEntities;
1528 intersectedEntities.clear();
1529 for(i=0;i<validEntities.size();i++)
1531 H_AUTO(RZ_Client_GEUP_box_intersect)
1532 CEntityCL *entity = validEntities[i];
1533 // if entity not visible, skip
1534 if(entity->getLastClip())
1535 continue;
1537 // if intersect the bbox
1538 NLMISC::CAABBox bbox = entity->selectBox();
1539 if(bbox.intersect(pos, pos+dir*distSelection))
1541 // add this entity to the list of possible entities
1542 CSortEntity e;
1543 e.Entity= entity;
1544 e.Depth= (bbox.getCenter()-pos).norm();
1545 intersectedEntities.push_back(e);
1547 // is it the last entity under pos?
1548 if(entity==precEntityUnderPos)
1549 precEntityUnderPosValid= true;
1553 // if no intersected entities, quit
1554 if(intersectedEntities.empty())
1555 return NULL;
1557 // Compute startDistBox: nearest entity distance, but the user
1558 float startDistBox;
1559 if(intersectedEntities[0].Entity==UserEntity)
1561 // if the nearest entity is the user, set res
1562 isPlayerUnderCursor= true;
1563 // if only player intersected, return NULL!
1564 if(intersectedEntities.size()==1)
1565 return NULL;
1566 // so take the second for startDistBox
1567 startDistBox= intersectedEntities[1].Depth;
1569 else
1571 // ok, take it.
1572 startDistBox= intersectedEntities[0].Depth;
1576 // **** get best entity according to distance face-camera or box-ray if no face intersection
1577 CEntityCL *entitySelected= NULL;
1578 float bestDistBox= FLT_MAX;
1579 float bestDistZ= FLT_MAX;
1580 for(i=0;i<intersectedEntities.size();i++)
1582 CEntityCL *entity = intersectedEntities[i].Entity;
1583 const NLMISC::CAABBox &bbox = entity->selectBox();
1585 // If this entity is the UserEntity, skip!!
1586 if(entity==UserEntity)
1587 continue;
1589 // if entity skeleton model was clipped, skip
1590 USkeleton *skeleton= entity->skeleton();
1591 if(!ClientCfg.Light && skeleton && !skeleton->getLastClippedState())
1592 continue;
1594 H_AUTO(RZ_Client_GEUP_face_intersect)
1597 // *** Try get face-intersection, result in distZ
1598 // if the entity support fast and precise intersection (and if it succeeds)
1599 bool trueIntersectComputed= false;
1600 float dist2D, distZ;
1601 if(!ClientCfg.Light)
1603 if(skeleton)
1605 if(skeleton->supportFastIntersect() && skeleton->fastIntersect(pos, dir, dist2D, distZ, false))
1606 trueIntersectComputed= true;
1608 // get the intersection with the instance (bot object)
1609 else if(!entity->instances().empty() && !entity->instances()[0].Current.empty())
1611 UInstance inst= entity->instances()[0].Current;
1612 if(inst.supportFastIntersect() && inst.fastIntersect(pos, dir, dist2D, distZ, false))
1613 trueIntersectComputed= true;
1617 // if true face-intersection not found
1618 if(!trueIntersectComputed)
1621 this happens especially for Forage Source. but could happens for anyhting else
1622 In this case, estimate face-instersection, with box:
1623 Suppose full intersection, if the ray is in the 1/3 of the bbox
1626 // clip the ray with the box
1627 CVector a= pos, b= pos+dir*distSelection;
1628 if(!bbox.clipSegment(a, b))
1629 continue;
1630 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1631 // This is false, but gives better results.
1632 CVector m= (a+b)/2;
1634 // Suppose full intersection, if the ray is in the 1/3 of the bbox
1635 CVector itToCenter= m-bbox.getCenter();
1636 itToCenter.maxof(itToCenter, -itToCenter);
1637 CVector smallBoxHS= bbox.getHalfSize()*0.3f;
1638 smallBoxHS.maxof(smallBoxHS, -smallBoxHS);
1639 if(itToCenter.x<=smallBoxHS.x && itToCenter.y<=smallBoxHS.y && itToCenter.z<=smallBoxHS.z)
1641 dist2D= 0;
1642 distZ= (m-pos).norm();
1644 else
1646 // no intersection
1647 dist2D= FLT_MAX;
1648 distZ= 0;
1651 // else it's ok, dist2D and distZ are computed
1654 // *** if intersect face, then take the best face-intersection, else use box-ray cost
1655 // true face-col found?
1656 if(dist2D==0)
1658 // yes, get the nearest
1659 if(distZ<bestDistZ)
1661 bestDistBox= 0;
1662 bestDistZ= distZ;
1663 entitySelected= entity;
1666 // else
1667 else
1669 // if a true face-intersection has not been found for others entities
1670 if(bestDistZ==FLT_MAX)
1672 // get the "distance to camera" contribution.
1673 CVector c= bbox.getCenter();
1674 float distCamCost= intersectedEntities[i].Depth;
1675 // get relative to the nearest intersected entity
1676 distCamCost-= startDistBox;
1678 // get the ratio "how many the ray is in the bbox"
1679 CVector a= pos, b= pos+dir*distSelection;
1680 bbox.clipSegment(a, b);
1681 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1682 // This is false, but gives better results.
1683 CVector m= (a+b)/2;
1684 // get the distance to center. NB: small entities are preferred since smaller mean lower cost
1685 float outBBoxCost= (m-c).norm();
1687 // the final cost is a weighted sum of the both. NB: distCamCost is in meter,
1688 // and outBBBoxCost is meters. Hence ClientCfg.SelectionOutBBoxWeight is a factor
1689 float boxCost= distCamCost + outBBoxCost * ClientCfg.SelectionOutBBoxWeight;
1691 // take the lowest cost
1692 if(boxCost<bestDistBox)
1694 entitySelected= entity;
1695 bestDistBox= boxCost;
1701 // If precise intersection not found
1702 if(bestDistZ==FLT_MAX)
1704 // if the last entity under pos is valid, prefer it among all other approximate ones
1705 if(precEntityUnderPos && precEntityUnderPosValid)
1706 entitySelected= precEntityUnderPos;
1709 // return the best entity
1710 _LastEntityUnderPos= entitySelected;
1711 return entitySelected;
1712 }// getEntityUnderPos //
1715 //-----------------------------------------------
1716 // getEntityInCamera
1717 //-----------------------------------------------
1718 CEntityCL *CEntityManager::getEntityInCamera(uint flags, float distSelection, CLFECOMMON::TCLEntityId precEntity)
1720 H_AUTO (RZ_Client_getEntityInCamera )
1722 // If not initialised, return
1723 if (_Entities.empty())
1724 return NULL;
1726 // list of valid entities
1727 static vector<CEntityCL*> validEntitiesTmp, validEntities;
1728 getEntityListForSelection(validEntitiesTmp, flags);
1730 // Remove entities not selectable by space key
1731 uint i;
1732 validEntities.clear();
1733 for (i=0 ; i<validEntitiesTmp.size() ; i++)
1735 CCharacterCL *entity = dynamic_cast<CCharacterCL*>(validEntitiesTmp[i]);
1736 if ((entity == NULL) || (entity && entity->isSelectableBySpace()))
1737 validEntities.push_back(entity);
1740 // Build the camera pyramid
1741 CMatrix camMatrix = MainCam.getMatrix();
1742 CFrustum camFrust = MainCam.getFrustum();
1743 static vector<CPlane> camPyramid;
1744 // No need to use worldMatrix. NB: not setuped if ClientLight.
1745 MainCam.buildCameraPyramid(camPyramid, false);
1747 // list of entities in screen
1748 static vector<CSortEntity> screenEntities;
1749 screenEntities.clear();
1751 // compute distance related to the user pos (not camera one).
1752 CVector userPos = UserEntity->pos();
1754 // prefer take the direction of the camera (can select backward for instance with camera rotation)
1755 CVector userDir = View.currentView().normed();
1757 // Get all entity in this pyramid, and in the dist selection
1758 for(i=0;i<validEntities.size();i++)
1760 CEntityCL *entity= validEntities[i];
1761 const NLMISC::CAABBox &b = entity->selectBox();
1762 bool isIn= true;
1763 for(uint j=0;j<camPyramid.size();j++)
1765 if( !b.clipBack(camPyramid[j]) )
1767 isIn= false;
1768 break;
1771 // if In the pyramid
1772 if(isIn)
1774 CVector dirToEntity= b.getCenter()-userPos;
1775 CSortEntity eSelect;
1776 eSelect.Entity= entity;
1777 eSelect.Depth= dirToEntity.norm();
1778 // if in max distance
1779 if(eSelect.Depth<distSelection)
1781 // The lower, the more the influence of direction (minimum should be 1)
1782 const float dirInfluence= 1.1f;
1784 // modulate the depth with dot3: force take the most in front of user.
1785 if(eSelect.Depth>0)
1786 dirToEntity/= eSelect.Depth;
1787 eSelect.Depth*= dirInfluence-dirToEntity*userDir;
1789 // append to sort list
1790 screenEntities.push_back(eSelect);
1795 // No one in screen?
1796 if(screenEntities.empty())
1797 return NULL;
1799 // sort them increasingly
1800 sort(screenEntities.begin(), screenEntities.end());
1802 // Try to find the precEntity in this list
1803 uint entitySelected= 0;
1804 if(precEntity!=CLFECOMMON::INVALID_SLOT)
1806 for(i=0;i<screenEntities.size();i++)
1808 // if found the precEntity, get the farther one
1809 if(screenEntities[i].Entity->slot()==precEntity)
1811 entitySelected= i+1;
1812 break;
1815 // reset to 0 if: no more entities, or if the max cycle is reached
1816 if(entitySelected>=screenEntities.size() || entitySelected>=ClientCfg.SpaceSelectionMaxCycle)
1817 entitySelected= 0;
1820 // found!
1821 return screenEntities[entitySelected].Entity;
1824 //-----------------------------------------------
1825 // changeContinent :
1826 // Continent has changed.
1827 //-----------------------------------------------
1828 void CEntityManager::changeContinent()
1830 // Re-create entities primitive.
1831 for(uint i=0; i<_NbMaxEntity; ++i)
1833 // Is the entity allocated.
1834 if(_Entities[i] == 0)
1835 continue;
1837 // Compute the new primitive.
1838 _Entities[i]->computePrimitive();
1840 // Compute the new collision entity.
1841 _Entities[i]->computeCollisionEntity();
1843 }// changeContinent //
1846 //-----------------------------------------------
1847 // updatePreCamera :
1848 // Update entites before the camera position is computed.
1849 // This update the entites position. Evaluate collisions. Compte final world position.
1850 //-----------------------------------------------
1851 void CEntityManager::updatePreCamera()
1853 H_AUTO ( RZ_Client_Entity_Mngr_Update_Pre_Cam )
1854 uint i;
1855 // Build an entity list..
1856 _ActiveEntities.reserve (_Entities.size ());
1857 _ActiveEntities.clear ();
1858 // Reset Counters
1859 resetCounters();
1860 // Update entities position.
1861 for(i=0; i<_NbMaxEntity; ++i)
1863 // Is the entity allocated.
1864 CEntityCL *entity = _Entities[i];
1865 if(entity == 0)
1866 continue;
1867 // Count Entities
1868 ++_EntitiesAllocated;
1869 switch(entity->Type)
1871 case CEntityCL::User:
1872 ++_NbUser; break;
1873 case CEntityCL::Player:
1874 ++_NbPlayer; break;
1875 /*case CEntityCL::NPC:
1876 case CEntityCL::Fauna:
1877 case CEntityCL::Entity:
1878 case CEntityCL::ForageSource:*/
1879 default:
1880 ++_NbChar; break;
1882 // Update the list of Active Entities
1883 _ActiveEntities.push_back (CEntityReference (i, entity));
1885 // Adjust the orientation of the NPC in trade with the user.
1886 if(UserEntity->trader() != CLFECOMMON::INVALID_SLOT)
1888 CEntityCL * trader = _Entities[UserEntity->trader()];
1889 if(trader)
1890 trader->front(UserEntity->pos() - trader->pos());
1892 // Adjust the orientation of the NPC in dyn chat with the user.
1893 if(UserEntity->interlocutor() != CLFECOMMON::INVALID_SLOT)
1895 CEntityCL * interlocutor = _Entities[UserEntity->interlocutor()];
1896 if(interlocutor)
1897 interlocutor->front(UserEntity->pos() - interlocutor->pos());
1900 // Update entities position except the User
1901 for(i=1; i<_EntitiesAllocated; ++i)
1903 CEntityReference &activeEntity = _ActiveEntities[i];
1905 // Get a poiner on the entity target
1906 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1908 // Update the entity.
1909 activeEntity.Entity->updatePreCollision(T1, target);
1911 // USER
1913 // Get a poiner on the entity target
1914 CEntityCL *target = entity(UserEntity->targetSlot());
1915 // update user behaviour/speed/heading/vectorUp/position/bodyHeading
1916 UserEntity->applyMotion(target);
1917 // Update the entity.
1918 UserEntity->updatePreCollision(T1, target);
1920 // Update PACS
1921 if(PACS)
1923 // Time since last Frame
1924 double DTEval = ((float)(T1-T0))*0.001f;
1925 PACS->evalCollision(DTEval, staticWI); // Eval the static world.
1926 PACS->evalCollision(DTEval, dynamicWI); // Eval the dynamic world.
1927 getDoorManager().getPACSTriggers(); // Copy triggers to be used in update
1928 managePACSTriggers();
1929 UserEntity->checkPos();
1931 // Update entities position.
1932 for(i=0; i<_EntitiesAllocated; ++i)
1934 CEntityReference &activeEntity = _ActiveEntities[i];
1935 // Get a poiner on the entity target
1936 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1937 // Update the entity.
1938 activeEntity.Entity->updatePostCollision(T1, target);
1940 // User Orientation
1941 UserEntity->applyForceLook();
1944 getDoorManager().update(); // Check for trigger to open/close doors
1946 MissionTargetObserver.update();
1948 }// updatePreCamera //
1951 //-----------------------------------------------
1952 // updatePostCamera :
1953 // Update the entity (position\animation).
1954 // Clip the primitives
1955 // Update visual entites parameters for clipped and non-clipped primitives
1956 // This update the entites position.
1957 //-----------------------------------------------
1958 void CEntityManager::updatePostCamera(uint clippedUpdateTime, const std::vector<CPlane> &clippingPlanes, const CVector &camPos)
1960 H_AUTO ( RZ_Client_Entity_Mngr_Update_Post_Cam )
1962 // Build a non clipped entity list..
1963 _VisibleEntities.reserve (_Entities.size ());
1964 _VisibleEntities.clear ();
1966 static bool firstTime = true;
1968 // Clip entities position.
1969 uint i;
1970 for(i=0; i<_EntitiesAllocated; ++i)
1972 CEntityReference &activeEntity = _ActiveEntities[i];
1974 // Get a poiner on the entity target
1975 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1977 // Clip it
1978 if (!activeEntity.Entity->clipped(clippingPlanes, camPos)
1979 || (R2::getEditor().getSelectedInstance() && R2::getEditor().getSelectedInstance()->getEntity()==activeEntity.Entity))
1981 // Add to visible primitives
1982 _VisibleEntities.push_back (activeEntity);
1983 activeEntity.Entity->setLastClip(false);
1984 activeEntity.Entity->updateVisible (T1, target);
1986 else
1988 activeEntity.Entity->setLastClip(true);
1989 if (firstTime)
1991 // Update texture Async Loading
1992 activeEntity.Entity->updateAsyncTexture();
1994 // Update lod Texture
1995 activeEntity.Entity->updateLodTexture();
1999 // Update this clipped primitive at this time ?
2000 if ((activeEntity.Slot&RZ_CLIPPED_UPDATE_TIME_MASK) == clippedUpdateTime)
2002 activeEntity.Entity->updateSomeClipped (T1, target);
2005 // Update clipped primitives
2006 activeEntity.Entity->updateClipped (T1, target);
2010 // Update visible entities post positions.
2011 const uint count = (uint)_VisibleEntities.size ();
2012 for(i=0; i<count; ++i)
2014 CEntityReference &visibleEntity = _VisibleEntities[i];
2015 // Get a poiner on the entity target
2016 CEntityCL *target = entity(visibleEntity.Entity->targetSlot());
2018 visibleEntity.Entity->updateVisiblePostPos(T1, target);
2021 // update ground fx
2022 _GroundFXManager.update(NLMISC::CVectorD(camPos));
2024 firstTime = false;
2025 }// updatePostCamera //
2027 //-----------------------------------------------
2028 // updatePostRender :
2029 // Update entites after the render 3D.
2030 //-----------------------------------------------
2031 void CEntityManager::updatePostRender()
2033 H_AUTO_USE ( RZ_Client_Update_Post_Render )
2035 TextContext->setHotSpot(UTextContext::MiddleMiddle);
2036 TextContext->setFontSize(ClientCfg.NameFontSize);
2037 CRGBA color;
2039 const uint activeCount = (uint)_ActiveEntities.size ();
2040 uint i;
2041 for(i=0; i<activeCount; i++)
2043 CEntityReference &visibleEntity = _ActiveEntities[i];
2045 // Update in-scene interface
2046 visibleEntity.Entity->updateAllPostRender ();
2049 const uint count = (uint)_VisibleEntities.size ();
2050 for(i=0; i<count; ++i)
2052 CEntityReference &visibleEntity = _VisibleEntities[i];
2053 // Update Visible Entities after the render.
2054 visibleEntity.Entity->updateVisiblePostRender();
2056 // Draw the entity Path.
2057 if(ClientCfg.ShowPath)
2058 visibleEntity.Entity->drawPath();
2059 // Draw the selection box.
2060 if(ClientCfg.DrawBoxes)
2061 visibleEntity.Entity->drawBox();
2062 // Display Modifiers (Dmgs/heals).
2063 if(1)
2064 visibleEntity.Entity->displayModifiers();
2067 // Flush any no more used Flying text. Must do it before interface display (to be sure texts are hid)
2068 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2069 pIM->FlyingTextManager.releaseNotUsedFlyingText();
2071 }// updatePostRender //
2074 //-----------------------------------------------
2075 // updateVisualProperty :
2076 // Method to update the visual property 'prop' for the entity in 'slot'.
2077 // \param uint slot : slot of the entity to update.
2078 // \param uint prop : the property to udapte.
2079 //-----------------------------------------------
2080 void CEntityManager::updateVisualProperty(const NLMISC::TGameCycle &gameCycle, const uint &slot, const uint &prop, const NLMISC::TGameCycle &predictedInterval)
2082 // INFO : log some debug information about visual properties.
2083 if(verboseVP(NULL))
2084 nlinfo("EM:updateVP: received prop '%d' for the slot '%d'.", prop, slot);
2086 // Check parameter : slot.
2087 if(slot >= _NbMaxEntity)
2089 nlwarning("CEntityManager::updateVisualProperty : Slot '%d' is not valid.", slot);
2090 return;
2093 // Entity still not allocated -> backup values received for the entity.
2094 if(_Entities[slot] == 0)
2096 // INFO : log some debug information about visual properties.
2097 if(verboseVP(NULL))
2098 nlinfo("EM:updateVP: backup the property as long as the entity is not allocated.", prop, slot);
2100 string propName = toString("SERVER:Entities:E%d:P%d", slot, prop);
2101 TProperty propty;
2102 propty.GC = gameCycle;
2103 propty.Value = 0;
2104 // propty.Value = IngameDbMngr.getProp(propName);
2107 TBackupedChanges::iterator it = _BackupedChanges.find(slot);
2108 // Entity does not have any changes backuped for the time.
2109 if(it == _BackupedChanges.end())
2111 TProperties propMap;
2112 propMap.insert(make_pair(prop, propty));
2113 _BackupedChanges.insert(make_pair(slot, propMap));
2115 // Entity already have some changes backuped.
2116 else
2118 TProperties &properties = (*it).second;
2119 TProperties::iterator itProp = properties.find(prop);
2120 // This properties is still not backuped for this entity.
2121 if(itProp == properties.end())
2122 properties.insert(make_pair(prop, propty));
2123 // There is already a backuped value
2124 else
2126 nlwarning("EM:updateVP:%d: property '%d' already backuped.", slot, prop);
2127 (*itProp).second = propty;
2131 // Entity already allocated -> apply values.
2132 else
2134 // Call the method from the entity to update the visual property.
2135 _Entities[slot]->updateVisualProperty(gameCycle, prop, predictedInterval);
2137 }// updateVisualProperty //
2139 //-----------------------------------------------
2140 // applyBackupedProperties :
2141 //-----------------------------------------------
2142 void CEntityManager::applyBackupedProperties(uint slot)
2144 TBackupedChanges::iterator it = _BackupedChanges.find(slot);
2145 if(it != _BackupedChanges.end())
2147 TProperties &properties = (*it).second;
2148 TProperties::iterator itProp = properties.begin();
2149 while(itProp != properties.end())
2151 _Entities[slot]->updateVisualProperty((*itProp).second.GC, (*itProp).first, 0);
2152 ++itProp;
2155 _BackupedChanges.erase(it);
2157 }// applyBackupedProperties //
2161 //-----------------------------------------------
2162 // writeEntities :
2163 // Write a file with the position of all entities.
2164 //-----------------------------------------------
2165 void CEntityManager::writeEntities()
2167 COFile f;
2168 if(!f.open("entities.txt", false, true))
2169 return;
2171 string strTmp = "StartCommands = {\n";
2172 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2174 const uint nb = (uint)_Entities.size();
2175 for(uint i=1; i<nb; ++i)
2177 if(_Entities[i])
2179 strTmp = toString("\"%s\",\t\"%f\", \"%f\", \"%f\", \"%f\", \"%f\", \"%f\",\t// %3d\n", _Entities[i]->sheetId().toString().c_str(), _Entities[i]->pos().x, _Entities[i]->pos().y, _Entities[i]->pos().z, _Entities[i]->front().x, _Entities[i]->front().y, _Entities[i]->front().z, i);
2180 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2184 strTmp = "};\n";
2185 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2187 // Close the File.
2188 f.close();
2189 }// writeEntities //
2191 //-----------------------------------------------
2192 // serial
2193 // Serialize entities.
2194 //-----------------------------------------------
2195 void CEntityManager::serial(NLMISC::IStream &f)
2197 // Get nb max entities possible.
2198 f.serial(_NbMaxEntity);
2199 if(f.isReading())
2201 release();
2202 initialize(_NbMaxEntity);
2205 // f.serial(_EntitiesAllocated); no need to serialize this one except maybe to check.
2207 // Serialize each entity.
2208 const uint nb = (uint)_Entities.size();
2209 for(uint i=0; i<nb; ++i)
2211 NLMISC::CSheetId si;
2212 if(!f.isReading())
2214 if(_Entities[i])
2215 si = _Entities[i]->sheetId();
2216 else
2217 si = NLMISC::CSheetId::Unknown;
2220 // ...
2221 f.serial(si);
2223 // Create the entity.
2224 if(f.isReading() && (si != CSheetId::Unknown))
2226 TNewEntityInfo emptyEntityInfo;
2227 emptyEntityInfo.reset();
2228 create(i, si.asInt(), emptyEntityInfo);
2231 // Get/Set entity state.
2232 if(_Entities[i])
2233 _Entities[i]->serial(f);
2235 }// serial //
2238 //-----------------------------------------------
2239 // dump :
2240 // // Dump entities state.
2241 //-----------------------------------------------
2242 void CEntityManager::dump(class NLMISC::IStream &f)
2244 // Serialize the class.
2245 serial(f);
2246 }// dump //
2248 //-----------------------------------------------
2249 // dumpXML :
2250 // Dump entities state (XML Format).
2251 //-----------------------------------------------
2252 void CEntityManager::dumpXML(class NLMISC::IStream &f)
2254 // Start the opening of a new node named Identity
2255 f.xmlPush("Entities");
2257 const uint nb = (uint)_Entities.size();
2258 for(uint i=0; i<nb; ++i)
2260 // Add a comment
2261 // f.xmlComment();//toString("Describ the entity in the slot %d.", i).c_str());
2262 // Start the opening of a new node named Identity
2263 f.xmlPush(toString("Entity%d", i));
2265 if(_Entities[i])
2267 // Open a new node header named Address
2268 f.xmlPushBegin("Name");
2269 // Set a property name
2270 f.xmlSetAttrib ("string");
2271 string n = _Entities[i]->getEntityName();
2272 f.serial(n);
2273 // Close the new node header
2274 f.xmlPushEnd();
2275 // Close the address node
2276 f.xmlPop();
2278 // Open a new node header named Address
2279 f.xmlPushBegin("Sheet");
2280 // Set a property name
2281 f.xmlSetAttrib ("name");
2282 string sheetName = _Entities[i]->sheetId().toString();
2283 f.serial(sheetName);
2284 // Close the new node header
2285 f.xmlPushEnd();
2286 // Close the address node
2287 f.xmlPop();
2289 // Open a new node header named Address
2290 f.xmlPushBegin("Position");
2291 // Close the new node header
2292 f.xmlPushEnd();
2293 f.serial(_Entities[i]->pos());
2294 // Close the address node
2295 f.xmlPop();
2297 // Open a new node header named Address
2298 f.xmlPushBegin("Front");
2299 // Close the new node header
2300 f.xmlPushEnd();
2301 NLMISC::CVector front = _Entities[i]->front();
2302 f.serial(front);
2303 // Close the address node
2304 f.xmlPop();
2306 // Open a new node header named Address
2307 f.xmlPushBegin("Mode");
2308 // Set a property name
2309 f.xmlSetAttrib ("name");
2310 string mode = MBEHAV::modeToString(_Entities[i]->mode());
2311 f.serial(mode);
2312 // Set a property name
2313 f.xmlSetAttrib ("num");
2314 uint8 m = _Entities[i]->mode();
2315 f.serial(m);
2316 // Close the new node header
2317 f.xmlPushEnd();
2318 // Close the address node
2319 f.xmlPop();
2321 // Open a new node header named Address
2322 f.xmlPushBegin("LastBehaviourPlayed");
2323 // Set a property name
2324 f.xmlSetAttrib ("name");
2325 string beh = MBEHAV::behaviourToString(_Entities[i]->behaviour());
2326 f.serial(beh);
2327 // Set a property name
2328 f.xmlSetAttrib ("num");
2329 uint8 b = _Entities[i]->behaviour();
2330 f.serial(b);
2331 // Close the new node header
2332 f.xmlPushEnd();
2333 // Close the address node
2334 f.xmlPop();
2337 // Close the address node
2338 f.xmlPop();
2341 // Close the identity node
2342 f.xmlPop();
2343 }// dumpXML //
2345 //-----------------------------------------------
2347 CEntityCL *CEntityManager::getEntityByName (uint32 stringId) const
2349 if (stringId)
2351 uint i;
2352 const uint count = (uint)_Entities.size();
2353 for (i=0; i<count; i++)
2355 if(_Entities[i])
2356 if(_Entities[i]->getNameId() == stringId)
2357 return _Entities[i];
2360 return NULL;
2363 //-----------------------------------------------
2364 CEntityCL *CEntityManager::getEntityByKeywords (const std::vector<string> &keywords, bool onlySelectable) const
2366 if (keywords.empty()) return NULL;
2368 std::vector<string> lcKeywords;
2369 lcKeywords.resize(keywords.size());
2370 for(uint k = 0; k < keywords.size(); k++)
2372 lcKeywords[k] = toLower(keywords[k]);
2375 const NLMISC::CVectorD &userPosD = UserEntity->pos();
2376 const uint count = (uint)_Entities.size();
2377 uint selectedEntityId = 0;
2378 float selectedEntityDist = FLT_MAX;
2379 for(uint i = 0; i < count; ++i)
2381 if (!_Entities[i]) continue;
2383 if (onlySelectable && !_Entities[i]->properties().selectable()) continue;
2385 string lcName = toLower(_Entities[i]->getDisplayName());
2386 if (lcName.empty()) continue;
2388 bool match = true;
2389 for (uint k = 0; k < lcKeywords.size(); ++k)
2391 if (lcName.find(lcKeywords[k]) == string::npos)
2393 match = false;
2394 break;
2398 if (match)
2400 const NLMISC::CVectorD &targetPosD = _Entities[i]->pos();
2402 float deltaX = (float) targetPosD.x - (float) userPosD.x;
2403 float deltaY = (float) targetPosD.y - (float) userPosD.y;
2404 float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY);
2405 if (dist < selectedEntityDist)
2407 selectedEntityDist = dist;
2408 selectedEntityId = i;
2413 if (selectedEntityDist != FLT_MAX)
2414 return _Entities[selectedEntityId];
2415 else
2416 return NULL;
2419 //-----------------------------------------------
2420 CEntityCL *CEntityManager::getEntityByName (const string &name, bool caseSensitive, bool complete) const
2422 string source;
2423 source = caseSensitive ? name : toLower(name); // TODO: toLowerInsensitive
2425 uint i;
2426 const uint count = (uint)_Entities.size();
2427 uint selectedEntityId = 0;
2428 float selectedEntityDist = FLT_MAX; // No selected Entity
2430 for (i=0; i<count; i++)
2432 if(_Entities[i])
2434 string value = caseSensitive ? _Entities[i]->getDisplayName() : toLower(_Entities[i]->getDisplayName()); // TODO: toLowerInsensitive
2435 bool foundEntity = false;
2437 // Complete test ?
2438 if (complete)
2440 if(value == source)
2441 foundEntity = true;
2443 else
2445 if (NLMISC::startsWith(value, source))
2446 foundEntity = true;
2449 if (foundEntity)
2451 const NLMISC::CVectorD &targetPosD = _Entities[i]->pos();
2452 const NLMISC::CVectorD &userPosD = UserEntity->pos();
2454 float deltaX = (float) targetPosD.x - (float) userPosD.x;
2455 float deltaY = (float) targetPosD.y - (float) userPosD.y;
2456 float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY);
2457 if (dist < selectedEntityDist)
2459 selectedEntityDist = dist;
2460 selectedEntityId = i;
2465 if (selectedEntityDist != FLT_MAX) // Entity found
2466 return _Entities[selectedEntityId];
2467 else
2468 return NULL;
2471 //-----------------------------------------------
2473 CEntityCL *CEntityManager::getEntityByCompressedIndex(TDataSetIndex compressedIndex) const
2475 if (compressedIndex != INVALID_DATASET_ROW)
2477 uint i;
2478 const uint count = (uint)_Entities.size();
2479 for (i=0; i<count; i++)
2481 if(_Entities[i])
2482 if(_Entities[i]->dataSetId() == compressedIndex)
2483 return _Entities[i];
2486 return NULL;
2488 //-----------------------------------------------
2489 // getEntityBySheetName :
2490 // Return an entity based on its sheet name
2491 //-----------------------------------------------
2492 CEntityCL *CEntityManager::getEntityBySheetName (const std::string &sheet) const
2494 if (!sheet.empty())
2496 uint i;
2497 const CSheetId& sheetRef = NLMISC::CSheetId(sheet);
2498 const uint count = (uint)_Entities.size();
2499 for (i=0; i<count; i++)
2501 if(_Entities[i])
2502 if(_Entities[i]->sheetId() == sheetRef)
2503 return _Entities[i];
2506 return NULL;
2508 //-----------------------------------------------
2509 // managePACSTriggers :
2510 // Manage PACS Triggers.
2511 //-----------------------------------------------
2512 void CEntityManager::managePACSTriggers()
2514 uint i;
2515 const uint nNbTrig = PACS->getNumTriggerInfo();
2516 for(i=0; i<nNbTrig; ++i)
2518 const NLPACS::UTriggerInfo &rTI = PACS->getTriggerInfo(i);
2519 // Detect collisions between user and other entities, to not be block (only the user is a trigger so no need to check).
2520 if(((rTI.Object0 & 0xFFFF) == UserDataEntity)
2521 && ((rTI.Object1 & 0xFFFF) == UserDataEntity))
2523 UserEntity->startColTimer();
2524 break;
2527 // Stop Collision.
2528 if(i >= nNbTrig)
2529 UserEntity->stopColTimer();
2530 }// managePACSTriggers //
2533 //-----------------------------------------------
2534 // removeColUserOther :
2536 //-----------------------------------------------
2537 void CEntityManager::removeColUserOther()
2539 uint i;
2540 const uint count = (uint)_Entities.size();
2541 for(i=1; i<count; i++)
2543 if(_Entities[i])
2545 if(_Entities[i]->getPrimitive())
2547 // remove collision only if the entity is Traversable (bot objects may not)
2548 if(_Entities[i]->getTraversable())
2549 _Entities[i]->getPrimitive()->setObstacle(false);
2553 }// removeColUserOther //
2555 //-----------------------------------------------
2556 // restoreColUserOther :
2558 //-----------------------------------------------
2559 void CEntityManager::restoreColUserOther()
2561 uint i;
2562 const uint count = (uint)_Entities.size();
2563 for(i=1; i<count; i++)
2565 if(_Entities[i])
2567 if(_Entities[i]->getPrimitive())
2568 _Entities[i]->getPrimitive()->setObstacle(true);
2571 }// restoreColUserOther //
2573 //-----------------------------------------------
2574 void CEntityManager::removeAllAttachedFX()
2576 for(TEntities::iterator it = _Entities.begin(); it != _Entities.end(); ++it)
2578 if (*it) (*it)->removeAllAttachedFX();
2583 // ***************************************************************************
2584 void CEntityManager::resetAllSoundAnimId()
2586 for(uint i=0;i<_Entities.size();i++)
2588 CEntityCL *ent= _Entities[i];
2589 if(ent)
2591 ent->resetAllSoundAnimId();
2596 // ***************************************************************************
2597 #define nldebugraw NLMISC::createDebug(), NLMISC::DebugLog->displayRawNL
2599 // ***************************************************************************
2600 void CEntityManager::startLogStageChange(sint32 currentGameCycle, sint64 currentLocalTime)
2602 // first stop
2603 stopLogStageChange();
2605 // enable
2606 _LogStageChange.Enabled= true;
2607 _LogStageChange.StartGameCycle= currentGameCycle;
2608 _LogStageChange.StartLocalTime= currentLocalTime;
2609 _LogStageChange.LastEntityLoged= CLFECOMMON::INVALID_SLOT;
2610 _LogStageChange.StageSet.clear();
2611 nldebugraw("*** Start Loging Stage changes");
2615 // ***************************************************************************
2616 void CEntityManager::logStageChange(sint64 currentLocalTime)
2618 if(!_LogStageChange.Enabled)
2619 return;
2621 // if still exist
2622 CCharacterCL *ent= dynamic_cast<CCharacterCL*>(entity(WatchedEntitySlot));
2623 if(ent)
2625 // if the watched entity has been changed
2626 if(WatchedEntitySlot!=_LogStageChange.LastEntityLoged)
2628 _LogStageChange.LastEntityLoged= WatchedEntitySlot;
2629 // backup set
2630 _LogStageChange.StageSet= ent->_Stages._StageSet;
2631 nldebugraw("*** Start Loging Stage changes for Entity %d", WatchedEntitySlot);
2633 else
2635 // can log it
2636 sint32 recGcRef= _LogStageChange.StartGameCycle;
2637 sint64 recTimeRef= _LogStageChange.StartLocalTime;
2638 // compare 2 logs and display differences
2639 CStageSet::TStageSet &oldStageSet= _LogStageChange.StageSet;
2640 const CStageSet::TStageSet &newStageSet= ent->_Stages._StageSet;
2642 // for pos log detail
2643 CVectorD precNewPos= ent->pos();
2644 CVectorD precOldPos= ent->pos();
2646 // compare each new/old stage
2647 CStageSet::TStageSet::const_iterator itOld= oldStageSet.begin();
2648 CStageSet::TStageSet::const_iterator itNew= newStageSet.begin();
2649 while(itOld!=oldStageSet.end() || itNew!=newStageSet.end())
2651 // compare 2 iterators
2652 sint signNewMinusOld;
2653 if(itNew==newStageSet.end())
2654 signNewMinusOld= +1;
2655 else if(itOld==oldStageSet.end())
2656 signNewMinusOld= -1;
2657 else
2659 if(itNew->first > itOld->first)
2660 signNewMinusOld= +1;
2661 else if(itNew->first < itOld->first)
2662 signNewMinusOld= -1;
2663 else
2664 signNewMinusOld= 0;
2667 // if signNewMinusOld= +1, it means an old exist, without a new (=> the stage has been removed)
2668 if(signNewMinusOld==+1)
2670 logPropertyChange(WatchedEntitySlot, itOld->second, CStage(), precOldPos, precNewPos, (sint32)itOld->first-recGcRef, currentLocalTime-recTimeRef);
2671 // new prec pos (if any)
2672 itOld->second.getPos(precOldPos);
2673 itOld++;
2675 // if signNewMinusOld= -1, it means an new exist, without an old (=> the stage has been added)
2676 else if(signNewMinusOld==-1)
2678 logPropertyChange(WatchedEntitySlot, CStage(), itNew->second, precOldPos, precNewPos, (sint32)itNew->first-recGcRef, currentLocalTime-recTimeRef);
2679 // new prec pos (if any)
2680 itNew->second.getPos(precNewPos);
2681 itNew++;
2683 // if ==0, means the stage exist in both, but properties set may be different
2684 else
2686 logPropertyChange(WatchedEntitySlot, itOld->second, itNew->second, precOldPos, precNewPos, (sint32)itNew->first-recGcRef, currentLocalTime-recTimeRef);
2687 // new prec pos (if any)
2688 itOld->second.getPos(precOldPos);
2689 itNew->second.getPos(precNewPos);
2690 itOld++;
2691 itNew++;
2695 // bkup the new stage set
2696 oldStageSet= newStageSet;
2699 // this entity might have been deleted, stop its log
2700 else
2702 _LogStageChange.LastEntityLoged= CLFECOMMON::INVALID_SLOT;
2703 _LogStageChange.StageSet.clear();
2707 // ***************************************************************************
2708 void CEntityManager::logPropertyChange(CLFECOMMON::TCLEntityId who, const CStage &oldStage, const CStage &newStage,
2709 const CVectorD &precOldPos, const CVectorD &precNewPos, sint32 relGameCycle, sint64 relLocalTime)
2711 // For all properties of interest
2712 CLFECOMMON::TPropIndex propLoged[]= {CLFECOMMON::PROPERTY_POSITION, CLFECOMMON::PROPERTY_ORIENTATION,
2713 CLFECOMMON::PROPERTY_MODE, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID,
2714 CLFECOMMON::PROPERTY_BEHAVIOUR, CLFECOMMON::PROPERTY_TARGET_ID,
2715 /*CLFECOMMON::PROPERTY_VISUAL_FX,
2716 CLFECOMMON::PROPERTY_TARGET_LIST_0, CLFECOMMON::PROPERTY_TARGET_LIST_1,
2717 CLFECOMMON::PROPERTY_TARGET_LIST_2, CLFECOMMON::PROPERTY_TARGET_LIST_3*/};
2718 uint32 numProps= sizeof(propLoged) / sizeof(propLoged[0]);
2719 for(uint i=0;i<numProps;i++)
2721 pair<bool, sint64> oldProp= oldStage.property(propLoged[i]);
2722 pair<bool, sint64> newProp= newStage.property(propLoged[i]);
2723 // if change of the prop, log it
2724 if((oldProp.first || newProp.first) && oldProp!=newProp)
2726 // get the change reason
2727 string reason;
2728 if(!oldProp.first)
2729 reason= "ADD";
2730 else if(!newProp.first)
2731 reason= "DEL";
2732 else
2733 reason= "CHG";
2735 // get the value
2736 sint64 value= newProp.second;
2737 if(!newProp.first) value= oldProp.second;
2738 string valStr;
2739 // mode?
2740 if(propLoged[i]==CLFECOMMON::PROPERTY_MODE)
2742 valStr= MBEHAV::TMode((uint64)value).toString();
2744 // behaviour
2745 else if(propLoged[i]==CLFECOMMON::PROPERTY_BEHAVIOUR)
2747 valStr= MBEHAV::CBehaviour((uint64)value).toString();
2749 // mount
2750 else if(propLoged[i]==CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID)
2752 valStr= NLMISC::toString(value);
2754 else if(propLoged[i]==CLFECOMMON::PROPERTY_RIDER_ENTITY_ID)
2756 valStr= NLMISC::toString(value);
2758 // Target
2759 else if(propLoged[i]==CLFECOMMON::PROPERTY_TARGET_ID)
2761 valStr= NLMISC::toString(value);
2763 // Position
2764 else if(propLoged[i]==CLFECOMMON::PROPERTY_POSITION)
2766 // get the delta of move from previous pos stage
2767 CVectorD pos;
2768 float dist= 0.f;
2769 if(newProp.first)
2771 if(newStage.getPos(pos))
2772 dist= float(CVectorD(pos.x-precNewPos.x, pos.y-precNewPos.y,0).norm());
2773 valStr= toString("dst=%.1f pi=%d", dist, newStage.predictedInterval());
2775 else
2777 if(oldStage.getPos(pos))
2778 dist= float(CVectorD(pos.x-precOldPos.x, pos.y-precOldPos.y,0).norm());
2779 valStr= toString("dst=%.1f pi=%d", dist, oldStage.predictedInterval());
2782 // Orientation
2783 else if(propLoged[i]==CLFECOMMON::PROPERTY_ORIENTATION)
2785 C64BitsParts rot;
2786 rot.i64[0] = value;
2788 valStr= toString(sint32(rot.f[0]*180/Pi));
2792 // display log
2793 nldebugraw("** Entity %d: (gc=%3d,t=%3d) %s: %s %s", (sint32)who, relGameCycle, (sint32)relLocalTime, reason.c_str(), CLFECOMMON::getPropShortText(propLoged[i]), valStr.c_str());
2799 // ***************************************************************************
2800 void CEntityManager::stopLogStageChange()
2802 _LogStageChange.Enabled= false;
2803 nldebugraw("*** Stop Loging Stage changes");
2806 // ***************************************************************************
2807 bool CEntityManager::isLogingStageChange() const
2809 return _LogStageChange.Enabled;
2812 // ***************************************************************************
2813 sint32 CEntityManager::getLogStageChangeStartCycle() const
2815 if(isLogingStageChange())
2816 return _LogStageChange.StartGameCycle;
2817 else
2818 return 0;
2821 // ***************************************************************************
2822 sint64 CEntityManager::getLogStageChangeStartLocalTime() const
2824 if(isLogingStageChange())
2825 return _LogStageChange.StartLocalTime;
2826 else
2827 return 0;
2830 // ***************************************************************************
2831 void CEntityManager::refreshInsceneInterfaceOfFriendNPC(uint slot)
2833 CCharacterCL *entity = dynamic_cast<CCharacterCL*>(_Entities[slot]);
2834 if (!entity)
2835 return;
2836 if (entity->canHaveMissionIcon()
2837 && entity->isFriend() // only valid once the Contextual property is received
2840 entity->releaseInSceneInterfaces();
2841 entity->buildInSceneInterface();