1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
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>
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/>.
30 #include "entity_cl.h"
32 #include "forage_source_cl.h"
34 #include "pacs_client.h"
35 #include "time_client.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"
52 #include "nel/3d/quad_tree.h"
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"
60 #include "nel/misc/stream.h"
61 #include "nel/misc/common.h"
63 #include "game_share/mission_desc.h"
64 #include "game_share/inventories.h"
66 #include "nel/pacs/u_collision_desc.h"
68 #include "interface_v3/group_compas.h"
70 #include "player_r2_cl.h"
71 #include "r2/editor.h"
77 using namespace NLMISC
;
79 using namespace NLPACS
;
89 extern UDriver
*Driver
;
91 extern UTextContext
*TextContext
;
92 extern UCamera MainCam
;
93 extern CLFECOMMON::TCLEntityId SlotUnderCursor
;
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
)
116 // ***************************************************************************
117 class CMissionTargetObserver
: public ICDBNode::IPropertyObserver
121 // From ICDBNode::IPropertyObserver
122 virtual void update(ICDBNode
* node
)
124 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
128 uint32 oldTarget
= leaf
->getOldValue32();
129 uint32 target
= leaf
->getValue32();
132 CEntityCL
*entity
= NULL
;
134 entity
= EntitiesMngr
.getEntityByName(oldTarget
);
136 entity
->updateMissionTarget();
140 entity
= EntitiesMngr
.getEntityByName(target
);
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()))
153 _PendingMissionTitle
.push_back(leaf
);
158 // When a mission name has been retrieved, update the compass to point it
161 std::list
<CCDBNodeLeaf
*>::iterator it
= _PendingMissionTitle
.begin();
162 while (it
!= _PendingMissionTitle
.end())
164 std::list
<CCDBNodeLeaf
*>::iterator tmpIt
= 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"));
175 nlwarning("Can't retrieve compass group");
178 CCompassTarget ct
= pGC
->getTarget();
180 STRING_MANAGER::CStringManagerClient
*pSMC
= STRING_MANAGER::CStringManagerClient::instance();
182 if (!pSMC
->getDynString(leaf
->getOldValue32(), oldName
))
184 nlwarning("Can't get compass target name");
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
);
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();
205 if (pSMC
->getDynString((*tmpIt
)->getValue32(), name
))
207 // if (_AlreadyReceived.count(name) == 0)
209 // _AlreadyReceived.insert(name);
210 CInterfaceManager
*im
= CInterfaceManager::getInstance();
211 CGroupCompas
*gc
= dynamic_cast<CGroupCompas
*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:compass"));
214 nlwarning("Can't retrieve compass group");
219 CCDBNodeLeaf
*leaf
= *tmpIt
;
220 CCDBNodeBranch
*parent
= leaf
->getParent();
223 CCDBNodeLeaf
*x
= dynamic_cast<CCDBNodeLeaf
*>(parent
->getNode(ICDBNode::CTextId("X")));
224 CCDBNodeLeaf
*y
= dynamic_cast<CCDBNodeLeaf
*>(parent
->getNode(ICDBNode::CTextId("Y")));
227 CSmartPtr
<CNamedEntityPositionState
> tracker
= new CNamedEntityPositionState
;
228 tracker
->build(*tmpIt
, x
, y
);
229 ct
.setPositionState(tracker
);
231 // make the compass appear and blink
236 CWidgetManager::getInstance()->setTopWindow(gc
);
241 _PendingMissionTitle
.erase(tmpIt
);
248 std::list
<CCDBNodeLeaf
*> _PendingMissionTitle
;
249 // std::set<std::string> _AlreadyReceived;
252 //-----------------------------------------------
253 CMissionTargetObserver MissionTargetObserver
;
257 // ***************************************************************************
258 class CTeamUIDObserver
: public ICDBNode::IPropertyObserver
262 // From ICDBNode::IPropertyObserver
263 virtual void update(ICDBNode
* node
)
265 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
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
);
276 entity
->updateIsInTeam();
278 // check if added in team
279 entity
= EntitiesMngr
.getEntityByCompressedIndex(entityId
);
281 entity
->updateIsInTeam();
286 //-----------------------------------------------
288 CTeamUIDObserver TeamUIDObserver
;
291 // ***************************************************************************
292 class CTeamPresentObserver
: public ICDBNode::IPropertyObserver
296 // From ICDBNode::IPropertyObserver
297 virtual void update(ICDBNode
* node
)
299 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
302 // Must get the NAME leaf
303 CCDBNodeBranch
*parent
= leaf
->getParent();
306 leaf
= dynamic_cast<CCDBNodeLeaf
*>(parent
->getNode(ICDBNode::CTextId("UID"), false));
308 CLFECOMMON::TClientDataSetIndex entityId
= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX
;
310 entityId
= leaf
->getValue32();
312 // Scan all entities.
313 // check if added/removed in team
314 CEntityCL
*entity
= EntitiesMngr
.getEntityByCompressedIndex(entityId
);
316 entity
->updateIsInTeam();
322 //-----------------------------------------------
324 CTeamPresentObserver TeamPresentObserver
;
327 // ***************************************************************************
328 class CAnimalUIDObserver
: public ICDBNode::IPropertyObserver
332 // From ICDBNode::IPropertyObserver
333 virtual void update(ICDBNode
* node
)
335 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
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
);
346 entity
->updateIsUserAnimal();
348 // check if added in animal list
349 entity
= EntitiesMngr
.getEntityByCompressedIndex(entityId
);
351 entity
->updateIsUserAnimal();
356 //-----------------------------------------------
358 CAnimalUIDObserver AnimalUIDObserver
;
361 // ***************************************************************************
362 class CAnimalStatusObserver
: public ICDBNode::IPropertyObserver
366 // From ICDBNode::IPropertyObserver
367 virtual void update(ICDBNode
* node
)
369 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
372 // Must get the NAME leaf
373 CCDBNodeBranch
*parent
= leaf
->getParent();
376 leaf
= dynamic_cast<CCDBNodeLeaf
*>(parent
->getNode(ICDBNode::CTextId("UID"), false));
378 CLFECOMMON::TClientDataSetIndex entityId
= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX
;
380 entityId
= leaf
->getValue32();
382 // Scan all entities.
383 // check if added/removed in animal list
384 CEntityCL
*entity
= EntitiesMngr
.getEntityByCompressedIndex(entityId
);
386 entity
->updateIsUserAnimal();
392 //-----------------------------------------------
394 CAnimalStatusObserver AnimalStatusObserver
;
397 // ***************************************************************************
401 //-----------------------------------------------
404 //-----------------------------------------------
405 CEntityManager::CEntityManager()
408 _EntitiesAllocated
= 0;
412 _LastEntityUnderPos
= NULL
;
413 _LastRemovedInstance
= -1;
414 }// CEntityManager //
416 //-----------------------------------------------
419 //-----------------------------------------------
420 CEntityManager::~CEntityManager()
423 }// ~CEntityManager //
426 //-----------------------------------------------
429 //-----------------------------------------------
430 void CEntityManager::initialize(uint nbMaxEntity
)
432 // Set the maximum number of entities.
433 _NbMaxEntity
= 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();
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
]);
495 //-----------------------------------------------
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
)
508 // remove from fx manager
509 if (_Entities
[i
]->supportGroundFX())
511 _GroundFXManager
.remove(_EntityGroundFXHandle
[i
]);
523 // Clean the backuped list.
524 _BackupedChanges
.clear();
526 _GroundFXManager
.reset();
529 //-----------------------------------------------
530 void CEntityManager::reinit()
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
)
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
));
580 vector
<uint32
> &shapes
= (*it
).second
;
581 shapes
.push_back(idx
);
583 return _ShapeInstances
[idx
];
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
));
600 vector
<uint32
> &shapes
= (*it
).second
;
601 shapes
.push_back(idx
);
609 bool CEntityManager::deleteInstance(uint32 idx
, bool force
)
611 if (!Scene
|| idx
>= _ShapeInstances
.size())
614 if (!force
&& _ShapeInstances
[idx
].InIGZone
)
617 if (!_ShapeInstances
[idx
].Instance
.empty())
618 Scene
->deleteInstance(_ShapeInstances
[idx
].Instance
);
620 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].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
;
637 void CEntityManager::removeInstancesInIgZone(uint16 igZone
)
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
;
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
)
670 UInstance instance
= _ShapeInstances
[idx
].Instance
;
674 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
677 primitive
->setGlobalPosition(_ShapeInstances
[idx
].PrimRelativePos
+ pos
, dynamicWI
);
680 instance
.setPos(pos
);
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
;
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
)
701 UInstance instance
= _ShapeInstances
[idx
].Instance
;
705 instance
.setRotEuler(rot
);
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
;
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
;
737 return CVector(0,0,0);
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
)
751 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
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
);
772 if (bbox
.getCenter() == CVector::Null
)
773 bbox_min
= CVector(-0.5f
, -0.5f
, -0.5f
);
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
;
791 return CVector(0,0,0);
793 NLMISC::CAABBox bbox
;
794 _ShapeInstances
[idx
].Instance
.getShapeAABBox(bbox
);
798 if (bbox
.getCenter() == CVector::Null
)
799 bbox_max
= CVector(-0.5f
, -0.5f
, -0.5f
);
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
)
822 bool CEntityManager::setupInstance(uint32 idx
, const vector
<string
> &keys
, const vector
<string
> &values
)
824 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
827 UInstance instance
= _ShapeInstances
[idx
].Instance
;
831 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
833 for (uint32 i
=0; i
< keys
.size(); i
++)
835 string param
= keys
[i
];
836 if (param
== "transparency")
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));
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")
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")
902 CVector pos
= getInstancePos(idx
);
904 if (getRelativeFloatFromString(values
[i
], v
))
906 updateVector(param
, pos
, v
, true);
910 updateVector(param
, pos
, v
, false);
912 setInstancePos(idx
, pos
);
914 else if (param
== "rot x" || param
== "rot y" || param
== "rot z")
918 CVector rot
= getInstanceRot(idx
);
920 if (getRelativeFloatFromString(values
[i
], v
))
922 updateVector(param
, rot
, v
, true);
926 updateVector(param
, rot
, v
, false);
928 setInstanceRot(idx
, rot
);
930 else if (param
== "scale x" || param
== "scale y" || param
== "scale z")
933 CVector scale
= instance
.getScale();
935 if (getRelativeFloatFromString(values
[i
], v
))
937 updateVector(param
, scale
, v
, true);
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")
953 primitive
->getSize(width
, depth
);
954 float height
= primitive
->getHeight();
956 CVector size
= CVector(width
, depth
, height
);
958 if (getRelativeFloatFromString(values
[i
], v
))
960 updateVector(param
, size
, v
, true);
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();
974 if (getRelativeFloatFromString(values
[i
], v
))
976 updateVector(param
, _ShapeInstances
[idx
].PrimRelativePos
, v
, false);
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
);
994 if (values
[i
].empty())
997 if (values
[i
][0] == '+')
999 fromString(values
[i
].substr(1), v
);
1004 fromString(values
[i
], v
);
1008 primitive
->setOrientation(orient
, dynamicWI
);
1010 else if (param
== "col mask player")
1013 fromString(values
[i
], active
);
1014 UMovePrimitive::TCollisionMask mask
=primitive
->getCollisionMask();
1016 primitive
->setCollisionMask(mask
|MaskColPlayer
);
1018 primitive
->setCollisionMask(mask
&~MaskColPlayer
);
1020 else if (param
== "col mask door")
1023 fromString(values
[i
], active
);
1024 UMovePrimitive::TCollisionMask mask
=primitive
->getCollisionMask();
1026 primitive
->setCollisionMask(mask
|MaskColDoor
);
1028 primitive
->setCollisionMask(mask
&~MaskColDoor
);
1030 else if (param
== "col obstacle")
1033 fromString(values
[i
], active
);
1034 primitive
->setObstacle(active
);
1036 else if (param
== "col obstacle")
1046 CShapeInstanceReference
CEntityManager::getShapeInstanceUnderPos(float x
, float y
, sint32
&idx
)
1048 CShapeInstanceReference
selectedInstance(UInstance(), string(""), string(""));
1049 _LastInstanceUnderPos
= NULL
;
1052 // If not initialised, return
1053 if (_ShapeInstances
.empty())
1054 return selectedInstance
;
1057 CMatrix camMatrix
= MainCam
.getMatrix();
1058 CFrustum camFrust
= MainCam
.getFrustum();
1059 CViewport viewport
= Driver
->getViewport();
1061 // Get the Ray made by the mouse.
1063 viewport
.getRayWithPoint(x
, y
, pos
, dir
, camMatrix
, camFrust
);
1064 // Normalize the direction.
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
);
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
);
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
];
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
)
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
);
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
);
1157 // Remove the old one (except the user).
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());
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());
1190 // Create the entity according to the type.
1193 switch(entitySheet
->type())
1195 case CEntitySheet::RACE_STATS
:
1196 case CEntitySheet::CHAR
:
1199 nlassert (UserEntity
== NULL
);
1200 UserEntity
= new CUserEntity
;
1201 _Entities
[slot
] = UserEntity
;
1205 _Entities
[slot
] = new CPlayerCL
;
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
;
1216 case CEntitySheet::FLORA
:
1217 _Entities
[slot
] = new CCharacterCL
;
1220 case CEntitySheet::FX
:
1221 _Entities
[slot
] = new CFxCL
;
1224 case CEntitySheet::ITEM
:
1225 _Entities
[slot
] = new CItemCL
;
1228 case CEntitySheet::FORAGE_SOURCE
:
1229 _Entities
[slot
] = new CForageSourceCL
;
1233 pushDebugStr(NLMISC::toString("Unknown Form Type '%d' -> entity not created.", entitySheet
->type()));
1237 // If the entity has been right created.
1240 // Set the sheet Id.
1241 _Entities
[slot
]->sheetId((CSheetId
)form
);
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
1262 // Everyone except the User
1265 nlwarning("EM:%d: Cannot build the Entity -> REMOVE IT", slot
);
1266 delete _Entities
[slot
];
1267 _Entities
[slot
] = 0;
1271 nlerror("EM: Cannot build the User");
1274 // Entity Not Allocated
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
];
1283 //-----------------------------------------------
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
)
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
);
1301 // Do not delete the user.
1304 nlwarning("CEntityManager::remove : Cannot remove the entity in the slot 0 (user slot).");
1308 // Slot not allocated.
1309 if(_Entities
[slot
] == 0)
1313 nlwarning("CEntityManager::remove : Attempt on delete the slot '%d' that is not allocated.", slot
);
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)
1325 // Inform about the slot of the entity that will be removed.
1326 _Entities
[i
]->slotRemoved(slot
);
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
1343 CBarManager::getInstance()->delEntity(_Entities
[slot
]->slot());
1345 // previous UnderPos?
1346 if(_LastEntityUnderPos
==_Entities
[slot
])
1347 _LastEntityUnderPos
= NULL
;
1350 delete _Entities
[slot
];
1351 _Entities
[slot
] = 0;
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)
1371 // Remove the entity primitive.
1372 _Entities
[i
]->removePrimitive();
1374 // Remove the collision entity.
1375 _Entities
[i
]->removeCollisionEntity();
1377 }// removeCollision //
1379 //-----------------------------------------------
1381 // Re-load animations (remove and load).
1382 //-----------------------------------------------
1383 void CEntityManager::reloadAnims()
1385 for(uint i
=0; i
<_Entities
.size(); ++i
)
1389 // Get a reference on the current entity.
1390 CEntityCL
&entity
= *(_Entities
[i
]);
1391 // Change the playlist
1392 entity
.buildPlaylist();
1398 //-----------------------------------------------
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
)
1409 // Check parameter : slot.
1410 if(slot
>= _Entities
.size())
1412 nlwarning("EM:entity: slot '%u' is invalid.", slot
);
1417 // Return the entity pointer.
1418 return _Entities
[slot
];
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)
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
) )
1446 // No Entity near the door.
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.
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())
1463 // If entity unselectable, skip
1464 if(!_Entities
[i
]->properties().selectable())
1467 // Apply each filter
1468 if ( (flags
&CEntityFilterFlag::Friend
) && !_Entities
[i
]->isFriend() )
1470 if ( (flags
&CEntityFilterFlag::Enemy
) && !_Entities
[i
]->isEnemy() )
1472 if ( (flags
&CEntityFilterFlag::Alive
) && _Entities
[i
]->isReallyDead() )
1474 if ( (flags
&CEntityFilterFlag::Dead
) && !_Entities
[i
]->isReallyDead() )
1476 if ( (flags
&CEntityFilterFlag::Player
) && !_Entities
[i
]->isPlayer() )
1478 if ( (flags
&CEntityFilterFlag::NonPlayer
) && _Entities
[i
]->isPlayer() )
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 //-----------------------------------------------
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
)
1505 // valid only if bbox still intersect
1506 CEntityCL
*precEntityUnderPos
= _LastEntityUnderPos
;
1507 bool precEntityUnderPosValid
= false;
1510 isPlayerUnderCursor
= false;
1511 _LastEntityUnderPos
= NULL
;
1513 // If not initialised, return
1514 if (_Entities
.empty())
1518 // **** list of valid entities to test
1519 static vector
<CEntityCL
*> validEntities
;
1520 uint filterFlags
= CEntityFilterFlag::NoFilter
;
1521 getEntityListForSelection(validEntities
, filterFlags
);
1524 CMatrix camMatrix
= MainCam
.getMatrix();
1525 CFrustum camFrust
= MainCam
.getFrustum();
1526 CViewport viewport
= Driver
->getViewport();
1528 // Get the Ray made by the mouse.
1530 viewport
.getRayWithPoint(x
, y
, pos
, dir
, camMatrix
, camFrust
);
1531 // Normalize the direction.
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())
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
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())
1566 // Compute startDistBox: nearest entity distance, but the user
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)
1575 // so take the second for startDistBox
1576 startDistBox
= intersectedEntities
[1].Depth
;
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
)
1598 // if entity skeleton model was clipped, skip
1599 USkeleton
*skeleton
= entity
->skeleton();
1600 if(!ClientCfg
.Light
&& skeleton
&& !skeleton
->getLastClippedState())
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
)
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
))
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.
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
)
1651 distZ
= (m
-pos
).norm();
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?
1667 // yes, get the nearest
1672 entitySelected
= entity
;
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.
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())
1735 // list of valid entities
1736 static vector
<CEntityCL
*> validEntitiesTmp
, validEntities
;
1737 getEntityListForSelection(validEntitiesTmp
, flags
);
1739 // Remove entities not selectable by space key
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();
1772 for(uint j
=0;j
<camPyramid
.size();j
++)
1774 if( !b
.clipBack(camPyramid
[j
]) )
1780 // if In the pyramid
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.
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())
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;
1824 // reset to 0 if: no more entities, or if the max cycle is reached
1825 if(entitySelected
>=screenEntities
.size() || entitySelected
>=ClientCfg
.SpaceSelectionMaxCycle
)
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)
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
)
1864 // Build an entity list..
1865 _ActiveEntities
.reserve (_Entities
.size ());
1866 _ActiveEntities
.clear ();
1869 // Update entities position.
1870 for(i
=0; i
<_NbMaxEntity
; ++i
)
1872 // Is the entity allocated.
1873 CEntityCL
*entity
= _Entities
[i
];
1877 ++_EntitiesAllocated
;
1878 switch(entity
->Type
)
1880 case CEntityCL::User
:
1882 case CEntityCL::Player
:
1884 /*case CEntityCL::NPC:
1885 case CEntityCL::Fauna:
1886 case CEntityCL::Entity:
1887 case CEntityCL::ForageSource:*/
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()];
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()];
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
);
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
);
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
);
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.
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());
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
);
1997 activeEntity
.Entity
->setLastClip(true);
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
);
2031 _GroundFXManager
.update(NLMISC::CVectorD(camPos
));
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
);
2048 const uint activeCount
= (uint
)_ActiveEntities
.size ();
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).
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.
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
);
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.
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
);
2111 propty
.GC
= gameCycle
;
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.
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
2135 nlwarning("EM:updateVP:%d: property '%d' already backuped.", slot
, prop
);
2136 (*itProp
).second
= propty
;
2140 // Entity already allocated -> apply values.
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);
2164 _BackupedChanges
.erase(it
);
2166 }// applyBackupedProperties //
2170 //-----------------------------------------------
2172 // Write a file with the position of all entities.
2173 //-----------------------------------------------
2174 void CEntityManager::writeEntities()
2177 if(!f
.open("entities.txt", false, true))
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
)
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());
2194 f
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
2198 }// writeEntities //
2200 //-----------------------------------------------
2202 // Serialize entities.
2203 //-----------------------------------------------
2204 void CEntityManager::serial(NLMISC::IStream
&f
)
2206 // Get nb max entities possible.
2207 f
.serial(_NbMaxEntity
);
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
;
2224 si
= _Entities
[i
]->sheetId();
2226 si
= NLMISC::CSheetId::Unknown
;
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.
2242 _Entities
[i
]->serial(f
);
2247 //-----------------------------------------------
2249 // // Dump entities state.
2250 //-----------------------------------------------
2251 void CEntityManager::dump(class NLMISC::IStream
&f
)
2253 // Serialize the class.
2257 //-----------------------------------------------
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
)
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
));
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();
2282 // Close the new node header
2284 // Close the address node
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
2295 // Close the address node
2298 // Open a new node header named Address
2299 f
.xmlPushBegin("Position");
2300 // Close the new node header
2302 f
.serial(_Entities
[i
]->pos());
2303 // Close the address node
2306 // Open a new node header named Address
2307 f
.xmlPushBegin("Front");
2308 // Close the new node header
2310 NLMISC::CVector front
= _Entities
[i
]->front();
2312 // Close the address node
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());
2321 // Set a property name
2322 f
.xmlSetAttrib ("num");
2323 uint8 m
= _Entities
[i
]->mode();
2325 // Close the new node header
2327 // Close the address node
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());
2336 // Set a property name
2337 f
.xmlSetAttrib ("num");
2338 uint8 b
= _Entities
[i
]->behaviour();
2340 // Close the new node header
2342 // Close the address node
2346 // Close the address node
2350 // Close the identity node
2354 //-----------------------------------------------
2356 CEntityCL
*CEntityManager::getEntityByName (uint32 stringId
) const
2361 const uint count
= (uint
)_Entities
.size();
2362 for (i
=0; i
<count
; i
++)
2365 if(_Entities
[i
]->getNameId() == stringId
)
2366 return _Entities
[i
];
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;
2398 for (uint k
= 0; k
< lcKeywords
.size(); ++k
)
2400 if (lcName
.find(lcKeywords
[k
]) == string::npos
)
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
];
2428 //-----------------------------------------------
2429 CEntityCL
*CEntityManager::getEntityByName (const string
&name
, bool caseSensitive
, bool complete
) const
2432 source
= caseSensitive
? name
: toLower(name
); // TODO: toLowerInsensitive
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
++)
2443 string value
= caseSensitive
? _Entities
[i
]->getDisplayName() : toLower(_Entities
[i
]->getDisplayName()); // TODO: toLowerInsensitive
2444 bool foundEntity
= false;
2454 if (NLMISC::startsWith(value
, source
))
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
];
2480 //-----------------------------------------------
2482 CEntityCL
*CEntityManager::getEntityByCompressedIndex(TDataSetIndex compressedIndex
) const
2484 if (compressedIndex
!= INVALID_DATASET_ROW
)
2487 const uint count
= (uint
)_Entities
.size();
2488 for (i
=0; i
<count
; i
++)
2491 if(_Entities
[i
]->dataSetId() == compressedIndex
)
2492 return _Entities
[i
];
2497 //-----------------------------------------------
2498 // getEntityBySheetName :
2499 // Return an entity based on its sheet name
2500 //-----------------------------------------------
2501 CEntityCL
*CEntityManager::getEntityBySheetName (const std::string
&sheet
) const
2506 const CSheetId
& sheetRef
= NLMISC::CSheetId(sheet
);
2507 const uint count
= (uint
)_Entities
.size();
2508 for (i
=0; i
<count
; i
++)
2511 if(_Entities
[i
]->sheetId() == sheetRef
)
2512 return _Entities
[i
];
2517 //-----------------------------------------------
2518 // managePACSTriggers :
2519 // Manage PACS Triggers.
2520 //-----------------------------------------------
2521 void CEntityManager::managePACSTriggers()
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();
2538 UserEntity
->stopColTimer();
2539 }// managePACSTriggers //
2542 //-----------------------------------------------
2543 // removeColUserOther :
2545 //-----------------------------------------------
2546 void CEntityManager::removeColUserOther()
2549 const uint count
= (uint
)_Entities
.size();
2550 for(i
=1; i
<count
; 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()
2571 const uint count
= (uint
)_Entities
.size();
2572 for(i
=1; i
<count
; 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
];
2600 ent
->resetAllSoundAnimId();
2605 // ***************************************************************************
2606 #define nldebugraw NLMISC::createDebug(), NLMISC::DebugLog->displayRawNL
2608 // ***************************************************************************
2609 void CEntityManager::startLogStageChange(sint32 currentGameCycle
, sint64 currentLocalTime
)
2612 stopLogStageChange();
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
)
2631 CCharacterCL
*ent
= dynamic_cast<CCharacterCL
*>(entity(WatchedEntitySlot
));
2634 // if the watched entity has been changed
2635 if(WatchedEntitySlot
!=_LogStageChange
.LastEntityLoged
)
2637 _LogStageChange
.LastEntityLoged
= WatchedEntitySlot
;
2639 _LogStageChange
.StageSet
= ent
->_Stages
._StageSet
;
2640 nldebugraw("*** Start Loging Stage changes for Entity %d", WatchedEntitySlot
);
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;
2668 if(itNew
->first
> itOld
->first
)
2669 signNewMinusOld
= +1;
2670 else if(itNew
->first
< itOld
->first
)
2671 signNewMinusOld
= -1;
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
);
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
);
2692 // if ==0, means the stage exist in both, but properties set may be different
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
);
2704 // bkup the new stage set
2705 oldStageSet
= newStageSet
;
2708 // this entity might have been deleted, stop its log
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
2739 else if(!newProp
.first
)
2745 sint64 value
= newProp
.second
;
2746 if(!newProp
.first
) value
= oldProp
.second
;
2749 if(propLoged
[i
]==CLFECOMMON::PROPERTY_MODE
)
2751 valStr
= MBEHAV::TMode((uint64
)value
).toString();
2754 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_BEHAVIOUR
)
2756 valStr
= MBEHAV::CBehaviour((uint64
)value
).toString();
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
);
2768 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_TARGET_ID
)
2770 valStr
= NLMISC::toString(value
);
2773 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_POSITION
)
2775 // get the delta of move from previous pos stage
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());
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());
2792 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_ORIENTATION
)
2797 valStr
= toString(sint32(rot
.f
[0]*180/Pi
));
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
;
2830 // ***************************************************************************
2831 sint64
CEntityManager::getLogStageChangeStartLocalTime() const
2833 if(isLogingStageChange())
2834 return _LogStageChange
.StartLocalTime
;
2839 // ***************************************************************************
2840 void CEntityManager::refreshInsceneInterfaceOfFriendNPC(uint slot
)
2842 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(_Entities
[slot
]);
2845 if (entity
->canHaveMissionIcon()
2846 && entity
->isFriend() // only valid once the Contextual property is received
2849 entity
->releaseInSceneInterfaces();
2850 entity
->buildInSceneInterface();