Merge branch '164-crash-on-patching-and-possibly-right-after-login' into main/gingo...
[ryzomcore.git] / ryzom / client / src / user_entity.cpp
blob9ec59f6fb117b3abdec50afa64e04e36defb3d03
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /////////////
25 // INCLUDE //
26 /////////////
27 #include "stdpch.h"
28 // Misc.
29 #include "nel/misc/vectord.h"
30 #include "nel/misc/matrix.h"
31 #include "nel/misc/quat.h"
32 // 3D Interface.
33 #include "nel/3d/u_scene.h"
34 #include "nel/3d/u_visual_collision_manager.h"
35 #include "nel/3d/viewport.h"
36 #include "nel/3d/u_bone.h"
37 #include "nel/3d/u_instance_material.h"
38 #include "nel/3d/u_play_list.h"
39 #include "nel/3d/u_point_light.h"
40 #include "nel/3d/u_particle_system_instance.h"
41 #include "nel/3d/u_camera.h"
42 // Pacs Interface
43 #include "nel/pacs/u_global_position.h"
44 // Client.
45 #include "user_entity.h"
46 #include "motion/user_controls.h"
47 #include "pacs_client.h"
48 #include "net_manager.h"
49 #include "time_client.h"
50 #include "entity_animation_manager.h"
51 #include "sheet_manager.h"
52 #include "sound_manager.h"
53 #include "interface_v3/interface_manager.h"
54 #include "entities.h"
55 #include "debug_client.h"
56 #include "misc.h"
57 #include "interface_v3/bot_chat_manager.h"
58 #include "fx_manager.h"
59 #include "main_loop.h"
60 #include "interface_v3/group_in_scene_bubble.h"
61 #include "interface_v3/inventory_manager.h"
62 #include "nel/gui/group_html.h"
63 #include "interface_v3/people_interraction.h"
64 #include "init_main_loop.h"
65 #include "view.h"
66 #include "interface_v3/sphrase_manager.h"
67 #include "interface_v3/sbrick_manager.h"
68 #include "interface_v3/action_phrase_faber.h"
69 #include "interface_v3/bar_manager.h"
70 #include "interface_v3/skill_manager.h"
71 #include "far_tp.h"
72 #include "npc_icon.h"
73 // game share
74 #include "game_share/slot_types.h"
75 #include "game_share/player_visual_properties.h"
76 #include "game_share/mode_and_behaviour.h"
77 #include "game_share/inventories.h"
78 #include "game_share/animal_type.h"
79 #include "game_share/bot_chat_types.h"
80 // Sound animation
81 #include "nel/sound/sound_anim_manager.h"
82 #include "nel/sound/sound_animation.h"
83 #include "nel/sound/sound_anim_marker.h"
84 // r2
85 #include "r2/editor.h"
87 #ifdef DEBUG_NEW
88 #define new DEBUG_NEW
89 #endif
91 ///////////
92 // USING //
93 ///////////
94 using namespace NLMISC;
95 using namespace NLPACS;
96 using namespace std;
97 using NL3D::UScene;
98 using NL3D::UVisualCollisionManager;
99 using NL3D::UTextContext;
102 ////////////
103 // EXTERN //
104 ////////////
105 extern UScene *Scene;
106 extern UVisualCollisionManager *CollisionManager;
107 extern CEntityAnimationManager *EAM;
108 extern UTextContext *TextContext;
109 extern NL3D::UCamera MainCam;
111 // Context help
112 extern void contextHelp (const std::string &help);
114 extern void beastOrder (const std::string &orderStr, const std::string &beastIndexStr, bool confirmFree = true);
116 // Out game received position
117 NLMISC::CVectorD UserEntityInitPos;
118 NLMISC::CVector UserEntityInitFront;
119 CUserEntity *UserEntity = NULL;
121 uint32 CharFirstConnectedTime = 0;
122 uint32 CharPlayedTime = 0;
124 const double MaxExtractionDistance = 1.0f;
126 ////////////
127 // GLOBAL //
128 ////////////
130 // Hierarchical timer
131 H_AUTO_DECL ( RZ_Client_Update_Sound )
133 //////////////
134 // FUNCTION //
135 //////////////
136 //string chooseRandom( const vector<string>& sounds, uint32& previousIndex );
138 //-----------------------------------------------
139 // CUserEntity :
140 // Constructor.
141 //-----------------------------------------------
142 CUserEntity::CUserEntity()
143 : CPlayerCL()
145 Type = User;
146 _Run = false;
147 _RunWhenAble = false;
148 _WalkVelocity = 1.0f;
149 _RunVelocity = 2.0f;
150 _CurrentVelocity = _WalkVelocity;
152 _FrontVelocity = 0.0f;
153 _LateralVelocity = 0.0f;
155 _SpeedServerAdjust = 1.0f;
157 // \todo GUIGUI : do it more generic.
158 _First_Pos = false;
160 // No selection, trader, interlocutor at the beginning.
161 _Selection = CLFECOMMON::INVALID_SLOT;
162 _Trader = CLFECOMMON::INVALID_SLOT;
163 _Interlocutor = CLFECOMMON::INVALID_SLOT;
165 // Not selectable at the beginning.
166 _Selectable = false;
168 // Your are not on a mount at the beginning.
169 _OnMount = false;
170 _HiddenMount = CLFECOMMON::INVALID_SLOT;
172 _AnimAttackOn = false;
174 _ViewMode = FirstPV;
175 _PermanentDeath = false;
176 _FollowMode = false;
178 _CheckPrimitive = 0;
179 // The user is not in collision with someone else.
180 _ColOn = false;
181 // Collisions are not removed.
182 _ColRemoved = false;
184 // No Move To at the beginning.
185 _MoveToSlot = CLFECOMMON::INVALID_SLOT;
186 _MoveToAction= CUserEntity::None;
187 _MoveToDist= 0.0;
188 _MoveToColStartTime= 0;
189 _HeadPitch = Pi/2;
190 _FollowForceHeadPitch= false;
192 _ForceLookSlot= CLFECOMMON::INVALID_SLOT;
193 _LastExecuteCombatSlot= CLFECOMMON::INVALID_SLOT;
195 _R2CharMode= R2::TCharMode::Player;
197 _CameraMoves = NLMISC::CVector(0, 0, 0);
198 }// CUserEntity //
201 //-----------------------------------------------
202 // ~CUserEntity :
203 // Destructor.
204 //-----------------------------------------------
205 CUserEntity::~CUserEntity()
207 // Remove observers
208 _SpeedFactor.release();
209 _MountHunger.release();
210 _MountSpeeds.release();
212 CInterfaceManager *pIM = CInterfaceManager::getInstance();
215 CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:IS_INVISIBLE", false);
216 if (node)
218 ICDBNode::CTextId textId;
219 node->removeObserver(&_InvisibleObs, textId);
223 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
225 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
226 if(node)
228 ICDBNode::CTextId textId;
229 node->removeObserver(_SkillPointObs+i, textId);
233 for( uint i=0; i<_FamesObs.size(); ++i )
235 uint32 factionIndex = _FamesObs[i]->FactionIndex;
236 uint32 fameIndexInDatabase = CStaticFames::getInstance().getDatabaseIndex(factionIndex);
237 string sDBPath = toString("SERVER:FAME:PLAYER%d:VALUE",fameIndexInDatabase);
239 CCDBNodeLeaf * node = NLGUI::CDBManager::getInstance()->getDbProp(sDBPath, false);
240 if(node)
242 ICDBNode::CTextId textId;
243 node->removeObserver(_FamesObs[i], textId);
246 contReset(_FamesObs);
248 CNPCIconCache::getInstance().removeObservers();
250 // Remove the Primitive used for check (because ~CEntityCL() will call CEntityCL::removePrimitive(), not CUserEntity::removePrimitive())
251 removeCheckPrimitive();
253 CNPCIconCache::release();
255 }// ~CUserEntity //
257 //-----------------------------------------------
258 // initProperties :
259 // Initialize properties of the entity (according to the class).
260 //-----------------------------------------------
261 void CUserEntity::initProperties()
263 properties().selectable(true);
264 }// initProperties //
267 //-----------------------------------------------
268 // build :
269 // Build the entity from a sheet.
270 //-----------------------------------------------
271 bool CUserEntity::build(const CEntitySheet *sheet) // virtual
273 // Init received position
274 pos(UserEntityInitPos);
275 front(UserEntityInitFront);
276 dir(front());
277 setHeadPitch(0);
279 // Cast the sheet in the right type.
280 _PlayerSheet = dynamic_cast<const CRaceStatsSheet *>(sheet);
281 if(_PlayerSheet == 0)
283 pushDebugStr("User Sheet is not a valid '.race_stats'.");
284 return false;
286 else
287 pushInfoStr("User Sheet is a valid '.race_stats'.");
288 // Get the DB Entry
289 if(IngameDbMngr.getNodePtr())
291 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
292 if(nodeRoot)
294 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
295 if(_DBEntry == 0)
296 pushDebugStr("Cannot get a pointer on the DB entry.");
300 disableFollow();
301 // Walk/Run ?
302 if(ClientCfg.RunAtTheBeginning != _Run)
303 switchVelocity();
305 // Set the up of the user.
306 up(CVector(0,0,1));
308 // Init User infos.
309 eyesHeight(ClientCfg.EyesHeight);
310 walkVelocity(ClientCfg.Walk);
311 runVelocity(ClientCfg.Run);
313 // Compute the first automaton.
314 _CurrentAutomaton = automatonType() + "_normal.automaton";
316 // Build the PACS Primitive.
317 if(initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::Slide, (UMovePrimitive::TTrigger)(UMovePrimitive::OverlapTrigger | UMovePrimitive::EnterTrigger), MaskColPlayer, MaskColPlayer | MaskColNpc | MaskColDoor))
318 _Primitive->insertInWorldImage(dynamicWI);
320 // Compute the element to be able to snap the entity to the ground.
321 computeCollisionEntity();
323 // Initialize properties of the client.
324 initProperties();
326 // Initialize the observer for the speed factor and mount stuff
327 _SpeedFactor.init();
328 _MountHunger.init();
329 _MountSpeeds.init();
331 // Create the user playlist
332 createPlayList();
334 // Initialize the internal time.
335 _LastFrameTime = ((double)T1) * 0.001;
337 // Set the gender in local mode.
338 if(ClientCfg.Local)
340 _Mode = MBEHAV::NORMAL;
341 _ModeWanted = MBEHAV::NORMAL;
342 _Gender = ClientCfg.Sex;
343 SPropVisualA visualA = buildPropVisualA(_PlayerSheet->GenderInfos[_Gender]);
344 SPropVisualB visualB = buildPropVisualB(_PlayerSheet->GenderInfos[_Gender]);
345 SPropVisualC visualC;
346 visualA.PropertySubData.Sex = _Gender;
347 visualC.PropertyC = 0;
348 visualC.PropertySubData.CharacterHeight = 0;
349 visualC.PropertySubData.ArmsWidth = 7;
350 visualC.PropertySubData.LegsWidth = 7;
351 visualC.PropertySubData.TorsoWidth = 7;
352 visualC.PropertySubData.BreastSize = 7;
353 // Set the Database
354 sint64 *prop = (sint64 *)&visualA;
355 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->setValue64(*prop); // Set the database
356 prop = (sint64 *)&visualB;
357 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->setValue64(*prop); // Set the database
358 prop = (sint64 *)&visualC;
359 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPC))->setValue64(*prop); // Set the database
360 // Apply Changes.
361 updateVisualProperty(0, CLFECOMMON::PROPERTY_VPA);
363 // \todo GUIGUI Retrieve the player's appearence during the avatar selection.
364 // Get Visual Properties From the character selection window.
365 else
369 // Rebuild interface
370 buildInSceneInterface ();
372 // Add observer on invisible property
373 CInterfaceManager *pIM = CInterfaceManager::getInstance();
375 CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:IS_INVISIBLE", false);
376 if (node)
378 ICDBNode::CTextId textId;
379 node->addObserver(&_InvisibleObs, textId);
383 // Add an observer on skill points
384 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
386 _SkillPointObs[i].SpType= i;
387 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
388 if(node)
390 ICDBNode::CTextId textId;
391 node->addObserver(_SkillPointObs+i, textId);
395 // Add an observer on Fames
396 for( uint i=(uint)PVP_CLAN::BeginClans; i<=(uint)PVP_CLAN::EndClans; ++i )
398 uint32 factionIndex = PVP_CLAN::getFactionIndex((PVP_CLAN::TPVPClan)i);
399 uint32 fameIndexInDatabase = CStaticFames::getInstance().getDatabaseIndex(factionIndex);
400 string sDBPath = toString("SERVER:FAME:PLAYER%d:VALUE",fameIndexInDatabase);
402 CFameObserver * fameObs = new CFameObserver();
403 if( fameObs )
405 fameObs->FactionIndex = factionIndex;
406 CCDBNodeLeaf * node = NLGUI::CDBManager::getInstance()->getDbProp(sDBPath, false);
407 if(node)
409 ICDBNode::CTextId textId;
410 node->addObserver(fameObs, textId);
412 _FamesObs.push_back(fameObs);
416 // Add an observer on Mission Journal
417 CNPCIconCache::getInstance().addObservers();
419 // Initialize the camera distance.
420 View.cameraDistance(ClientCfg.CameraDistance);
422 // char and account time properties
423 CSkillManager *pSM = CSkillManager::getInstance();
424 if( pSM )
426 pSM->tryToUnblockTitleFromCharOldness( CharFirstConnectedTime );
427 pSM->tryToUnblockTitleFromCharPlayedTime( CharPlayedTime );
430 // Entity created.
431 return true;
432 }// build //
435 //-----------------------------------------------
436 // eyesHeight :
437 // \todo GUIGUI : do it better in mount mode
438 //-----------------------------------------------
439 float CUserEntity::eyesHeight()
441 if(!_OnMount)
442 return _EyesHeight * _CharacterScalePos;
443 else
444 return _EyesHeight * _CharacterScalePos;
445 }// eyesHeight //
448 /////////////////////////////////////////////////
449 /////////////////////////////////////////////////
450 /////////////// VISUAL PROPERTIES ///////////////
451 /////////////////////////////////////////////////
452 /////////////////////////////////////////////////
453 //-----------------------------------------------
454 // updateVisualPropertyPos :
455 // Update Entity Position.
456 //-----------------------------------------------
457 void CUserEntity::updateVisualPropertyPos(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */, const NLMISC::TGameCycle &/* pI */)
459 }// updateVisualPropertyPos //
461 //-----------------------------------------------
462 // updateVisualPropertyOrient :
463 // Update Entity Orientation.
464 //-----------------------------------------------
465 void CUserEntity::updateVisualPropertyOrient(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */)
467 }// updateVisualPropertyOrient //
470 void CUserEntity::updateVisualPropertyTargetList(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */, uint /* listIndex */)
474 void CUserEntity::updateVisualPropertyVisualFX(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
476 applyVisualFX(prop);
479 //-----------------------------------------------
480 // updateVisualPropertyBehaviour :
481 // Update Entity Behaviour.
482 //-----------------------------------------------
483 void CUserEntity::updateVisualPropertyBehaviour(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
485 // Compute the behaviour.
486 CBehaviourContext bc;
487 bc.Behav = MBEHAV::CBehaviour(prop);
488 bc.BehavTime = TimeInSec;
489 if(VerboseAnimUser)
491 nlinfo("UE::updateVPBeha: '%d(%s)'.", (sint)bc.Behav.Behaviour, MBEHAV::behaviourToString(bc.Behav.Behaviour).c_str());
493 CCDBNodeLeaf *targetList0 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_0));
494 CCDBNodeLeaf *targetList1 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_1));
495 CCDBNodeLeaf *targetList2 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_2));
496 CCDBNodeLeaf *targetList3 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_3));
497 if (targetList0 && targetList1 && targetList2 && targetList3)
499 uint64 vp[4] =
501 (uint64) targetList0->getValue64(),
502 (uint64) targetList1->getValue64(),
503 (uint64) targetList2->getValue64(),
504 (uint64) targetList3->getValue64()
506 bc.Targets.unpack(vp, 4);
508 applyBehaviour(bc);
509 }// updateVisualPropertyBehaviour //
511 //-----------------------------------------------
512 // updateVisualPropertyName :
513 // Update Entity Name.
514 //-----------------------------------------------
515 void CUserEntity::updateVisualPropertyName(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
517 uint32 oldNameId = _NameId;
519 CPlayerCL::updateVisualPropertyName(gameCycle, prop);
521 // Name changed ?
522 /* if (oldNameId != _NameId)
524 CInterfaceManager *pIM = CInterfaceManager::getInstance();
525 CInterfaceElement *element = CWidgetManager::getInstance()->getElementFromId("ui:interface:mailbox:content:html");
526 if (element)
528 CGroupHTML *html = dynamic_cast<CGroupHTML*>(element);
529 if (html)
530 html->browse("home");
534 }// updateVisualPropertyName //
536 //-----------------------------------------------
537 // updateVisualPropertyTarget :
538 // Update Entity Target.
539 //-----------------------------------------------
540 void CUserEntity::updateVisualPropertyTarget(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */)
542 // Don't override the Player Target, cause client side entirely => no lag.
543 //targetSlot((CLFECOMMON::TCLEntityId)prop);
544 }// updateVisualPropertyTarget //
546 //-----------------------------------------------
547 // updateVisualPropertyMode :
548 // New mode received -> immediately change the mode for the user.
549 // \warning Read the position or orientation from the database when reading the mode (no more updated in updateVisualPropertyPos and updateVisualPropertyOrient).
550 //-----------------------------------------------
551 void CUserEntity::updateVisualPropertyMode(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
553 // Combat Float Check
554 if((MBEHAV::EMode)prop == MBEHAV::COMBAT_FLOAT)
556 nlwarning("UE:updateVPMode: the user should never have the COMBAT_FLOAT mode.");
557 return;
559 // New Mode Received.
560 _TheoreticalMode = (MBEHAV::EMode)prop;
561 // Change the user mode.
562 mode(_TheoreticalMode);
563 }// updateVisualPropertyMode //
565 //-----------------------------------------------
566 // updateVisualPropertyVpa :
567 // Update Entity Visual Property A
568 //-----------------------------------------------
569 void CUserEntity::updateVisualPropertyVpa(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
571 CPlayerCL::updateVisualPropertyVpa(gameCycle, prop);
573 // Special for user: Disable Character Lod, because always want it at full rez.
574 if(!_Skeleton.empty())
576 _Skeleton.setLodCharacterShape(-1);
579 updateVisualDisplay();
580 }// updateVisualPropertyVpa //
582 //-----------------------------------------------
583 // updateVisualPropertyVpb :
584 // Update Entity Visual Property B
585 //-----------------------------------------------
586 void CUserEntity::updateVisualPropertyVpb(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
588 CPlayerCL::updateVisualPropertyVpb(gameCycle, prop);
589 updateVisualDisplay();
590 }// updateVisualPropertyVpb //
592 //-----------------------------------------------
593 // updateVisualPropertyVpc :
594 // Update Entity Visual Property C
595 //-----------------------------------------------
596 void CUserEntity::updateVisualPropertyVpc(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
598 CPlayerCL::updateVisualPropertyVpc(gameCycle, prop);
599 updateVisualDisplay();
600 }// updateVisualPropertyVpc //
602 //-----------------------------------------------
603 // updateVisualPropertyEntityMounted :
604 // Update Entity Mount
605 //-----------------------------------------------
606 void CUserEntity::updateVisualPropertyEntityMounted(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
608 if(isFighting())
609 CPlayerCL::updateVisualPropertyEntityMounted(gameCycle, prop);
610 else
611 _Mount = (CLFECOMMON::TCLEntityId)prop;
612 }// updateVisualPropertyEntityMounted //
614 //-----------------------------------------------
615 // updateVisualPropertyRiderEntity :
616 // Update Entity Rider
617 //-----------------------------------------------
618 void CUserEntity::updateVisualPropertyRiderEntity(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
620 if(isFighting())
621 CPlayerCL::updateVisualPropertyRiderEntity(gameCycle, prop);
622 else
623 _Rider = (CLFECOMMON::TCLEntityId)prop;
624 }// updateVisualPropertyRiderEntity //
626 //-----------------------------------------------
627 //-----------------------------------------------
628 void CUserEntity::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
630 CPlayerCL::updateVisualPropertyPvpMode(gameCycle, prop);
631 // Additionaly, inform interface of the change
632 CInterfaceManager *pIM= CInterfaceManager::getInstance();
633 // For PVP ZoneFaction
634 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_MODE");
635 if(pDB)
637 sint32 val= pDB->getValue32();
638 pDB->setValue32(val+1);
640 // For Any PVP change
641 pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
642 if(pDB)
644 sint32 val= pDB->getValue32();
645 pDB->setValue32(val+1);
649 //-----------------------------------------------
650 //-----------------------------------------------
651 void CUserEntity::updateVisualPropertyOutpostInfos(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
653 CPlayerCL::updateVisualPropertyOutpostInfos(gameCycle, prop);
654 // For Any PVP change
655 CInterfaceManager *pIM= CInterfaceManager::getInstance();
656 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
657 if(pDB)
659 sint32 val= pDB->getValue32();
660 pDB->setValue32(val+1);
664 //-----------------------------------------------
665 //-----------------------------------------------
666 void CUserEntity::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
668 CPlayerCL::updateVisualPropertyPvpClan(gameCycle, prop);
669 // For Any PVP change
670 CInterfaceManager *pIM= CInterfaceManager::getInstance();
671 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
672 if(pDB)
674 sint32 val= pDB->getValue32();
675 pDB->setValue32(val+1);
680 /////////////////////////////////////////////////
681 /////////////////////////////////////////////////
682 /////////////////////////////////////////////////
683 /////////////////////////////////////////////////
686 //-----------------------------------------------
687 // mode :
688 // Method called to change the mode (Combat/Mount/etc.).
689 // \todo GUIGUI : apply stage in combat modes instead of just removing them.
690 // \todo GUIGUI : go or teleport the player to the mode position (see how to manage it).
691 //-----------------------------------------------
692 bool CUserEntity::mode(MBEHAV::EMode m)
694 if(Verbose & VerboseAnim)
695 nlinfo("UE::mode: old mode '%s(%d)' new mode '%s(%d)'.", MBEHAV::modeToString(_Mode).c_str(), _Mode, MBEHAV::modeToString(m).c_str(), m);
696 // Nothing to do if the mode is the same as the previous one.
697 if(m == _Mode)
698 return true;
699 // Release the old Mode.
700 switch(_Mode)
702 // Leave COMBAT Mode
703 case MBEHAV::COMBAT:
704 case MBEHAV::COMBAT_FLOAT:
706 // If there are some stage not complete -> remove them
707 if(_Stages._StageSet.size() != 0)
708 _Stages._StageSet.clear();
710 break;
711 // Leave MOUNTED Mode
712 case MBEHAV::MOUNT_NORMAL:
713 case MBEHAV::MOUNT_SWIM:
715 if ( m == MBEHAV::REST )
717 // can't go afk while mounting
718 return false;
721 // if changing mode for another mount mode, do nothing
722 if (m != MBEHAV::MOUNT_NORMAL && m != MBEHAV::MOUNT_SWIM)
724 // Display the mount again.
725 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
726 if(mount)
728 // Set the mount.
729 mount->rider(CLFECOMMON::INVALID_SLOT);
730 mount->_Stages._StageSet.clear();
731 mount->setMode(MBEHAV::NORMAL);
732 mount->computeAutomaton();
733 mount->computeAnimSet();
734 mount->setAnim(CAnimationStateSheet::Idle);
735 if(mount->getPrimitive())
736 mount->getPrimitive()->setOcclusionMask(MaskColNpc); // the mount is an npc
737 mount->_ForbidClipping = false;
740 _Mount = CLFECOMMON::INVALID_SLOT;
741 // Restore the user Primitive
742 if(_Primitive)
744 _Primitive->setRadius( std::min(0.5f, (float)(RYZOM_ENTITY_SIZE_MAX/2)) );
745 _Primitive->setHeight(2);
747 _OnMount = false;
749 // Shift the player position (not to stand inside the mount)
750 CVectorD unmountShift;
751 unmountShift.sphericToCartesian(1.0, frontYaw() + NLMISC::Pi/2, 0);
752 pacsPos(pos() + unmountShift);
754 // Restore running if necessary
755 if (!_Run && _RunWhenAble)
757 switchVelocity();
761 if (_Mode == MBEHAV::MOUNT_SWIM && ( m == MBEHAV::COMBAT || m == MBEHAV::COMBAT_FLOAT))
763 //TODO : display "you can't fight while swimming"
764 return true;
767 break;
768 // Leave DEATH Mode
769 case MBEHAV::DEATH:
770 // Restore the last view.
771 viewMode(viewMode());
772 break;
773 case MBEHAV::SWIM:
774 if( m == MBEHAV::COMBAT || m == MBEHAV::COMBAT_FLOAT)
776 //TODO : display "you can't fight while swimming"
777 return true;
779 break;
780 default:
781 nlwarning("Invalid behaviour change.");
784 // Reset Parent, unless we stay in mount mode
785 if ((_Mode != MBEHAV::MOUNT_SWIM && _Mode != MBEHAV::MOUNT_NORMAL)
786 || (m != MBEHAV::MOUNT_SWIM && m != MBEHAV::MOUNT_NORMAL)
789 parent(CLFECOMMON::INVALID_SLOT);
792 // Change the Mode for the user ( if user sits down or stands up we wait in order to play the sit/stand transition anim)
793 if( m != MBEHAV::SIT && _Mode != MBEHAV::SIT )
794 _Mode = m;
795 _ModeWanted = m;
797 // Initialize the new Mode.
798 switch(m)
800 // Combat mode
801 case MBEHAV::COMBAT:
802 case MBEHAV::COMBAT_FLOAT:
804 C64BitsParts rot;
806 // Compute the angle
807 const string propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_ORIENTATION);
808 rot.i64[0] = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
809 _TargetAngle = rot.f[0];
811 // Initialize controls for the combat.
812 UserControls.startCombat();
814 // Context help
815 contextHelp ("action_bar");
817 break;
819 // Mount Normal or mount swim
820 case MBEHAV::MOUNT_NORMAL:
822 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(parent()));
823 if(mount)
825 mount->_Stages.removePosWithNoMode();
826 dir(mount->dir());
829 case MBEHAV::MOUNT_SWIM:
831 // Hide the mount unless we come from another mounted mode
832 if (_Mode != MBEHAV::MOUNT_SWIM && _Mode != MBEHAV::MOUNT_NORMAL)
834 if(_Mount != CLFECOMMON::INVALID_SLOT) // if _Mount is still invalid, the following code will be done in updatePos()
836 setOnMount();
839 // refresh target
840 UserEntity->selection(_Selection);
842 break;
844 // Dead mode.
845 case MBEHAV::DEATH:
846 setDead();
847 if(isSwimming())
848 _Mode = MBEHAV::SWIM_DEATH;
849 break;
851 // Normal or Default mode.
852 case MBEHAV::NORMAL:
853 _CurrentBehaviour.Behaviour = MBEHAV::IDLE;
854 default:
856 setAlive();
857 viewMode(viewMode());
858 break;
861 bool doSetAnim = true;
862 // if user sits down or stands up we set transition anim before changing animset
863 if( m == MBEHAV::SIT )
865 setAnim(CAnimationStateSheet::SitMode);
866 _Mode = m;
867 doSetAnim = false;
869 else
870 if( _Mode == MBEHAV::SIT && m!=MBEHAV::DEATH )
872 setAnim(CAnimationStateSheet::SitEnd);
873 _Mode = m;
874 doSetAnim = false;
877 // Show/Hide all or parts of the body.
878 updateVisualDisplay();
879 if( ClientCfg.AutomaticCamera )
881 // Set the direction as the front.
882 dir(front());
884 // Compute the current automaton
885 computeAutomaton();
886 // Update the animation set according to the mode.
887 computeAnimSet();
889 if( doSetAnim )
891 // Animset changed -> update current animation
892 setAnim(CAnimationStateSheet::Idle);
895 // Changing the mode well done.
896 return true;
897 }// mode //
901 * Mount the mount in _Mount
903 void CUserEntity::setOnMount()
905 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
906 if(mount)
908 // Update primitives for the mount and the rider.
909 if(_Primitive && mount->getPrimitive())
911 _Primitive->setRadius( std::min(mount->getPrimitive()->getRadius(), (float)(RYZOM_ENTITY_SIZE_MAX/2)) );
912 _Primitive->setHeight(mount->getPrimitive()->getHeight());
913 mount->getPrimitive()->setOcclusionMask(MaskColNone); // Remove collisions.
915 // Now on a mount
916 _OnMount = true;
917 // Link the mount and the user.
918 parent(_Mount);
919 // Refresh the View Mode
920 viewMode(viewMode());
921 // Close the crafting window if open
922 closeFaberCastWindow();
924 // Keep run in mind
925 _RunWhenAble = _Run;
927 mount->_ForbidClipping = true;
933 //-----------------------------------------------
934 // getVelocity :
935 // compute and return the entity velocity
936 //-----------------------------------------------
937 CVector CUserEntity::getVelocity() const
939 static const CVector lateral(0,0,1);
940 static CVector velocity;
941 velocity = front() * _FrontVelocity + ( lateral ^ front()) * _LateralVelocity;
942 velocity.normalize();
943 // User is Dead
944 if(isDead())
946 velocity *= 0.0;
948 // User is sitting
949 else if(isSit())
951 velocity *= 0.0;
953 // User is swimming
954 else if(isSwimming())
956 // We are a Ring DM so speed up a litle
959 // Forward Run or Walk
960 if(_FrontVelocity > 0.0f)
962 if(_Run)
963 velocity *= 3.0;
964 else
965 velocity *= 1.0;
967 // Lateral or Backward Walk
968 else
969 velocity *= 1.0;
971 if (_R2CharMode == R2::TCharMode::Editer || _R2CharMode == R2::TCharMode::Dm)
973 velocity *= (float(ClientCfg.DmRun) / 6.0f); // velocity max = max run / 2
977 else if(isRiding())
979 // Forward Run or Walk
980 if(_FrontVelocity > 0.0f)
982 if(_Run)
983 velocity *= getMountRunVelocity();
984 else
985 velocity *= getMountWalkVelocity();
987 // Lateral or Backward Walk (currently, not used)
988 else
989 velocity *= 0.66f;//getMountWalkVelocity();
991 else
993 // Forward Run or Walk
994 if(_FrontVelocity > 0.0f)
995 velocity *= currentVelocity();
996 // Lateral or Backward Walk
997 else
998 velocity *= _WalkVelocity;
1000 return velocity;
1001 }// getVelocity //
1003 //-----------------------------------------------
1004 // speed :
1005 // Return the Entity Current Speed.
1006 //-----------------------------------------------
1007 double CUserEntity::speed() const // virtual
1009 return CPlayerCL::speed();
1010 // return (double)getVelocity().norm();
1011 }// speed //
1013 //-----------------------------------------------
1014 // applyMotion :
1015 // Apply the motion to the entity.
1016 //-----------------------------------------------
1017 void CUserEntity::applyMotion(CEntityCL *target)
1019 // default each frame
1020 _ForceLookSlot= CLFECOMMON::INVALID_SLOT;
1022 bool lastHasMoved = _HasMoved;
1023 _HasMoved = false;
1024 // Remove Positions in stages
1025 _Stages.removePosWithNoMode();
1026 // Remove Positions in stages for the mount.
1027 CCharacterCL *mount = 0;
1028 if(parent() != CLFECOMMON::INVALID_SLOT)
1030 mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(parent()));
1031 if(mount)
1032 mount->_Stages.removePosWithNoMode();
1034 // NO PRIMITIVE -> NO MOVE
1035 if(_Primitive == 0)
1036 return;
1037 // BAD CONNECTION -> NO MOVE
1038 if(NetMngr.getConnectionQuality() == false)
1039 return;
1040 // MS per TICK <=0 -> NO MOVE
1041 if(NetMngr.getMsPerTick() <= 0)
1042 return;
1043 // Compute Speed Vector
1044 NLMISC::CVectorD speed;
1045 if(_MoveToSlot != CLFECOMMON::INVALID_SLOT)
1047 // Check the Target.
1048 if(target == 0 || target == this)
1049 return;
1050 // Compute the vector between the user and the target.
1051 CVectorD dir2targ = target->pos() - pos();
1052 dir2targ.z = 0.0;
1053 if(dir2targ == CVectorD::Null)
1054 dir2targ = front();
1055 const double angToTarget = atan2(dir2targ.y, dir2targ.x);
1056 CVectorD aimingPos = target->getAttackerPos(angToTarget, attackRadius() + ClientCfg.AttackDist);
1057 // Aiming Position
1058 CVectorD dirToAimingPos = aimingPos-pos();
1059 dirToAimingPos.z = 0.0;
1060 const double distToAimingPos = dirToAimingPos.norm();
1062 // Decide if the target is reached or not
1063 bool targetReached= false;
1064 if(distToAimingPos > 0.5)
1066 // Because of Tryker Counter, may increase the Threshold, when the player is stalled
1067 if(distToAimingPos<_MoveToDist)
1069 // If the player is stalled too much time, abort the move and launch the action
1070 float actualSpeed= float((_Position - _LastFramePos).norm() / DT);
1072 // if player effectively runs twice slower, start colision timer
1073 if( actualSpeed*2 < getMaxSpeed() )
1075 if(!_MoveToColStartTime)
1076 _MoveToColStartTime= T1;
1078 // else ok, reset colision timer
1079 else
1080 _MoveToColStartTime= 0;
1082 // if too much time stalled, stop run.
1083 if(_MoveToColStartTime && (T1 - _MoveToColStartTime >= ClientCfg.MoveToTimeToStopStall) )
1084 targetReached= true;
1086 else
1087 _MoveToColStartTime= 0;
1089 else
1090 targetReached= true;
1092 // If the target is reached
1093 if(targetReached)
1095 // stop and execute action
1096 speed = CVectorD::Null;
1097 moveToAction(target);
1099 // else continue follow
1100 else
1102 // Look at the entity. delay it after pacs evalCollision(), for correct orientation
1103 _ForceLookSlot= target->slot();
1104 // but still estimate now an approximative front (may be used between now and applyForceLook())
1105 forceLookEntity(dir2targ, false);
1107 // Normalize
1108 dirToAimingPos.normalize();
1109 // Set the Velocity Direction
1110 speed = dirToAimingPos*distToAimingPos;
1111 speed /= DT;
1112 if(speed.norm() > getMaxSpeed())
1113 speed = dirToAimingPos*getMaxSpeed();
1116 else if(follow())
1118 // Check the Target.
1119 if(target == 0 || target == this)
1120 return;
1121 // If the target is moving, orientate the user to the target.
1122 // if(target->hasMoved())
1124 // Compute the vector between the user and the target.
1125 CVectorD dir2targ = target->pos() - pos();
1126 dir2targ.z = 0.0;
1127 if(dir2targ != CVectorD::Null)
1129 // Look at the entity. delay it after pacs evalCollision(), for correct orientation
1130 _ForceLookSlot= target->slot();
1131 // but still estimate now an approximative front (may be used between now and applyForceLook())
1132 forceLookEntity(dir2targ, false);
1135 // Compute the angle in the world to attack the target.
1136 const double angToTarget = frontYaw();
1137 // Get the best position to attack the target with the given angle.
1138 const CVectorD aimingPos = target->getAttackerPos(angToTarget, attackRadius() + ClientCfg.AttackDist);
1139 // Aiming Position
1140 CVectorD dirToAimingPos = aimingPos-pos();
1141 dirToAimingPos.z = 0.0;
1142 const double distToAimingPos = dirToAimingPos.norm();
1143 // If the User was not moving and the distance to re-move is not enough big, stay idle
1144 if(lastHasMoved
1145 || (isFighting() && distToAimingPos >= 0.5)
1146 || (!isFighting() && distToAimingPos >= 3.0))
1148 dirToAimingPos.normalize();
1149 // User is in combat mode -> follow as close as possible.
1150 if(isFighting())
1152 // Target is moving
1153 if(target->hasMoved())
1155 // Get closer if too far.
1156 if(distToAimingPos >= 0.2)
1158 // Set the Velocity Direction
1159 speed = dirToAimingPos*distToAimingPos;
1160 speed /= DT;
1161 if(speed.norm() > getMaxSpeed())
1162 speed = dirToAimingPos*getMaxSpeed();
1164 else
1166 // Try to have the same speed as the target.
1167 if(target->getSpeed() > getMaxSpeed())
1168 speed = target->dir()* ((float)getMaxSpeed());
1169 else
1170 speed = target->dir()* ((float)target->getSpeed());
1173 // Target is not moving.
1174 else
1176 // Get closer if too far.
1177 if(distToAimingPos >= 0.1)
1179 // Set the Velocity Direction
1180 speed = dirToAimingPos*distToAimingPos;
1181 speed /= DT;
1182 if(speed.norm() > getMaxSpeed())
1183 speed = dirToAimingPos*getMaxSpeed();
1185 else
1186 speed = CVectorD::Null;
1189 // User is not in combat mode -> follow not so close.
1190 else
1192 // Too far, get closer as fast as possible
1193 if(distToAimingPos >= 3.0)
1195 // Set the Velocity Direction
1196 speed = dirToAimingPos*distToAimingPos;
1197 speed /= DT;
1198 if(speed.norm() > getMaxSpeed())
1199 speed = dirToAimingPos*getMaxSpeed();
1201 // Just far enough, adjust the user speed to the target
1202 else if(target->hasMoved() && distToAimingPos >= 1.0)
1204 // Try to have the same speed as the target.
1205 if(target->getSpeed() > getMaxSpeed())
1206 speed = target->dir()* ((float)getMaxSpeed());
1207 else
1208 speed = target->dir()* ((float)target->getSpeed());
1210 // Too close, Stop
1211 else
1212 speed = CVectorD::Null;
1215 // User was stop and the next pos is to close to begin to move.
1216 else
1217 speed = CVectorD::Null;
1219 else
1221 speed = getVelocity()*_SpeedFactor.getValue();
1222 _SpeedFactor.addFactorValue(0.005f);
1225 // SPEED VECTOR NULL -> NO MOVE
1226 if(speed == CVectorD::Null)
1227 return;
1229 // First Person View
1230 if(UserControls.isInternalView())
1232 // If the server is slow, the client move slower too (only needed in FPV).
1233 double modif = (100.0f/(float)NetMngr.getMsPerTick());
1234 // don't increase speed
1235 clamp(modif, 0.0, 1.0);
1236 speed *= modif;
1237 // Move
1238 _HasMoved = true;
1239 _Primitive->move(speed, dynamicWI);
1240 if(mount && mount->getPrimitive())
1241 mount->getPrimitive()->move(speed, dynamicWI);
1243 // Third Person View
1244 else
1246 double modif = (100.0f/(float)NetMngr.getMsPerTick());
1247 clamp(modif, 0.0, 1.0);
1248 speed *= modif;
1249 speed += pos();
1250 sint64 x = (sint64)((sint32)(speed.x * 1000.0));
1251 sint64 y = (sint64)((sint32)(speed.y * 1000.0));
1252 sint64 z = (sint64)((sint32)(speed.z * 1000.0));
1253 const uint time = 10; // = 1sec because the speed is in Meter per Sec.
1254 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSX, x, 0);
1255 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSY, y);
1256 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSZ, z);
1257 // Move the Mount
1258 if(mount)
1260 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSX, x, 0);
1261 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSY, y);
1262 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSZ, z);
1267 }// applyMotion //
1270 //---------------------------------------------------
1271 //---------------------------------------------------
1272 void CUserEntity::applyForceLook()
1274 if(_ForceLookSlot!=CLFECOMMON::INVALID_SLOT)
1276 CEntityCL *target= EntitiesMngr.entity(_ForceLookSlot);
1277 if(target && target!=this)
1279 // Compute the vector between the user and the target.
1280 CVectorD dir2targ = target->pos() - pos();
1281 dir2targ.z = 0.0;
1282 if(dir2targ == CVectorD::Null)
1283 dir2targ = front();
1284 // Look at the entity
1285 forceLookEntity(dir2targ, true);
1291 //---------------------------------------------------
1292 // updatePosCombatFloatChanged :
1293 //---------------------------------------------------
1294 void CUserEntity::updatePosCombatFloatChanged(CEntityCL * /* target */) // virtual
1296 if(viewMode() == FirstPV)
1298 pos(_FirstPos);
1300 }// updatePosCombatFloatChanged //
1303 //-----------------------------------------------
1304 // forceIndoorFPV //
1305 // Return true if the user is indoor and the CFG want to force the FPV Indoor.
1306 //-----------------------------------------------
1307 bool CUserEntity::forceIndoorFPV()
1309 return (ClientCfg.ForceIndoorFPV && indoor());
1310 }// forceIndoorFPV //
1313 //-----------------------------------------------
1314 // enableFollow :
1315 //-----------------------------------------------
1316 void CUserEntity::disableFollow()
1318 if (_FollowMode==false)
1319 return;
1321 _FollowMode = false;
1323 // Send the message to the server.
1324 CBitMemStream out;
1325 if(GenericMsgHeaderMngr.pushNameToStream("TARGET:NO_FOLLOW", out))
1326 NetMngr.push(out);
1327 else
1328 nlwarning("UE:follow: unknown message named 'TARGET:NO_FOLLOW'.");
1330 }// follow //
1333 //-----------------------------------------------
1334 // enableFollow :
1335 //-----------------------------------------------
1336 void CUserEntity::enableFollow(bool resetCameraRot)
1338 if (_FollowMode == true)
1339 return;
1341 if( _Mode == MBEHAV::DEATH )
1342 return;
1344 _FollowMode = true;
1346 // Send the message to the server.
1347 CBitMemStream out;
1348 if(GenericMsgHeaderMngr.pushNameToStream("TARGET:FOLLOW", out))
1349 NetMngr.push(out);
1350 else
1351 nlwarning("UE:follow: unknown message named 'TARGET:FOLLOW'.");
1353 // Disable any autowalk (else when follow re-disabled, it will effect, which is weird)
1354 UserControls.autowalkState(false);
1356 // disable afk mode
1357 setAFK(false);
1359 // if want to reset camera rotation
1360 if(resetCameraRot)
1361 startForceLookEntity(targetSlot());
1363 }// follow //
1366 //-----------------------------------------------
1367 // resetAnyMoveTo
1368 //-----------------------------------------------
1369 void CUserEntity::resetAnyMoveTo()
1371 // if we cancel a MoveToPhrase action, MUST dec the action counter, and hide the Action Icon
1372 if(_MoveToAction==CUserEntity::CombatPhrase || _MoveToAction==CUserEntity::ExtractRM)
1374 // the clientExecute has not been called in case of "ExtractRM autoFind"
1375 bool autoFindExtractRM= _MoveToAction==CUserEntity::ExtractRM && _MoveToPhraseMemoryLine == std::numeric_limits<uint>::max();
1376 if(!autoFindExtractRM)
1378 CSPhraseManager *pPM= CSPhraseManager::getInstance();
1379 pPM->cancelClientExecute(_MoveToPhraseCyclic);
1383 // reset to no moveTo
1384 _MoveToSlot = CLFECOMMON::INVALID_SLOT;
1385 _MoveToAction = CUserEntity::None;
1386 _MoveToDist = 0.0;
1387 _MoveToColStartTime= 0;
1390 //-----------------------------------------------
1391 // moveToCheckStartDist :
1392 // Check if the user is not already well placed.
1393 //-----------------------------------------------
1394 void CUserEntity::moveToCheckStartDist(CLFECOMMON::TCLEntityId slot, double dist, TMoveToAction /* action */)
1396 if(_MoveToSlot != CLFECOMMON::INVALID_SLOT)
1398 // Start a new Force Look entity
1399 startForceLookEntity(_MoveToSlot);
1401 // Disable any autowalk (else when moveTo re-disabled, it will effect, which is weird)
1402 UserControls.autowalkState(false);
1404 // disable afk mode
1405 setAFK(false);
1407 // if sufficiently near, launch the action
1408 CEntityCL *target = EntitiesMngr.entity(slot);
1409 if(target)
1411 CVectorD dir2targ = target->pos() - pos();
1412 dir2targ.z = 0.0;
1413 if((dir2targ==CVectorD::Null) || (dir2targ.norm() < dist))
1415 moveToAction(target);
1419 }// moveToCheckStartDist //
1421 //-----------------------------------------------
1422 // moveTo :
1423 // Method to move to someone else.
1424 //-----------------------------------------------
1425 void CUserEntity::moveTo(CLFECOMMON::TCLEntityId slot, double dist, TMoveToAction action)
1427 resetAnyMoveTo();
1429 // setup new state
1430 _MoveToSlot = slot;
1431 _MoveToDist = dist;
1432 _MoveToAction = action;
1434 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1435 }// moveTo //
1437 //-----------------------------------------------
1438 // moveToMission :
1439 // Method to move to someone else for a mission. NB: if prec action was a CombatPhrase action, action counter are decremented
1440 //-----------------------------------------------
1441 void CUserEntity::moveToMission(CLFECOMMON::TCLEntityId slot, double dist, uint32 id)
1443 resetAnyMoveTo();
1445 // setup new state
1446 _MoveToSlot = slot;
1447 _MoveToDist = dist;
1448 _MoveToAction = CUserEntity::Mission;
1449 _MoveToMissionId = id;
1451 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1452 }// moveToMission //
1454 //-----------------------------------------------
1455 // moveToMissionRing :
1456 // Method to move to someone else for a ring mission. NB: if prec action was a CombatPhrase action, action counter are decremented
1457 //-----------------------------------------------
1458 void CUserEntity::moveToMissionRing(CLFECOMMON::TCLEntityId slot, double dist, uint32 id)
1460 resetAnyMoveTo();
1462 // setup new state
1463 _MoveToSlot = slot;
1464 _MoveToDist = dist;
1465 _MoveToAction = CUserEntity::MissionRing;
1466 _MoveToMissionId = id;
1468 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1469 }// moveToMissionRing //
1471 //-----------------------------------------------
1472 // moveTo :
1473 // Method to move to someone else.
1474 //-----------------------------------------------
1475 void CUserEntity::moveToCombatPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool phraseCyclic)
1477 resetAnyMoveTo();
1479 // setup new state
1480 _MoveToSlot = slot;
1481 _MoveToDist = dist;
1482 _MoveToAction = CUserEntity::CombatPhrase;
1483 _MoveToPhraseMemoryLine= phraseMemoryLine;
1484 _MoveToPhraseMemorySlot= phraseMemorySlot;
1485 _MoveToPhraseCyclic= phraseCyclic;
1487 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1490 //-----------------------------------------------
1491 // Method to move to someone else, for foraging extraction
1492 // The caller MUST call after CSPhraseManager::clientExecute(), to increment action counter
1493 //-----------------------------------------------
1494 void CUserEntity::moveToExtractionPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool cyclic)
1496 // Test if forage tool in hand, otherwise auto-equip with it
1497 bool validForageToolInHand = false;
1498 CInventoryManager * inv = CInventoryManager::getInstance();
1499 if ( !inv )
1501 return;
1503 uint32 rightHandSheet = inv->getRightHandItemSheet();
1504 if ( rightHandSheet )
1506 validForageToolInHand = inv->isForageToolItem( rightHandSheet );
1508 if ( !validForageToolInHand )
1510 // Find a forage tool in the bag
1511 uint i;
1512 for ( i=0; i<MAX_BAGINV_ENTRIES; ++i )
1514 uint32 itemSheet = inv->getBagItem(i).getSheetID();
1515 if ( itemSheet && inv->isForageToolItem(itemSheet) )
1517 break;
1520 if ( i != MAX_BAGINV_ENTRIES && ClientCfg.AutoEquipTool )
1522 // remember last used weapon(s)
1523 rememberWeaponsInHand();
1525 // Clear hands
1526 inv->unequip( "LOCAL:INVENTORY:HAND:1" );
1527 inv->unequip( "LOCAL:INVENTORY:HAND:0" );
1529 // Equip hands
1530 string bagPath = toString( "LOCAL:INVENTORY:BAG:%u", i );
1531 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:0" );
1533 else
1535 // No forage tool in bag
1536 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiForageToolMissing"), "CHK" );
1537 return;
1541 resetAnyMoveTo();
1543 // setup new state
1544 _MoveToSlot = slot;
1545 _MoveToDist = dist;
1546 _MoveToAction = CUserEntity::ExtractRM;
1547 _MoveToPhraseMemoryLine= phraseMemoryLine;
1548 _MoveToPhraseMemorySlot= phraseMemorySlot;
1549 _MoveToPhraseCyclic= cyclic;
1551 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1554 //-----------------------------------------------
1555 // Method to begin a spire construction
1556 // The caller MUST call after CSPhraseManager::clientExecute(), to increment action counter
1557 //-----------------------------------------------
1558 void CUserEntity::moveToTotemBuildingPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool cyclic)
1560 resetAnyMoveTo();
1562 // setup new state
1563 _MoveToSlot = slot;
1564 _MoveToDist = dist;
1565 _MoveToAction = CUserEntity::BuildTotem;
1566 _MoveToPhraseMemoryLine= phraseMemoryLine;
1567 _MoveToPhraseMemorySlot= phraseMemorySlot;
1568 _MoveToPhraseCyclic= cyclic;
1570 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1573 //-----------------------------------------------
1574 // moveToAction :
1575 // Launch the Action Once the Move is done.
1576 // \param CEntityCL * : pointer on the destination entity.
1577 // \warning entity pointer must be valid(allocated).
1578 //-----------------------------------------------
1579 void CUserEntity::moveToAction(CEntityCL *ent)
1581 // Check entity pointer
1582 nlassert(ent);
1584 UserControls.needReleaseForward();
1586 // Get the interface instance.
1587 CInterfaceManager *IM = CInterfaceManager::getInstance();
1588 switch(_MoveToAction)
1590 // Attack
1591 case CUserEntity::Attack:
1592 UserEntity->attack();
1593 break;
1594 // Quartering
1595 case CUserEntity::Quarter:
1596 if((ent->properties()).harvestable())
1597 CAHManager::getInstance()->runActionHandler("context_quartering", 0);
1598 break;
1599 // Loot
1600 case CUserEntity::Loot:
1601 if((ent->properties()).lootable())
1602 CAHManager::getInstance()->runActionHandler("context_loot", 0);
1603 break;
1604 // Pick Up
1605 case CUserEntity::PickUp:
1606 nlwarning("UE:checkMoveTo: not yet implemented");
1607 break;
1608 case CUserEntity::ExtractRM:
1609 extractRM();
1610 break;
1611 // Trade Item
1612 case CUserEntity::TradeItem:
1613 CAHManager::getInstance()->runActionHandler("context_trade_item", 0);
1614 break;
1615 // Trade Phrase
1616 case CUserEntity::TradePhrase:
1617 CAHManager::getInstance()->runActionHandler("context_trade_phrase", 0);
1618 break;
1619 // Trade Pact
1620 case CUserEntity::TradePact:
1621 CAHManager::getInstance()->runActionHandler("context_trade_pact", 0);
1622 break;
1623 // Mission
1624 case CUserEntity::Mission:
1626 string param = toString("id=%d", _MoveToMissionId);
1627 CAHManager::getInstance()->runActionHandler("open_mission_option", 0, param);
1629 break;
1630 // Dynamic Mission
1631 case CUserEntity::DynamicMission:
1632 CAHManager::getInstance()->runActionHandler("context_dynamic_mission", 0);
1633 break;
1634 // Static Mission
1635 case CUserEntity::StaticMission:
1636 CAHManager::getInstance()->runActionHandler("context_choose_mission", 0);
1637 break;
1638 // Mission
1639 case CUserEntity::MissionRing:
1641 string param = toString("id=%d", _MoveToMissionId);
1642 CAHManager::getInstance()->runActionHandler("mission_ring", 0, param);
1644 break;
1645 // Create Guild
1646 case CUserEntity::CreateGuild:
1647 CAHManager::getInstance()->runActionHandler("context_create_guild", 0);
1648 break;
1649 // News
1650 case CUserEntity::News:
1651 CAHManager::getInstance()->runActionHandler("context_talk", 0);
1652 break;
1653 // Trade Teleport
1654 case CUserEntity::TradeTeleport:
1655 CAHManager::getInstance()->runActionHandler("context_trade_teleport", 0);
1656 break;
1657 // Trade Faction items
1658 case CUserEntity::TradeFaction:
1659 CAHManager::getInstance()->runActionHandler("context_trade_faction", 0);
1660 break;
1661 // Trade Cosmetic
1662 case CUserEntity::TradeCosmetic:
1663 CAHManager::getInstance()->runActionHandler("context_trade_cosmetic", 0);
1664 break;
1665 // Talk
1666 case CUserEntity::Talk:
1667 nlwarning("UE:checkMoveTo: not yet implemented");
1668 break;
1669 // CombatPhrase
1670 case CUserEntity::CombatPhrase:
1671 UserEntity->attackWithPhrase();
1672 break;
1673 // Mount
1674 case CUserEntity::Mount:
1676 string orderStr = "mount";
1677 string beastIndexStr = "@UI:GCM_BEAST_SELECTED";
1678 bool confirmFree = true;
1679 beastOrder(orderStr,beastIndexStr,confirmFree);
1680 break;
1682 // WebPage
1683 case CUserEntity::WebPage:
1684 CAHManager::getInstance()->runActionHandler("context_web_page", 0);
1685 break;
1686 // Outpost
1687 case CUserEntity::Outpost:
1688 CLuaManager::getInstance().executeLuaScript("game:outpostBCOpenStateWindow()", 0);
1689 break;
1690 // BuildTotem
1691 case CUserEntity::BuildTotem:
1692 buildTotem();
1693 break;
1694 // openArkUrl
1695 case CUserEntity::OpenArkUrl:
1696 CLuaManager::getInstance().executeLuaScript("getUI('ui:interface:web_transactions'):find('html'):browse(ArkTargetUrl)", 0);
1697 break;
1698 default:
1699 break;
1702 // Move To Done.
1703 resetAnyMoveTo();
1704 }// moveToAction //
1707 //-----------------------------------------------
1708 // sendToServer :
1709 // Send the position and orientation to the server.
1710 //-----------------------------------------------
1711 bool CUserEntity::sendToServer(CBitMemStream &out)
1713 if(GenericMsgHeaderMngr.pushNameToStream("POSITION", out))
1715 // Backup the position sent.
1716 if (_Primitive) _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1717 // Send Position & Orientation
1718 CPositionMsg positionMsg;
1719 positionMsg.X = (sint32)(pos().x * 1000);
1720 positionMsg.Y = (sint32)(pos().y * 1000);
1721 positionMsg.Z = (sint32)(pos().z * 1000);
1722 positionMsg.Heading = frontYaw();
1723 out.serial(positionMsg);
1724 return true;
1726 else
1728 nlwarning("UE:sendToServer: unknown message named 'POSITION'.");
1729 return false;
1731 }// sendToServer //
1733 //-----------------------------------------------
1734 // msgForCombatPos :
1735 // Fill the msg to know if the user is well placed to fight.
1736 //-----------------------------------------------
1737 bool CUserEntity::msgForCombatPos(NLMISC::CBitMemStream &out)
1739 static bool wellPosition = false;
1740 bool wellP = false;
1741 // if(isFighting())
1743 // Is the user well Placed
1744 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
1745 if(target)
1746 wellP = target->isPlacedToFight(UserEntity->pos(), front(), attackRadius() + ClientCfg.AttackDist);
1748 // If different from the last time
1749 if(wellPosition != wellP)
1751 wellPosition = wellP;
1752 // Send state to server.
1753 if(GenericMsgHeaderMngr.pushNameToStream("COMBAT:VALIDATE_MELEE", out))
1755 uint8 flag = wellP?1:0;
1756 out.serial(flag);
1757 return true;
1759 else
1760 nlwarning("UE:msgForCombatPos: unknown message named 'COMBAT:TOGGLE_COMBAT_FLOAT_MODE'.");
1762 // Do not send the msg.
1763 return false;
1764 }// msgForCombatPos //
1767 //-----------------------------------------------
1768 // checkPos :
1769 // Check the User Position according to the server code.
1770 // \todo GUIGUI : put checks on GPos
1771 // \todo GUIGUI : refact the check primitive when creating a PACS again
1772 //-----------------------------------------------
1773 void CUserEntity::checkPos()
1775 if(PACS && _Primitive)
1777 // Is in water ?
1778 if(GR)
1780 UGlobalPosition gPos;
1781 _Primitive->getGlobalPosition(gPos, dynamicWI);
1782 float waterHeight;
1783 if(GR->isWaterPosition(gPos, waterHeight))
1785 if(isSwimming() == false)
1787 // Player is Dead set the right mode (swim and dead)
1788 if(isDead())
1789 mode(MBEHAV::SWIM_DEATH);
1790 else
1792 // Do not swim when in combat mode.
1793 if(isFighting())
1795 _HasMoved = false;
1796 pacsPos(_LastPositionValidated, _LastGPosValidated);
1798 // \todo GUIGUI : if the move was cancelled, do not switch to swimming mode.
1799 else if (isRiding())
1801 mode(MBEHAV::MOUNT_SWIM);
1802 // also change mounted entity mode
1803 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
1804 if(mount)
1806 // Set the mount.
1807 mount->setMode(MBEHAV::MOUNT_SWIM);
1808 mount->computeAutomaton();
1809 mount->computeAnimSet();
1810 mount->setAnim(CAnimationStateSheet::Idle);
1813 else
1815 mode(MBEHAV::SWIM);
1820 else
1822 if(isSwimming())
1824 if(isDead())
1826 mode(MBEHAV::DEATH);
1828 else if (isRiding())
1830 mode(MBEHAV::MOUNT_NORMAL);
1831 // also change mounted entity mode
1832 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
1833 if(mount)
1835 // Set the mount.
1836 mount->setMode(MBEHAV::MOUNT_NORMAL);
1837 mount->computeAutomaton();
1838 mount->computeAnimSet();
1839 mount->setAnim(CAnimationStateSheet::Idle);
1842 else
1844 mode(MBEHAV::NORMAL);
1850 // Create the Primitive used to check if the move will be accepted by the server
1851 if(_CheckPrimitive == 0)
1853 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1854 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1855 _CheckPrimitive = PACS->addNonCollisionablePrimitive(_Primitive);
1856 _CheckPrimitive->setOcclusionMask(MaskColNone); // Collide with nothing
1857 _CheckPrimitive->setCollisionMask(MaskColNone); // Collide with nothing
1858 _LastPositionValidated = pos();
1860 if(_CheckPrimitive)
1862 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1863 CVectorD speed = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1865 _CheckPrimitive->move(speed, dynamicWI);
1866 if(PACS->evalNCPrimitiveCollision(1.0, _CheckPrimitive, dynamicWI) == false)
1867 nlwarning("UE:checkPos: _CheckPrimitive is a collisionable primitive !!!");
1869 CVectorD vectDist = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1870 if(vectDist.norm() > 0.001)
1872 // The server will not be able to reproduce this move, restoring the last one.
1873 _HasMoved = false;
1874 pacsPos(_LastPositionValidated,_LastGPosValidated);
1876 else
1878 _LastPositionValidated = _Primitive->getFinalPosition(dynamicWI);
1879 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1883 }// checkPos //
1886 //-----------------------------------------------
1887 // testPacsPos :
1888 // test the move from posToTest to current pos
1889 //-----------------------------------------------
1890 bool CUserEntity::testPacsPos( CVectorD& posToTest )
1892 if(PACS && _Primitive && _CheckPrimitive)
1894 _CheckPrimitive->setGlobalPosition(posToTest, dynamicWI);
1895 CVectorD speed = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1897 _CheckPrimitive->move(speed, dynamicWI);
1898 if(PACS->evalNCPrimitiveCollision(1.0, _CheckPrimitive, dynamicWI) == false)
1899 nlwarning("UE:testPacsPos: _CheckPrimitive is a collisionable primitive !!!");
1901 CVectorD vectDist = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1902 if(vectDist.norm() > 0.001)
1904 return false;
1906 else
1907 return true;
1909 return false;
1911 } // testPacsPos //
1914 //-----------------------------------------------
1915 // tp :
1916 // Teleport the player (remove selection, re-init checkPos, etc.).
1917 //-----------------------------------------------
1918 void CUserEntity::tp(const CVectorD &dest)
1920 // Remove the selection.
1921 UserEntity->selection(CLFECOMMON::INVALID_SLOT);
1922 // Update PACS
1923 pacsPos(dest);
1924 // Update the primitive useful to check the user position.
1925 _LastPositionValidated = dest;
1926 // Get the Global position
1927 if(_Primitive)
1929 // this is the last PACS position validated too
1930 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1931 // consider this is the last position sent to server (since actually received!)
1932 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1933 // Set the new position of the 'check' primitive
1934 if(_CheckPrimitive)
1935 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1937 else
1938 nlwarning("UE:tp: the entity has a Null primitive.");
1939 // Return to interface mode.
1940 viewMode(UserEntity->viewMode());
1941 // User well oriented.
1942 dir(UserEntity->front());
1943 // Set Normal Mode after a teleport.
1944 mode(MBEHAV::NORMAL);
1945 // Set animation with idle.
1946 setAnim(CAnimationStateSheet::Idle);
1947 }// tp //
1949 //-----------------------------------------------
1950 // correctPos :
1951 // Teleport the player to correct his position.
1952 //-----------------------------------------------
1953 void CUserEntity::correctPos(const NLMISC::CVectorD &dest)
1955 nlinfo("UE:correctPos: new user position %f %f %f", dest.x, dest.y, dest.z);
1956 // Change the user poisition.
1957 UserEntity->pacsPos(dest);
1958 // Update the primitive useful to check the user position.
1959 _LastPositionValidated = dest;
1960 // Get the Global position
1961 if(_Primitive)
1963 // this is the last PACS position validated too
1964 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1965 // consider this is the last position sent to server (since actually received!)
1966 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1967 // Set the new position of the 'check' primitive
1968 if(_CheckPrimitive)
1969 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1971 else
1972 nlwarning("UE:correctPos: the entity has a Null primitive.");
1974 // Correct the pos of the mount, if riding
1975 if ( isRiding() )
1977 if ( _Mount < EntitiesMngr.entities().size() )
1979 CEntityCL *mount = EntitiesMngr.entities()[_Mount];
1980 if ( mount )
1981 mount->pacsPos( dest );
1984 }// correctPos //
1988 * Check if the mount is able to run, and force walking mode if not
1989 * (if the player clicked on run, set back to walk).
1991 void CUserEntity::checkMountAbleToRun()
1993 if ( isRiding() )
1995 if ( running() )
1997 // Make the mount walk if she's hungry
1998 if ( ! _MountHunger.canRun() )
1999 switchVelocity( false );
2001 else
2003 // Make the mount run if she's not hungry anymore
2004 if ( _RunWhenAble && _MountHunger.canRun() )
2005 switchVelocity( false );
2011 //-----------------------------------------------
2012 // Update the position of the entity after the motion.
2013 // \param t : Time for the position of the entity after the motion.
2014 // \param target : pointer on the current target.
2015 //-----------------------------------------------
2016 void CUserEntity::updatePos(const TTime &t, CEntityCL *target)
2018 // Update Mount if mounting (if _Mount was still not valid when mode received)
2019 if((_Mount != CLFECOMMON::INVALID_SLOT && isRiding()) && _OnMount==false)
2021 setOnMount();
2023 // External view are managed like other entities.
2024 if(!UserControls.isInternalView())
2026 // Update position according to the animations.
2027 CPlayerCL::updatePos(t, target);
2028 _LastFrameTime = t*0.001;
2029 // Set The FPV when indoor.
2030 if(forceIndoorFPV())
2031 viewMode(FirstPV, false);
2032 return;
2035 else
2037 // update anim state at least (needed when a spell is cast to play the cast fx at the right time, because they may be visible)
2038 updateAnimationState();
2041 // Compute the Time Step.
2042 double frameTimeRemaining = computeTimeStep(((double)t)*0.001);
2043 // Do not update animation if Client Light
2044 if (!ClientCfg.Light)
2046 // Attack Animation.
2047 if(_AnimAttackOn)
2049 if(animIndex(MOVE) != ::CAnimation::UnknownAnim)
2051 if(animOffset(MOVE) >= EAM->getAnimationLength(animId(MOVE)))
2053 _AnimAttackOn = false;
2054 updateVisualDisplay();
2059 // Increase the time in the current animation.
2060 double previousTimeOffset = animOffset(MOVE);
2061 double offset = previousTimeOffset + frameTimeRemaining;
2062 // Check Anim length
2063 double animLength = EAM->getAnimationLength(animId(MOVE));
2064 if(offset > animLength)
2065 animOffset(MOVE, animLength);
2066 else
2067 animOffset(MOVE, offset);
2068 // Manage Events that could be created by the animation (like sound).
2069 animEventsProcessing(previousTimeOffset, animOffset(MOVE));
2071 // Get the right pos from PACS.
2072 if(_Primitive && GR)
2074 UGlobalPosition gPos;
2075 _Primitive->getGlobalPosition(gPos, dynamicWI);
2077 // Set the new current pos.
2078 pos(GR->getGlobalPosition(gPos));
2080 // Set the direction.
2081 if( ClientCfg.AutomaticCamera )
2083 //dir(front());
2085 // Reset the TPV when outdoor if needed.
2086 if(_Primitive && GR)
2088 UGlobalPosition gPos;
2089 _Primitive->getGlobalPosition(gPos, dynamicWI);
2090 if(!GR->isInterior(gPos))
2091 if(!ClientCfg.FPV)
2092 viewMode(ThirdPV, false);
2094 // Current time is now the entity time too.
2095 _LastFrameTime = t*0.001;
2096 }// updatePos //
2098 //-----------------------------------------------
2099 // updateDisplay :
2100 // Update the PACS position after the evalCollision. The entity position is set too. This is fast.
2101 // If the entity position is too far from its PACS position, setGlobalPosition is called.
2102 // After this call, the position.z is valid.
2103 //-----------------------------------------------
2104 void CUserEntity::pacsFinalizeMove() // virtual
2106 if(_Primitive == 0)
2107 return;
2109 // Get the global position
2110 _FinalPacsPos = _Primitive->getFinalPosition(dynamicWI);
2112 // Get the global position
2113 UGlobalPosition gPos;
2114 _Primitive->getGlobalPosition(gPos, dynamicWI);
2115 if(gPos.InstanceId != -1)
2117 pos(_FinalPacsPos);
2118 if(_Mount != CLFECOMMON::INVALID_SLOT)
2120 CEntityCL *mount = EntitiesMngr.entity(_Mount);
2121 if(mount)
2122 mount->pacsPos(pos());
2125 else
2126 _SetGlobalPositionDone = false;
2127 }// pacsFinalizeMove //
2130 //-----------------------------------------------
2131 // applyBehaviour :
2132 // Apply the behaviour for the user.
2133 //-----------------------------------------------
2134 void CUserEntity::applyBehaviour(const CBehaviourContext &behaviourContext) // virtual
2136 const MBEHAV::CBehaviour &behaviour = behaviourContext.Behav;
2137 // Special code for the First Person View
2138 if((viewMode() == FirstPV) && behaviour.isCombat())
2140 // Backup the current behaviour.
2141 _CurrentBehaviour = behaviourContext.Behav;
2142 dir(front());
2144 // check if self-target
2145 bool selfSpell = false;
2146 if (behaviourContext.Targets.Targets.size() == 1)
2148 if (behaviourContext.Targets.Targets[0].TargetSlot == 0)
2150 selfSpell = true;
2153 bool isOffensif;
2154 switch(behaviour.Behaviour)
2156 case MBEHAV::CAST_OFF:
2157 case MBEHAV::CAST_OFF_FAIL:
2158 case MBEHAV::CAST_OFF_SUCCESS:
2159 case MBEHAV::CAST_OFF_LINK:
2160 isOffensif = true;
2161 break;
2162 default:
2163 isOffensif = false;
2164 break;
2167 // Default target hit dates
2168 static vector<double> targetHitDates;
2169 targetHitDates.clear();
2170 targetHitDates.resize(behaviourContext.Targets.Targets.size(), TimeInSec);
2172 // cast projectiles/impact linked to behaviour
2173 updateCurrentAttack();
2174 if (isCurrentBehaviourAttackEnd())
2176 // In First Person (don't care), use a default animation => will choose a default delay of 0.5
2177 performCurrentAttackEnd(behaviourContext, selfSpell && isOffensif,
2178 targetHitDates, CAnimationStateSheet::UnknownState);
2181 _RightFXActivated = false;
2182 _LeftFXActivated = false;
2183 if(EAM)
2185 animState (MOVE, CAnimationStateSheet::FirstPersonAttack);
2186 animIndex (MOVE, CAnimation::UnknownAnim);
2187 animOffset(MOVE, 0.0);
2188 _CurrentState = EAM->mState(_CurrentAutomaton, animState(MOVE));
2189 if(_CurrentState && _CurrentAnimSet[MOVE])
2191 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
2192 if(animStatePtr)
2194 animIndex(MOVE, animStatePtr->chooseAnim(_AnimJobSpecialisation, people(), getGender(), 0.0));
2195 if(animIndex(MOVE) != CAnimation::UnknownAnim)
2197 if(_PlayList)
2199 _PlayList->setAnimation (MOVE, animId(MOVE));
2200 _PlayList->setTimeOrigin (MOVE, ryzomGetLocalTime ()*0.001);//_LastFrameTime);
2201 _AnimAttackOn = true;
2202 updateVisualDisplay();
2208 // Start FX to play at the animation beginning like trails.
2209 if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK)
2211 startItemAttackFXs(_CurrentBehaviour.Range.ImpactIntensity != 0, _CurrentBehaviour.Range.ImpactIntensity);
2213 else
2215 startItemAttackFXs(_CurrentBehaviour.Combat.ImpactIntensity != 0 && _CurrentBehaviour.Combat.HitType != HITTYPE::Failed, _CurrentBehaviour.Combat.ImpactIntensity);
2217 // DeltaHP
2218 applyBehaviourFlyingHPs(behaviourContext, behaviour, targetHitDates);
2220 // In third person view (or camera mode), play the same way than for the others.
2221 else
2222 CPlayerCL::applyBehaviour(behaviourContext);
2223 }// applyBehaviour //
2225 //---------------------------------------------------
2226 // setDead :
2227 // Method to Flag the character as dead and do everything needed.
2228 //---------------------------------------------------
2229 void CUserEntity::setDead() // virtual
2231 // User is dead -> NO FOLLOW
2232 disableFollow();
2233 // Remove the selection.
2234 UserEntity->selection(CLFECOMMON::INVALID_SLOT);
2235 // Death Mode Control and view.
2236 UserControls.mode(CUserControls::DeathMode);
2237 // Play the FX for the death
2238 NL3D::UParticleSystemInstance deathFX = FXMngr.instantFX(ClientCfg.DeadFXName);
2239 if(!deathFX.empty())
2240 deathFX.setPos(selectBox().getCenter());
2242 // Last teleport is a death
2244 // get player's kami fame
2245 CInterfaceManager *pIM = CInterfaceManager::getInstance();
2246 sint8 kamiFame = 0;
2247 uint kamiFameIndex = CStaticFames::getInstance().getFactionIndex("kami");
2248 if (pIM && kamiFameIndex != CStaticFames::INVALID_FACTION_INDEX)
2250 CCDBNodeLeaf *pLeafKamiFame = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", kamiFameIndex - 1), false);
2251 if (pLeafKamiFame != NULL)
2252 kamiFame = pLeafKamiFame->getValue8();
2255 // get player's karavan fame
2256 sint8 karavanFame = 0;
2257 uint karavanFameIndex = CStaticFames::getInstance().getFactionIndex("karavan");
2258 if (pIM && karavanFameIndex != CStaticFames::INVALID_FACTION_INDEX)
2260 CCDBNodeLeaf *pLeafKaravanFame = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", karavanFameIndex - 1), false);
2261 if (pLeafKaravanFame != NULL)
2262 karavanFame = pLeafKaravanFame->getValue8();
2265 // set loading background depending on player's faction fame
2266 if (kamiFame > karavanFame)
2267 LoadingBackground = ResurectKamiBackground;
2268 else
2269 LoadingBackground = ResurectKaravanBackground;
2271 // disable or enable shadowing
2272 updateCastShadowMap();
2274 // enable death warning popup
2275 //CInterfaceManager * pIM = CInterfaceManager::getInstance();
2276 if( pIM )
2278 CAHManager::getInstance()->runActionHandler("set",NULL,"dblink=UI:VARIABLES:DEATH_WARNING_WANTED|value=1");
2280 }// setDead //
2282 //-----------------------------------------------
2283 // skillUp :
2284 // Skill Up
2285 //-----------------------------------------------
2286 void CUserEntity::skillUp()
2288 // Play the FX for the death
2289 NL3D::UParticleSystemInstance skillUpFX = FXMngr.instantFX(ClientCfg.SkillUpFXName);
2290 skillUpFX.setClusterSystem(getClusterSystem());
2291 if(!skillUpFX.empty())
2292 skillUpFX.setPos(pos());
2293 }// skillUp //
2295 //-----------------------------------------------
2296 // startColTimer :
2297 // After a few time, if the user is still in collision with someone else, remove collisions with other entitites.
2298 //-----------------------------------------------
2299 void CUserEntity::startColTimer()
2301 if(_ColOn)
2303 if(_ColRemoved==false)
2305 if((T1-_ColStartTime) > ClientCfg.TimeToRemoveCol)
2307 EntitiesMngr.removeColUserOther();
2308 _ColRemoved = true;
2312 else
2314 _ColOn = true;
2315 _ColStartTime = T1;
2317 }// startColTimer //
2319 //-----------------------------------------------
2320 // stopColTimer :
2321 // Called when the user is no more in collision with another entity.
2322 //-----------------------------------------------
2323 void CUserEntity::stopColTimer()
2325 // Restore collisions.
2326 if(_ColRemoved)
2328 EntitiesMngr.restoreColUserOther();
2329 _ColRemoved = false;
2331 _ColOn = false;
2332 }// stopColTimer //
2336 //-----------------------------------------------
2337 // getMaxSpeed
2338 // Return the current max speed for the entity in meter per sec
2339 // The method return a max according to the speed factor (given by the server)
2340 // It's also return a value according to the landscape (water)
2341 // Also managed mounts
2342 //-----------------------------------------------
2343 double CUserEntity::getMaxSpeed() const // virtual
2345 // Max Defined speed according to the current factor (slow, or speed increased)
2346 double maxSpeed = _SpeedFactor.getValue();
2347 // User is Dead
2348 if(isDead())
2350 maxSpeed *= 0.0;
2352 // User is sitting
2353 else if(isSit())
2355 maxSpeed *= 0.0;
2357 // User is swimming
2358 else if(isSwimming())
2361 // We are a Ring DM so speed up a litle
2362 if (_R2CharMode == R2::TCharMode::Editer || _R2CharMode == R2::TCharMode::Dm)
2364 maxSpeed *= ClientCfg.DmRun / 2;
2366 else
2368 // Run
2369 maxSpeed *= 3.0;
2372 else if(isRiding())
2374 // Run if mount not hungry, otherwise, use walk velocity
2375 if (_MountHunger.canRun())
2376 maxSpeed *= (double)getMountRunVelocity();
2377 else
2378 maxSpeed *= (double)getMountWalkVelocity();
2380 else
2382 // Run
2383 maxSpeed *= runVelocity();
2385 // Return the current max
2386 return maxSpeed;
2387 // return ClientCfg.Run * _SpeedFactor.getValue();
2388 }// getMaxSpeed //
2391 //-----------------------------------------------
2392 // snapToGround :
2393 // Snap the user to the ground.
2394 //-----------------------------------------------
2395 void CUserEntity::snapToGround()
2397 CEntityCL::snapToGround();
2399 updateSound (ryzomGetLocalTime ());
2400 }// // snapToGround //
2403 //-----------------------------------------------
2404 // updateSound :
2405 //-----------------------------------------------
2406 void CUserEntity::updateSound(const TTime &time)
2408 H_AUTO_USE ( RZ_Client_Update_Sound );
2410 // no sound manager, no need to update sound
2411 if (SoundMngr == NULL)
2412 return;
2414 if (!(StereoHMD && true)) // TODO: ClientCfg.Headphone
2416 // NOTE: StereoHMD+Headphone impl in main_loop.cpp
2417 SoundMngr->setListenerPos(pos());
2418 const CMatrix &camMat = MainCam.getMatrix();
2419 SoundMngr->setListenerOrientation(camMat.getJ(), camMat.getK());
2422 if (ClientCfg.Light)
2423 return;
2425 // step sound of self : refind the sound animations associated to the walk and run animation
2427 static bool computeAnim = true;
2428 static bool lastMode = false;
2429 static TTime previousTime = 0;
2430 static TTime stepTime = 0;
2431 static bool leftRight = false;
2432 static NLSOUND::CSoundAnimMarker *leftStep = 0;
2433 static NLSOUND::CSoundAnimMarker *rightStep = 0;
2435 // TODO : Remove when bug corrected:
2436 if (_Gender == GSGENDER::unknown)
2438 nlwarning("CUserEntity::updateSound : The gender is unknown, forcing it to male");
2439 _Gender = GSGENDER::male;
2442 // force recompute of anim if walk/run change.
2443 computeAnim = computeAnim || (lastMode != _Run);
2445 // Check the sound animation to find the time between to step
2446 if(computeAnim && SoundMngr && _CurrentAnimSet[MOVE] != 0) // && _SoundId[MOVE] != NLSOUND::CSoundAnimationNoId)
2448 lastMode = _Run;
2449 TAnimStateId mode = _Run ? CAnimationStateSheet::Run : CAnimationStateSheet::Walk;
2450 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState (mode);
2451 if (animStatePtr)
2453 ::CAnimation::TAnimId animId = animStatePtr->chooseAnim (_AnimJobSpecialisation, people(), getGender(), 0);
2454 if (animId != ::CAnimation::UnknownAnim)
2456 const ::CAnimation *anim = animStatePtr->getAnimation (animId);
2457 if(anim)
2459 // Select the sound ID
2460 _SoundId[MOVE] = anim->soundId();
2462 if (_SoundId[MOVE] != NLSOUND::CSoundAnimationNoId)
2464 // retrieve the anim
2465 NLSOUND::CSoundAnimManager *mgr = NLSOUND::CSoundAnimManager::instance();
2467 string name = mgr->idToName(_SoundId[MOVE]);
2469 if (!name.empty())
2471 NLSOUND::CSoundAnimation *sanim = mgr->findAnimation(name);
2472 if (sanim->countMarkers() != 2)
2474 static set<string> warnOnce;
2475 if (warnOnce.find(sanim->getName()) == warnOnce.end())
2477 nlwarning("Sound animation '%s' has not 2 markers, not a biped ? (Display Once)", sanim->getName().c_str());
2478 warnOnce.insert(sanim->getName());
2481 else
2483 stepTime = TTime((sanim->getMarker(1)->getTime() - sanim->getMarker(0)->getTime()) * 1000);
2484 rightStep = sanim->getMarker(0);
2485 leftStep = sanim->getMarker(1);
2486 computeAnim = false;
2495 if( SoundMngr && _Mode == MBEHAV::NORMAL)
2497 if (!getVelocity().isNull())
2499 if (stepTime > 0 && time-previousTime>=stepTime)
2501 previousTime = time;
2503 // set the sound 1 meter below listener
2504 _SoundContext.Position = pos() + CVector(0,0,-1);
2505 _SoundContext.RelativeGain = SoundMngr->getUserEntitySoundLevel();;
2507 uint32 matId= getGroundType();
2508 //nldebug("Current material = %u", matId);
2509 _SoundContext.Args[0] = matId;
2511 if (leftRight)
2513 if (leftStep)
2514 // TODO : find the correct cluster
2515 leftStep->play(SoundMngr->getMixer(), NULL, _SoundContext);
2517 else
2519 if (rightStep)
2520 // TODO : find the correct cluster
2521 rightStep->play(SoundMngr->getMixer(), NULL, _SoundContext);
2523 // recompute a new sound anim
2524 computeAnim = true;
2527 leftRight = !leftRight;
2530 else
2532 // resets the counter
2533 previousTime = 0;
2536 }// updateSound //
2539 //-----------------------------------------------
2540 // rotate :
2541 // rotate the body on the left or right (front changes).
2542 //-----------------------------------------------
2543 void CUserEntity::rotate(float ang)
2545 // Rotate the body.
2546 CMatrix m;
2547 m.identity();
2548 m.rotateZ(ang);
2549 front(m * front().normed());
2550 }// rotate //
2553 //-----------------------------------------------
2554 // rotHeadVertically :
2555 // rotate the head vertically.
2556 //-----------------------------------------------
2557 void CUserEntity::rotHeadVertically(float ang)
2559 setHeadPitch(_HeadPitch+ang);
2560 }// rotHeadVertically //
2563 //-----------------------------------------------
2564 // setHeadPitch(double hp)
2565 //-----------------------------------------------
2566 void CUserEntity::setHeadPitch(double hp)
2568 _HeadPitch = hp;
2569 // epsilon to avoid gimbal lock
2570 clamp(_HeadPitch, -Pi/2 + 0.01, Pi/2 - 0.5);
2573 //---------------------------------------------------
2574 // slotRemoved :
2575 // To Inform about an entity removed (to remove from selection for example).
2576 // This will remove the entity from the target.
2577 // \param slot : Slot of the entity that will be removed.
2578 //---------------------------------------------------
2579 void CUserEntity::slotRemoved(const CLFECOMMON::TCLEntityId &slot)
2581 // parent call
2582 CPlayerCL::slotRemoved(slot);
2584 // reset also selection
2585 if(selection() == slot)
2586 selection(CLFECOMMON::INVALID_SLOT);
2587 }// slotRemoved //
2589 //---------------------------------------------------
2590 // selection :
2591 // Change the entity selected.
2592 // \param slot : slot now selected (CLFECOMMON::INVALID_SLOT for an empty selection).
2593 // \warning Can be different from the entity targeted (in combat mode for example).
2594 //---------------------------------------------------
2595 void CUserEntity::selection(const CLFECOMMON::TCLEntityId &slot) // virtual
2597 //allows reselection in Ring client: even if the selected slots is equal to the selection,
2598 //the client must send the messages.
2599 if ((_Selection == slot) && !ClientCfg.R2EDEnabled)
2600 return;
2602 // The selection will be the entity to watch
2603 WatchedEntitySlot = slot;
2604 disableFollow();
2606 // Send the entity selected to the server.
2607 NetMngr.pushTarget(slot);
2610 // Target the slot on client, don't wait NetWork response
2611 targetSlot(slot);
2612 _TargetSlotNoLag= slot;
2614 if (ClientCfg.R2EDEnabled)
2616 R2::getEditor().inGameSelection(slot);
2620 // Change the current selection so un color the current selection.
2621 CEntityCL *sel = EntitiesMngr.entity(_Selection);
2622 if(sel != NULL)
2623 sel->visualSelectionStop(); // Blink off == restore to normal
2625 // Set the entity selected
2626 _Selection = slot;
2628 // Update visual selection and interface
2629 if ( sel && sel->isForageSource() )
2630 sel->buildInSceneInterface(); // remove focus on previous selected source
2631 sel = EntitiesMngr.entity(_Selection);
2632 if(sel != NULL)
2634 sel->visualSelectionStart();
2635 if ( sel->isForageSource() )
2636 sel->buildInSceneInterface(); // set focus on new selected source
2640 // **** Update Target interface
2641 //get the new target slot and set it in the database
2642 CInterfaceManager *pIM = CInterfaceManager::getInstance();
2643 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:SLOT")->setValue64(slot);
2645 // Get the new target UID, and set in Database
2646 uint tgtSlot= _Selection;
2647 uint32 tgtEntityId= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
2648 CEntityCL *entity = NULL;
2649 if (tgtSlot!=CLFECOMMON::INVALID_SLOT)
2651 entity = EntitiesMngr.entity(tgtSlot);
2652 if (entity)
2653 tgtEntityId= entity->dataSetId();
2656 // Set the User Target
2657 CCDBNodeLeaf *prop = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:UID", false);
2658 if(prop)
2659 prop->setValue32(tgtEntityId);
2661 // Bar Manager. Update now the Target View (so it takes VP if data available or 0... but result is immediate)
2662 CBarManager::getInstance()->setLocalTarget(tgtEntityId);
2664 // **** Update DB for InGameMenu
2665 // clear the entries for mission option
2666 for(uint k = 0; k < NUM_MISSION_OPTIONS; ++k)
2668 CCDBNodeLeaf *missionOption = NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:TITLE", (int) k), false);
2669 if (missionOption)
2671 missionOption->setValue32(0);
2673 CCDBNodeLeaf *playerGiftNeeded = NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:PLAYER_GIFT_NEEDED", (int) k), false);
2674 if (playerGiftNeeded)
2676 playerGiftNeeded->setValue32(0);
2679 /* TODO ULU : Add RP tags */
2681 // update pvp tags
2682 if ((tgtSlot!=CLFECOMMON::INVALID_SLOT) && entity)
2684 CPlayerCL *pPlayer = dynamic_cast<CPlayerCL*>(entity);
2686 if (pPlayer)
2688 /*// Pvp Mode
2689 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:mode"));
2690 if (tagMode)
2692 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2693 tagMode->setTexture("pvp_orange.tga");
2694 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2695 tagMode->setTexture("pvp_red.tga");
2696 else
2697 tagMode->setTexture("alpha_10.tga");
2700 /*// Pvp available actions (attack, heal, both)
2701 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:actions"));
2702 if (tagMode)
2704 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2705 tag->setTexture("pvp_orange.tga");
2706 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2707 tag->setTexture("pvp_red.tga");
2708 else
2709 tag->setTexture("alpha_10.tga");
2715 // clear web page
2716 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_URL", false);
2717 if(prop) prop->setValue32(0);
2718 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_TITLE", false);
2719 if(prop) prop->setValue32(0);
2721 // clear mission ring
2722 for(uint k = 0; k < BOTCHATTYPE::MaxR2MissionEntryDatabase; ++k)
2724 prop= NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSION_RING:%d:TITLE", k), false);
2725 if(prop) prop->setValue32(0);
2728 // clear programs
2729 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:PROGRAMMES", false);
2730 if(prop) prop->setValue32(0);
2731 prop= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:TARGET:CONTEXT_MENU:PROGRAMMES");
2732 if(prop) prop->setValue32(0);
2733 // increment db counter for context menu
2734 pIM->incLocalSyncActionCounter();
2736 }// selection //
2738 //---------------------------------------------------
2739 // moveToAttack :
2740 // Method to place the user to attack the target and attack.
2741 //---------------------------------------------------
2742 void CUserEntity::moveToAttack()
2744 // **** For clarity, try to launch a "default attack" found in the memory bar instead of an "anonymous" action
2745 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2746 uint32 memLine, memSlot;
2747 CEntityCL *target= EntitiesMngr.entity(selection());
2749 CInventoryManager *inv = CInventoryManager::getInstance();
2751 // auto-equip with valid weapon
2752 if( ClientCfg.AutoEquipTool )
2754 if(inv)
2756 // if no valid weapons in had -> auto-equip with last used weapons
2757 bool validWeaponInHand = true;
2758 uint32 rightHandSheet = inv->getRightHandItemSheet();
2759 if(rightHandSheet)
2761 validWeaponInHand = inv->isMeleeWeaponItem(rightHandSheet) || inv->isRangeWeaponItem(rightHandSheet);
2763 if( !validWeaponInHand )
2765 autoEquipWithLastUsedWeapons();
2768 // remember last used weapon(s)
2769 rememberWeaponsInHand();
2773 if(target && pPM->findDefaultAttack(memLine, memSlot))
2775 // launch instead a phrase execution with this phrase
2776 executeCombatWithPhrase(target, memLine, memSlot, true);
2778 // **** Else launch an anonymous "default attack"
2779 else
2781 // melee or range?
2782 bool melee = true;
2783 if(inv)
2785 uint32 rightHandSheet = inv->getRightHandItemSheet();
2786 if(rightHandSheet)
2787 melee = inv->isMeleeWeaponItem(rightHandSheet);
2790 // Move to target if melee weapon
2791 if(melee)
2792 moveTo(selection(), 2.0, CUserEntity::Attack);
2793 // Just attack if range weapon.
2794 else
2795 attack();
2797 }// moveToAttack //
2799 //---------------------------------------------------
2800 // attack :
2801 // Method to attack the target.
2802 //---------------------------------------------------
2803 void CUserEntity::attack()
2805 // execute the default attack
2806 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2807 pPM->executeDefaultAttack();
2809 bool melee = true;
2810 CInventoryManager *inv = CInventoryManager::getInstance();
2811 if(inv)
2813 uint32 rightHandSheet = inv->getRightHandItemSheet();
2814 if(rightHandSheet)
2815 melee = inv->isMeleeWeaponItem(rightHandSheet);
2818 // If option ON, follow when attacking.
2819 if(ClientCfg.FollowOnAtk)
2821 // Follow only if attacking with a melee weapon
2822 if(melee)
2823 // enable, but don't reset camera rotation
2824 enableFollow(false);
2826 }// attack //
2828 //---------------------------------------------------
2829 // attack :
2830 // Method to attack the target, with a special phrase
2831 //---------------------------------------------------
2832 void CUserEntity::attackWithPhrase()
2834 if( !canEngageCombat() )
2835 return;
2837 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2839 // validate the execution on server
2840 pPM->sendExecuteToServer(_MoveToPhraseMemoryLine, _MoveToPhraseMemorySlot, _MoveToPhraseCyclic);
2842 // If the Attack is a "Next", and not a cycle, do a default attack
2843 if(!_MoveToPhraseCyclic)
2844 pPM->executeDefaultAttack();
2846 // If option ON, follow when attacking.
2847 if(ClientCfg.FollowOnAtk)
2849 bool melee = true;
2850 CInventoryManager *inv = CInventoryManager::getInstance();
2851 if(inv)
2853 uint32 rightHandSheet = inv->getRightHandItemSheet();
2854 if(rightHandSheet)
2855 melee = inv->isMeleeWeaponItem(rightHandSheet);
2857 // Follow only if attacking with a melee weapon
2858 if(melee)
2859 // enable, but don't reset camera rotation
2860 enableFollow(false);
2863 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
2864 _MoveToAction= CUserEntity::None;
2867 //-----------------------------------------------
2868 // assist :
2869 // your current target become the target of your current one.
2870 //-----------------------------------------------
2871 void CUserEntity::assist()
2873 assist(targetSlot());
2874 }// assist //
2876 //-----------------------------------------------
2878 void CUserEntity::assist(uint slot)
2880 // Check the current target
2881 if(slot == CLFECOMMON::INVALID_SLOT || slot == _Slot)
2882 return;
2883 // Check the target
2884 CEntityCL *target = EntitiesMngr.entity(slot);
2885 if(target == 0)
2886 return;
2887 // Check the new slot.
2888 CLFECOMMON::TCLEntityId newSlot = target->targetSlot();
2889 if(newSlot == CLFECOMMON::INVALID_SLOT || newSlot == _Slot)
2890 return;
2891 // Select the new target.
2892 selection(newSlot);
2895 //---------------------------------------------------
2896 // disengage :
2897 // Method to disengage the target.
2898 //---------------------------------------------------
2899 void CUserEntity::disengage()
2901 // Set the database in local.
2902 if(ClientCfg.Local)
2904 IngameDbMngr.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_MODE), MBEHAV::NORMAL); // Mode
2905 IngameDbMngr.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_TARGET_ID), _Selection); // Target
2906 UserEntity->updateVisualProperty(0, CLFECOMMON::PROPERTY_MODE);
2907 UserEntity->updateVisualProperty(0, CLFECOMMON::PROPERTY_TARGET_ID);
2908 return;
2911 // Disengage MSG.
2912 const string msgName = "COMBAT:DISENGAGE";
2913 CBitMemStream out;
2914 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2915 NetMngr.push(out);
2916 else
2917 nlwarning("UE::disengage: unknown message named '%s'.", msgName.c_str());
2919 // Change the current mode.
2920 mode(MBEHAV::NORMAL);
2921 }// disengage //
2923 //-----------------------------------------------
2924 // sit :
2925 // Ask for the client to sit/stand ('true' to sit).
2926 //-----------------------------------------------
2927 bool CUserEntity::sit(bool s)
2929 bool ok= false;
2931 // SIT
2932 if(s)
2934 if(canSit() == true)
2936 // disable afk mode
2937 setAFK(false);
2939 // Sit MSG.
2940 if(mode(MBEHAV::SIT))
2942 // autowalk disabled
2943 UserControls.autowalkState(false);
2945 static const string msgName = "COMMAND:SIT";
2946 CBitMemStream out;
2947 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2949 out.serial(s);
2950 NetMngr.push(out);
2952 else
2953 nlwarning("UE:sit: unknown message named '%s'.", msgName.c_str());
2955 // mode changed
2956 ok= true;
2958 // display sit msg
2959 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2960 string msg = CI18N::get("msgUserIsSitting");
2961 string cat = getStringCategory(msg, msg);
2962 pIM->displaySystemInfo(msg, cat);
2966 // STAND
2967 else
2969 if(_Mode == MBEHAV::SIT)
2971 if(mode(MBEHAV::NORMAL))
2973 static const string msgName = "COMMAND:SIT";
2974 CBitMemStream out;
2975 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2977 out.serial(s);
2978 NetMngr.push(out);
2980 else
2981 nlwarning("UE:sit: unknown message named '%s'.", msgName.c_str());
2983 // mode changed
2984 ok= true;
2986 // display stand msg
2987 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2988 string msg = CI18N::get("msgUserIsStanding");
2989 string cat = getStringCategory(msg, msg);
2990 pIM->displaySystemInfo(msg, cat);
2995 // if mode changed, Write to the UI database, to update views
2996 if(ok)
2998 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2999 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_STAND", false);
3000 if(node)
3001 node->setValue32(_Mode != MBEHAV::SIT);
3004 // mode changed
3005 return ok;
3006 }// sit //
3008 //-----------------------------------------------
3009 // canSit :
3010 // Return true if the user can sit.
3011 //-----------------------------------------------
3012 bool CUserEntity::canSit() const
3014 // If the user is not already sitting or is on a mount
3015 if(!isSit() && (!isRiding()) && !isDead() && !isSwimming())
3017 return true;
3019 else
3020 return false;
3021 }// canSit //
3023 //-----------------------------------------------
3024 // setAFK
3025 //-----------------------------------------------
3026 void CUserEntity::setAFK(bool b, string afkTxt)
3028 if( isAFK() == b ) return;
3030 if (b)
3032 if( isDead() || isRiding() || moveTo() || follow() )
3033 return;
3035 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3036 //sint64 start = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TSTART")->getValue64();
3037 //sint64 end = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TEND")->getValue64();
3038 //sint64 type = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64();
3039 //sint64 num = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_NUMBER")->getValue64();
3040 if( pIM )
3042 if( NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64() != 0 )
3043 return;
3046 if( !isSit() && !isSwimming() )
3048 if( !mode(MBEHAV::REST) )
3049 return;
3052 else
3054 if( !isSit() && !isSwimming() )
3056 if (isDead())
3057 return;
3058 else
3059 mode(MBEHAV::NORMAL);
3063 // send afk state
3064 static const string msgName = "COMMAND:AFK";
3065 CBitMemStream out;
3066 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
3068 out.serial(b);
3069 NetMngr.push(out);
3071 else
3072 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgName.c_str());
3074 // custom afk txt
3075 ucstring ucstr; // TODO: UTF-8 (serial)
3076 ucstr.fromUtf8( afkTxt );
3077 CBitMemStream outTxt;
3078 static const string msgNameTxt = "STRING:AFK_TXT";
3079 if( GenericMsgHeaderMngr.pushNameToStream(msgNameTxt,outTxt) )
3081 outTxt.serial( ucstr );
3082 NetMngr.push( outTxt );
3084 else
3086 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgNameTxt.c_str());
3090 }// setAFK //
3092 //-----------------------------------------------
3093 // rollDice
3094 //-----------------------------------------------
3095 void CUserEntity::rollDice(sint16 min, sint16 max, bool local)
3097 if (local)
3099 // no need to broadcast over network here
3100 static NLMISC::CRandom* dice = (NLMISC::CRandom*)NULL;
3101 if (!dice)
3103 dice = new NLMISC::CRandom;
3104 dice->srand(CTickEventHandler::getGameCycle());
3106 sint16 roll = min + (sint16)dice->rand(max-min);
3108 string msg = CI18N::get("msgRollDiceLocal");
3109 strFindReplace(msg, "%min", toString(min));
3110 strFindReplace(msg, "%max", toString(max));
3111 strFindReplace(msg, "%roll", toString(roll));
3113 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3115 pIM->displaySystemInfo(msg, getStringCategory(msg, msg));
3116 return;
3118 const string msgName = "COMMAND:RANDOM";
3119 CBitMemStream out;
3120 if (GenericMsgHeaderMngr.pushNameToStream(msgName, out))
3122 out.serial(min);
3123 out.serial(max);
3124 NetMngr.push(out);
3126 else
3127 nlwarning("CUserEntity:rollDice: unknown message named '%s'.", msgName.c_str());
3128 }// rollDice //
3130 //---------------------------------------------------
3131 // canEngageCombat :
3132 // return true if user can engage melee combat, else return false and display system msg
3133 //---------------------------------------------------
3134 bool CUserEntity::canEngageCombat()
3136 if( isSit() )
3138 // display "you can't fight while sitting" message)
3139 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3140 string msg = CI18N::get("msgCantFightSit");
3141 string cat = getStringCategory(msg, msg);
3142 pIM->displaySystemInfo(msg, cat);
3144 return false;
3147 if( isSwimming() )
3149 // display "you can't fight while swiming" message)
3150 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3151 string msg = CI18N::get("msgCantFightSwim");
3152 string cat = getStringCategory(msg, msg);
3153 pIM->displaySystemInfo(msg, cat);
3155 return false;
3158 if ( isRiding() )
3160 // display "you can't fight while swimming" message)
3161 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3162 string msg = CI18N::get("msgCantFightRide");
3163 string cat = getStringCategory(msg, msg);
3164 pIM->displaySystemInfo(msg, cat);
3166 return false;
3169 return true;
3170 } // canEngageCombat //
3173 //---------------------------------------------------
3174 // viewMode :
3175 // Change the View (First/Third Person View).
3176 //---------------------------------------------------
3177 void CUserEntity::viewMode(CUserEntity::TView viewMode, bool changeView)
3179 switch(viewMode)
3181 // First Person View
3182 case FirstPV:
3183 if(changeView)
3184 ClientCfg.FPV = true;
3185 if(_Mount != CLFECOMMON::INVALID_SLOT)
3187 CEntityCL *mount = EntitiesMngr.entity(_Mount);
3188 if(mount)
3189 mount->displayable(false);
3191 _HiddenMount = _Mount;
3193 // Change Controls.
3194 if( isRiding() )
3196 bool autoWalk = UserControls.autowalkState();
3197 UserControls.mode(CUserControls::MountMode);
3198 if( autoWalk )
3199 UserControls.autowalkState( true );
3201 else
3202 UserControls.mode(CUserControls::InterfaceMode);
3203 break;
3205 // Third Person View
3206 case ThirdPV:
3207 if(changeView)
3208 ClientCfg.FPV = false;
3210 if(_HiddenMount != CLFECOMMON::INVALID_SLOT)
3212 CEntityCL *mount = EntitiesMngr.entity(_HiddenMount);
3213 if(mount)
3214 mount->displayable(true);
3216 _HiddenMount = CLFECOMMON::INVALID_SLOT;
3218 // Change Controls.
3219 UserControls.mode(CUserControls::ThirdMode);
3220 break;
3222 // Unknown
3223 default:
3224 nlwarning("UE:viewMode: Unknown View Asked '%d'.", (sint)viewMode);
3225 return;
3228 // Change the current View like asked.
3229 _ViewMode = viewMode;
3231 // disable or enable shadowing
3232 updateCastShadowMap();
3233 }// viewMode //
3235 //-----------------------------------------------
3236 // toggleCamera :
3237 // Toggle Camera (First/Third Person)
3238 //-----------------------------------------------
3239 void CUserEntity::toggleCamera()
3241 // You cannot change the camera view when dead.
3242 if(isDead())
3243 return;
3244 // Only if not inside a building.
3245 if(!UserEntity->forceIndoorFPV())
3247 // Leave the 1st Person Mode -> Enter the 3rd Person View Mode
3248 if (UserEntity->viewMode() == CUserEntity::FirstPV)
3249 UserEntity->viewMode(CUserEntity::ThirdPV);
3250 // Leave the 3rd Person Mode -> Enter the 1st Person View Mode
3251 else
3252 UserEntity->viewMode(CUserEntity::FirstPV);
3254 }// toggleCamera //
3256 //-----------------------------------------------
3257 // forceCameraFirstPerson :
3258 // Force Camera to First Person View
3259 //-----------------------------------------------
3260 void CUserEntity::forceCameraFirstPerson()
3262 // You cannot change the camera view when dead.
3263 if(isDead())
3264 return;
3265 // Only if not inside a building.
3266 if(!UserEntity->forceIndoorFPV())
3268 if (UserEntity->viewMode() != CUserEntity::FirstPV)
3269 //Enter the 1st Person View Mode
3270 UserEntity->viewMode(CUserEntity::FirstPV);
3272 }// forceCameraFirstPerson //
3274 //---------------------------------------------------
3275 // getScale :
3276 // Return the entity scale. (return 1.0 if there is any problem).
3277 // \todo GUIGUI : do we have to take care of the user's race kwnowing it can favour him ?
3278 //---------------------------------------------------
3279 float CUserEntity::getScale() const // virtual
3281 // Default Scale.
3282 return 1.0f;
3283 }// getScale //
3285 //---------------------------------------------------
3286 // removeCheckPrimitive :
3287 // Remove the check primitive
3288 //---------------------------------------------------
3289 void CUserEntity::removeCheckPrimitive()
3291 if(PACS && _CheckPrimitive)
3292 PACS->removePrimitive(_CheckPrimitive);
3293 _CheckPrimitive = 0;
3296 //---------------------------------------------------
3297 // removePrimitive :
3298 // Remove the primitive
3299 //---------------------------------------------------
3300 void CUserEntity::removePrimitive() // virtual (will not be called by ~CEntityCL())
3302 // Remove the Primitive used for check
3303 removeCheckPrimitive();
3305 // Remove the other primitive
3306 CPlayerCL::removePrimitive();
3307 }// removePrimitive //
3309 //---------------------------------------------------
3310 // computePrimitive :
3311 // Create a primitive for the entity.
3312 //---------------------------------------------------
3313 void CUserEntity::computePrimitive() // virtual
3315 // Initialize the primitive.
3316 if(initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::Slide, (UMovePrimitive::TTrigger)(UMovePrimitive::OverlapTrigger | UMovePrimitive::EnterTrigger), MaskColPlayer, MaskColPlayer | MaskColNpc | MaskColDoor))
3317 _Primitive->insertInWorldImage(dynamicWI);
3318 // Set the position.
3319 pacsPos(pos());
3320 }// computePrimitive //
3323 //---------------------------------------------------
3324 // isBusy :
3325 // Return if the user is already busy (combat/bo chat/loot/ etc.).
3326 //---------------------------------------------------
3327 bool CUserEntity::isBusy() const
3329 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3330 // Check Trade.
3332 // TODO : put the right DB entry !
3334 CCDBNodeLeaf *nod = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:INVENTORY:EXCHANGE:BEGUN", false);
3335 if(nod)
3337 if(nod->getValueBool())
3338 return true;
3340 // else
3341 // nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:EXCHANGE:BEGUN'.");
3344 // Check Loot
3345 static const uint nbSlot = 4;
3346 uint i;
3347 for(i=0; i<nbSlot; ++i)
3349 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::pickup, i), false);
3350 if(nod)
3352 if(nod->getValue32() != 0)
3353 return true;
3355 else
3356 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::pickup, i);
3359 // Check Harvest
3360 for(i=0; i<nbSlot; ++i)
3362 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::harvest, i), false);
3363 if(nod)
3365 if(nod->getValue32() != 0)
3366 return true;
3368 else
3369 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::harvest, i);
3373 // Check Bot chat.
3374 CBotChatPage * currPage = CBotChatManager::getInstance()->getCurrPage();
3375 if( currPage!= NULL )
3377 return true;
3380 // Not Busy
3381 return false;
3382 }// isBusy //
3385 //---------------------------------------------------
3386 // updateVisualDisplay :
3387 // Show/Hide all or parts of the user body.
3388 // todo GUIGUI : it's bad for the _Face to be a separated instance
3389 //---------------------------------------------------
3390 void CUserEntity::updateVisualDisplay()
3392 // We need a skeleton.
3393 if(_Skeleton.empty())
3394 return;
3396 // 1st person View
3397 if(UserControls.isInternalView() || View.forceFirstPersonView())
3399 // Hide the mount
3400 if (_Mount != CLFECOMMON::INVALID_SLOT)
3402 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
3403 if(mount)
3404 mount->displayable(false);
3406 _HiddenMount = _Mount;
3408 else if (_HiddenMount != CLFECOMMON::INVALID_SLOT)
3410 // not on mount anymore, but still in FPV
3411 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_HiddenMount));
3412 if(mount)
3413 mount->displayable(true);
3415 _HiddenMount = CLFECOMMON::INVALID_SLOT;
3418 // Hide all user body parts.
3419 for(uint i=0; i<_Instances.size(); ++i)
3420 if(!_Instances[i].Current.empty())
3422 _Instances[i].Current.hide();
3423 _Instances[i].hideStaticFXs();
3425 // Hide the face
3426 if(!_Face.Current.empty())
3427 _Face.Current.hide();
3429 // We want to display weapons in 1st person view when attacking.
3430 if(modeWithHiddenItems() == false)
3432 if( _Mode == MBEHAV::COMBAT_FLOAT || _Mode == MBEHAV::COMBAT )
3434 if( _ObjectsVisible )
3436 // Show just Few parts
3437 if(!_Instances[SLOTTYPE::HANDS_SLOT].Current.empty())
3439 _Instances[SLOTTYPE::HANDS_SLOT].Current.show();
3440 _Instances[SLOTTYPE::HANDS_SLOT].showStaticFXs();
3442 if(!_Instances[SLOTTYPE::ARMS_SLOT].Current.empty())
3444 _Instances[SLOTTYPE::ARMS_SLOT].Current.show();
3445 _Instances[SLOTTYPE::ARMS_SLOT].showStaticFXs();
3447 if(!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current.empty())
3449 _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current.show();
3450 _Instances[SLOTTYPE::RIGHT_HAND_SLOT].showStaticFXs();
3452 if(!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Current.empty())
3454 _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current.show();
3455 _Instances[SLOTTYPE::LEFT_HAND_SLOT].showStaticFXs();
3461 else
3463 // Show the mount
3464 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
3465 if(mount)
3467 mount->displayable(true);
3469 showOrHideBodyParts( objectsVisible() );
3472 // Show the face
3474 if(!_Face.Current.empty())
3475 _Face.Current.show();
3478 }// updateVisualDisplay //
3480 void CUserEntity::lightOn()
3482 _LightOn = false;
3483 light();
3486 //---------------------------------------------------
3487 // light:
3488 // Show/Hide the user light.
3489 //---------------------------------------------------
3490 void CUserEntity::light()
3492 // Create the light
3493 if(_Light.empty())
3495 // Check there is a skeleton and a bone to stick the light before to create it.
3496 if(!_Skeleton.empty() && _NameBoneId!=-1)
3498 _Light = Scene->createPointLight();
3499 if(!_Light.empty())
3501 // front of the player
3502 _Light.setPos(0.f,0.3f,0.f);
3503 // Setup the light
3504 _Light.setupAttenuation(12.0f, 20.0f);
3505 // Attach the light
3506 _Skeleton.stickObject(_Light, _NameBoneId);
3507 // The player light is the only one who can interact with Lightmapped objects
3508 _Light.setInfluenceLightMap(true);
3510 // TestYoyo
3512 NL3D::UInstance inst;
3513 inst= Scene->createInstance("box.shape");
3514 if(!inst.empty())
3516 inst.setScale(0.2f, 0.2f, 0.2f);
3517 inst.parent(_Light);
3522 else
3523 nlwarning("UE:light: there is no skeleton or Name Bone to stick the light.");
3525 // Turn On/Off the Light
3526 _LightOn = !_LightOn;
3527 if(!_Light.empty())
3529 if(_LightOn)
3530 _Light.show();
3531 else
3532 _Light.hide();
3534 }// light //
3536 //---------------------------------------------------
3537 // CSpeedFactor::init :
3538 // Initialize the Observer for the Speed Factor.
3539 //---------------------------------------------------
3540 void CUserEntity::CSpeedFactor::init()
3542 _Value = 1.0f; // Default speed factor is 1.
3543 _ServerFactor = 1.0f;
3544 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3545 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3546 if(pNodeLeaf)
3548 /* if(!pNodeLeaf->addToLeaves(this))
3549 nlwarning("UE:SP:init: cannot add the observer");*/
3550 ICDBNode::CTextId textId;
3551 pNodeLeaf->addObserver(this, textId);
3552 if ( pNodeLeaf->getValue64() != 0 )
3553 _Value = ((float)pNodeLeaf->getValue64())/100.0f; // may have been received before
3555 else
3556 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3557 }// CSpeedFactor::init //
3559 //---------------------------------------------------
3560 // CMountHunger::init :
3561 //---------------------------------------------------
3562 void CUserEntity::CMountHunger::init()
3565 //---------------------------------------------------
3566 // CMountSpeeds::init :
3567 //---------------------------------------------------
3568 void CUserEntity::CMountSpeeds::init()
3570 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3571 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3572 BOMB_IF( ! pNodeLeaf, "MOUNT_WALK_SPEED not found", return );
3573 if(pNodeLeaf)
3575 ICDBNode::CTextId textId;
3576 pNodeLeaf->addObserver(this, textId);
3577 _WalkSpeed = ((float)pNodeLeaf->getValue32()) / 1000.0f; // may have been received before
3579 pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3580 BOMB_IF( ! pNodeLeaf, "MOUNT_RUN_SPEED not found", return );
3581 if(pNodeLeaf)
3583 ICDBNode::CTextId textId;
3584 pNodeLeaf->addObserver(this, textId);
3585 _RunSpeed = ((float)pNodeLeaf->getValue32()) / 1000.0f; // may have been received before
3589 //---------------------------------------------------
3591 void CUserEntity::CSpeedFactor::release()
3593 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3594 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3595 if(pNodeLeaf)
3597 /* if(!pNodeLeaf->addToLeaves(this))
3598 nlwarning("UE:SP:init: cannot add the observer");*/
3599 ICDBNode::CTextId textId;
3600 pNodeLeaf->removeObserver(this, textId);
3602 else
3603 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3604 }// CSpeedFactor::init //
3606 void CUserEntity::CMountHunger::release()
3609 void CUserEntity::CMountSpeeds::release()
3611 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3612 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3613 BOMB_IF( ! pNodeLeaf, "MOUNT_WALK_SPEED not found", return );
3614 if(pNodeLeaf)
3616 ICDBNode::CTextId textId;
3617 pNodeLeaf->removeObserver(this, textId);
3619 pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3620 BOMB_IF( ! pNodeLeaf, "MOUNT_RUN_SPEED not found", return );
3621 if(pNodeLeaf)
3623 ICDBNode::CTextId textId;
3624 pNodeLeaf->removeObserver(this, textId);
3629 //---------------------------------------------------
3630 // CSpeedFactor::update :
3631 // Callback called to update the speed factor.
3632 //---------------------------------------------------
3633 void CUserEntity::CSpeedFactor::update(ICDBNode *node) // virtual
3635 CCDBNodeLeaf *leaf = safe_cast<CCDBNodeLeaf *>(node);
3636 _Value = ((float)leaf->getValue64())/100.0f;
3637 //nlinfo("SpeedFactor changed to %f / %" NL_I64 "u", _Value, leaf->getValue64());
3639 // clamp the value (2.0 is the egg item or the level 6 speed up power up, nothing should be faster)
3640 // commented because ring editor speed is in fact faster
3641 //if(_Value > 2.0f)
3643 //nlwarning("HACK: you try to change the speed factor to %f", _Value);
3644 //nlstop;
3645 //_Value = 2.0f;
3647 }// CSpeedFactor::update //
3651 * Return true if the mount can run. Precondition: UserEntity->isRiding().
3653 bool CUserEntity::CMountHunger::canRun() const
3655 CEntityCL *mountEntity = UserEntity->getMountEntity();
3656 if ( ! mountEntity )
3657 return false;
3659 // Find the mount's db leaf and check hunger
3660 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3661 CCDBNodeBranch *animalsNode = safe_cast<CCDBNodeBranch*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3662 BOMB_IF( ! animalsNode, "! animalsNode", return false; );
3663 uint nbAnimals = (uint)animalsNode->getNbNodes();
3664 for ( uint i=0; i!=nbAnimals; ++i )
3666 ICDBNode *beastNode = animalsNode->getNode( i );
3667 CCDBNodeLeaf *uidLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "UID" ) ));
3668 if ( ((CLFECOMMON::TClientDataSetIndex)uidLeaf->getValue32()) == mountEntity->dataSetId() )
3670 CCDBNodeLeaf *hungerLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "HUNGER" ) ));
3671 return (hungerLeaf->getValue32() != (sint)ANIMAL_TYPE::DbHungryValue);
3674 return true;
3679 //---------------------------------------------------
3680 // CMountSpeeds::update :
3681 // Callback called to update the mount speed.
3682 //---------------------------------------------------
3683 void CUserEntity::CMountSpeeds::update(ICDBNode * /* node */) // virtual
3685 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3686 _WalkSpeed = ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_WALK_SPEED")->getValue32())) / 1000.0f;
3687 _RunSpeed = ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_RUN_SPEED")->getValue32())) / 1000.0f;
3692 * Return the mount entity if the user is riding, otherwise NULL
3694 CEntityCL* CUserEntity::getMountEntity()
3696 if ( _Mount < EntitiesMngr.entities().size() )
3698 return EntitiesMngr.entities()[_Mount];
3700 return NULL;
3704 * Return the DB entry for the specified user's animal (NULL if not found)
3706 CCDBNodeBranch *CUserEntity::getBeastDBEntry( CLFECOMMON::TClientDataSetIndex uid )
3708 // Find animal entry corresponding to datasetId
3709 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3710 CCDBNodeBranch *animalsNode = safe_cast<CCDBNodeBranch*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3711 BOMB_IF( ! animalsNode, "! animalsNode", return NULL );
3712 uint nbAnimals = (uint)animalsNode->getNbNodes();
3713 for ( uint i=0; i!=nbAnimals; ++i )
3715 ICDBNode *beastNode = animalsNode->getNode( i );
3716 CCDBNodeLeaf *pNodeLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "UID" ) ));
3717 if ( pNodeLeaf && (pNodeLeaf->getValue32() == (sint32)uid) )
3718 return (CCDBNodeBranch*)beastNode;
3720 return NULL;
3724 //---------------------------------------------------
3725 // displayDebug :
3726 // Display Debug Information.
3727 //---------------------------------------------------
3728 void CUserEntity::displayDebug(float x, float &y, float lineStep) // virtual
3730 CPlayerCL::displayDebug(x, y, lineStep);
3731 }// displayDebug //
3733 //---------------------------------------------------
3734 // displayModifiers :
3735 // Display dmg/heal numbers above the head.
3736 //---------------------------------------------------
3737 void CUserEntity::displayModifiers() // virtual
3739 if(!UserControls.isInternalView())
3740 CPlayerCL::displayModifiers();
3741 }// displayModifiers //
3743 //---------------------------------------------------
3744 // isVisible :
3745 // Return 'true' is the entity is displayed.
3746 //---------------------------------------------------
3747 bool CUserEntity::isVisible() const // virtual
3749 return !UserControls.isInternalView();
3750 }// isVisible //
3757 //---------------------------------------------------
3758 // readWrite :
3759 // Read/Write Variables from/to the stream.
3760 //---------------------------------------------------
3761 void CUserEntity::readWrite(NLMISC::IStream &f)
3763 CPlayerCL::readWrite(f);
3765 // PROTECTED
3766 f.serial(_SpeedFactor);
3767 f.serial(_FrontVelocity);
3768 f.serial(_LateralVelocity);
3769 CVector dummyHead;
3770 f.serial(dummyHead);
3771 f.serial(_HeadPitch);
3772 f.serial(_EyesHeight);
3773 f.serial(_Run);
3774 f.serial(_WalkVelocity);
3775 f.serial(_RunVelocity);
3776 f.serial(_CurrentVelocity);
3777 f.serial(_Selection);
3778 f.serial(_Trader);
3779 f.serial(_Interlocutor);
3780 f.serial(_Selectable);
3782 // PRIVATE
3783 f.serial(_OnMount);
3784 f.serial(_AnimAttackOn);
3785 // f.serialEnum(_ViewMode);
3786 }// readWrite //
3788 //---------------------------------------------------
3789 // load :
3790 // To call after a read from a stream to re-initialize the entity.
3791 //---------------------------------------------------
3792 void CUserEntity::load() // virtual
3794 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3795 // Insert the user into PACS at his saved position
3796 pacsPos(pos());
3798 // update
3799 if(!_WaitForAppearance)
3801 // Visual properties A
3802 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
3803 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
3805 }// load //
3808 //---------------------------------------------------
3809 void CUserEntity::CInvisibleObserver::update(ICDBNode* node)
3811 UserEntity->buildInSceneInterface();
3814 //---------------------------------------------------
3815 void CUserEntity::CSkillPointsObserver::update(ICDBNode* node )
3817 if (FarTP.isFarTPInProgress() || IngameDbMngr.initInProgress()) // prevent from displaying at the beginning of a FarTP (due to RESET_BANK or CDB resetData())
3818 return;
3820 CInterfaceManager *pIM = CInterfaceManager::getInstance ();
3821 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
3822 if (leaf)
3824 sint32 oldValue = leaf->getOldValue32();
3825 if (oldValue != 0)
3827 sint delta = leaf->getValue32()-oldValue;
3828 string deltaStr = toString("%+d", delta);
3830 // get the sp title
3831 const string &spTitle = CI18N::get(toString("uiSkillPointsBold%d",SpType));
3833 // run the popup
3834 CAHManager::getInstance()->runActionHandler("message_popup", NULL, "text1="+deltaStr+"|text0="+spTitle);
3836 // Context help
3837 contextHelp ("skill_point");
3843 //---------------------------------------------------
3844 // CFameObserver::update
3845 //---------------------------------------------------
3846 void CUserEntity::CFameObserver::update(ICDBNode* node )
3848 CSkillManager *pSM = CSkillManager::getInstance();
3849 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
3850 if (leaf)
3852 sint32 fameValue = leaf->getValue32();
3853 pSM->tryToUnblockTitleFromMinFames( FactionIndex, fameValue );
3854 pSM->tryToUnblockTitleFromMaxFames( FactionIndex, fameValue );
3859 //---------------------------------------------------
3860 void CUserEntity::makeTransparent(bool t)
3862 CPlayerCL::makeTransparent(t);
3864 uint32 opaMin= getOpacityMin();
3865 uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor));
3867 getFace()->makeInstanceTransparent(opacity, (uint8)opaMin);
3868 }// makeTransparent //
3870 //---------------------------------------------------
3871 void CUserEntity::setDiffuse(bool onOff, NLMISC::CRGBA diffuse)
3873 CPlayerCL::setDiffuse(onOff, diffuse);
3874 getFace()->setDiffuse(onOff, diffuse);
3880 // Helper for CUserEntity::extractRM()
3881 bool findExtractionActionInMemory( CSPhraseManager *pm, CSBrickManager *bm, uint memoryLine, uint& index )
3883 uint x;
3884 for ( x=0; x!=PHRASE_MAX_MEMORY_SLOT; ++x )
3886 uint32 phraseSlot = pm->getMemorizedPhrase( memoryLine, x );
3887 const CSPhraseCom& phraseCom = pm->getPhrase( phraseSlot );
3888 if ( ! phraseCom.empty() )
3890 CSBrickSheet *brickSheet = bm->getBrick( phraseCom.Bricks[0] );
3891 if ( brickSheet->isForageExtraction() && (!brickSheet->MandatoryFamilies.empty()) ) // assumes care root bricks have not mandatories
3893 index = x;
3894 return true;
3898 return false;
3901 //---------------------------------------------------
3902 void CUserEntity::extractRM()
3904 CSPhraseManager *pm = CSPhraseManager::getInstance();
3905 uint index;
3906 uint memoryLine;
3907 bool autoFindPhrase = (_MoveToPhraseMemoryLine == std::numeric_limits<uint>::max());
3908 if ( ! autoFindPhrase )
3910 // Use clicked phrase
3911 memoryLine = _MoveToPhraseMemoryLine;
3912 index = _MoveToPhraseMemorySlot;
3914 else
3916 // Find the first extraction phrase in the memory bar
3917 CSBrickManager *bm = CSBrickManager::getInstance();
3918 memoryLine = pm->getSelectedMemoryLineDB();
3919 if ( ! findExtractionActionInMemory( pm, bm, memoryLine, index ) )
3921 // Search in other memory bar lines (because the auto-equip does not set the current line at once)
3922 memoryLine = std::numeric_limits<uint>::max();
3923 uint nbLines = pm->getNbMemoryLines();
3924 for ( uint j=0; j!=nbLines; ++j )
3926 if ( j == memoryLine )
3927 continue;
3928 if ( findExtractionActionInMemory( pm, bm, j, index ) )
3930 memoryLine = j;
3931 break;
3937 if ( memoryLine != std::numeric_limits<uint>::max() )
3939 // Open the forage (but not for care actions). Necessary for the case of redoing an extraction after a Drop All on the same source.
3940 uint32 phraseId = pm->getMemorizedPhrase( memoryLine, index );
3941 if ( phraseId != 0 )
3943 const CSPhraseCom& phraseCom = pm->getPhrase( phraseId );
3944 if ( ! phraseCom.empty() )
3946 CSBrickSheet *rootBrick = CSBrickManager::getInstance()->getBrick( phraseCom.Bricks[0] );
3947 if ( rootBrick )
3949 if ( rootBrick->IndexInFamily == 1 ) // only extracting actions
3950 CTempInvManager::getInstance()->open( TEMP_INV_MODE::Forage );
3955 // Cast the extraction. if autoFindPhrase, clientExecute() not already called.
3956 if ( autoFindPhrase )
3958 // decide now if cyclic or not
3959 _MoveToPhraseCyclic= true;
3960 if(pm->avoidCyclicForPhrase(pm->getMemorizedPhrase(memoryLine, index)))
3961 _MoveToPhraseCyclic= false;
3963 // execute on client now
3964 pm->clientExecute( memoryLine, index, _MoveToPhraseCyclic);
3967 // execute on server
3968 pm->sendExecuteToServer( memoryLine, index, _MoveToPhraseCyclic );
3970 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
3971 _MoveToAction= CUserEntity::None;
3973 else
3975 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiExtractionPhraseMissing"), "CHK" );
3976 return;
3981 // ***************************************************************************
3982 bool CUserEntity::canCastShadowMap() const
3984 if(!CCharacterCL::canCastShadowMap())
3985 return false;
3987 // don't cast shadow in first person, but in death mode (third person actually...)
3988 return viewMode() != FirstPV || UserControls.mode() == CUserControls::DeathMode;
3992 // ***************************************************************************
3993 void CUserEntity::forceLookEntity(const NLMISC::CVectorD &dir2targIn, bool updateHeadPitch, bool /* start */)
3995 CVectorD dir2targ= dir2targIn;
3996 float frontYawBefore = 0.f;
3997 float frontYawAfter;
3999 // Third person: bkup current yaw
4000 if(viewMode()==ThirdPV)
4002 frontYawBefore = frontYaw();
4006 // **** Look at the entity
4007 dir2targ.normalize();
4008 front(dir2targ, false, false);
4011 // **** FirstPerson
4012 if(viewMode() == FirstPV)
4014 if(updateHeadPitch && _FollowForceHeadPitch)
4016 // rotate the head to the target
4017 CEntityCL *target = EntitiesMngr.entity(targetSlot());
4018 if(target)
4020 // Both Z must be correct
4021 snapToGround();
4022 target->snapToGround();
4024 // don't update to the real head position each frame (else jitter too much cause of target anim)
4025 CVector targetPos= target->pos() + CVector(0,0,_FollowHeadOffset);
4027 // then look at this target
4028 CVector dirToTarget = targetPos - (pos()+CVector(0,0, UserEntity->eyesHeight()));
4029 if((dirToTarget.x != 0.0f) || (dirToTarget.y!=0.0f))
4031 dirToTarget.normalize();
4032 setHeadPitch(atan2(dirToTarget.z, sqrt(sqr(dirToTarget.x)+sqr(dirToTarget.y))));
4035 // TestYoyo
4036 /*if(ClientCfg.Fly!=0.f)
4038 nlinfo("Uy: %.3f. Hp: %.3f. UPos:(%.3f,%.3f,%.3f). TPos:(%.3f,%.3f,%.3f)",
4039 UserEntity->frontYaw(), UserEntity->getHeadPitch(), pos().x, pos().y, pos().z,
4040 targetPos.x, targetPos.y, targetPos.z);
4041 static float uy=0.f;
4042 static float hp=0.f;
4043 if( fabs(fmod(UserEntity->frontYaw()-uy, 2*Pi))>ClientCfg.Fly ||
4044 fabs(fmod(UserEntity->getHeadPitch()-hp, 2*Pi))>ClientCfg.Fly )
4046 nlinfo("YOYOBREAK: ^^^^^^^^^^");
4048 uy=UserEntity->frontYaw();
4049 hp=UserEntity->getHeadPitch();
4054 // **** Third person
4055 else if(viewMode()==ThirdPV)
4057 // keep the current effective yaw. ONLY if no SmoothResetCameraYaw forced
4058 if(!UserControls.isResetSmoothCDYForced())
4060 frontYawAfter = frontYaw();
4061 float deltaYaw= frontYawAfter - frontYawBefore;
4063 // compensate rotation (NB: it stops also any SmoothReset)
4064 UserControls.appendCameraDeltaYaw(-deltaYaw);
4068 // when looking to an entity, if automatic camera, center the view on it.
4069 if( ClientCfg.AutomaticCamera /*&& start*/ )
4071 UserControls.resetSmoothCameraDeltaYaw();
4076 // ***************************************************************************
4077 void CUserEntity::startForceLookEntity(CLFECOMMON::TCLEntityId slot)
4079 // Start a new follow: force head pitch to follow by default
4080 _FollowForceHeadPitch= true;
4081 // if a follow is started in first person, reset CameraYaw
4082 if(viewMode()==FirstPV)
4083 UserControls.resetCameraDeltaYaw();
4085 // reorient now (important else may have a time shift because of resetCameraDeltaYaw above)
4086 CEntityCL *target = EntitiesMngr.entity(slot);
4087 if(target)
4089 /* For complex reason, the target may not be still snapped on the ground. snap it now
4090 - this is because in common case, entities are snap only if they are visible (for speed up)
4091 => depends on camera
4092 - but in this case, the camera depends on real entity position (headPitch in first person)
4094 target->snapToGround();
4096 // For FirstPerson targeting. Get the current target head offset
4097 _FollowHeadOffset= 0.f;
4098 CVector headPos;
4099 if(target->getHeadPos(headPos))
4101 _FollowHeadOffset= headPos.z - float(target->pos().z);
4104 // Look at the entity
4105 CVectorD dir2targ = target->pos() - pos();
4106 dir2targ.z = 0.0;
4107 if(dir2targ!=CVectorD::Null)
4109 forceLookEntity(dir2targ, true, true);
4115 // ***************************************************************************
4116 void CUserEntity::stopForceHeadPitchInFollow()
4118 _FollowForceHeadPitch= false;
4121 // ***************************************************************************
4122 void CUserEntity::switchVelocity(bool userRequest)
4124 if (ClientCfg.R2EDEnabled && R2::getEditor().getMode() == R2::CEditor::EditionMode)
4126 // when in the R2 editor, force to run all the time
4127 _Run = true;
4129 else
4131 _Run = !_Run;
4133 _CurrentVelocity = _Run ? runVelocity() : walkVelocity();
4135 if (userRequest)
4137 _RunWhenAble = _Run;
4140 // display message : your are running, you are walking
4141 CInterfaceManager *pIM= CInterfaceManager::getInstance();
4142 string msg;
4143 if( _Run )
4144 msg = CI18N::get("msgUserIsRunning");
4145 else
4146 msg = CI18N::get("msgUserIsWalking");
4147 string cat = getStringCategory(msg, msg);
4148 pIM->displaySystemInfo(msg, cat);
4150 // Write to the UI database, to update views
4151 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_RUNNING", false);
4152 if(node)
4153 node->setValue32(_Run);
4156 //-----------------------------------------------
4157 // autoEquipWithLastUsedWeapons
4159 //-----------------------------------------------
4160 void CUserEntity::autoEquipWithLastUsedWeapons()
4162 CInventoryManager *inv = CInventoryManager::getInstance();
4163 if ( !inv )
4165 return;
4168 // Clear hands
4169 inv->unequip( "LOCAL:INVENTORY:HAND:1" );
4170 inv->unequip( "LOCAL:INVENTORY:HAND:0" );
4172 uint ir,il;
4173 // Equip right hand
4174 if( _PreviousRightHandItem.Sheet != 0 )
4176 // find item in bag with same properties than last used one in right hand
4177 for ( ir=0; ir<MAX_BAGINV_ENTRIES; ++ir )
4179 if( _PreviousRightHandItem.Sheet == inv->getBagItem(ir).getSheetID() &&
4180 _PreviousRightHandItem.Quality == inv->getBagItem(ir).getQuality() &&
4181 _PreviousRightHandItem.Weight == inv->getBagItem(ir).getWeight() &&
4182 _PreviousRightHandItem.NameId == inv->getBagItem(ir).getNameId() )
4184 break;
4187 if ( ir != MAX_BAGINV_ENTRIES )
4189 // Equip right hand
4190 string bagPath = toString( "LOCAL:INVENTORY:BAG:%u", ir );
4191 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:0" );
4193 // Equip left hand if needed
4194 if( _PreviousLeftHandItem.Sheet != 0 )
4196 for ( il=0; il<MAX_BAGINV_ENTRIES; ++il )
4198 if( il != ir &&
4199 _PreviousLeftHandItem.Sheet == inv->getBagItem(il).getSheetID() &&
4200 _PreviousLeftHandItem.Quality == inv->getBagItem(il).getQuality() &&
4201 _PreviousLeftHandItem.Weight == inv->getBagItem(il).getWeight() &&
4202 _PreviousLeftHandItem.NameId == inv->getBagItem(il).getNameId() )
4204 break;
4207 if ( il != MAX_BAGINV_ENTRIES )
4209 bagPath = toString( "LOCAL:INVENTORY:BAG:%u", il );
4210 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:1" );
4213 return;
4218 // TODO : choose the best one
4223 // ***************************************************************************
4224 void CUserEntity::executeCombatWithPhrase(CEntityCL *target, uint32 memoryLine, uint32 memoryIndex, bool cyclic)
4226 nlassert(target);
4227 CSPhraseManager *pPM= CSPhraseManager::getInstance();
4229 // is a melee combat?
4230 bool meleeCombat = false;
4231 // empty hand => yes!
4232 meleeCombat= true;
4233 uint32 rightHandSheet = getInventory().getRightHandItemSheet();
4234 if(rightHandSheet)
4235 meleeCombat = getInventory().isMeleeWeaponItem(rightHandSheet);
4237 // If melee combat, and if the user entity is not well placed for fight, or if it has changed his target
4238 if( meleeCombat &&
4240 !target->isPlacedToFight(pos(), front(), attackRadius() + ClientCfg.AttackDist) ||
4241 target->slot()!=_LastExecuteCombatSlot
4245 _LastExecuteCombatSlot= target->slot();
4247 // Cancel any follow
4248 disableFollow();
4250 // Launch the moveToCombatPhrase, canceling any old action client execution.
4251 // NB: this will also force him to look at the entity
4252 moveToCombatPhrase(target->slot(), 2.0, memoryLine, memoryIndex, cyclic);
4254 // And after (order is important), start the phrase execution on client
4255 pPM->clientExecute(memoryLine, memoryIndex, cyclic);
4257 else
4259 // Cancel any moveTo(), because don't want to continue reaching the prec entity
4260 // VERY important if previous MoveTo was a SPhrase MoveTo (because cancelClientExecute() must be called)
4261 resetAnyMoveTo();
4263 // start client execution: NB: start client execution even if it
4264 pPM->clientExecute(memoryLine, memoryIndex, cyclic);
4266 // inform Server of phrase cast
4267 pPM->sendExecuteToServer(memoryLine, memoryIndex, cyclic);
4269 if( !meleeCombat && !cyclic )
4271 pPM->executeDefaultAttack();
4276 // ***************************************************************************
4277 void CUserEntity::beginCast(const MBEHAV::CBehaviour &behaviour)
4279 if(viewMode()==ThirdPV)
4281 // backup front yaw
4282 float frontYawBefore = frontYaw();
4283 // begin cast
4284 CCharacterCL::beginCast( behaviour );
4285 // compensate the front change using a camera move
4286 float frontYawAfter = frontYaw();
4287 float deltaYaw= frontYawAfter - frontYawBefore;
4288 UserControls.appendCameraDeltaYaw(-deltaYaw);
4289 // if automatic camera, center the view behind the user
4290 if( ClientCfg.AutomaticCamera )
4292 UserControls.resetSmoothCameraDeltaYaw();
4295 else
4297 // in first person mode, reset the delta yaw
4298 UserControls.resetCameraDeltaYaw();
4299 CCharacterCL::beginCast( behaviour );
4303 // ***************************************************************************
4304 void CUserEntity::updatePreCollision(const NLMISC::TTime &time, CEntityCL *target)
4306 CPlayerCL::updatePreCollision(time, target);
4308 // test each frame if the mode has changed
4309 if(SoundMngr)
4311 // Play/stop music if comes from or goes to dead
4312 bool isDead = _Mode == MBEHAV::DEATH || _Mode == MBEHAV::SWIM_DEATH;
4314 // must start music?
4315 if (isDead && SoundMngr->getEventMusicPlayed() != ClientCfg.DeathMusic)
4317 SoundMngr->playEventMusic(ClientCfg.DeathMusic, 0, true);
4320 // must end music?
4321 if (!isDead && SoundMngr->getEventMusicPlayed() == ClientCfg.DeathMusic)
4323 SoundMngr->stopEventMusic(ClientCfg.DeathMusic, CSoundManager::LoadingMusicXFade);
4328 // ***************************************************************************
4329 void CUserEntity::buildTotem()
4331 const string msgName = "TOTEM:BUILD";
4332 CBitMemStream out;
4333 if( GenericMsgHeaderMngr.pushNameToStream( msgName, out ) )
4335 NetMngr.push( out );
4336 nlinfo( "sending TOTEM:build message to server" );
4340 // ***************************************************************************
4341 void CUserEntity::setR2CharMode(R2::TCharMode mode)
4343 if (mode == R2::TCharMode::Editer || mode == R2::TCharMode::Dm)
4345 _R2CharMode= mode;
4346 walkVelocity(ClientCfg.DmWalk);
4347 runVelocity(ClientCfg.DmRun);
4348 View.setCameraDistanceMaxForDm();
4349 CEntityCL *user = EntitiesMngr.entity(0);
4350 NLPACS::UMovePrimitive* prim = user?user->getPrimitive():0;
4351 if (prim)
4353 prim->setObstacle(false);
4357 else if (mode == R2::TCharMode::Player || mode == R2::TCharMode::Tester)
4359 _R2CharMode= mode;
4360 walkVelocity(ClientCfg.Walk);
4361 runVelocity(ClientCfg.Run);
4362 View.setCameraDistanceMaxForPlayer();
4363 CEntityCL *user = EntitiesMngr.entity(0);
4364 NLPACS::UMovePrimitive* prim = user?user->getPrimitive():0;
4365 if (prim)
4367 prim->setObstacle(true);
4370 else
4372 nlwarning("R2Ed: Error Char Mode not handled %u", (uint32)mode.getValue());
4376 bool CUserEntity::isInNpcControl() const
4378 CInterfaceManager* pIM = CInterfaceManager::getInstance();
4379 CCDBNodeLeaf *sheet = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4380 return sheet && NLMISC::CSheetId(sheet->getValue32())!=NLMISC::CSheetId::Unknown;
4384 void CUserEntity::updateNpcContolSpeed()
4386 CInterfaceManager* pIM = CInterfaceManager::getInstance();
4387 CCDBNodeLeaf *sheet = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4388 CCDBNodeLeaf *walk = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:WALK", false);
4389 CCDBNodeLeaf *run = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:RUN", false);
4390 if (!sheet || !walk || !run)
4392 return;
4395 static NLMISC::CSheetId oldSheet = NLMISC::CSheetId::Unknown;
4396 static float oldRun=0.f;
4397 static float oldWalk=0.f;
4399 NLMISC::CSheetId sheetId(sheet->getValue32());
4400 float newRun = float(run->getValue32()) / 1000.0f;
4401 float newWalk = float(walk->getValue32()) / 1000.0f;
4403 if (sheetId == oldSheet && oldRun == newRun && oldWalk == newWalk )
4405 return;
4408 oldSheet = sheetId;
4409 oldRun = newRun;
4410 oldWalk = newWalk;
4412 if (sheetId != NLMISC::CSheetId::Unknown)
4414 walkVelocity(newWalk);
4415 runVelocity(newRun);
4417 else
4419 setR2CharMode(_R2CharMode);
4424 //-----------------------------------------------
4425 // cancelAllPhrases
4426 //-----------------------------------------------
4427 void CUserEntity::cancelAllPhrases()
4429 CBitMemStream out;
4430 if(GenericMsgHeaderMngr.pushNameToStream("PHRASE:CANCEL_ALL", out))
4432 NetMngr.push(out);
4434 else
4436 nlwarning("<CUserEntity::cancelAllPhrases> unknown message name '%s'", "PHRASE:CANCEL_ALL");
4441 //-----------------------------------------------
4442 // canChangeFront
4443 //-----------------------------------------------
4444 bool CUserEntity::canChangeFront()
4446 return !(_CurrentBehaviour.Behaviour == MBEHAV::EXTRACTING
4447 || (_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK && _Mode==MBEHAV::COMBAT && !UserControls.isMoving())
4448 || (_CurrentBehaviour.Behaviour >= MBEHAV::MAGIC_CASTING_BEHAVIOUR_BEGIN && _CurrentBehaviour.Behaviour <= MBEHAV::MAGIC_CASTING_BEHAVIOUR_END));
4452 //-----------------------------------------------
4453 // rememberWeaponsInHand
4455 //-----------------------------------------------
4456 void CUserEntity::rememberWeaponsInHand()
4458 CInventoryManager * inv = CInventoryManager::getInstance();
4459 if( !inv )
4461 return;
4464 // keep right hand item
4465 CItemImage * rightItemImg = inv->getHandItem(0);
4466 if( rightItemImg )
4468 if( inv->isMeleeWeaponItem(rightItemImg->getSheetID()) || inv->isRangeWeaponItem(rightItemImg->getSheetID()) )
4470 _PreviousRightHandItem = CItemSnapshot(*rightItemImg);
4472 // keep left hand item too (could be ammo, second weapon, etc ..)
4473 CItemImage * leftItemImg = inv->getHandItem(1);
4474 if( leftItemImg )
4476 _PreviousLeftHandItem = CItemSnapshot(*leftItemImg);
4478 else
4480 _PreviousLeftHandItem = CItemSnapshot();
4487 //-----------------------------------------------
4488 // snapshot of a CItemImage
4490 //-----------------------------------------------
4491 CUserEntity::CItemSnapshot::CItemSnapshot( const CItemImage& i )
4493 Sheet = i.getSheetID();
4494 Quality = i.getQuality();
4495 Quantity = i.getQuantity();
4496 UserColor = i.getUserColor();
4497 Price = i.getPrice();
4498 Weight = i.getWeight();
4499 NameId = i.getNameId();
4500 InfoVersion = i.getInfoVersion();
4503 sint CUserEntity::getLevel() const
4505 CInterfaceManager *pIM = CInterfaceManager::getInstance();
4506 sint level = -1;
4507 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
4509 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
4510 if(node)
4512 level = std::max(level, (sint) node->getValue32());
4515 return level;
4518 //-----------------------------------------------
4519 // interlocutor
4520 //-----------------------------------------------
4521 void CUserEntity::interlocutor( const CLFECOMMON::TCLEntityId &slot)
4523 CLFECOMMON::TCLEntityId prevInterlocutor = _Interlocutor;
4524 _Interlocutor = slot;
4526 // Refresh (hide or unhide) the icon for the interlocutor NPC
4527 if (prevInterlocutor != CLFECOMMON::INVALID_SLOT)
4528 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(prevInterlocutor);
4529 if (_Interlocutor != CLFECOMMON::INVALID_SLOT)
4530 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(_Interlocutor);
4533 //-----------------------------------------------
4534 // trader
4535 //-----------------------------------------------
4536 void CUserEntity::trader(const CLFECOMMON::TCLEntityId &slot)
4538 CLFECOMMON::TCLEntityId prevTrader = _Trader;
4539 _Trader = slot;
4541 // Refresh (hide or unhide) the icon for the trader NPC
4542 if (prevTrader != CLFECOMMON::INVALID_SLOT)
4543 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(prevTrader);
4544 if (_Trader != CLFECOMMON::INVALID_SLOT)
4545 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(_Trader);