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 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
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 _ShapeInstances
[idx
].Primitive
= primitive
;
560 _ShapeInstances
[idx
].ContextText
= text
;
561 _ShapeInstances
[idx
].ContextURL
= url
;
562 _ShapeInstances
[idx
].BboxActive
= !text
.empty() || !url
.empty();
563 _ShapeInstances
[idx
].Deleted
= false;
564 _ShapeInstances
[idx
].InIGZone
= inIgZone
> 0;
566 _LastRemovedInstance
= _ShapeInstances
[idx
].LastDeleted
;
567 _ShapeInstances
[idx
].LastDeleted
= -1;
568 TIGZoneShapes::iterator it
= _IgZoneShapes
.find(inIgZone
);
569 if (it
== _IgZoneShapes
.end())
571 vector
<uint32
> shapes
;
572 shapes
.push_back(idx
);
573 _IgZoneShapes
.insert(make_pair(inIgZone
, shapes
));
577 vector
<uint32
> &shapes
= (*it
).second
;
578 shapes
.push_back(idx
);
580 return _ShapeInstances
[idx
];
584 CShapeInstanceReference instref
= CShapeInstanceReference(instance
, text
, url
, !text
.empty() || !url
.empty(), inIgZone
> 0);
585 instref
.Primitive
= primitive
;
586 idx
= _ShapeInstances
.size();
587 _ShapeInstances
.push_back(instref
);
588 TIGZoneShapes::iterator it
= _IgZoneShapes
.find(inIgZone
);
589 if (it
== _IgZoneShapes
.end())
591 vector
<uint32
> shapes
;
592 shapes
.push_back(idx
);
593 _IgZoneShapes
.insert(make_pair(inIgZone
, shapes
));
597 vector
<uint32
> &shapes
= (*it
).second
;
598 shapes
.push_back(idx
);
606 bool CEntityManager::deleteInstance(uint32 idx
)
608 if (!Scene
|| idx
>= _ShapeInstances
.size())
611 if (!_ShapeInstances
[idx
].Instance
.empty())
612 Scene
->deleteInstance(_ShapeInstances
[idx
].Instance
);
613 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
615 PACS
->removePrimitive(primitive
);
617 if (!_ShapeInstances
[idx
].Deleted
)
619 _ShapeInstances
[idx
].Primitive
= NULL
;
620 _ShapeInstances
[idx
].Deleted
= true;
621 _ShapeInstances
[idx
].LastDeleted
= _LastRemovedInstance
;
622 _LastRemovedInstance
= idx
;
628 void CEntityManager::removeInstancesInIgZone(uint16 igZone
)
633 TIGZoneShapes::iterator it
= _IgZoneShapes
.find(igZone
);
634 if (it
!= _IgZoneShapes
.end())
636 vector
<uint32
> &shapes
= (*it
).second
;
637 for (uint i
= 0; i
< shapes
.size(); i
++)
638 deleteInstance(shapes
[i
]);
639 _IgZoneShapes
.erase(it
);
644 CVector
CEntityManager::getInstancePos(uint32 idx
)
646 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
647 return CVector(0,0,0);
649 UInstance instance
= _ShapeInstances
[idx
].Instance
;
651 return CVector(0,0,0);
653 return instance
.getPos();
656 bool CEntityManager::setInstancePos(uint32 idx
, CVector pos
)
658 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
661 UInstance instance
= _ShapeInstances
[idx
].Instance
;
665 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
668 primitive
->setGlobalPosition(_ShapeInstances
[idx
].PrimRelativePos
+ pos
, dynamicWI
);
671 instance
.setPos(pos
);
675 CVector
CEntityManager::getInstanceRot(uint32 idx
)
677 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
678 return CVector(0,0,0);
680 UInstance instance
= _ShapeInstances
[idx
].Instance
;
682 return CVector(0,0,0);
684 return instance
.getRotEuler();
687 bool CEntityManager::setInstanceRot(uint32 idx
, CVector rot
)
689 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
692 UInstance instance
= _ShapeInstances
[idx
].Instance
;
696 instance
.setRotEuler(rot
);
701 CVector
CEntityManager::getInstanceScale(uint32 idx
)
703 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
704 return CVector(0,0,0);
706 UInstance instance
= _ShapeInstances
[idx
].Instance
;
708 return CVector(0,0,0);
710 return instance
.getScale();
713 CVector
CEntityManager::getInstanceColPos(uint32 idx
)
715 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
716 return CVector(0,0,0);
718 return _ShapeInstances
[idx
].PrimRelativePos
;
721 CVector
CEntityManager::getInstanceColScale(uint32 idx
)
723 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
724 return CVector(0,0,0);
726 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
728 return CVector(0,0,0);
731 primitive
->getSize(width
, depth
);
732 float height
= primitive
->getHeight();
734 return CVector(width
, depth
, height
);
737 double CEntityManager::getInstanceColOrient(uint32 idx
)
739 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
742 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
746 return primitive
->getOrientation(dynamicWI
);
749 CVector
CEntityManager::getInstanceBBoxMin(uint32 idx
)
751 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
752 return CVector(0,0,0);
754 UInstance instance
= _ShapeInstances
[idx
].Instance
;
755 if (instance
.empty())
756 return CVector(0,0,0);
758 NLMISC::CAABBox bbox
;
759 _ShapeInstances
[idx
].Instance
.getShapeAABBox(bbox
);
763 if (bbox
.getCenter() == CVector::Null
)
764 bbox_min
= CVector(-0.5f
, -0.5f
, -0.5f
);
766 bbox_min
= bbox
.getMin();
768 bbox_min
.x
*= _ShapeInstances
[idx
].Instance
.getScale().x
;
769 bbox_min
.y
*= _ShapeInstances
[idx
].Instance
.getScale().y
;
770 bbox_min
.z
*= _ShapeInstances
[idx
].Instance
.getScale().z
;
772 return bbox_min
+_ShapeInstances
[idx
].Instance
.getPos();
775 CVector
CEntityManager::getInstanceBBoxMax(uint32 idx
)
777 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
778 return CVector(0,0,0);
780 UInstance instance
= _ShapeInstances
[idx
].Instance
;
782 return CVector(0,0,0);
784 NLMISC::CAABBox bbox
;
785 _ShapeInstances
[idx
].Instance
.getShapeAABBox(bbox
);
789 if (bbox
.getCenter() == CVector::Null
)
790 bbox_max
= CVector(-0.5f
, -0.5f
, -0.5f
);
792 bbox_max
= bbox
.getMax();
794 bbox_max
.x
*= _ShapeInstances
[idx
].Instance
.getScale().x
;
795 bbox_max
.y
*= _ShapeInstances
[idx
].Instance
.getScale().y
;
796 bbox_max
.z
*= _ShapeInstances
[idx
].Instance
.getScale().z
;
798 return bbox_max
+_ShapeInstances
[idx
].Instance
.getPos();
801 bool CEntityManager::removeInstances()
803 if (!Scene
) return false;
805 for(uint i
=0; i
<_ShapeInstances
.size(); ++i
)
807 if (!_ShapeInstances
[i
].InIGZone
)
813 bool CEntityManager::setupInstance(uint32 idx
, const vector
<string
> &keys
, const vector
<string
> &values
)
815 if (!Scene
|| idx
>= _ShapeInstances
.size() || _ShapeInstances
[idx
].Deleted
)
818 UInstance instance
= _ShapeInstances
[idx
].Instance
;
822 UMovePrimitive
*primitive
= _ShapeInstances
[idx
].Primitive
;
824 for (uint32 i
=0; i
< keys
.size(); i
++)
826 string param
= keys
[i
];
827 if (param
== "transparency")
830 if (fromString(values
[i
], t
))
832 t
= max(0, min((int)t
, 255));
833 makeInstanceTransparent(instance
, t
, t
== 255);
836 else if (param
== "colorize")
838 if (values
[i
] == "0")
840 for(uint j
=0;j
<instance
.getNumMaterials();j
++)
842 instance
.getMaterial(j
).setShininess( 10.0f
);
843 instance
.getMaterial(j
).setEmissive(CRGBA(255,255,255,255));
844 instance
.getMaterial(j
).setAmbient(CRGBA(0,0,0,255));
845 instance
.getMaterial(j
).setDiffuse(CRGBA(255,255,255,255));
851 if( fromString( values
[i
], c
) )
853 for(uint j
=0;j
<instance
.getNumMaterials();j
++)
855 instance
.getMaterial(j
).setShininess( 1000.0f
);
856 instance
.getMaterial(j
).setEmissive(c
);
857 instance
.getMaterial(j
).setAmbient(c
);
858 instance
.getMaterial(j
).setDiffuse(c
);
863 else if (param
== "texture")
865 if (!values
[i
].empty())
867 for(uint j
=0;j
<instance
.getNumMaterials();j
++)
869 sint numStages
= instance
.getMaterial(j
).getLastTextureStage() + 1;
870 for(sint l
= 0; l
< numStages
; l
++)
872 if (instance
.getMaterial(j
).isTextureFile((uint
) l
))
873 instance
.getMaterial(j
).setTextureFileName(values
[i
], (uint
) l
);
878 else if (param
== "skeleton")
882 else if (param
== "context")
884 _ShapeInstances
[idx
].ContextText
= values
[i
];
886 else if (param
== "url")
888 _ShapeInstances
[idx
].ContextURL
= values
[i
];
890 else if (param
== "move x" || param
== "move y" || param
== "move z")
893 CVector pos
= getInstancePos(idx
);
895 if (getRelativeFloatFromString(values
[i
], v
))
897 updateVector(param
, pos
, v
, true);
901 updateVector(param
, pos
, v
, false);
903 setInstancePos(idx
, pos
);
905 else if (param
== "rot x" || param
== "rot y" || param
== "rot z")
909 CVector rot
= getInstanceRot(idx
);
911 if (getRelativeFloatFromString(values
[i
], v
))
913 updateVector(param
, rot
, v
, true);
917 updateVector(param
, rot
, v
, false);
919 setInstanceRot(idx
, rot
);
921 else if (param
== "scale x" || param
== "scale y" || param
== "scale z")
924 CVector scale
= instance
.getScale();
926 if (getRelativeFloatFromString(values
[i
], v
))
928 updateVector(param
, scale
, v
, true);
932 updateVector(param
, scale
, v
, false);
934 instance
.setScale(scale
);
937 // Primitive colissions setups
939 if (!primitive
) continue;
941 if (param
== "col size x" || param
== "col size y" || param
== "col size z")
944 primitive
->getSize(width
, depth
);
945 float height
= primitive
->getHeight();
947 CVector size
= CVector(width
, depth
, height
);
949 if (getRelativeFloatFromString(values
[i
], v
))
951 updateVector(param
, size
, v
, true);
955 updateVector(param
, size
, v
, false);
957 primitive
->setSize(size
.x
, size
.y
);
958 primitive
->setHeight(size
.z
);
960 else if (param
== "col pos x" || param
== "col pos y" || param
== "col pos z")
962 CVector pos
= instance
.getPos();
965 if (getRelativeFloatFromString(values
[i
], v
))
967 updateVector(param
, _ShapeInstances
[idx
].PrimRelativePos
, v
, false);
971 if (param
== "col pos x")
972 _ShapeInstances
[idx
].PrimRelativePos
.x
= v
- pos
.x
;
973 if (param
== "col pos y")
974 _ShapeInstances
[idx
].PrimRelativePos
.y
= v
- pos
.y
;
975 if (param
== "col pos z")
976 _ShapeInstances
[idx
].PrimRelativePos
.z
= v
- pos
.z
;
978 primitive
->setGlobalPosition(pos
+ _ShapeInstances
[idx
].PrimRelativePos
, dynamicWI
);
980 else if (param
== "col orientation")
982 double orient
= primitive
->getOrientation(dynamicWI
);
985 if (values
[i
].empty())
988 if (values
[i
][0] == '+')
990 fromString(values
[i
].substr(1), v
);
995 fromString(values
[i
], v
);
999 primitive
->setOrientation(orient
, dynamicWI
);
1001 else if (param
== "col mask player")
1004 fromString(values
[i
], active
);
1005 UMovePrimitive::TCollisionMask mask
=primitive
->getCollisionMask();
1007 primitive
->setCollisionMask(mask
|MaskColPlayer
);
1009 primitive
->setCollisionMask(mask
&~MaskColPlayer
);
1011 else if (param
== "col mask door")
1014 fromString(values
[i
], active
);
1015 UMovePrimitive::TCollisionMask mask
=primitive
->getCollisionMask();
1017 primitive
->setCollisionMask(mask
|MaskColDoor
);
1019 primitive
->setCollisionMask(mask
&~MaskColDoor
);
1021 else if (param
== "col obstacle")
1024 fromString(values
[i
], active
);
1025 primitive
->setObstacle(active
);
1027 else if (param
== "col obstacle")
1037 CShapeInstanceReference
CEntityManager::getShapeInstanceUnderPos(float x
, float y
, sint32
&idx
)
1039 CShapeInstanceReference
selectedInstance(UInstance(), string(""), string(""));
1040 _LastInstanceUnderPos
= NULL
;
1043 // If not initialised, return
1044 if (_ShapeInstances
.empty())
1045 return selectedInstance
;
1048 CMatrix camMatrix
= MainCam
.getMatrix();
1049 CFrustum camFrust
= MainCam
.getFrustum();
1050 CViewport viewport
= Driver
->getViewport();
1052 // Get the Ray made by the mouse.
1054 viewport
.getRayWithPoint(x
, y
, pos
, dir
, camMatrix
, camFrust
);
1055 // Normalize the direction.
1058 // **** Get instances with box intersecting the ray.
1059 float bestDist
= 255;
1060 for(uint i
=0; i
<_ShapeInstances
.size(); i
++)
1062 if (!_ShapeInstances
[i
].Deleted
&& _ShapeInstances
[i
].BboxActive
)
1064 H_AUTO(RZ_Client_GEUP_box_intersect
)
1066 // if intersect the bbox
1067 NLMISC::CAABBox bbox
;
1068 //= _ShapeInstances[i].SelectionBox;
1069 if(!_ShapeInstances
[i
].Instance
.empty())
1071 _ShapeInstances
[i
].Instance
.getShapeAABBox(bbox
);
1075 if (bbox
.getCenter() == CVector::Null
)
1077 bbox_min
= CVector(-0.5f
, -0.5f
, -0.5f
);
1078 bbox_max
= CVector(-0.5f
, -0.5f
, -0.5f
);
1082 bbox_min
= bbox
.getMin();
1083 bbox_max
= bbox
.getMax();
1086 bbox_min
.x
*= _ShapeInstances
[i
].Instance
.getScale().x
;
1087 bbox_min
.y
*= _ShapeInstances
[i
].Instance
.getScale().y
;
1088 bbox_min
.z
*= _ShapeInstances
[i
].Instance
.getScale().z
;
1090 bbox_max
.x
*= _ShapeInstances
[i
].Instance
.getScale().x
;
1091 bbox_max
.y
*= _ShapeInstances
[i
].Instance
.getScale().y
;
1092 bbox_max
.z
*= _ShapeInstances
[i
].Instance
.getScale().z
;
1095 bbox
.setMinMax(bbox_min
+_ShapeInstances
[i
].Instance
.getPos(), bbox_max
+_ShapeInstances
[i
].Instance
.getPos());
1097 if(bbox
.intersect(pos
, pos
+dir
*100.0f
))
1099 float dist
= (bbox
.getCenter()-pos
).norm();
1100 if (dist
< bestDist
)
1102 selectedInstance
= _ShapeInstances
[i
];
1110 return selectedInstance
;
1114 //-----------------------------------------------
1115 // Create an entity according to the slot and the form.
1116 // \param uint slot : slot for the entity.
1117 // \param uint32 form : form to create the entity.
1118 // \param TClientDataSetIndex : persitent id while the entity is connected.
1119 // \return CEntityCL * : pointer on the new entity.
1120 //-----------------------------------------------
1121 CEntityCL
*CEntityManager::create(uint slot
, uint32 form
, const TNewEntityInfo
& newEntityInfo
)
1124 if(verboseVP(NULL
, form
))
1125 nlinfo("(%05d,%03d) EM:create: slot '%u': %s", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), slot
, CSheetId(form
).toString().c_str());
1126 // Check parameter : slot.
1127 if(slot
>= _NbMaxEntity
)
1129 nlwarning("EM:create: Cannot create the entity, the slot '%u' is invalid.", slot
);
1134 // Slot 0 is for the user and so should be allocated only once (at beginning of main loop).
1135 if( slot
== 0 && _Entities
[0] )
1137 if (newEntityInfo
.DataSetIndex
!= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX
)
1139 // Store the dataSetId received
1140 _Entities
[0]->dataSetId(newEntityInfo
.DataSetIndex
);
1142 // Store the alias (although there should not be one for the slot 0!)
1143 _Entities
[0]->npcAlias(newEntityInfo
.Alias
);
1148 // Remove the old one (except the user).
1151 nlwarning("EM:create: There is already an entity in the slot '%u' ! Old entity will be removed.", slot
);
1152 // remove from ground fx manager
1153 // TODO : test if entity has ground fxs
1154 if (_Entities
[slot
]->supportGroundFX())
1156 _GroundFXManager
.remove(_EntityGroundFXHandle
[slot
]);
1158 delete _Entities
[slot
];
1159 _Entities
[slot
] = 0;
1162 // Check parameter : form.
1163 CEntitySheet
*entitySheet
= SheetMngr
.get((CSheetId
)form
);
1164 if(entitySheet
== 0)
1166 nlwarning("EM:create: Attempt on create an entity with a bad form number %d (%s) for the slot '%d' trying to compute the default one.", form
, ((CSheetId
)form
).toString().c_str(), slot
);
1167 CSheetId defaultEntity
;
1168 if(defaultEntity
.buildSheetId(ClientCfg
.DefaultEntity
)==false)
1170 nlwarning("EM:create: The default entity (%s) is not in the sheetid.bin.", ClientCfg
.DefaultEntity
.c_str());
1173 entitySheet
= SheetMngr
.get(defaultEntity
);
1174 if(entitySheet
== 0)
1176 nlwarning("EM:create: The default entity (%s) is not in the sheet manager.", ClientCfg
.DefaultEntity
.c_str());
1181 // Create the entity according to the type.
1184 switch(entitySheet
->type())
1186 case CEntitySheet::RACE_STATS
:
1187 case CEntitySheet::CHAR
:
1190 nlassert (UserEntity
== NULL
);
1191 UserEntity
= new CUserEntity
;
1192 _Entities
[slot
] = UserEntity
;
1196 _Entities
[slot
] = new CPlayerCL
;
1200 case CEntitySheet::FAUNA
:
1202 CCharacterSheet
*sheet
= NLMISC::safe_cast
<CCharacterSheet
*>(entitySheet
);
1203 if (!sheet
->R2Npc
) _Entities
[slot
] = new CCharacterCL
;
1204 else _Entities
[slot
] = new CPlayerR2CL
;
1207 case CEntitySheet::FLORA
:
1208 _Entities
[slot
] = new CCharacterCL
;
1211 case CEntitySheet::FX
:
1212 _Entities
[slot
] = new CFxCL
;
1215 case CEntitySheet::ITEM
:
1216 _Entities
[slot
] = new CItemCL
;
1219 case CEntitySheet::FORAGE_SOURCE
:
1220 _Entities
[slot
] = new CForageSourceCL
;
1224 pushDebugStr(NLMISC::toString("Unknown Form Type '%d' -> entity not created.", entitySheet
->type()));
1228 // If the entity has been right created.
1231 // Set the sheet Id.
1232 _Entities
[slot
]->sheetId((CSheetId
)form
);
1234 _Entities
[slot
]->slot(slot
);
1235 // Set the DataSet Index. AFTER slot(), so bar manager is correctly init
1236 _Entities
[slot
]->dataSetId(newEntityInfo
.DataSetIndex
);
1237 // Set the Mission Giver Alias
1238 _Entities
[slot
]->npcAlias(newEntityInfo
.Alias
);
1239 // Build the entity from a sheet.
1240 if(_Entities
[slot
]->build(entitySheet
))
1242 // Apply properties backuped;
1243 applyBackupedProperties(slot
);
1244 // register to the ground fx manager
1245 if(_Entities
[slot
]->supportGroundFX())
1247 _EntityGroundFXHandle
[slot
] = _GroundFXManager
.add(_Entities
[slot
]);
1250 // Entity is not valid -> REMOVE IT
1253 // Everyone except the User
1256 nlwarning("EM:%d: Cannot build the Entity -> REMOVE IT", slot
);
1257 delete _Entities
[slot
];
1258 _Entities
[slot
] = 0;
1262 nlerror("EM: Cannot build the User");
1265 // Entity Not Allocated
1267 pushDebugStr(NLMISC::toString("Cannot Allocated the Entity in the slot '%d'.", slot
));
1268 // Log problems about the entity creation.
1269 flushDebugStack(NLMISC::toString("Create Entity in slot '%d' with Form '%s' :", slot
, ((CSheetId
)form
).toString().c_str()));
1270 // Return a pointer on the entity created.
1271 return _Entities
[slot
];
1274 //-----------------------------------------------
1276 // Delete an entity.
1277 // \todo GUIGUI : rename into free.
1278 // \todo GUIGUI : finish the function.
1279 //-----------------------------------------------
1280 bool CEntityManager::remove(uint slot
, bool warning
)
1284 nlinfo("EM:remove: slot '%u'.", slot
);
1285 // Check parameter : slot.
1286 if(slot
>= _NbMaxEntity
)
1288 nlwarning("CEntityManager::remove : Attempt on delete a bad slot (slot %d)", slot
);
1292 // Do not delete the user.
1295 nlwarning("CEntityManager::remove : Cannot remove the entity in the slot 0 (user slot).");
1299 // Slot not allocated.
1300 if(_Entities
[slot
] == 0)
1304 nlwarning("CEntityManager::remove : Attempt on delete the slot '%d' that is not allocated.", slot
);
1309 // Remove the entity from others target.
1310 for(uint i
=0; i
<_Entities
.size(); ++i
)
1312 // This entity is not allocated.
1313 if(_Entities
[i
] == 0)
1316 // Inform about the slot of the entity that will be removed.
1317 _Entities
[i
]->slotRemoved(slot
);
1321 if(_Entities
[slot
] != 0)
1323 if (_Entities
[slot
]->supportGroundFX())
1325 _GroundFXManager
.remove(_EntityGroundFXHandle
[slot
]);
1329 // notify the projectile manager that entity has been removed
1330 CProjectileManager::getInstance().entityRemoved(slot
);
1332 // notify the Bar Manager
1334 CBarManager::getInstance()->delEntity(_Entities
[slot
]->slot());
1336 // previous UnderPos?
1337 if(_LastEntityUnderPos
==_Entities
[slot
])
1338 _LastEntityUnderPos
= NULL
;
1341 delete _Entities
[slot
];
1342 _Entities
[slot
] = 0;
1349 //-----------------------------------------------
1350 // removeCollision :
1351 // Remove the collision for all entities.
1352 //-----------------------------------------------
1353 void CEntityManager::removeCollision()
1355 const uint nbEntities
= (uint
)_Entities
.size();
1356 for(uint i
=0; i
<nbEntities
; ++i
)
1358 // Is the entity allocated.
1359 if(_Entities
[i
] == 0)
1362 // Remove the entity primitive.
1363 _Entities
[i
]->removePrimitive();
1365 // Remove the collision entity.
1366 _Entities
[i
]->removeCollisionEntity();
1368 }// removeCollision //
1370 //-----------------------------------------------
1372 // Re-load animations (remove and load).
1373 //-----------------------------------------------
1374 void CEntityManager::reloadAnims()
1376 for(uint i
=0; i
<_Entities
.size(); ++i
)
1380 // Get a reference on the current entity.
1381 CEntityCL
&entity
= *(_Entities
[i
]);
1382 // Change the playlist
1383 entity
.buildPlaylist();
1389 //-----------------------------------------------
1391 // Get a pointer on an entity according to the asked slot.
1392 // \param uint slot : the asked slot.
1393 // \return CEntityCL * : pointer on the entity or 0.
1394 //-----------------------------------------------
1395 CEntityCL
*CEntityManager::entity(uint slot
)
1397 // Return 0 if the slot is the INVALID_SLOT
1398 if(slot
==CLFECOMMON::INVALID_SLOT
)
1400 // Check parameter : slot.
1401 if(slot
>= _Entities
.size())
1403 nlwarning("EM:entity: slot '%u' is invalid.", slot
);
1408 // Return the entity pointer.
1409 return _Entities
[slot
];
1412 //-----------------------------------------------
1413 // entitiesNearDoors :
1414 // Return if there is an entity near a door.
1415 // \param float openingDist : near is when you are under the 'openingDist'.
1416 // \param const CVector& posDoor1 : first door position.
1417 // \param const CVector& posDoor2 : second door position.
1418 // \return bool ; 'true' if any entity is near one of the door.
1419 //-----------------------------------------------
1420 bool CEntityManager::entitiesNearDoors(float openingDist
, const CVector
& posDoor1
, const CVector
& posDoor2
)
1422 for(uint i
=0; i
<_NbMaxEntity
; ++i
)
1424 // Is the entity allocated.
1425 if(_Entities
[i
] == 0)
1428 // Get a reference on the current entity.
1429 CEntityCL
&entity
= *(_Entities
[i
]);
1431 // If the entity is close enough from the door -> return true.
1432 if( ((entity
.pos() - posDoor1
).sqrnorm() < openingDist
)
1433 || ((entity
.pos() - posDoor2
).sqrnorm() < openingDist
) )
1437 // No Entity near the door.
1439 }// entitiesNearDoors //
1441 //-----------------------------------------------
1442 // getEntityListForSelection
1443 //-----------------------------------------------
1444 void CEntityManager::getEntityListForSelection(std::vector
<CEntityCL
*> &entities
, uint flags
)
1446 // According to the view (first or third person), the user can or cannot be selected.
1448 uint firstEntity
= (flags
&CEntityFilterFlag::NotUser
)?1:0;
1449 for(uint i
=firstEntity
; i
<_NbMaxEntity
; ++i
)
1451 // Is the entity allocated and not user mount.
1452 if(_Entities
[i
] == 0 || i
==UserEntity
->mount())
1454 // If entity unselectable, skip
1455 if(!_Entities
[i
]->properties().selectable())
1458 // Apply each filter
1459 if ( (flags
&CEntityFilterFlag::Friend
) && !_Entities
[i
]->isFriend() )
1461 if ( (flags
&CEntityFilterFlag::Enemy
) && !_Entities
[i
]->isEnemy() )
1463 if ( (flags
&CEntityFilterFlag::Alive
) && _Entities
[i
]->isReallyDead() )
1465 if ( (flags
&CEntityFilterFlag::Dead
) && !_Entities
[i
]->isReallyDead() )
1467 if ( (flags
&CEntityFilterFlag::Player
) && !_Entities
[i
]->isPlayer() )
1469 if ( (flags
&CEntityFilterFlag::NonPlayer
) && _Entities
[i
]->isPlayer() )
1472 // Insert every entity in the valid list.
1473 entities
.push_back(_Entities
[i
]);
1477 //-----------------------------------------------
1478 // getEntityUnderPos :
1479 // Get the entity under the (2d) position. Return NULL if not entity under this position.
1480 //-----------------------------------------------
1486 bool operator<(const CSortEntity
&o
) const
1488 return Depth
<o
.Depth
;
1491 CEntityCL
*CEntityManager::getEntityUnderPos(float x
, float y
, float distSelection
, bool &isPlayerUnderCursor
)
1493 H_AUTO (RZ_Client_getEntityUnderPos
)
1496 // valid only if bbox still intersect
1497 CEntityCL
*precEntityUnderPos
= _LastEntityUnderPos
;
1498 bool precEntityUnderPosValid
= false;
1501 isPlayerUnderCursor
= false;
1502 _LastEntityUnderPos
= NULL
;
1504 // If not initialised, return
1505 if (_Entities
.empty())
1509 // **** list of valid entities to test
1510 static vector
<CEntityCL
*> validEntities
;
1511 uint filterFlags
= CEntityFilterFlag::NoFilter
;
1512 getEntityListForSelection(validEntities
, filterFlags
);
1515 CMatrix camMatrix
= MainCam
.getMatrix();
1516 CFrustum camFrust
= MainCam
.getFrustum();
1517 CViewport viewport
= Driver
->getViewport();
1519 // Get the Ray made by the mouse.
1521 viewport
.getRayWithPoint(x
, y
, pos
, dir
, camMatrix
, camFrust
);
1522 // Normalize the direction.
1526 // **** Get entities with box intersecting the ray.
1527 static vector
<CSortEntity
> intersectedEntities
;
1528 intersectedEntities
.clear();
1529 for(i
=0;i
<validEntities
.size();i
++)
1531 H_AUTO(RZ_Client_GEUP_box_intersect
)
1532 CEntityCL
*entity
= validEntities
[i
];
1533 // if entity not visible, skip
1534 if(entity
->getLastClip())
1537 // if intersect the bbox
1538 NLMISC::CAABBox bbox
= entity
->selectBox();
1539 if(bbox
.intersect(pos
, pos
+dir
*distSelection
))
1541 // add this entity to the list of possible entities
1544 e
.Depth
= (bbox
.getCenter()-pos
).norm();
1545 intersectedEntities
.push_back(e
);
1547 // is it the last entity under pos?
1548 if(entity
==precEntityUnderPos
)
1549 precEntityUnderPosValid
= true;
1553 // if no intersected entities, quit
1554 if(intersectedEntities
.empty())
1557 // Compute startDistBox: nearest entity distance, but the user
1559 if(intersectedEntities
[0].Entity
==UserEntity
)
1561 // if the nearest entity is the user, set res
1562 isPlayerUnderCursor
= true;
1563 // if only player intersected, return NULL!
1564 if(intersectedEntities
.size()==1)
1566 // so take the second for startDistBox
1567 startDistBox
= intersectedEntities
[1].Depth
;
1572 startDistBox
= intersectedEntities
[0].Depth
;
1576 // **** get best entity according to distance face-camera or box-ray if no face intersection
1577 CEntityCL
*entitySelected
= NULL
;
1578 float bestDistBox
= FLT_MAX
;
1579 float bestDistZ
= FLT_MAX
;
1580 for(i
=0;i
<intersectedEntities
.size();i
++)
1582 CEntityCL
*entity
= intersectedEntities
[i
].Entity
;
1583 const NLMISC::CAABBox
&bbox
= entity
->selectBox();
1585 // If this entity is the UserEntity, skip!!
1586 if(entity
==UserEntity
)
1589 // if entity skeleton model was clipped, skip
1590 USkeleton
*skeleton
= entity
->skeleton();
1591 if(!ClientCfg
.Light
&& skeleton
&& !skeleton
->getLastClippedState())
1594 H_AUTO(RZ_Client_GEUP_face_intersect
)
1597 // *** Try get face-intersection, result in distZ
1598 // if the entity support fast and precise intersection (and if it succeeds)
1599 bool trueIntersectComputed
= false;
1600 float dist2D
, distZ
;
1601 if(!ClientCfg
.Light
)
1605 if(skeleton
->supportFastIntersect() && skeleton
->fastIntersect(pos
, dir
, dist2D
, distZ
, false))
1606 trueIntersectComputed
= true;
1608 // get the intersection with the instance (bot object)
1609 else if(!entity
->instances().empty() && !entity
->instances()[0].Current
.empty())
1611 UInstance inst
= entity
->instances()[0].Current
;
1612 if(inst
.supportFastIntersect() && inst
.fastIntersect(pos
, dir
, dist2D
, distZ
, false))
1613 trueIntersectComputed
= true;
1617 // if true face-intersection not found
1618 if(!trueIntersectComputed
)
1621 this happens especially for Forage Source. but could happens for anyhting else
1622 In this case, estimate face-instersection, with box:
1623 Suppose full intersection, if the ray is in the 1/3 of the bbox
1626 // clip the ray with the box
1627 CVector a
= pos
, b
= pos
+dir
*distSelection
;
1628 if(!bbox
.clipSegment(a
, b
))
1630 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1631 // This is false, but gives better results.
1634 // Suppose full intersection, if the ray is in the 1/3 of the bbox
1635 CVector itToCenter
= m
-bbox
.getCenter();
1636 itToCenter
.maxof(itToCenter
, -itToCenter
);
1637 CVector smallBoxHS
= bbox
.getHalfSize()*0.3f
;
1638 smallBoxHS
.maxof(smallBoxHS
, -smallBoxHS
);
1639 if(itToCenter
.x
<=smallBoxHS
.x
&& itToCenter
.y
<=smallBoxHS
.y
&& itToCenter
.z
<=smallBoxHS
.z
)
1642 distZ
= (m
-pos
).norm();
1651 // else it's ok, dist2D and distZ are computed
1654 // *** if intersect face, then take the best face-intersection, else use box-ray cost
1655 // true face-col found?
1658 // yes, get the nearest
1663 entitySelected
= entity
;
1669 // if a true face-intersection has not been found for others entities
1670 if(bestDistZ
==FLT_MAX
)
1672 // get the "distance to camera" contribution.
1673 CVector c
= bbox
.getCenter();
1674 float distCamCost
= intersectedEntities
[i
].Depth
;
1675 // get relative to the nearest intersected entity
1676 distCamCost
-= startDistBox
;
1678 // get the ratio "how many the ray is in the bbox"
1679 CVector a
= pos
, b
= pos
+dir
*distSelection
;
1680 bbox
.clipSegment(a
, b
);
1681 // take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
1682 // This is false, but gives better results.
1684 // get the distance to center. NB: small entities are preferred since smaller mean lower cost
1685 float outBBoxCost
= (m
-c
).norm();
1687 // the final cost is a weighted sum of the both. NB: distCamCost is in meter,
1688 // and outBBBoxCost is meters. Hence ClientCfg.SelectionOutBBoxWeight is a factor
1689 float boxCost
= distCamCost
+ outBBoxCost
* ClientCfg
.SelectionOutBBoxWeight
;
1691 // take the lowest cost
1692 if(boxCost
<bestDistBox
)
1694 entitySelected
= entity
;
1695 bestDistBox
= boxCost
;
1701 // If precise intersection not found
1702 if(bestDistZ
==FLT_MAX
)
1704 // if the last entity under pos is valid, prefer it among all other approximate ones
1705 if(precEntityUnderPos
&& precEntityUnderPosValid
)
1706 entitySelected
= precEntityUnderPos
;
1709 // return the best entity
1710 _LastEntityUnderPos
= entitySelected
;
1711 return entitySelected
;
1712 }// getEntityUnderPos //
1715 //-----------------------------------------------
1716 // getEntityInCamera
1717 //-----------------------------------------------
1718 CEntityCL
*CEntityManager::getEntityInCamera(uint flags
, float distSelection
, CLFECOMMON::TCLEntityId precEntity
)
1720 H_AUTO (RZ_Client_getEntityInCamera
)
1722 // If not initialised, return
1723 if (_Entities
.empty())
1726 // list of valid entities
1727 static vector
<CEntityCL
*> validEntitiesTmp
, validEntities
;
1728 getEntityListForSelection(validEntitiesTmp
, flags
);
1730 // Remove entities not selectable by space key
1732 validEntities
.clear();
1733 for (i
=0 ; i
<validEntitiesTmp
.size() ; i
++)
1735 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(validEntitiesTmp
[i
]);
1736 if ((entity
== NULL
) || (entity
&& entity
->isSelectableBySpace()))
1737 validEntities
.push_back(entity
);
1740 // Build the camera pyramid
1741 CMatrix camMatrix
= MainCam
.getMatrix();
1742 CFrustum camFrust
= MainCam
.getFrustum();
1743 static vector
<CPlane
> camPyramid
;
1744 // No need to use worldMatrix. NB: not setuped if ClientLight.
1745 MainCam
.buildCameraPyramid(camPyramid
, false);
1747 // list of entities in screen
1748 static vector
<CSortEntity
> screenEntities
;
1749 screenEntities
.clear();
1751 // compute distance related to the user pos (not camera one).
1752 CVector userPos
= UserEntity
->pos();
1754 // prefer take the direction of the camera (can select backward for instance with camera rotation)
1755 CVector userDir
= View
.currentView().normed();
1757 // Get all entity in this pyramid, and in the dist selection
1758 for(i
=0;i
<validEntities
.size();i
++)
1760 CEntityCL
*entity
= validEntities
[i
];
1761 const NLMISC::CAABBox
&b
= entity
->selectBox();
1763 for(uint j
=0;j
<camPyramid
.size();j
++)
1765 if( !b
.clipBack(camPyramid
[j
]) )
1771 // if In the pyramid
1774 CVector dirToEntity
= b
.getCenter()-userPos
;
1775 CSortEntity eSelect
;
1776 eSelect
.Entity
= entity
;
1777 eSelect
.Depth
= dirToEntity
.norm();
1778 // if in max distance
1779 if(eSelect
.Depth
<distSelection
)
1781 // The lower, the more the influence of direction (minimum should be 1)
1782 const float dirInfluence
= 1.1f
;
1784 // modulate the depth with dot3: force take the most in front of user.
1786 dirToEntity
/= eSelect
.Depth
;
1787 eSelect
.Depth
*= dirInfluence
-dirToEntity
*userDir
;
1789 // append to sort list
1790 screenEntities
.push_back(eSelect
);
1795 // No one in screen?
1796 if(screenEntities
.empty())
1799 // sort them increasingly
1800 sort(screenEntities
.begin(), screenEntities
.end());
1802 // Try to find the precEntity in this list
1803 uint entitySelected
= 0;
1804 if(precEntity
!=CLFECOMMON::INVALID_SLOT
)
1806 for(i
=0;i
<screenEntities
.size();i
++)
1808 // if found the precEntity, get the farther one
1809 if(screenEntities
[i
].Entity
->slot()==precEntity
)
1811 entitySelected
= i
+1;
1815 // reset to 0 if: no more entities, or if the max cycle is reached
1816 if(entitySelected
>=screenEntities
.size() || entitySelected
>=ClientCfg
.SpaceSelectionMaxCycle
)
1821 return screenEntities
[entitySelected
].Entity
;
1824 //-----------------------------------------------
1825 // changeContinent :
1826 // Continent has changed.
1827 //-----------------------------------------------
1828 void CEntityManager::changeContinent()
1830 // Re-create entities primitive.
1831 for(uint i
=0; i
<_NbMaxEntity
; ++i
)
1833 // Is the entity allocated.
1834 if(_Entities
[i
] == 0)
1837 // Compute the new primitive.
1838 _Entities
[i
]->computePrimitive();
1840 // Compute the new collision entity.
1841 _Entities
[i
]->computeCollisionEntity();
1843 }// changeContinent //
1846 //-----------------------------------------------
1847 // updatePreCamera :
1848 // Update entites before the camera position is computed.
1849 // This update the entites position. Evaluate collisions. Compte final world position.
1850 //-----------------------------------------------
1851 void CEntityManager::updatePreCamera()
1853 H_AUTO ( RZ_Client_Entity_Mngr_Update_Pre_Cam
)
1855 // Build an entity list..
1856 _ActiveEntities
.reserve (_Entities
.size ());
1857 _ActiveEntities
.clear ();
1860 // Update entities position.
1861 for(i
=0; i
<_NbMaxEntity
; ++i
)
1863 // Is the entity allocated.
1864 CEntityCL
*entity
= _Entities
[i
];
1868 ++_EntitiesAllocated
;
1869 switch(entity
->Type
)
1871 case CEntityCL::User
:
1873 case CEntityCL::Player
:
1875 /*case CEntityCL::NPC:
1876 case CEntityCL::Fauna:
1877 case CEntityCL::Entity:
1878 case CEntityCL::ForageSource:*/
1882 // Update the list of Active Entities
1883 _ActiveEntities
.push_back (CEntityReference (i
, entity
));
1885 // Adjust the orientation of the NPC in trade with the user.
1886 if(UserEntity
->trader() != CLFECOMMON::INVALID_SLOT
)
1888 CEntityCL
* trader
= _Entities
[UserEntity
->trader()];
1890 trader
->front(UserEntity
->pos() - trader
->pos());
1892 // Adjust the orientation of the NPC in dyn chat with the user.
1893 if(UserEntity
->interlocutor() != CLFECOMMON::INVALID_SLOT
)
1895 CEntityCL
* interlocutor
= _Entities
[UserEntity
->interlocutor()];
1897 interlocutor
->front(UserEntity
->pos() - interlocutor
->pos());
1900 // Update entities position except the User
1901 for(i
=1; i
<_EntitiesAllocated
; ++i
)
1903 CEntityReference
&activeEntity
= _ActiveEntities
[i
];
1905 // Get a poiner on the entity target
1906 CEntityCL
*target
= entity(activeEntity
.Entity
->targetSlot());
1908 // Update the entity.
1909 activeEntity
.Entity
->updatePreCollision(T1
, target
);
1913 // Get a poiner on the entity target
1914 CEntityCL
*target
= entity(UserEntity
->targetSlot());
1915 // update user behaviour/speed/heading/vectorUp/position/bodyHeading
1916 UserEntity
->applyMotion(target
);
1917 // Update the entity.
1918 UserEntity
->updatePreCollision(T1
, target
);
1923 // Time since last Frame
1924 double DTEval
= ((float)(T1
-T0
))*0.001f
;
1925 PACS
->evalCollision(DTEval
, staticWI
); // Eval the static world.
1926 PACS
->evalCollision(DTEval
, dynamicWI
); // Eval the dynamic world.
1927 getDoorManager().getPACSTriggers(); // Copy triggers to be used in update
1928 managePACSTriggers();
1929 UserEntity
->checkPos();
1931 // Update entities position.
1932 for(i
=0; i
<_EntitiesAllocated
; ++i
)
1934 CEntityReference
&activeEntity
= _ActiveEntities
[i
];
1935 // Get a poiner on the entity target
1936 CEntityCL
*target
= entity(activeEntity
.Entity
->targetSlot());
1937 // Update the entity.
1938 activeEntity
.Entity
->updatePostCollision(T1
, target
);
1941 UserEntity
->applyForceLook();
1944 getDoorManager().update(); // Check for trigger to open/close doors
1946 MissionTargetObserver
.update();
1948 }// updatePreCamera //
1951 //-----------------------------------------------
1952 // updatePostCamera :
1953 // Update the entity (position\animation).
1954 // Clip the primitives
1955 // Update visual entites parameters for clipped and non-clipped primitives
1956 // This update the entites position.
1957 //-----------------------------------------------
1958 void CEntityManager::updatePostCamera(uint clippedUpdateTime
, const std::vector
<CPlane
> &clippingPlanes
, const CVector
&camPos
)
1960 H_AUTO ( RZ_Client_Entity_Mngr_Update_Post_Cam
)
1962 // Build a non clipped entity list..
1963 _VisibleEntities
.reserve (_Entities
.size ());
1964 _VisibleEntities
.clear ();
1966 static bool firstTime
= true;
1968 // Clip entities position.
1970 for(i
=0; i
<_EntitiesAllocated
; ++i
)
1972 CEntityReference
&activeEntity
= _ActiveEntities
[i
];
1974 // Get a poiner on the entity target
1975 CEntityCL
*target
= entity(activeEntity
.Entity
->targetSlot());
1978 if (!activeEntity
.Entity
->clipped(clippingPlanes
, camPos
)
1979 || (R2::getEditor().getSelectedInstance() && R2::getEditor().getSelectedInstance()->getEntity()==activeEntity
.Entity
))
1981 // Add to visible primitives
1982 _VisibleEntities
.push_back (activeEntity
);
1983 activeEntity
.Entity
->setLastClip(false);
1984 activeEntity
.Entity
->updateVisible (T1
, target
);
1988 activeEntity
.Entity
->setLastClip(true);
1991 // Update texture Async Loading
1992 activeEntity
.Entity
->updateAsyncTexture();
1994 // Update lod Texture
1995 activeEntity
.Entity
->updateLodTexture();
1999 // Update this clipped primitive at this time ?
2000 if ((activeEntity
.Slot
&RZ_CLIPPED_UPDATE_TIME_MASK
) == clippedUpdateTime
)
2002 activeEntity
.Entity
->updateSomeClipped (T1
, target
);
2005 // Update clipped primitives
2006 activeEntity
.Entity
->updateClipped (T1
, target
);
2010 // Update visible entities post positions.
2011 const uint count
= (uint
)_VisibleEntities
.size ();
2012 for(i
=0; i
<count
; ++i
)
2014 CEntityReference
&visibleEntity
= _VisibleEntities
[i
];
2015 // Get a poiner on the entity target
2016 CEntityCL
*target
= entity(visibleEntity
.Entity
->targetSlot());
2018 visibleEntity
.Entity
->updateVisiblePostPos(T1
, target
);
2022 _GroundFXManager
.update(NLMISC::CVectorD(camPos
));
2025 }// updatePostCamera //
2027 //-----------------------------------------------
2028 // updatePostRender :
2029 // Update entites after the render 3D.
2030 //-----------------------------------------------
2031 void CEntityManager::updatePostRender()
2033 H_AUTO_USE ( RZ_Client_Update_Post_Render
)
2035 TextContext
->setHotSpot(UTextContext::MiddleMiddle
);
2036 TextContext
->setFontSize(ClientCfg
.NameFontSize
);
2039 const uint activeCount
= (uint
)_ActiveEntities
.size ();
2041 for(i
=0; i
<activeCount
; i
++)
2043 CEntityReference
&visibleEntity
= _ActiveEntities
[i
];
2045 // Update in-scene interface
2046 visibleEntity
.Entity
->updateAllPostRender ();
2049 const uint count
= (uint
)_VisibleEntities
.size ();
2050 for(i
=0; i
<count
; ++i
)
2052 CEntityReference
&visibleEntity
= _VisibleEntities
[i
];
2053 // Update Visible Entities after the render.
2054 visibleEntity
.Entity
->updateVisiblePostRender();
2056 // Draw the entity Path.
2057 if(ClientCfg
.ShowPath
)
2058 visibleEntity
.Entity
->drawPath();
2059 // Draw the selection box.
2060 if(ClientCfg
.DrawBoxes
)
2061 visibleEntity
.Entity
->drawBox();
2062 // Display Modifiers (Dmgs/heals).
2064 visibleEntity
.Entity
->displayModifiers();
2067 // Flush any no more used Flying text. Must do it before interface display (to be sure texts are hid)
2068 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2069 pIM
->FlyingTextManager
.releaseNotUsedFlyingText();
2071 }// updatePostRender //
2074 //-----------------------------------------------
2075 // updateVisualProperty :
2076 // Method to update the visual property 'prop' for the entity in 'slot'.
2077 // \param uint slot : slot of the entity to update.
2078 // \param uint prop : the property to udapte.
2079 //-----------------------------------------------
2080 void CEntityManager::updateVisualProperty(const NLMISC::TGameCycle
&gameCycle
, const uint
&slot
, const uint
&prop
, const NLMISC::TGameCycle
&predictedInterval
)
2082 // INFO : log some debug information about visual properties.
2084 nlinfo("EM:updateVP: received prop '%d' for the slot '%d'.", prop
, slot
);
2086 // Check parameter : slot.
2087 if(slot
>= _NbMaxEntity
)
2089 nlwarning("CEntityManager::updateVisualProperty : Slot '%d' is not valid.", slot
);
2093 // Entity still not allocated -> backup values received for the entity.
2094 if(_Entities
[slot
] == 0)
2096 // INFO : log some debug information about visual properties.
2098 nlinfo("EM:updateVP: backup the property as long as the entity is not allocated.", prop
, slot
);
2100 string propName
= toString("SERVER:Entities:E%d:P%d", slot
, prop
);
2102 propty
.GC
= gameCycle
;
2104 // propty.Value = IngameDbMngr.getProp(propName);
2107 TBackupedChanges::iterator it
= _BackupedChanges
.find(slot
);
2108 // Entity does not have any changes backuped for the time.
2109 if(it
== _BackupedChanges
.end())
2111 TProperties propMap
;
2112 propMap
.insert(make_pair(prop
, propty
));
2113 _BackupedChanges
.insert(make_pair(slot
, propMap
));
2115 // Entity already have some changes backuped.
2118 TProperties
&properties
= (*it
).second
;
2119 TProperties::iterator itProp
= properties
.find(prop
);
2120 // This properties is still not backuped for this entity.
2121 if(itProp
== properties
.end())
2122 properties
.insert(make_pair(prop
, propty
));
2123 // There is already a backuped value
2126 nlwarning("EM:updateVP:%d: property '%d' already backuped.", slot
, prop
);
2127 (*itProp
).second
= propty
;
2131 // Entity already allocated -> apply values.
2134 // Call the method from the entity to update the visual property.
2135 _Entities
[slot
]->updateVisualProperty(gameCycle
, prop
, predictedInterval
);
2137 }// updateVisualProperty //
2139 //-----------------------------------------------
2140 // applyBackupedProperties :
2141 //-----------------------------------------------
2142 void CEntityManager::applyBackupedProperties(uint slot
)
2144 TBackupedChanges::iterator it
= _BackupedChanges
.find(slot
);
2145 if(it
!= _BackupedChanges
.end())
2147 TProperties
&properties
= (*it
).second
;
2148 TProperties::iterator itProp
= properties
.begin();
2149 while(itProp
!= properties
.end())
2151 _Entities
[slot
]->updateVisualProperty((*itProp
).second
.GC
, (*itProp
).first
, 0);
2155 _BackupedChanges
.erase(it
);
2157 }// applyBackupedProperties //
2161 //-----------------------------------------------
2163 // Write a file with the position of all entities.
2164 //-----------------------------------------------
2165 void CEntityManager::writeEntities()
2168 if(!f
.open("entities.txt", false, true))
2171 string strTmp
= "StartCommands = {\n";
2172 f
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
2174 const uint nb
= (uint
)_Entities
.size();
2175 for(uint i
=1; i
<nb
; ++i
)
2179 strTmp
= toString("\"%s\",\t\"%f\", \"%f\", \"%f\", \"%f\", \"%f\", \"%f\",\t// %3d\n", _Entities
[i
]->sheetId().toString().c_str(), _Entities
[i
]->pos().x
, _Entities
[i
]->pos().y
, _Entities
[i
]->pos().z
, _Entities
[i
]->front().x
, _Entities
[i
]->front().y
, _Entities
[i
]->front().z
, i
);
2180 f
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
2185 f
.serialBuffer((uint8
*)strTmp
.c_str(), (uint
)strTmp
.size());
2189 }// writeEntities //
2191 //-----------------------------------------------
2193 // Serialize entities.
2194 //-----------------------------------------------
2195 void CEntityManager::serial(NLMISC::IStream
&f
)
2197 // Get nb max entities possible.
2198 f
.serial(_NbMaxEntity
);
2202 initialize(_NbMaxEntity
);
2205 // f.serial(_EntitiesAllocated); no need to serialize this one except maybe to check.
2207 // Serialize each entity.
2208 const uint nb
= (uint
)_Entities
.size();
2209 for(uint i
=0; i
<nb
; ++i
)
2211 NLMISC::CSheetId si
;
2215 si
= _Entities
[i
]->sheetId();
2217 si
= NLMISC::CSheetId::Unknown
;
2223 // Create the entity.
2224 if(f
.isReading() && (si
!= CSheetId::Unknown
))
2226 TNewEntityInfo emptyEntityInfo
;
2227 emptyEntityInfo
.reset();
2228 create(i
, si
.asInt(), emptyEntityInfo
);
2231 // Get/Set entity state.
2233 _Entities
[i
]->serial(f
);
2238 //-----------------------------------------------
2240 // // Dump entities state.
2241 //-----------------------------------------------
2242 void CEntityManager::dump(class NLMISC::IStream
&f
)
2244 // Serialize the class.
2248 //-----------------------------------------------
2250 // Dump entities state (XML Format).
2251 //-----------------------------------------------
2252 void CEntityManager::dumpXML(class NLMISC::IStream
&f
)
2254 // Start the opening of a new node named Identity
2255 f
.xmlPush("Entities");
2257 const uint nb
= (uint
)_Entities
.size();
2258 for(uint i
=0; i
<nb
; ++i
)
2261 // f.xmlComment();//toString("Describ the entity in the slot %d.", i).c_str());
2262 // Start the opening of a new node named Identity
2263 f
.xmlPush(toString("Entity%d", i
));
2267 // Open a new node header named Address
2268 f
.xmlPushBegin("Name");
2269 // Set a property name
2270 f
.xmlSetAttrib ("string");
2271 string n
= _Entities
[i
]->getEntityName();
2273 // Close the new node header
2275 // Close the address node
2278 // Open a new node header named Address
2279 f
.xmlPushBegin("Sheet");
2280 // Set a property name
2281 f
.xmlSetAttrib ("name");
2282 string sheetName
= _Entities
[i
]->sheetId().toString();
2283 f
.serial(sheetName
);
2284 // Close the new node header
2286 // Close the address node
2289 // Open a new node header named Address
2290 f
.xmlPushBegin("Position");
2291 // Close the new node header
2293 f
.serial(_Entities
[i
]->pos());
2294 // Close the address node
2297 // Open a new node header named Address
2298 f
.xmlPushBegin("Front");
2299 // Close the new node header
2301 NLMISC::CVector front
= _Entities
[i
]->front();
2303 // Close the address node
2306 // Open a new node header named Address
2307 f
.xmlPushBegin("Mode");
2308 // Set a property name
2309 f
.xmlSetAttrib ("name");
2310 string mode
= MBEHAV::modeToString(_Entities
[i
]->mode());
2312 // Set a property name
2313 f
.xmlSetAttrib ("num");
2314 uint8 m
= _Entities
[i
]->mode();
2316 // Close the new node header
2318 // Close the address node
2321 // Open a new node header named Address
2322 f
.xmlPushBegin("LastBehaviourPlayed");
2323 // Set a property name
2324 f
.xmlSetAttrib ("name");
2325 string beh
= MBEHAV::behaviourToString(_Entities
[i
]->behaviour());
2327 // Set a property name
2328 f
.xmlSetAttrib ("num");
2329 uint8 b
= _Entities
[i
]->behaviour();
2331 // Close the new node header
2333 // Close the address node
2337 // Close the address node
2341 // Close the identity node
2345 //-----------------------------------------------
2347 CEntityCL
*CEntityManager::getEntityByName (uint32 stringId
) const
2352 const uint count
= (uint
)_Entities
.size();
2353 for (i
=0; i
<count
; i
++)
2356 if(_Entities
[i
]->getNameId() == stringId
)
2357 return _Entities
[i
];
2363 //-----------------------------------------------
2364 CEntityCL
*CEntityManager::getEntityByKeywords (const std::vector
<string
> &keywords
, bool onlySelectable
) const
2366 if (keywords
.empty()) return NULL
;
2368 std::vector
<string
> lcKeywords
;
2369 lcKeywords
.resize(keywords
.size());
2370 for(uint k
= 0; k
< keywords
.size(); k
++)
2372 lcKeywords
[k
] = toLower(keywords
[k
]);
2375 const NLMISC::CVectorD
&userPosD
= UserEntity
->pos();
2376 const uint count
= (uint
)_Entities
.size();
2377 uint selectedEntityId
= 0;
2378 float selectedEntityDist
= FLT_MAX
;
2379 for(uint i
= 0; i
< count
; ++i
)
2381 if (!_Entities
[i
]) continue;
2383 if (onlySelectable
&& !_Entities
[i
]->properties().selectable()) continue;
2385 string lcName
= toLower(_Entities
[i
]->getDisplayName());
2386 if (lcName
.empty()) continue;
2389 for (uint k
= 0; k
< lcKeywords
.size(); ++k
)
2391 if (lcName
.find(lcKeywords
[k
]) == string::npos
)
2400 const NLMISC::CVectorD
&targetPosD
= _Entities
[i
]->pos();
2402 float deltaX
= (float) targetPosD
.x
- (float) userPosD
.x
;
2403 float deltaY
= (float) targetPosD
.y
- (float) userPosD
.y
;
2404 float dist
= (float)sqrt(deltaX
* deltaX
+ deltaY
* deltaY
);
2405 if (dist
< selectedEntityDist
)
2407 selectedEntityDist
= dist
;
2408 selectedEntityId
= i
;
2413 if (selectedEntityDist
!= FLT_MAX
)
2414 return _Entities
[selectedEntityId
];
2419 //-----------------------------------------------
2420 CEntityCL
*CEntityManager::getEntityByName (const string
&name
, bool caseSensitive
, bool complete
) const
2423 source
= caseSensitive
? name
: toLower(name
); // TODO: toLowerInsensitive
2426 const uint count
= (uint
)_Entities
.size();
2427 uint selectedEntityId
= 0;
2428 float selectedEntityDist
= FLT_MAX
; // No selected Entity
2430 for (i
=0; i
<count
; i
++)
2434 string value
= caseSensitive
? _Entities
[i
]->getDisplayName() : toLower(_Entities
[i
]->getDisplayName()); // TODO: toLowerInsensitive
2435 bool foundEntity
= false;
2445 if (NLMISC::startsWith(value
, source
))
2451 const NLMISC::CVectorD
&targetPosD
= _Entities
[i
]->pos();
2452 const NLMISC::CVectorD
&userPosD
= UserEntity
->pos();
2454 float deltaX
= (float) targetPosD
.x
- (float) userPosD
.x
;
2455 float deltaY
= (float) targetPosD
.y
- (float) userPosD
.y
;
2456 float dist
= (float)sqrt(deltaX
* deltaX
+ deltaY
* deltaY
);
2457 if (dist
< selectedEntityDist
)
2459 selectedEntityDist
= dist
;
2460 selectedEntityId
= i
;
2465 if (selectedEntityDist
!= FLT_MAX
) // Entity found
2466 return _Entities
[selectedEntityId
];
2471 //-----------------------------------------------
2473 CEntityCL
*CEntityManager::getEntityByCompressedIndex(TDataSetIndex compressedIndex
) const
2475 if (compressedIndex
!= INVALID_DATASET_ROW
)
2478 const uint count
= (uint
)_Entities
.size();
2479 for (i
=0; i
<count
; i
++)
2482 if(_Entities
[i
]->dataSetId() == compressedIndex
)
2483 return _Entities
[i
];
2488 //-----------------------------------------------
2489 // getEntityBySheetName :
2490 // Return an entity based on its sheet name
2491 //-----------------------------------------------
2492 CEntityCL
*CEntityManager::getEntityBySheetName (const std::string
&sheet
) const
2497 const CSheetId
& sheetRef
= NLMISC::CSheetId(sheet
);
2498 const uint count
= (uint
)_Entities
.size();
2499 for (i
=0; i
<count
; i
++)
2502 if(_Entities
[i
]->sheetId() == sheetRef
)
2503 return _Entities
[i
];
2508 //-----------------------------------------------
2509 // managePACSTriggers :
2510 // Manage PACS Triggers.
2511 //-----------------------------------------------
2512 void CEntityManager::managePACSTriggers()
2515 const uint nNbTrig
= PACS
->getNumTriggerInfo();
2516 for(i
=0; i
<nNbTrig
; ++i
)
2518 const NLPACS::UTriggerInfo
&rTI
= PACS
->getTriggerInfo(i
);
2519 // Detect collisions between user and other entities, to not be block (only the user is a trigger so no need to check).
2520 if(((rTI
.Object0
& 0xFFFF) == UserDataEntity
)
2521 && ((rTI
.Object1
& 0xFFFF) == UserDataEntity
))
2523 UserEntity
->startColTimer();
2529 UserEntity
->stopColTimer();
2530 }// managePACSTriggers //
2533 //-----------------------------------------------
2534 // removeColUserOther :
2536 //-----------------------------------------------
2537 void CEntityManager::removeColUserOther()
2540 const uint count
= (uint
)_Entities
.size();
2541 for(i
=1; i
<count
; i
++)
2545 if(_Entities
[i
]->getPrimitive())
2547 // remove collision only if the entity is Traversable (bot objects may not)
2548 if(_Entities
[i
]->getTraversable())
2549 _Entities
[i
]->getPrimitive()->setObstacle(false);
2553 }// removeColUserOther //
2555 //-----------------------------------------------
2556 // restoreColUserOther :
2558 //-----------------------------------------------
2559 void CEntityManager::restoreColUserOther()
2562 const uint count
= (uint
)_Entities
.size();
2563 for(i
=1; i
<count
; i
++)
2567 if(_Entities
[i
]->getPrimitive())
2568 _Entities
[i
]->getPrimitive()->setObstacle(true);
2571 }// restoreColUserOther //
2573 //-----------------------------------------------
2574 void CEntityManager::removeAllAttachedFX()
2576 for(TEntities::iterator it
= _Entities
.begin(); it
!= _Entities
.end(); ++it
)
2578 if (*it
) (*it
)->removeAllAttachedFX();
2583 // ***************************************************************************
2584 void CEntityManager::resetAllSoundAnimId()
2586 for(uint i
=0;i
<_Entities
.size();i
++)
2588 CEntityCL
*ent
= _Entities
[i
];
2591 ent
->resetAllSoundAnimId();
2596 // ***************************************************************************
2597 #define nldebugraw NLMISC::createDebug(), NLMISC::DebugLog->displayRawNL
2599 // ***************************************************************************
2600 void CEntityManager::startLogStageChange(sint32 currentGameCycle
, sint64 currentLocalTime
)
2603 stopLogStageChange();
2606 _LogStageChange
.Enabled
= true;
2607 _LogStageChange
.StartGameCycle
= currentGameCycle
;
2608 _LogStageChange
.StartLocalTime
= currentLocalTime
;
2609 _LogStageChange
.LastEntityLoged
= CLFECOMMON::INVALID_SLOT
;
2610 _LogStageChange
.StageSet
.clear();
2611 nldebugraw("*** Start Loging Stage changes");
2615 // ***************************************************************************
2616 void CEntityManager::logStageChange(sint64 currentLocalTime
)
2618 if(!_LogStageChange
.Enabled
)
2622 CCharacterCL
*ent
= dynamic_cast<CCharacterCL
*>(entity(WatchedEntitySlot
));
2625 // if the watched entity has been changed
2626 if(WatchedEntitySlot
!=_LogStageChange
.LastEntityLoged
)
2628 _LogStageChange
.LastEntityLoged
= WatchedEntitySlot
;
2630 _LogStageChange
.StageSet
= ent
->_Stages
._StageSet
;
2631 nldebugraw("*** Start Loging Stage changes for Entity %d", WatchedEntitySlot
);
2636 sint32 recGcRef
= _LogStageChange
.StartGameCycle
;
2637 sint64 recTimeRef
= _LogStageChange
.StartLocalTime
;
2638 // compare 2 logs and display differences
2639 CStageSet::TStageSet
&oldStageSet
= _LogStageChange
.StageSet
;
2640 const CStageSet::TStageSet
&newStageSet
= ent
->_Stages
._StageSet
;
2642 // for pos log detail
2643 CVectorD precNewPos
= ent
->pos();
2644 CVectorD precOldPos
= ent
->pos();
2646 // compare each new/old stage
2647 CStageSet::TStageSet::const_iterator itOld
= oldStageSet
.begin();
2648 CStageSet::TStageSet::const_iterator itNew
= newStageSet
.begin();
2649 while(itOld
!=oldStageSet
.end() || itNew
!=newStageSet
.end())
2651 // compare 2 iterators
2652 sint signNewMinusOld
;
2653 if(itNew
==newStageSet
.end())
2654 signNewMinusOld
= +1;
2655 else if(itOld
==oldStageSet
.end())
2656 signNewMinusOld
= -1;
2659 if(itNew
->first
> itOld
->first
)
2660 signNewMinusOld
= +1;
2661 else if(itNew
->first
< itOld
->first
)
2662 signNewMinusOld
= -1;
2667 // if signNewMinusOld= +1, it means an old exist, without a new (=> the stage has been removed)
2668 if(signNewMinusOld
==+1)
2670 logPropertyChange(WatchedEntitySlot
, itOld
->second
, CStage(), precOldPos
, precNewPos
, (sint32
)itOld
->first
-recGcRef
, currentLocalTime
-recTimeRef
);
2671 // new prec pos (if any)
2672 itOld
->second
.getPos(precOldPos
);
2675 // if signNewMinusOld= -1, it means an new exist, without an old (=> the stage has been added)
2676 else if(signNewMinusOld
==-1)
2678 logPropertyChange(WatchedEntitySlot
, CStage(), itNew
->second
, precOldPos
, precNewPos
, (sint32
)itNew
->first
-recGcRef
, currentLocalTime
-recTimeRef
);
2679 // new prec pos (if any)
2680 itNew
->second
.getPos(precNewPos
);
2683 // if ==0, means the stage exist in both, but properties set may be different
2686 logPropertyChange(WatchedEntitySlot
, itOld
->second
, itNew
->second
, precOldPos
, precNewPos
, (sint32
)itNew
->first
-recGcRef
, currentLocalTime
-recTimeRef
);
2687 // new prec pos (if any)
2688 itOld
->second
.getPos(precOldPos
);
2689 itNew
->second
.getPos(precNewPos
);
2695 // bkup the new stage set
2696 oldStageSet
= newStageSet
;
2699 // this entity might have been deleted, stop its log
2702 _LogStageChange
.LastEntityLoged
= CLFECOMMON::INVALID_SLOT
;
2703 _LogStageChange
.StageSet
.clear();
2707 // ***************************************************************************
2708 void CEntityManager::logPropertyChange(CLFECOMMON::TCLEntityId who
, const CStage
&oldStage
, const CStage
&newStage
,
2709 const CVectorD
&precOldPos
, const CVectorD
&precNewPos
, sint32 relGameCycle
, sint64 relLocalTime
)
2711 // For all properties of interest
2712 CLFECOMMON::TPropIndex propLoged
[]= {CLFECOMMON::PROPERTY_POSITION
, CLFECOMMON::PROPERTY_ORIENTATION
,
2713 CLFECOMMON::PROPERTY_MODE
, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
,
2714 CLFECOMMON::PROPERTY_BEHAVIOUR
, CLFECOMMON::PROPERTY_TARGET_ID
,
2715 /*CLFECOMMON::PROPERTY_VISUAL_FX,
2716 CLFECOMMON::PROPERTY_TARGET_LIST_0, CLFECOMMON::PROPERTY_TARGET_LIST_1,
2717 CLFECOMMON::PROPERTY_TARGET_LIST_2, CLFECOMMON::PROPERTY_TARGET_LIST_3*/};
2718 uint32 numProps
= sizeof(propLoged
) / sizeof(propLoged
[0]);
2719 for(uint i
=0;i
<numProps
;i
++)
2721 pair
<bool, sint64
> oldProp
= oldStage
.property(propLoged
[i
]);
2722 pair
<bool, sint64
> newProp
= newStage
.property(propLoged
[i
]);
2723 // if change of the prop, log it
2724 if((oldProp
.first
|| newProp
.first
) && oldProp
!=newProp
)
2726 // get the change reason
2730 else if(!newProp
.first
)
2736 sint64 value
= newProp
.second
;
2737 if(!newProp
.first
) value
= oldProp
.second
;
2740 if(propLoged
[i
]==CLFECOMMON::PROPERTY_MODE
)
2742 valStr
= MBEHAV::TMode((uint64
)value
).toString();
2745 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_BEHAVIOUR
)
2747 valStr
= MBEHAV::CBehaviour((uint64
)value
).toString();
2750 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
)
2752 valStr
= NLMISC::toString(value
);
2754 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
)
2756 valStr
= NLMISC::toString(value
);
2759 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_TARGET_ID
)
2761 valStr
= NLMISC::toString(value
);
2764 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_POSITION
)
2766 // get the delta of move from previous pos stage
2771 if(newStage
.getPos(pos
))
2772 dist
= float(CVectorD(pos
.x
-precNewPos
.x
, pos
.y
-precNewPos
.y
,0).norm());
2773 valStr
= toString("dst=%.1f pi=%d", dist
, newStage
.predictedInterval());
2777 if(oldStage
.getPos(pos
))
2778 dist
= float(CVectorD(pos
.x
-precOldPos
.x
, pos
.y
-precOldPos
.y
,0).norm());
2779 valStr
= toString("dst=%.1f pi=%d", dist
, oldStage
.predictedInterval());
2783 else if(propLoged
[i
]==CLFECOMMON::PROPERTY_ORIENTATION
)
2788 valStr
= toString(sint32(rot
.f
[0]*180/Pi
));
2793 nldebugraw("** Entity %d: (gc=%3d,t=%3d) %s: %s %s", (sint32
)who
, relGameCycle
, (sint32
)relLocalTime
, reason
.c_str(), CLFECOMMON::getPropShortText(propLoged
[i
]), valStr
.c_str());
2799 // ***************************************************************************
2800 void CEntityManager::stopLogStageChange()
2802 _LogStageChange
.Enabled
= false;
2803 nldebugraw("*** Stop Loging Stage changes");
2806 // ***************************************************************************
2807 bool CEntityManager::isLogingStageChange() const
2809 return _LogStageChange
.Enabled
;
2812 // ***************************************************************************
2813 sint32
CEntityManager::getLogStageChangeStartCycle() const
2815 if(isLogingStageChange())
2816 return _LogStageChange
.StartGameCycle
;
2821 // ***************************************************************************
2822 sint64
CEntityManager::getLogStageChangeStartLocalTime() const
2824 if(isLogingStageChange())
2825 return _LogStageChange
.StartLocalTime
;
2830 // ***************************************************************************
2831 void CEntityManager::refreshInsceneInterfaceOfFriendNPC(uint slot
)
2833 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(_Entities
[slot
]);
2836 if (entity
->canHaveMissionIcon()
2837 && entity
->isFriend() // only valid once the Contextual property is received
2840 entity
->releaseInSceneInterfaces();
2841 entity
->buildInSceneInterface();