Add infos into target window
[ryzomcore.git] / ryzom / client / src / entities.cpp
blob7f4172f11b2f490c55107f87c21b491f1913aa6e
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 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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 if (_ShapeInstances[idx].Primitive)
560 PACS->removePrimitive(_ShapeInstances[idx].Primitive);
562 _ShapeInstances[idx].Primitive = primitive;
563 _ShapeInstances[idx].ContextText = text;
564 _ShapeInstances[idx].ContextURL = url;
565 _ShapeInstances[idx].BboxActive = !text.empty() || !url.empty();
566 _ShapeInstances[idx].Deleted = false;
567 _ShapeInstances[idx].InIGZone = inIgZone > 0;
569 _LastRemovedInstance = _ShapeInstances[idx].LastDeleted;
570 _ShapeInstances[idx].LastDeleted = -1;
571 TIGZoneShapes::iterator it = _IgZoneShapes.find(inIgZone);
572 if (it == _IgZoneShapes.end())
574 vector<uint32> shapes;
575 shapes.push_back(idx);
576 _IgZoneShapes.insert(make_pair(inIgZone, shapes));
578 else
580 vector<uint32> &shapes = (*it).second;
581 shapes.push_back(idx);
583 return _ShapeInstances[idx];
585 else
587 CShapeInstanceReference instref = CShapeInstanceReference(instance, text, url, !text.empty() || !url.empty(), inIgZone > 0);
588 instref.Primitive = primitive;
589 idx = _ShapeInstances.size();
590 _ShapeInstances.push_back(instref);
591 TIGZoneShapes::iterator it = _IgZoneShapes.find(inIgZone);
592 if (it == _IgZoneShapes.end())
594 vector<uint32> shapes;
595 shapes.push_back(idx);
596 _IgZoneShapes.insert(make_pair(inIgZone, shapes));
598 else
600 vector<uint32> &shapes = (*it).second;
601 shapes.push_back(idx);
603 return instref;
606 return nullinstref;
609 bool CEntityManager::deleteInstance(uint32 idx, bool force)
611 if (!Scene || idx >= _ShapeInstances.size())
612 return false;
614 if (!force && _ShapeInstances[idx].InIGZone)
615 return true;
617 if (!_ShapeInstances[idx].Instance.empty())
618 Scene->deleteInstance(_ShapeInstances[idx].Instance);
620 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
621 if (primitive)
623 PACS->removePrimitive(primitive);
624 _ShapeInstances[idx].Primitive = NULL;
627 if (!_ShapeInstances[idx].Deleted)
629 _ShapeInstances[idx].Deleted = true;
630 _ShapeInstances[idx].LastDeleted = _LastRemovedInstance;
631 _LastRemovedInstance = idx;
634 return true;
637 void CEntityManager::removeInstancesInIgZone(uint16 igZone)
639 if (!Scene)
640 return;
642 TIGZoneShapes::iterator it = _IgZoneShapes.find(igZone);
643 if (it != _IgZoneShapes.end())
645 vector<uint32> &shapes = (*it).second;
646 for (uint i = 0; i < shapes.size(); i++)
647 deleteInstance(shapes[i], true);
648 _IgZoneShapes.erase(it);
653 CVector CEntityManager::getInstancePos(uint32 idx)
655 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
656 return CVector(0,0,0);
658 UInstance instance = _ShapeInstances[idx].Instance;
659 if(instance.empty())
660 return CVector(0,0,0);
662 return instance.getPos();
665 bool CEntityManager::setInstancePos(uint32 idx, CVector pos)
667 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
668 return false;
670 UInstance instance = _ShapeInstances[idx].Instance;
671 if(instance.empty())
672 return false;
674 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
675 if (primitive)
677 primitive->setGlobalPosition(_ShapeInstances[idx].PrimRelativePos + pos, dynamicWI);
680 instance.setPos(pos);
681 return true;
684 CVector CEntityManager::getInstanceRot(uint32 idx)
686 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
687 return CVector(0,0,0);
689 UInstance instance = _ShapeInstances[idx].Instance;
690 if(instance.empty())
691 return CVector(0,0,0);
693 return instance.getRotEuler();
696 bool CEntityManager::setInstanceRot(uint32 idx, CVector rot)
698 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
699 return false;
701 UInstance instance = _ShapeInstances[idx].Instance;
702 if(instance.empty())
703 return false;
705 instance.setRotEuler(rot);
707 return true;
710 CVector CEntityManager::getInstanceScale(uint32 idx)
712 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
713 return CVector(0,0,0);
715 UInstance instance = _ShapeInstances[idx].Instance;
716 if(instance.empty())
717 return CVector(0,0,0);
719 return instance.getScale();
722 CVector CEntityManager::getInstanceColPos(uint32 idx)
724 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
725 return CVector(0,0,0);
727 return _ShapeInstances[idx].PrimRelativePos;
730 CVector CEntityManager::getInstanceColScale(uint32 idx)
732 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
733 return CVector(0,0,0);
735 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
736 if (!primitive)
737 return CVector(0,0,0);
739 float width, depth;
740 primitive->getSize(width, depth);
741 float height = primitive->getHeight();
743 return CVector(width, depth, height);
746 double CEntityManager::getInstanceColOrient(uint32 idx)
748 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
749 return 0.f;
751 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
752 if (!primitive)
753 return 0.f;
755 return primitive->getOrientation(dynamicWI);
758 CVector CEntityManager::getInstanceBBoxMin(uint32 idx)
760 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
761 return CVector(0,0,0);
763 UInstance instance = _ShapeInstances[idx].Instance;
764 if (instance.empty())
765 return CVector(0,0,0);
767 NLMISC::CAABBox bbox;
768 _ShapeInstances[idx].Instance.getShapeAABBox(bbox);
770 CVector bbox_min;
772 if (bbox.getCenter() == CVector::Null)
773 bbox_min = CVector(-0.5f, -0.5f, -0.5f);
774 else
775 bbox_min = bbox.getMin();
777 bbox_min.x *= _ShapeInstances[idx].Instance.getScale().x;
778 bbox_min.y *= _ShapeInstances[idx].Instance.getScale().y;
779 bbox_min.z *= _ShapeInstances[idx].Instance.getScale().z;
781 return bbox_min+_ShapeInstances[idx].Instance.getPos();
784 CVector CEntityManager::getInstanceBBoxMax(uint32 idx)
786 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
787 return CVector(0,0,0);
789 UInstance instance = _ShapeInstances[idx].Instance;
790 if(instance.empty())
791 return CVector(0,0,0);
793 NLMISC::CAABBox bbox;
794 _ShapeInstances[idx].Instance.getShapeAABBox(bbox);
796 CVector bbox_max;
798 if (bbox.getCenter() == CVector::Null)
799 bbox_max = CVector(-0.5f, -0.5f, -0.5f);
800 else
801 bbox_max = bbox.getMax();
803 bbox_max.x *= _ShapeInstances[idx].Instance.getScale().x;
804 bbox_max.y *= _ShapeInstances[idx].Instance.getScale().y;
805 bbox_max.z *= _ShapeInstances[idx].Instance.getScale().z;
807 return bbox_max+_ShapeInstances[idx].Instance.getPos();
810 bool CEntityManager::removeInstances()
812 if (!Scene) return false;
814 for(uint i=0; i<_ShapeInstances.size(); ++i)
816 if (!_ShapeInstances[i].InIGZone)
817 deleteInstance(i);
819 return true;
822 bool CEntityManager::setupInstance(uint32 idx, const vector<string> &keys, const vector<string> &values)
824 if (!Scene || idx >= _ShapeInstances.size() || _ShapeInstances[idx].Deleted)
825 return false;
827 UInstance instance = _ShapeInstances[idx].Instance;
828 if(instance.empty())
829 return false;
831 UMovePrimitive *primitive = _ShapeInstances[idx].Primitive;
833 for (uint32 i=0; i < keys.size(); i++)
835 string param = keys[i];
836 if (param == "transparency")
838 uint t;
839 if (fromString(values[i], t))
841 t = max(0, min((int)t, 255));
842 makeInstanceTransparent(instance, t, t == 255);
845 else if (param == "colorize")
847 if (values[i] == "0")
849 for(uint j=0;j<instance.getNumMaterials();j++)
851 instance.getMaterial(j).setShininess( 10.0f );
852 instance.getMaterial(j).setEmissive(CRGBA(255,255,255,255));
853 instance.getMaterial(j).setAmbient(CRGBA(0,0,0,255));
854 instance.getMaterial(j).setDiffuse(CRGBA(255,255,255,255));
857 else
859 CRGBA c;
860 if( fromString( values[i], c ) )
862 for(uint j=0;j<instance.getNumMaterials();j++)
864 instance.getMaterial(j).setShininess( 1000.0f );
865 instance.getMaterial(j).setEmissive(c);
866 instance.getMaterial(j).setAmbient(c);
867 instance.getMaterial(j).setDiffuse(c);
872 else if (param == "texture")
874 if (!values[i].empty())
876 for(uint j=0;j<instance.getNumMaterials();j++)
878 sint numStages = instance.getMaterial(j).getLastTextureStage() + 1;
879 for(sint l = 0; l < numStages; l++)
881 if (instance.getMaterial(j).isTextureFile((uint) l))
882 instance.getMaterial(j).setTextureFileName(values[i], (uint) l);
887 else if (param == "skeleton")
889 // TODO
891 else if (param == "context")
893 _ShapeInstances[idx].ContextText = values[i];
895 else if (param == "url")
897 _ShapeInstances[idx].ContextURL = values[i];
899 else if (param == "move x" || param == "move y" || param == "move z")
901 float v;
902 CVector pos = getInstancePos(idx);
904 if (getRelativeFloatFromString(values[i], v))
906 updateVector(param, pos, v, true);
908 else
910 updateVector(param, pos, v, false);
912 setInstancePos(idx, pos);
914 else if (param == "rot x" || param == "rot y" || param == "rot z")
917 float v;
918 CVector rot = getInstanceRot(idx);
920 if (getRelativeFloatFromString(values[i], v))
922 updateVector(param, rot, v, true);
924 else
926 updateVector(param, rot, v, false);
928 setInstanceRot(idx, rot);
930 else if (param == "scale x" || param == "scale y" || param == "scale z")
932 float v;
933 CVector scale = instance.getScale();
935 if (getRelativeFloatFromString(values[i], v))
937 updateVector(param, scale, v, true);
939 else
941 updateVector(param, scale, v, false);
943 instance.setScale(scale);
946 // Primitive colissions setups
948 if (!primitive) continue;
950 if (param == "col size x" || param == "col size y" || param == "col size z")
952 float width, depth;
953 primitive->getSize(width, depth);
954 float height = primitive->getHeight();
956 CVector size = CVector(width, depth, height);
957 float v;
958 if (getRelativeFloatFromString(values[i], v))
960 updateVector(param, size, v, true);
962 else
964 updateVector(param, size, v, false);
966 primitive->setSize(size.x, size.y);
967 primitive->setHeight(size.z);
969 else if (param == "col pos x" || param == "col pos y" || param == "col pos z")
971 CVector pos = instance.getPos();
972 float v;
974 if (getRelativeFloatFromString(values[i], v))
976 updateVector(param, _ShapeInstances[idx].PrimRelativePos, v, false);
978 else
980 if (param == "col pos x")
981 _ShapeInstances[idx].PrimRelativePos.x = v - pos.x;
982 if (param == "col pos y")
983 _ShapeInstances[idx].PrimRelativePos.y = v - pos.y;
984 if (param == "col pos z")
985 _ShapeInstances[idx].PrimRelativePos.z = v - pos.z;
987 primitive->setGlobalPosition(pos + _ShapeInstances[idx].PrimRelativePos, dynamicWI);
989 else if (param == "col orientation")
991 double orient = primitive->getOrientation(dynamicWI);
992 double v = 0.f;
994 if (values[i].empty())
995 continue;
997 if (values[i][0] == '+')
999 fromString(values[i].substr(1), v);
1000 orient += v;
1002 else
1004 fromString(values[i], v);
1005 orient = v;
1008 primitive->setOrientation(orient, dynamicWI);
1010 else if (param == "col mask player")
1012 bool active;
1013 fromString(values[i], active);
1014 UMovePrimitive::TCollisionMask mask=primitive->getCollisionMask();
1015 if (active)
1016 primitive->setCollisionMask(mask|MaskColPlayer);
1017 else
1018 primitive->setCollisionMask(mask&~MaskColPlayer);
1020 else if (param == "col mask door")
1022 bool active;
1023 fromString(values[i], active);
1024 UMovePrimitive::TCollisionMask mask=primitive->getCollisionMask();
1025 if (active)
1026 primitive->setCollisionMask(mask|MaskColDoor);
1027 else
1028 primitive->setCollisionMask(mask&~MaskColDoor);
1030 else if (param == "col obstacle")
1032 bool active;
1033 fromString(values[i], active);
1034 primitive->setObstacle(active);
1036 else if (param == "col obstacle")
1042 return true;
1046 CShapeInstanceReference CEntityManager::getShapeInstanceUnderPos(float x, float y, sint32 &idx)
1048 CShapeInstanceReference selectedInstance(UInstance(), string(""), string(""));
1049 _LastInstanceUnderPos= NULL;
1050 idx = -1;
1052 // If not initialised, return
1053 if (_ShapeInstances.empty())
1054 return selectedInstance;
1056 // build the ray
1057 CMatrix camMatrix = MainCam.getMatrix();
1058 CFrustum camFrust = MainCam.getFrustum();
1059 CViewport viewport = Driver->getViewport();
1061 // Get the Ray made by the mouse.
1062 CVector pos, dir;
1063 viewport.getRayWithPoint(x, y, pos, dir, camMatrix, camFrust);
1064 // Normalize the direction.
1065 dir.normalize();
1067 // **** Get instances with box intersecting the ray.
1068 float bestDist = 255;
1069 for(uint i=0; i<_ShapeInstances.size(); i++)
1071 if (!_ShapeInstances[i].Deleted && _ShapeInstances[i].BboxActive)
1073 H_AUTO(RZ_Client_GEUP_box_intersect)
1075 // if intersect the bbox
1076 NLMISC::CAABBox bbox;
1077 //= _ShapeInstances[i].SelectionBox;
1078 if(!_ShapeInstances[i].Instance.empty())
1080 _ShapeInstances[i].Instance.getShapeAABBox(bbox);
1081 CVector bbox_min;
1082 CVector bbox_max;
1084 if (bbox.getCenter() == CVector::Null)
1086 bbox_min = CVector(-0.5f, -0.5f, -0.5f);
1087 bbox_max = CVector(-0.5f, -0.5f, -0.5f);
1089 else
1091 bbox_min = bbox.getMin();
1092 bbox_max = bbox.getMax();
1095 bbox_min.x *= _ShapeInstances[i].Instance.getScale().x;
1096 bbox_min.y *= _ShapeInstances[i].Instance.getScale().y;
1097 bbox_min.z *= _ShapeInstances[i].Instance.getScale().z;
1099 bbox_max.x *= _ShapeInstances[i].Instance.getScale().x;
1100 bbox_max.y *= _ShapeInstances[i].Instance.getScale().y;
1101 bbox_max.z *= _ShapeInstances[i].Instance.getScale().z;
1104 bbox.setMinMax(bbox_min+_ShapeInstances[i].Instance.getPos(), bbox_max+_ShapeInstances[i].Instance.getPos());
1106 if(bbox.intersect(pos, pos+dir*100.0f))
1108 float dist = (bbox.getCenter()-pos).norm();
1109 if (dist < bestDist)
1111 selectedInstance = _ShapeInstances[i];
1112 bestDist = dist;
1113 idx = (sint32)i;
1119 return selectedInstance;
1123 //-----------------------------------------------
1124 // Create an entity according to the slot and the form.
1125 // \param uint slot : slot for the entity.
1126 // \param uint32 form : form to create the entity.
1127 // \param TClientDataSetIndex : persitent id while the entity is connected.
1128 // \return CEntityCL * : pointer on the new entity.
1129 //-----------------------------------------------
1130 CEntityCL *CEntityManager::create(uint slot, uint32 form, const TNewEntityInfo& newEntityInfo)
1132 // DEBUG
1133 if(verboseVP(NULL, form))
1134 nlinfo("(%05d,%03d) EM:create: slot '%u': %s", sint32(T1%100000), NetMngr.getCurrentServerTick(), slot, CSheetId(form).toString().c_str());
1135 // Check parameter : slot.
1136 if(slot >= _NbMaxEntity)
1138 nlwarning("EM:create: Cannot create the entity, the slot '%u' is invalid.", slot);
1139 return 0;
1141 else
1143 // Slot 0 is for the user and so should be allocated only once (at beginning of main loop).
1144 if( slot == 0 && _Entities[0] )
1146 if (newEntityInfo.DataSetIndex != CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
1148 // Store the dataSetId received
1149 _Entities[0]->dataSetId(newEntityInfo.DataSetIndex);
1151 // Store the alias (although there should not be one for the slot 0!)
1152 _Entities[0]->npcAlias(newEntityInfo.Alias);
1153 return 0;
1157 // Remove the old one (except the user).
1158 if(_Entities[slot])
1160 nlwarning("EM:create: There is already an entity in the slot '%u' ! Old entity will be removed.", slot);
1161 // remove from ground fx manager
1162 // TODO : test if entity has ground fxs
1163 if (_Entities[slot]->supportGroundFX())
1165 _GroundFXManager.remove(_EntityGroundFXHandle[slot]);
1167 delete _Entities[slot];
1168 _Entities[slot] = 0;
1171 // Check parameter : form.
1172 CEntitySheet *entitySheet = SheetMngr.get((CSheetId)form);
1173 if(entitySheet == 0)
1175 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);
1176 CSheetId defaultEntity;
1177 if(defaultEntity.buildSheetId(ClientCfg.DefaultEntity)==false)
1179 nlwarning("EM:create: The default entity (%s) is not in the sheetid.bin.", ClientCfg.DefaultEntity.c_str());
1180 return 0;
1182 entitySheet = SheetMngr.get(defaultEntity);
1183 if(entitySheet == 0)
1185 nlwarning("EM:create: The default entity (%s) is not in the sheet manager.", ClientCfg.DefaultEntity.c_str());
1186 return 0;
1190 // Create the entity according to the type.
1193 switch(entitySheet->type())
1195 case CEntitySheet::RACE_STATS:
1196 case CEntitySheet::CHAR:
1197 if (slot == 0)
1199 nlassert (UserEntity == NULL);
1200 UserEntity = new CUserEntity;
1201 _Entities[slot] = UserEntity;
1203 else
1205 _Entities[slot] = new CPlayerCL;
1207 break;
1209 case CEntitySheet::FAUNA:
1211 CCharacterSheet *sheet = NLMISC::safe_cast<CCharacterSheet *>(entitySheet);
1212 if (!sheet->R2Npc) _Entities[slot] = new CCharacterCL;
1213 else _Entities[slot] = new CPlayerR2CL;
1215 break;
1216 case CEntitySheet::FLORA:
1217 _Entities[slot] = new CCharacterCL;
1218 break;
1220 case CEntitySheet::FX:
1221 _Entities[slot] = new CFxCL;
1222 break;
1224 case CEntitySheet::ITEM:
1225 _Entities[slot] = new CItemCL;
1226 break;
1228 case CEntitySheet::FORAGE_SOURCE:
1229 _Entities[slot] = new CForageSourceCL;
1230 break;
1232 default:
1233 pushDebugStr(NLMISC::toString("Unknown Form Type '%d' -> entity not created.", entitySheet->type()));
1234 break;
1237 // If the entity has been right created.
1238 if(_Entities[slot])
1240 // Set the sheet Id.
1241 _Entities[slot]->sheetId((CSheetId)form);
1242 // Set the slot.
1243 _Entities[slot]->slot(slot);
1244 // Set the DataSet Index. AFTER slot(), so bar manager is correctly init
1245 _Entities[slot]->dataSetId(newEntityInfo.DataSetIndex);
1246 // Set the Mission Giver Alias
1247 _Entities[slot]->npcAlias(newEntityInfo.Alias);
1248 // Build the entity from a sheet.
1249 if(_Entities[slot]->build(entitySheet))
1251 // Apply properties backuped;
1252 applyBackupedProperties(slot);
1253 // register to the ground fx manager
1254 if(_Entities[slot]->supportGroundFX())
1256 _EntityGroundFXHandle[slot] = _GroundFXManager.add(_Entities[slot]);
1259 // Entity is not valid -> REMOVE IT
1260 else
1262 // Everyone except the User
1263 if(slot != 0)
1265 nlwarning("EM:%d: Cannot build the Entity -> REMOVE IT", slot);
1266 delete _Entities[slot];
1267 _Entities[slot] = 0;
1269 // The User
1270 else
1271 nlerror("EM: Cannot build the User");
1274 // Entity Not Allocated
1275 else
1276 pushDebugStr(NLMISC::toString("Cannot Allocated the Entity in the slot '%d'.", slot));
1277 // Log problems about the entity creation.
1278 flushDebugStack(NLMISC::toString("Create Entity in slot '%d' with Form '%s' :", slot, ((CSheetId)form).toString().c_str()));
1279 // Return a pointer on the entity created.
1280 return _Entities[slot];
1281 }// create //
1283 //-----------------------------------------------
1284 // remove :
1285 // Delete an entity.
1286 // \todo GUIGUI : rename into free.
1287 // \todo GUIGUI : finish the function.
1288 //-----------------------------------------------
1289 bool CEntityManager::remove(uint slot, bool warning)
1291 // DEBUG
1292 if(verboseVP(NULL))
1293 nlinfo("EM:remove: slot '%u'.", slot);
1294 // Check parameter : slot.
1295 if(slot >= _NbMaxEntity)
1297 nlwarning("CEntityManager::remove : Attempt on delete a bad slot (slot %d)", slot);
1298 return false;
1301 // Do not delete the user.
1302 if(slot == 0)
1304 nlwarning("CEntityManager::remove : Cannot remove the entity in the slot 0 (user slot).");
1305 return false;
1308 // Slot not allocated.
1309 if(_Entities[slot] == 0)
1311 if(warning)
1313 nlwarning("CEntityManager::remove : Attempt on delete the slot '%d' that is not allocated.", slot);
1314 return false;
1318 // Remove the entity from others target.
1319 for(uint i=0; i<_Entities.size(); ++i)
1321 // This entity is not allocated.
1322 if(_Entities[i] == 0)
1323 continue;
1325 // Inform about the slot of the entity that will be removed.
1326 _Entities[i]->slotRemoved(slot);
1329 // remove ground fx
1330 if(_Entities[slot] != 0)
1332 if (_Entities[slot]->supportGroundFX())
1334 _GroundFXManager.remove(_EntityGroundFXHandle[slot]);
1338 // notify the projectile manager that entity has been removed
1339 CProjectileManager::getInstance().entityRemoved(slot);
1341 // notify the Bar Manager
1342 if(_Entities[slot])
1343 CBarManager::getInstance()->delEntity(_Entities[slot]->slot());
1345 // previous UnderPos?
1346 if(_LastEntityUnderPos==_Entities[slot])
1347 _LastEntityUnderPos= NULL;
1349 // Free the slot.
1350 delete _Entities[slot];
1351 _Entities[slot] = 0;
1353 // Done.
1354 return true;
1355 }// remove //
1358 //-----------------------------------------------
1359 // removeCollision :
1360 // Remove the collision for all entities.
1361 //-----------------------------------------------
1362 void CEntityManager::removeCollision()
1364 const uint nbEntities = (uint)_Entities.size();
1365 for(uint i=0; i<nbEntities; ++i)
1367 // Is the entity allocated.
1368 if(_Entities[i] == 0)
1369 continue;
1371 // Remove the entity primitive.
1372 _Entities[i]->removePrimitive();
1374 // Remove the collision entity.
1375 _Entities[i]->removeCollisionEntity();
1377 }// removeCollision //
1379 //-----------------------------------------------
1380 // reloadAnims :
1381 // Re-load animations (remove and load).
1382 //-----------------------------------------------
1383 void CEntityManager::reloadAnims()
1385 for(uint i=0; i<_Entities.size(); ++i)
1387 if(_Entities[i])
1389 // Get a reference on the current entity.
1390 CEntityCL &entity = *(_Entities[i]);
1391 // Change the playlist
1392 entity.buildPlaylist();
1395 }// reloadAnims //
1398 //-----------------------------------------------
1399 // entity :
1400 // Get a pointer on an entity according to the asked slot.
1401 // \param uint slot : the asked slot.
1402 // \return CEntityCL * : pointer on the entity or 0.
1403 //-----------------------------------------------
1404 CEntityCL *CEntityManager::entity(uint slot)
1406 // Return 0 if the slot is the INVALID_SLOT
1407 if(slot==CLFECOMMON::INVALID_SLOT)
1408 return 0;
1409 // Check parameter : slot.
1410 if(slot >= _Entities.size())
1412 nlwarning("EM:entity: slot '%u' is invalid.", slot);
1413 if(ClientCfg.Check)
1414 nlstop;
1415 return 0;
1417 // Return the entity pointer.
1418 return _Entities[slot];
1419 }// entity //
1421 //-----------------------------------------------
1422 // entitiesNearDoors :
1423 // Return if there is an entity near a door.
1424 // \param float openingDist : near is when you are under the 'openingDist'.
1425 // \param const CVector& posDoor1 : first door position.
1426 // \param const CVector& posDoor2 : second door position.
1427 // \return bool ; 'true' if any entity is near one of the door.
1428 //-----------------------------------------------
1429 bool CEntityManager::entitiesNearDoors(float openingDist, const CVector& posDoor1, const CVector& posDoor2)
1431 for(uint i=0; i<_NbMaxEntity; ++i)
1433 // Is the entity allocated.
1434 if(_Entities[i] == 0)
1435 continue;
1437 // Get a reference on the current entity.
1438 CEntityCL &entity = *(_Entities[i]);
1440 // If the entity is close enough from the door -> return true.
1441 if( ((entity.pos() - posDoor1).sqrnorm() < openingDist)
1442 || ((entity.pos() - posDoor2).sqrnorm() < openingDist) )
1443 return true;
1446 // No Entity near the door.
1447 return false;
1448 }// entitiesNearDoors //
1450 //-----------------------------------------------
1451 // getEntityListForSelection
1452 //-----------------------------------------------
1453 void CEntityManager::getEntityListForSelection(std::vector<CEntityCL*> &entities, uint flags)
1455 // According to the view (first or third person), the user can or cannot be selected.
1456 entities.clear();
1457 uint firstEntity = (flags&CEntityFilterFlag::NotUser)?1:0;
1458 for(uint i=firstEntity; i<_NbMaxEntity; ++i)
1460 // Is the entity allocated and not user mount.
1461 if(_Entities[i] == 0 || i==UserEntity->mount())
1462 continue;
1463 // If entity unselectable, skip
1464 if(!_Entities[i]->properties().selectable())
1465 continue;
1467 // Apply each filter
1468 if ( (flags&CEntityFilterFlag::Friend) && !_Entities[i]->isFriend() )
1469 continue;
1470 if ( (flags&CEntityFilterFlag::Enemy) && !_Entities[i]->isEnemy() )
1471 continue;
1472 if ( (flags&CEntityFilterFlag::Alive) && _Entities[i]->isReallyDead() )
1473 continue;
1474 if ( (flags&CEntityFilterFlag::Dead) && !_Entities[i]->isReallyDead() )
1475 continue;
1476 if ( (flags&CEntityFilterFlag::Player) && !_Entities[i]->isPlayer() )
1477 continue;
1478 if ( (flags&CEntityFilterFlag::NonPlayer) && _Entities[i]->isPlayer() )
1479 continue;
1481 // Insert every entity in the valid list.
1482 entities.push_back(_Entities[i]);
1486 //-----------------------------------------------
1487 // getEntityUnderPos :
1488 // Get the entity under the (2d) position. Return NULL if not entity under this position.
1489 //-----------------------------------------------
1490 struct CSortEntity
1492 CEntityCL *Entity;
1493 float Depth;
1495 bool operator<(const CSortEntity &o) const
1497 return Depth<o.Depth;
1500 CEntityCL *CEntityManager::getEntityUnderPos(float x, float y, float distSelection, bool &isPlayerUnderCursor)
1502 H_AUTO (RZ_Client_getEntityUnderPos )
1503 uint i;
1505 // valid only if bbox still intersect
1506 CEntityCL *precEntityUnderPos= _LastEntityUnderPos;
1507 bool precEntityUnderPosValid= false;
1509 // reset result
1510 isPlayerUnderCursor= false;
1511 _LastEntityUnderPos= NULL;
1513 // If not initialised, return
1514 if (_Entities.empty())
1515 return NULL;
1518 // **** list of valid entities to test
1519 static vector<CEntityCL*> validEntities;
1520 uint filterFlags= CEntityFilterFlag::NoFilter;
1521 getEntityListForSelection(validEntities, filterFlags);
1523 // build the ray
1524 CMatrix camMatrix = MainCam.getMatrix();
1525 CFrustum camFrust = MainCam.getFrustum();
1526 CViewport viewport = Driver->getViewport();
1528 // Get the Ray made by the mouse.
1529 CVector pos, dir;
1530 viewport.getRayWithPoint(x, y, pos, dir, camMatrix, camFrust);
1531 // Normalize the direction.
1532 dir.normalize();
1535 // **** Get entities with box intersecting the ray.
1536 static vector<CSortEntity> intersectedEntities;
1537 intersectedEntities.clear();
1538 for(i=0;i<validEntities.size();i++)
1540 H_AUTO(RZ_Client_GEUP_box_intersect)
1541 CEntityCL *entity = validEntities[i];
1542 // if entity not visible, skip
1543 if(entity->getLastClip())
1544 continue;
1546 // if intersect the bbox
1547 NLMISC::CAABBox bbox = entity->selectBox();
1548 if(bbox.intersect(pos, pos+dir*distSelection))
1550 // add this entity to the list of possible entities
1551 CSortEntity e;
1552 e.Entity= entity;
1553 e.Depth= (bbox.getCenter()-pos).norm();
1554 intersectedEntities.push_back(e);
1556 // is it the last entity under pos?
1557 if(entity==precEntityUnderPos)
1558 precEntityUnderPosValid= true;
1562 // if no intersected entities, quit
1563 if(intersectedEntities.empty())
1564 return NULL;
1566 // Compute startDistBox: nearest entity distance, but the user
1567 float startDistBox;
1568 if(intersectedEntities[0].Entity==UserEntity)
1570 // if the nearest entity is the user, set res
1571 isPlayerUnderCursor= true;
1572 // if only player intersected, return NULL!
1573 if(intersectedEntities.size()==1)
1574 return NULL;
1575 // so take the second for startDistBox
1576 startDistBox= intersectedEntities[1].Depth;
1578 else
1580 // ok, take it.
1581 startDistBox= intersectedEntities[0].Depth;
1585 // **** get best entity according to distance face-camera or box-ray if no face intersection
1586 CEntityCL *entitySelected= NULL;
1587 float bestDistBox= FLT_MAX;
1588 float bestDistZ= FLT_MAX;
1589 for(i=0;i<intersectedEntities.size();i++)
1591 CEntityCL *entity = intersectedEntities[i].Entity;
1592 const NLMISC::CAABBox &bbox = entity->selectBox();
1594 // If this entity is the UserEntity, skip!!
1595 if(entity==UserEntity)
1596 continue;
1598 // if entity skeleton model was clipped, skip
1599 USkeleton *skeleton= entity->skeleton();
1600 if(!ClientCfg.Light && skeleton && !skeleton->getLastClippedState())
1601 continue;
1603 H_AUTO(RZ_Client_GEUP_face_intersect)
1606 // *** Try get face-intersection, result in distZ
1607 // if the entity support fast and precise intersection (and if it succeeds)
1608 bool trueIntersectComputed= false;
1609 float dist2D, distZ;
1610 if(!ClientCfg.Light)
1612 if(skeleton)
1614 if(skeleton->supportFastIntersect() && skeleton->fastIntersect(pos, dir, dist2D, distZ, false))
1615 trueIntersectComputed= true;
1617 // get the intersection with the instance (bot object)
1618 else if(!entity->instances().empty() && !entity->instances()[0].Current.empty())
1620 UInstance inst= entity->instances()[0].Current;
1621 if(inst.supportFastIntersect() && inst.fastIntersect(pos, dir, dist2D, distZ, false))
1622 trueIntersectComputed= true;
1626 // if true face-intersection not found
1627 if(!trueIntersectComputed)
1630 this happens especially for Forage Source. but could happens for anyhting else
1631 In this case, estimate face-instersection, with box:
1632 Suppose full intersection, if the ray is in the 1/3 of the bbox
1635 // clip the ray with the box
1636 CVector a= pos, b= pos+dir*distSelection;
1637 if(!bbox.clipSegment(a, b))
1638 continue;
1639 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1640 // This is false, but gives better results.
1641 CVector m= (a+b)/2;
1643 // Suppose full intersection, if the ray is in the 1/3 of the bbox
1644 CVector itToCenter= m-bbox.getCenter();
1645 itToCenter.maxof(itToCenter, -itToCenter);
1646 CVector smallBoxHS= bbox.getHalfSize()*0.3f;
1647 smallBoxHS.maxof(smallBoxHS, -smallBoxHS);
1648 if(itToCenter.x<=smallBoxHS.x && itToCenter.y<=smallBoxHS.y && itToCenter.z<=smallBoxHS.z)
1650 dist2D= 0;
1651 distZ= (m-pos).norm();
1653 else
1655 // no intersection
1656 dist2D= FLT_MAX;
1657 distZ= 0;
1660 // else it's ok, dist2D and distZ are computed
1663 // *** if intersect face, then take the best face-intersection, else use box-ray cost
1664 // true face-col found?
1665 if(dist2D==0)
1667 // yes, get the nearest
1668 if(distZ<bestDistZ)
1670 bestDistBox= 0;
1671 bestDistZ= distZ;
1672 entitySelected= entity;
1675 // else
1676 else
1678 // if a true face-intersection has not been found for others entities
1679 if(bestDistZ==FLT_MAX)
1681 // get the "distance to camera" contribution.
1682 CVector c= bbox.getCenter();
1683 float distCamCost= intersectedEntities[i].Depth;
1684 // get relative to the nearest intersected entity
1685 distCamCost-= startDistBox;
1687 // get the ratio "how many the ray is in the bbox"
1688 CVector a= pos, b= pos+dir*distSelection;
1689 bbox.clipSegment(a, b);
1690 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1691 // This is false, but gives better results.
1692 CVector m= (a+b)/2;
1693 // get the distance to center. NB: small entities are preferred since smaller mean lower cost
1694 float outBBoxCost= (m-c).norm();
1696 // the final cost is a weighted sum of the both. NB: distCamCost is in meter,
1697 // and outBBBoxCost is meters. Hence ClientCfg.SelectionOutBBoxWeight is a factor
1698 float boxCost= distCamCost + outBBoxCost * ClientCfg.SelectionOutBBoxWeight;
1700 // take the lowest cost
1701 if(boxCost<bestDistBox)
1703 entitySelected= entity;
1704 bestDistBox= boxCost;
1710 // If precise intersection not found
1711 if(bestDistZ==FLT_MAX)
1713 // if the last entity under pos is valid, prefer it among all other approximate ones
1714 if(precEntityUnderPos && precEntityUnderPosValid)
1715 entitySelected= precEntityUnderPos;
1718 // return the best entity
1719 _LastEntityUnderPos= entitySelected;
1720 return entitySelected;
1721 }// getEntityUnderPos //
1724 //-----------------------------------------------
1725 // getEntityInCamera
1726 //-----------------------------------------------
1727 CEntityCL *CEntityManager::getEntityInCamera(uint flags, float distSelection, CLFECOMMON::TCLEntityId precEntity)
1729 H_AUTO (RZ_Client_getEntityInCamera )
1731 // If not initialised, return
1732 if (_Entities.empty())
1733 return NULL;
1735 // list of valid entities
1736 static vector<CEntityCL*> validEntitiesTmp, validEntities;
1737 getEntityListForSelection(validEntitiesTmp, flags);
1739 // Remove entities not selectable by space key
1740 uint i;
1741 validEntities.clear();
1742 for (i=0 ; i<validEntitiesTmp.size() ; i++)
1744 CCharacterCL *entity = dynamic_cast<CCharacterCL*>(validEntitiesTmp[i]);
1745 if ((entity == NULL) || (entity && entity->isSelectableBySpace()))
1746 validEntities.push_back(entity);
1749 // Build the camera pyramid
1750 CMatrix camMatrix = MainCam.getMatrix();
1751 CFrustum camFrust = MainCam.getFrustum();
1752 static vector<CPlane> camPyramid;
1753 // No need to use worldMatrix. NB: not setuped if ClientLight.
1754 MainCam.buildCameraPyramid(camPyramid, false);
1756 // list of entities in screen
1757 static vector<CSortEntity> screenEntities;
1758 screenEntities.clear();
1760 // compute distance related to the user pos (not camera one).
1761 CVector userPos = UserEntity->pos();
1763 // prefer take the direction of the camera (can select backward for instance with camera rotation)
1764 CVector userDir = View.currentView().normed();
1766 // Get all entity in this pyramid, and in the dist selection
1767 for(i=0;i<validEntities.size();i++)
1769 CEntityCL *entity= validEntities[i];
1770 const NLMISC::CAABBox &b = entity->selectBox();
1771 bool isIn= true;
1772 for(uint j=0;j<camPyramid.size();j++)
1774 if( !b.clipBack(camPyramid[j]) )
1776 isIn= false;
1777 break;
1780 // if In the pyramid
1781 if(isIn)
1783 CVector dirToEntity= b.getCenter()-userPos;
1784 CSortEntity eSelect;
1785 eSelect.Entity= entity;
1786 eSelect.Depth= dirToEntity.norm();
1787 // if in max distance
1788 if(eSelect.Depth<distSelection)
1790 // The lower, the more the influence of direction (minimum should be 1)
1791 const float dirInfluence= 1.1f;
1793 // modulate the depth with dot3: force take the most in front of user.
1794 if(eSelect.Depth>0)
1795 dirToEntity/= eSelect.Depth;
1796 eSelect.Depth*= dirInfluence-dirToEntity*userDir;
1798 // append to sort list
1799 screenEntities.push_back(eSelect);
1804 // No one in screen?
1805 if(screenEntities.empty())
1806 return NULL;
1808 // sort them increasingly
1809 sort(screenEntities.begin(), screenEntities.end());
1811 // Try to find the precEntity in this list
1812 uint entitySelected= 0;
1813 if(precEntity!=CLFECOMMON::INVALID_SLOT)
1815 for(i=0;i<screenEntities.size();i++)
1817 // if found the precEntity, get the farther one
1818 if(screenEntities[i].Entity->slot()==precEntity)
1820 entitySelected= i+1;
1821 break;
1824 // reset to 0 if: no more entities, or if the max cycle is reached
1825 if(entitySelected>=screenEntities.size() || entitySelected>=ClientCfg.SpaceSelectionMaxCycle)
1826 entitySelected= 0;
1829 // found!
1830 return screenEntities[entitySelected].Entity;
1833 //-----------------------------------------------
1834 // changeContinent :
1835 // Continent has changed.
1836 //-----------------------------------------------
1837 void CEntityManager::changeContinent()
1839 // Re-create entities primitive.
1840 for(uint i=0; i<_NbMaxEntity; ++i)
1842 // Is the entity allocated.
1843 if(_Entities[i] == 0)
1844 continue;
1846 // Compute the new primitive.
1847 _Entities[i]->computePrimitive();
1849 // Compute the new collision entity.
1850 _Entities[i]->computeCollisionEntity();
1852 }// changeContinent //
1855 //-----------------------------------------------
1856 // updatePreCamera :
1857 // Update entites before the camera position is computed.
1858 // This update the entites position. Evaluate collisions. Compte final world position.
1859 //-----------------------------------------------
1860 void CEntityManager::updatePreCamera()
1862 H_AUTO ( RZ_Client_Entity_Mngr_Update_Pre_Cam )
1863 uint i;
1864 // Build an entity list..
1865 _ActiveEntities.reserve (_Entities.size ());
1866 _ActiveEntities.clear ();
1867 // Reset Counters
1868 resetCounters();
1869 // Update entities position.
1870 for(i=0; i<_NbMaxEntity; ++i)
1872 // Is the entity allocated.
1873 CEntityCL *entity = _Entities[i];
1874 if(entity == 0)
1875 continue;
1876 // Count Entities
1877 ++_EntitiesAllocated;
1878 switch(entity->Type)
1880 case CEntityCL::User:
1881 ++_NbUser; break;
1882 case CEntityCL::Player:
1883 ++_NbPlayer; break;
1884 /*case CEntityCL::NPC:
1885 case CEntityCL::Fauna:
1886 case CEntityCL::Entity:
1887 case CEntityCL::ForageSource:*/
1888 default:
1889 ++_NbChar; break;
1891 // Update the list of Active Entities
1892 _ActiveEntities.push_back (CEntityReference (i, entity));
1894 // Adjust the orientation of the NPC in trade with the user.
1895 if(UserEntity->trader() != CLFECOMMON::INVALID_SLOT)
1897 CEntityCL * trader = _Entities[UserEntity->trader()];
1898 if(trader)
1899 trader->front(UserEntity->pos() - trader->pos());
1901 // Adjust the orientation of the NPC in dyn chat with the user.
1902 if(UserEntity->interlocutor() != CLFECOMMON::INVALID_SLOT)
1904 CEntityCL * interlocutor = _Entities[UserEntity->interlocutor()];
1905 if(interlocutor)
1906 interlocutor->front(UserEntity->pos() - interlocutor->pos());
1909 // Update entities position except the User
1910 for(i=1; i<_EntitiesAllocated; ++i)
1912 CEntityReference &activeEntity = _ActiveEntities[i];
1914 // Get a poiner on the entity target
1915 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1917 // Update the entity.
1918 activeEntity.Entity->updatePreCollision(T1, target);
1920 // USER
1922 // Get a poiner on the entity target
1923 CEntityCL *target = entity(UserEntity->targetSlot());
1924 // update user behaviour/speed/heading/vectorUp/position/bodyHeading
1925 UserEntity->applyMotion(target);
1926 // Update the entity.
1927 UserEntity->updatePreCollision(T1, target);
1929 // Update PACS
1930 if(PACS)
1932 // Time since last Frame
1933 double DTEval = ((float)(T1-T0))*0.001f;
1934 PACS->evalCollision(DTEval, staticWI); // Eval the static world.
1935 PACS->evalCollision(DTEval, dynamicWI); // Eval the dynamic world.
1936 getDoorManager().getPACSTriggers(); // Copy triggers to be used in update
1937 managePACSTriggers();
1938 UserEntity->checkPos();
1940 // Update entities position.
1941 for(i=0; i<_EntitiesAllocated; ++i)
1943 CEntityReference &activeEntity = _ActiveEntities[i];
1944 // Get a poiner on the entity target
1945 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1946 // Update the entity.
1947 activeEntity.Entity->updatePostCollision(T1, target);
1949 // User Orientation
1950 UserEntity->applyForceLook();
1953 getDoorManager().update(); // Check for trigger to open/close doors
1955 MissionTargetObserver.update();
1957 }// updatePreCamera //
1960 //-----------------------------------------------
1961 // updatePostCamera :
1962 // Update the entity (position\animation).
1963 // Clip the primitives
1964 // Update visual entites parameters for clipped and non-clipped primitives
1965 // This update the entites position.
1966 //-----------------------------------------------
1967 void CEntityManager::updatePostCamera(uint clippedUpdateTime, const std::vector<CPlane> &clippingPlanes, const CVector &camPos)
1969 H_AUTO ( RZ_Client_Entity_Mngr_Update_Post_Cam )
1971 // Build a non clipped entity list..
1972 _VisibleEntities.reserve (_Entities.size ());
1973 _VisibleEntities.clear ();
1975 static bool firstTime = true;
1977 // Clip entities position.
1978 uint i;
1979 for(i=0; i<_EntitiesAllocated; ++i)
1981 CEntityReference &activeEntity = _ActiveEntities[i];
1983 // Get a poiner on the entity target
1984 CEntityCL *target = entity(activeEntity.Entity->targetSlot());
1986 // Clip it
1987 if (!activeEntity.Entity->clipped(clippingPlanes, camPos)
1988 || (R2::getEditor().getSelectedInstance() && R2::getEditor().getSelectedInstance()->getEntity()==activeEntity.Entity))
1990 // Add to visible primitives
1991 _VisibleEntities.push_back (activeEntity);
1992 activeEntity.Entity->setLastClip(false);
1993 activeEntity.Entity->updateVisible (T1, target);
1995 else
1997 activeEntity.Entity->setLastClip(true);
1998 if (firstTime)
2000 // Update texture Async Loading
2001 activeEntity.Entity->updateAsyncTexture();
2003 // Update lod Texture
2004 activeEntity.Entity->updateLodTexture();
2008 // Update this clipped primitive at this time ?
2009 if ((activeEntity.Slot&RZ_CLIPPED_UPDATE_TIME_MASK) == clippedUpdateTime)
2011 activeEntity.Entity->updateSomeClipped (T1, target);
2014 // Update clipped primitives
2015 activeEntity.Entity->updateClipped (T1, target);
2019 // Update visible entities post positions.
2020 const uint count = (uint)_VisibleEntities.size ();
2021 for(i=0; i<count; ++i)
2023 CEntityReference &visibleEntity = _VisibleEntities[i];
2024 // Get a poiner on the entity target
2025 CEntityCL *target = entity(visibleEntity.Entity->targetSlot());
2027 visibleEntity.Entity->updateVisiblePostPos(T1, target);
2030 // update ground fx
2031 _GroundFXManager.update(NLMISC::CVectorD(camPos));
2033 firstTime = false;
2034 }// updatePostCamera //
2036 //-----------------------------------------------
2037 // updatePostRender :
2038 // Update entites after the render 3D.
2039 //-----------------------------------------------
2040 void CEntityManager::updatePostRender()
2042 H_AUTO_USE ( RZ_Client_Update_Post_Render )
2044 TextContext->setHotSpot(UTextContext::MiddleMiddle);
2045 TextContext->setFontSize(ClientCfg.NameFontSize);
2046 CRGBA color;
2048 const uint activeCount = (uint)_ActiveEntities.size ();
2049 uint i;
2050 for(i=0; i<activeCount; i++)
2052 CEntityReference &visibleEntity = _ActiveEntities[i];
2054 // Update in-scene interface
2055 visibleEntity.Entity->updateAllPostRender ();
2058 const uint count = (uint)_VisibleEntities.size ();
2059 for(i=0; i<count; ++i)
2061 CEntityReference &visibleEntity = _VisibleEntities[i];
2062 // Update Visible Entities after the render.
2063 visibleEntity.Entity->updateVisiblePostRender();
2065 // Draw the entity Path.
2066 if(ClientCfg.ShowPath)
2067 visibleEntity.Entity->drawPath();
2068 // Draw the selection box.
2069 if(ClientCfg.DrawBoxes)
2070 visibleEntity.Entity->drawBox();
2071 // Display Modifiers (Dmgs/heals).
2072 if(1)
2073 visibleEntity.Entity->displayModifiers();
2076 // Flush any no more used Flying text. Must do it before interface display (to be sure texts are hid)
2077 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2078 pIM->FlyingTextManager.releaseNotUsedFlyingText();
2080 }// updatePostRender //
2083 //-----------------------------------------------
2084 // updateVisualProperty :
2085 // Method to update the visual property 'prop' for the entity in 'slot'.
2086 // \param uint slot : slot of the entity to update.
2087 // \param uint prop : the property to udapte.
2088 //-----------------------------------------------
2089 void CEntityManager::updateVisualProperty(const NLMISC::TGameCycle &gameCycle, const uint &slot, const uint &prop, const NLMISC::TGameCycle &predictedInterval)
2091 // INFO : log some debug information about visual properties.
2092 if(verboseVP(NULL))
2093 nlinfo("EM:updateVP: received prop '%d' for the slot '%d'.", prop, slot);
2095 // Check parameter : slot.
2096 if(slot >= _NbMaxEntity)
2098 nlwarning("CEntityManager::updateVisualProperty : Slot '%d' is not valid.", slot);
2099 return;
2102 // Entity still not allocated -> backup values received for the entity.
2103 if(_Entities[slot] == 0)
2105 // INFO : log some debug information about visual properties.
2106 if(verboseVP(NULL))
2107 nlinfo("EM:updateVP: backup the property as long as the entity is not allocated.", prop, slot);
2109 string propName = toString("SERVER:Entities:E%d:P%d", slot, prop);
2110 TProperty propty;
2111 propty.GC = gameCycle;
2112 propty.Value = 0;
2113 // propty.Value = IngameDbMngr.getProp(propName);
2116 TBackupedChanges::iterator it = _BackupedChanges.find(slot);
2117 // Entity does not have any changes backuped for the time.
2118 if(it == _BackupedChanges.end())
2120 TProperties propMap;
2121 propMap.insert(make_pair(prop, propty));
2122 _BackupedChanges.insert(make_pair(slot, propMap));
2124 // Entity already have some changes backuped.
2125 else
2127 TProperties &properties = (*it).second;
2128 TProperties::iterator itProp = properties.find(prop);
2129 // This properties is still not backuped for this entity.
2130 if(itProp == properties.end())
2131 properties.insert(make_pair(prop, propty));
2132 // There is already a backuped value
2133 else
2135 nlwarning("EM:updateVP:%d: property '%d' already backuped.", slot, prop);
2136 (*itProp).second = propty;
2140 // Entity already allocated -> apply values.
2141 else
2143 // Call the method from the entity to update the visual property.
2144 _Entities[slot]->updateVisualProperty(gameCycle, prop, predictedInterval);
2146 }// updateVisualProperty //
2148 //-----------------------------------------------
2149 // applyBackupedProperties :
2150 //-----------------------------------------------
2151 void CEntityManager::applyBackupedProperties(uint slot)
2153 TBackupedChanges::iterator it = _BackupedChanges.find(slot);
2154 if(it != _BackupedChanges.end())
2156 TProperties &properties = (*it).second;
2157 TProperties::iterator itProp = properties.begin();
2158 while(itProp != properties.end())
2160 _Entities[slot]->updateVisualProperty((*itProp).second.GC, (*itProp).first, 0);
2161 ++itProp;
2164 _BackupedChanges.erase(it);
2166 }// applyBackupedProperties //
2170 //-----------------------------------------------
2171 // writeEntities :
2172 // Write a file with the position of all entities.
2173 //-----------------------------------------------
2174 void CEntityManager::writeEntities()
2176 COFile f;
2177 if(!f.open("entities.txt", false, true))
2178 return;
2180 string strTmp = "StartCommands = {\n";
2181 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2183 const uint nb = (uint)_Entities.size();
2184 for(uint i=1; i<nb; ++i)
2186 if(_Entities[i])
2188 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);
2189 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2193 strTmp = "};\n";
2194 f.serialBuffer((uint8*)strTmp.c_str(), (uint)strTmp.size());
2196 // Close the File.
2197 f.close();
2198 }// writeEntities //
2200 //-----------------------------------------------
2201 // serial
2202 // Serialize entities.
2203 //-----------------------------------------------
2204 void CEntityManager::serial(NLMISC::IStream &f)
2206 // Get nb max entities possible.
2207 f.serial(_NbMaxEntity);
2208 if(f.isReading())
2210 release();
2211 initialize(_NbMaxEntity);
2214 // f.serial(_EntitiesAllocated); no need to serialize this one except maybe to check.
2216 // Serialize each entity.
2217 const uint nb = (uint)_Entities.size();
2218 for(uint i=0; i<nb; ++i)
2220 NLMISC::CSheetId si;
2221 if(!f.isReading())
2223 if(_Entities[i])
2224 si = _Entities[i]->sheetId();
2225 else
2226 si = NLMISC::CSheetId::Unknown;
2229 // ...
2230 f.serial(si);
2232 // Create the entity.
2233 if(f.isReading() && (si != CSheetId::Unknown))
2235 TNewEntityInfo emptyEntityInfo;
2236 emptyEntityInfo.reset();
2237 create(i, si.asInt(), emptyEntityInfo);
2240 // Get/Set entity state.
2241 if(_Entities[i])
2242 _Entities[i]->serial(f);
2244 }// serial //
2247 //-----------------------------------------------
2248 // dump :
2249 // // Dump entities state.
2250 //-----------------------------------------------
2251 void CEntityManager::dump(class NLMISC::IStream &f)
2253 // Serialize the class.
2254 serial(f);
2255 }// dump //
2257 //-----------------------------------------------
2258 // dumpXML :
2259 // Dump entities state (XML Format).
2260 //-----------------------------------------------
2261 void CEntityManager::dumpXML(class NLMISC::IStream &f)
2263 // Start the opening of a new node named Identity
2264 f.xmlPush("Entities");
2266 const uint nb = (uint)_Entities.size();
2267 for(uint i=0; i<nb; ++i)
2269 // Add a comment
2270 // f.xmlComment();//toString("Describ the entity in the slot %d.", i).c_str());
2271 // Start the opening of a new node named Identity
2272 f.xmlPush(toString("Entity%d", i));
2274 if(_Entities[i])
2276 // Open a new node header named Address
2277 f.xmlPushBegin("Name");
2278 // Set a property name
2279 f.xmlSetAttrib ("string");
2280 string n = _Entities[i]->getEntityName();
2281 f.serial(n);
2282 // Close the new node header
2283 f.xmlPushEnd();
2284 // Close the address node
2285 f.xmlPop();
2287 // Open a new node header named Address
2288 f.xmlPushBegin("Sheet");
2289 // Set a property name
2290 f.xmlSetAttrib ("name");
2291 string sheetName = _Entities[i]->sheetId().toString();
2292 f.serial(sheetName);
2293 // Close the new node header
2294 f.xmlPushEnd();
2295 // Close the address node
2296 f.xmlPop();
2298 // Open a new node header named Address
2299 f.xmlPushBegin("Position");
2300 // Close the new node header
2301 f.xmlPushEnd();
2302 f.serial(_Entities[i]->pos());
2303 // Close the address node
2304 f.xmlPop();
2306 // Open a new node header named Address
2307 f.xmlPushBegin("Front");
2308 // Close the new node header
2309 f.xmlPushEnd();
2310 NLMISC::CVector front = _Entities[i]->front();
2311 f.serial(front);
2312 // Close the address node
2313 f.xmlPop();
2315 // Open a new node header named Address
2316 f.xmlPushBegin("Mode");
2317 // Set a property name
2318 f.xmlSetAttrib ("name");
2319 string mode = MBEHAV::modeToString(_Entities[i]->mode());
2320 f.serial(mode);
2321 // Set a property name
2322 f.xmlSetAttrib ("num");
2323 uint8 m = _Entities[i]->mode();
2324 f.serial(m);
2325 // Close the new node header
2326 f.xmlPushEnd();
2327 // Close the address node
2328 f.xmlPop();
2330 // Open a new node header named Address
2331 f.xmlPushBegin("LastBehaviourPlayed");
2332 // Set a property name
2333 f.xmlSetAttrib ("name");
2334 string beh = MBEHAV::behaviourToString(_Entities[i]->behaviour());
2335 f.serial(beh);
2336 // Set a property name
2337 f.xmlSetAttrib ("num");
2338 uint8 b = _Entities[i]->behaviour();
2339 f.serial(b);
2340 // Close the new node header
2341 f.xmlPushEnd();
2342 // Close the address node
2343 f.xmlPop();
2346 // Close the address node
2347 f.xmlPop();
2350 // Close the identity node
2351 f.xmlPop();
2352 }// dumpXML //
2354 //-----------------------------------------------
2356 CEntityCL *CEntityManager::getEntityByName (uint32 stringId) const
2358 if (stringId)
2360 uint i;
2361 const uint count = (uint)_Entities.size();
2362 for (i=0; i<count; i++)
2364 if(_Entities[i])
2365 if(_Entities[i]->getNameId() == stringId)
2366 return _Entities[i];
2369 return NULL;
2372 //-----------------------------------------------
2373 CEntityCL *CEntityManager::getEntityByKeywords (const std::vector<string> &keywords, bool onlySelectable) const
2375 if (keywords.empty()) return NULL;
2377 std::vector<string> lcKeywords;
2378 lcKeywords.resize(keywords.size());
2379 for(uint k = 0; k < keywords.size(); k++)
2381 lcKeywords[k] = toLower(keywords[k]);
2384 const NLMISC::CVectorD &userPosD = UserEntity->pos();
2385 const uint count = (uint)_Entities.size();
2386 uint selectedEntityId = 0;
2387 float selectedEntityDist = FLT_MAX;
2388 for(uint i = 0; i < count; ++i)
2390 if (!_Entities[i]) continue;
2392 if (onlySelectable && !_Entities[i]->properties().selectable()) continue;
2394 string lcName = toLower(_Entities[i]->getDisplayName());
2395 if (lcName.empty()) continue;
2397 bool match = true;
2398 for (uint k = 0; k < lcKeywords.size(); ++k)
2400 if (lcName.find(lcKeywords[k]) == string::npos)
2402 match = false;
2403 break;
2407 if (match)
2409 const NLMISC::CVectorD &targetPosD = _Entities[i]->pos();
2411 float deltaX = (float) targetPosD.x - (float) userPosD.x;
2412 float deltaY = (float) targetPosD.y - (float) userPosD.y;
2413 float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY);
2414 if (dist < selectedEntityDist)
2416 selectedEntityDist = dist;
2417 selectedEntityId = i;
2422 if (selectedEntityDist != FLT_MAX)
2423 return _Entities[selectedEntityId];
2424 else
2425 return NULL;
2428 //-----------------------------------------------
2429 CEntityCL *CEntityManager::getEntityByName (const string &name, bool caseSensitive, bool complete) const
2431 string source;
2432 source = caseSensitive ? name : toLower(name); // TODO: toLowerInsensitive
2434 uint i;
2435 const uint count = (uint)_Entities.size();
2436 uint selectedEntityId = 0;
2437 float selectedEntityDist = FLT_MAX; // No selected Entity
2439 for (i=0; i<count; i++)
2441 if(_Entities[i])
2443 string value = caseSensitive ? _Entities[i]->getDisplayName() : toLower(_Entities[i]->getDisplayName()); // TODO: toLowerInsensitive
2444 bool foundEntity = false;
2446 // Complete test ?
2447 if (complete)
2449 if(value == source)
2450 foundEntity = true;
2452 else
2454 if (NLMISC::startsWith(value, source))
2455 foundEntity = true;
2458 if (foundEntity)
2460 const NLMISC::CVectorD &targetPosD = _Entities[i]->pos();
2461 const NLMISC::CVectorD &userPosD = UserEntity->pos();
2463 float deltaX = (float) targetPosD.x - (float) userPosD.x;
2464 float deltaY = (float) targetPosD.y - (float) userPosD.y;
2465 float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY);
2466 if (dist < selectedEntityDist)
2468 selectedEntityDist = dist;
2469 selectedEntityId = i;
2474 if (selectedEntityDist != FLT_MAX) // Entity found
2475 return _Entities[selectedEntityId];
2476 else
2477 return NULL;
2480 //-----------------------------------------------
2482 CEntityCL *CEntityManager::getEntityByCompressedIndex(TDataSetIndex compressedIndex) const
2484 if (compressedIndex != INVALID_DATASET_ROW)
2486 uint i;
2487 const uint count = (uint)_Entities.size();
2488 for (i=0; i<count; i++)
2490 if(_Entities[i])
2491 if(_Entities[i]->dataSetId() == compressedIndex)
2492 return _Entities[i];
2495 return NULL;
2497 //-----------------------------------------------
2498 // getEntityBySheetName :
2499 // Return an entity based on its sheet name
2500 //-----------------------------------------------
2501 CEntityCL *CEntityManager::getEntityBySheetName (const std::string &sheet) const
2503 if (!sheet.empty())
2505 uint i;
2506 const CSheetId& sheetRef = NLMISC::CSheetId(sheet);
2507 const uint count = (uint)_Entities.size();
2508 for (i=0; i<count; i++)
2510 if(_Entities[i])
2511 if(_Entities[i]->sheetId() == sheetRef)
2512 return _Entities[i];
2515 return NULL;
2517 //-----------------------------------------------
2518 // managePACSTriggers :
2519 // Manage PACS Triggers.
2520 //-----------------------------------------------
2521 void CEntityManager::managePACSTriggers()
2523 uint i;
2524 const uint nNbTrig = PACS->getNumTriggerInfo();
2525 for(i=0; i<nNbTrig; ++i)
2527 const NLPACS::UTriggerInfo &rTI = PACS->getTriggerInfo(i);
2528 // Detect collisions between user and other entities, to not be block (only the user is a trigger so no need to check).
2529 if(((rTI.Object0 & 0xFFFF) == UserDataEntity)
2530 && ((rTI.Object1 & 0xFFFF) == UserDataEntity))
2532 UserEntity->startColTimer();
2533 break;
2536 // Stop Collision.
2537 if(i >= nNbTrig)
2538 UserEntity->stopColTimer();
2539 }// managePACSTriggers //
2542 //-----------------------------------------------
2543 // removeColUserOther :
2545 //-----------------------------------------------
2546 void CEntityManager::removeColUserOther()
2548 uint i;
2549 const uint count = (uint)_Entities.size();
2550 for(i=1; i<count; i++)
2552 if(_Entities[i])
2554 if(_Entities[i]->getPrimitive())
2556 // remove collision only if the entity is Traversable (bot objects may not)
2557 if(_Entities[i]->getTraversable())
2558 _Entities[i]->getPrimitive()->setObstacle(false);
2562 }// removeColUserOther //
2564 //-----------------------------------------------
2565 // restoreColUserOther :
2567 //-----------------------------------------------
2568 void CEntityManager::restoreColUserOther()
2570 uint i;
2571 const uint count = (uint)_Entities.size();
2572 for(i=1; i<count; i++)
2574 if(_Entities[i])
2576 if(_Entities[i]->getPrimitive())
2577 _Entities[i]->getPrimitive()->setObstacle(true);
2580 }// restoreColUserOther //
2582 //-----------------------------------------------
2583 void CEntityManager::removeAllAttachedFX()
2585 for(TEntities::iterator it = _Entities.begin(); it != _Entities.end(); ++it)
2587 if (*it) (*it)->removeAllAttachedFX();
2592 // ***************************************************************************
2593 void CEntityManager::resetAllSoundAnimId()
2595 for(uint i=0;i<_Entities.size();i++)
2597 CEntityCL *ent= _Entities[i];
2598 if(ent)
2600 ent->resetAllSoundAnimId();
2605 // ***************************************************************************
2606 #define nldebugraw NLMISC::createDebug(), NLMISC::DebugLog->displayRawNL
2608 // ***************************************************************************
2609 void CEntityManager::startLogStageChange(sint32 currentGameCycle, sint64 currentLocalTime)
2611 // first stop
2612 stopLogStageChange();
2614 // enable
2615 _LogStageChange.Enabled= true;
2616 _LogStageChange.StartGameCycle= currentGameCycle;
2617 _LogStageChange.StartLocalTime= currentLocalTime;
2618 _LogStageChange.LastEntityLoged= CLFECOMMON::INVALID_SLOT;
2619 _LogStageChange.StageSet.clear();
2620 nldebugraw("*** Start Loging Stage changes");
2624 // ***************************************************************************
2625 void CEntityManager::logStageChange(sint64 currentLocalTime)
2627 if(!_LogStageChange.Enabled)
2628 return;
2630 // if still exist
2631 CCharacterCL *ent= dynamic_cast<CCharacterCL*>(entity(WatchedEntitySlot));
2632 if(ent)
2634 // if the watched entity has been changed
2635 if(WatchedEntitySlot!=_LogStageChange.LastEntityLoged)
2637 _LogStageChange.LastEntityLoged= WatchedEntitySlot;
2638 // backup set
2639 _LogStageChange.StageSet= ent->_Stages._StageSet;
2640 nldebugraw("*** Start Loging Stage changes for Entity %d", WatchedEntitySlot);
2642 else
2644 // can log it
2645 sint32 recGcRef= _LogStageChange.StartGameCycle;
2646 sint64 recTimeRef= _LogStageChange.StartLocalTime;
2647 // compare 2 logs and display differences
2648 CStageSet::TStageSet &oldStageSet= _LogStageChange.StageSet;
2649 const CStageSet::TStageSet &newStageSet= ent->_Stages._StageSet;
2651 // for pos log detail
2652 CVectorD precNewPos= ent->pos();
2653 CVectorD precOldPos= ent->pos();
2655 // compare each new/old stage
2656 CStageSet::TStageSet::const_iterator itOld= oldStageSet.begin();
2657 CStageSet::TStageSet::const_iterator itNew= newStageSet.begin();
2658 while(itOld!=oldStageSet.end() || itNew!=newStageSet.end())
2660 // compare 2 iterators
2661 sint signNewMinusOld;
2662 if(itNew==newStageSet.end())
2663 signNewMinusOld= +1;
2664 else if(itOld==oldStageSet.end())
2665 signNewMinusOld= -1;
2666 else
2668 if(itNew->first > itOld->first)
2669 signNewMinusOld= +1;
2670 else if(itNew->first < itOld->first)
2671 signNewMinusOld= -1;
2672 else
2673 signNewMinusOld= 0;
2676 // if signNewMinusOld= +1, it means an old exist, without a new (=> the stage has been removed)
2677 if(signNewMinusOld==+1)
2679 logPropertyChange(WatchedEntitySlot, itOld->second, CStage(), precOldPos, precNewPos, (sint32)itOld->first-recGcRef, currentLocalTime-recTimeRef);
2680 // new prec pos (if any)
2681 itOld->second.getPos(precOldPos);
2682 itOld++;
2684 // if signNewMinusOld= -1, it means an new exist, without an old (=> the stage has been added)
2685 else if(signNewMinusOld==-1)
2687 logPropertyChange(WatchedEntitySlot, CStage(), itNew->second, precOldPos, precNewPos, (sint32)itNew->first-recGcRef, currentLocalTime-recTimeRef);
2688 // new prec pos (if any)
2689 itNew->second.getPos(precNewPos);
2690 itNew++;
2692 // if ==0, means the stage exist in both, but properties set may be different
2693 else
2695 logPropertyChange(WatchedEntitySlot, itOld->second, itNew->second, precOldPos, precNewPos, (sint32)itNew->first-recGcRef, currentLocalTime-recTimeRef);
2696 // new prec pos (if any)
2697 itOld->second.getPos(precOldPos);
2698 itNew->second.getPos(precNewPos);
2699 itOld++;
2700 itNew++;
2704 // bkup the new stage set
2705 oldStageSet= newStageSet;
2708 // this entity might have been deleted, stop its log
2709 else
2711 _LogStageChange.LastEntityLoged= CLFECOMMON::INVALID_SLOT;
2712 _LogStageChange.StageSet.clear();
2716 // ***************************************************************************
2717 void CEntityManager::logPropertyChange(CLFECOMMON::TCLEntityId who, const CStage &oldStage, const CStage &newStage,
2718 const CVectorD &precOldPos, const CVectorD &precNewPos, sint32 relGameCycle, sint64 relLocalTime)
2720 // For all properties of interest
2721 CLFECOMMON::TPropIndex propLoged[]= {CLFECOMMON::PROPERTY_POSITION, CLFECOMMON::PROPERTY_ORIENTATION,
2722 CLFECOMMON::PROPERTY_MODE, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID,
2723 CLFECOMMON::PROPERTY_BEHAVIOUR, CLFECOMMON::PROPERTY_TARGET_ID,
2724 /*CLFECOMMON::PROPERTY_VISUAL_FX,
2725 CLFECOMMON::PROPERTY_TARGET_LIST_0, CLFECOMMON::PROPERTY_TARGET_LIST_1,
2726 CLFECOMMON::PROPERTY_TARGET_LIST_2, CLFECOMMON::PROPERTY_TARGET_LIST_3*/};
2727 uint32 numProps= sizeof(propLoged) / sizeof(propLoged[0]);
2728 for(uint i=0;i<numProps;i++)
2730 pair<bool, sint64> oldProp= oldStage.property(propLoged[i]);
2731 pair<bool, sint64> newProp= newStage.property(propLoged[i]);
2732 // if change of the prop, log it
2733 if((oldProp.first || newProp.first) && oldProp!=newProp)
2735 // get the change reason
2736 string reason;
2737 if(!oldProp.first)
2738 reason= "ADD";
2739 else if(!newProp.first)
2740 reason= "DEL";
2741 else
2742 reason= "CHG";
2744 // get the value
2745 sint64 value= newProp.second;
2746 if(!newProp.first) value= oldProp.second;
2747 string valStr;
2748 // mode?
2749 if(propLoged[i]==CLFECOMMON::PROPERTY_MODE)
2751 valStr= MBEHAV::TMode((uint64)value).toString();
2753 // behaviour
2754 else if(propLoged[i]==CLFECOMMON::PROPERTY_BEHAVIOUR)
2756 valStr= MBEHAV::CBehaviour((uint64)value).toString();
2758 // mount
2759 else if(propLoged[i]==CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID)
2761 valStr= NLMISC::toString(value);
2763 else if(propLoged[i]==CLFECOMMON::PROPERTY_RIDER_ENTITY_ID)
2765 valStr= NLMISC::toString(value);
2767 // Target
2768 else if(propLoged[i]==CLFECOMMON::PROPERTY_TARGET_ID)
2770 valStr= NLMISC::toString(value);
2772 // Position
2773 else if(propLoged[i]==CLFECOMMON::PROPERTY_POSITION)
2775 // get the delta of move from previous pos stage
2776 CVectorD pos;
2777 float dist= 0.f;
2778 if(newProp.first)
2780 if(newStage.getPos(pos))
2781 dist= float(CVectorD(pos.x-precNewPos.x, pos.y-precNewPos.y,0).norm());
2782 valStr= toString("dst=%.1f pi=%d", dist, newStage.predictedInterval());
2784 else
2786 if(oldStage.getPos(pos))
2787 dist= float(CVectorD(pos.x-precOldPos.x, pos.y-precOldPos.y,0).norm());
2788 valStr= toString("dst=%.1f pi=%d", dist, oldStage.predictedInterval());
2791 // Orientation
2792 else if(propLoged[i]==CLFECOMMON::PROPERTY_ORIENTATION)
2794 C64BitsParts rot;
2795 rot.i64[0] = value;
2797 valStr= toString(sint32(rot.f[0]*180/Pi));
2801 // display log
2802 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());
2808 // ***************************************************************************
2809 void CEntityManager::stopLogStageChange()
2811 _LogStageChange.Enabled= false;
2812 nldebugraw("*** Stop Loging Stage changes");
2815 // ***************************************************************************
2816 bool CEntityManager::isLogingStageChange() const
2818 return _LogStageChange.Enabled;
2821 // ***************************************************************************
2822 sint32 CEntityManager::getLogStageChangeStartCycle() const
2824 if(isLogingStageChange())
2825 return _LogStageChange.StartGameCycle;
2826 else
2827 return 0;
2830 // ***************************************************************************
2831 sint64 CEntityManager::getLogStageChangeStartLocalTime() const
2833 if(isLogingStageChange())
2834 return _LogStageChange.StartLocalTime;
2835 else
2836 return 0;
2839 // ***************************************************************************
2840 void CEntityManager::refreshInsceneInterfaceOfFriendNPC(uint slot)
2842 CCharacterCL *entity = dynamic_cast<CCharacterCL*>(_Entities[slot]);
2843 if (!entity)
2844 return;
2845 if (entity->canHaveMissionIcon()
2846 && entity->isFriend() // only valid once the Contextual property is received
2849 entity->releaseInSceneInterfaces();
2850 entity->buildInSceneInterface();