Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / user_entity.cpp
blob0077b2bc4671f7947e08327e0b64884827a9bfe2
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 }// CUserEntity //
200 //-----------------------------------------------
201 // ~CUserEntity :
202 // Destructor.
203 //-----------------------------------------------
204 CUserEntity::~CUserEntity()
206 // Remove observers
207 _SpeedFactor.release();
208 _MountHunger.release();
209 _MountSpeeds.release();
211 CInterfaceManager *pIM = CInterfaceManager::getInstance();
214 CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:IS_INVISIBLE", false);
215 if (node)
217 ICDBNode::CTextId textId;
218 node->removeObserver(&_InvisibleObs, textId);
222 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
224 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
225 if(node)
227 ICDBNode::CTextId textId;
228 node->removeObserver(_SkillPointObs+i, textId);
232 for( uint i=0; i<_FamesObs.size(); ++i )
234 uint32 factionIndex = _FamesObs[i]->FactionIndex;
235 uint32 fameIndexInDatabase = CStaticFames::getInstance().getDatabaseIndex(factionIndex);
236 string sDBPath = toString("SERVER:FAME:PLAYER%d:VALUE",fameIndexInDatabase);
238 CCDBNodeLeaf * node = NLGUI::CDBManager::getInstance()->getDbProp(sDBPath, false);
239 if(node)
241 ICDBNode::CTextId textId;
242 node->removeObserver(_FamesObs[i], textId);
245 contReset(_FamesObs);
247 CNPCIconCache::getInstance().removeObservers();
249 // Remove the Primitive used for check (because ~CEntityCL() will call CEntityCL::removePrimitive(), not CUserEntity::removePrimitive())
250 removeCheckPrimitive();
252 CNPCIconCache::release();
254 }// ~CUserEntity //
256 //-----------------------------------------------
257 // initProperties :
258 // Initialize properties of the entity (according to the class).
259 //-----------------------------------------------
260 void CUserEntity::initProperties()
262 properties().selectable(true);
263 }// initProperties //
266 //-----------------------------------------------
267 // build :
268 // Build the entity from a sheet.
269 //-----------------------------------------------
270 bool CUserEntity::build(const CEntitySheet *sheet) // virtual
272 // Init received position
273 pos(UserEntityInitPos);
274 front(UserEntityInitFront);
275 dir(front());
276 setHeadPitch(0);
278 // Cast the sheet in the right type.
279 _PlayerSheet = dynamic_cast<const CRaceStatsSheet *>(sheet);
280 if(_PlayerSheet == 0)
282 pushDebugStr("User Sheet is not a valid '.race_stats'.");
283 return false;
285 else
286 pushInfoStr("User Sheet is a valid '.race_stats'.");
287 // Get the DB Entry
288 if(IngameDbMngr.getNodePtr())
290 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
291 if(nodeRoot)
293 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
294 if(_DBEntry == 0)
295 pushDebugStr("Cannot get a pointer on the DB entry.");
299 disableFollow();
300 // Walk/Run ?
301 if(ClientCfg.RunAtTheBeginning != _Run)
302 switchVelocity();
304 // Set the up of the user.
305 up(CVector(0,0,1));
307 // Init User infos.
308 eyesHeight(ClientCfg.EyesHeight);
309 walkVelocity(ClientCfg.Walk);
310 runVelocity(ClientCfg.Run);
312 // Compute the first automaton.
313 _CurrentAutomaton = automatonType() + "_normal.automaton";
315 // Build the PACS Primitive.
316 if(initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::Slide, (UMovePrimitive::TTrigger)(UMovePrimitive::OverlapTrigger | UMovePrimitive::EnterTrigger), MaskColPlayer, MaskColPlayer | MaskColNpc | MaskColDoor))
317 _Primitive->insertInWorldImage(dynamicWI);
319 // Compute the element to be able to snap the entity to the ground.
320 computeCollisionEntity();
322 // Initialize properties of the client.
323 initProperties();
325 // Initialize the observer for the speed factor and mount stuff
326 _SpeedFactor.init();
327 _MountHunger.init();
328 _MountSpeeds.init();
330 // Create the user playlist
331 createPlayList();
333 // Initialize the internal time.
334 _LastFrameTime = ((double)T1) * 0.001;
336 // Set the gender in local mode.
337 if(ClientCfg.Local)
339 _Mode = MBEHAV::NORMAL;
340 _ModeWanted = MBEHAV::NORMAL;
341 _Gender = ClientCfg.Sex;
342 SPropVisualA visualA = buildPropVisualA(_PlayerSheet->GenderInfos[_Gender]);
343 SPropVisualB visualB = buildPropVisualB(_PlayerSheet->GenderInfos[_Gender]);
344 SPropVisualC visualC;
345 visualA.PropertySubData.Sex = _Gender;
346 visualC.PropertyC = 0;
347 visualC.PropertySubData.CharacterHeight = 0;
348 visualC.PropertySubData.ArmsWidth = 7;
349 visualC.PropertySubData.LegsWidth = 7;
350 visualC.PropertySubData.TorsoWidth = 7;
351 visualC.PropertySubData.BreastSize = 7;
352 // Set the Database
353 sint64 *prop = (sint64 *)&visualA;
354 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->setValue64(*prop); // Set the database
355 prop = (sint64 *)&visualB;
356 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->setValue64(*prop); // Set the database
357 prop = (sint64 *)&visualC;
358 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPC))->setValue64(*prop); // Set the database
359 // Apply Changes.
360 updateVisualProperty(0, CLFECOMMON::PROPERTY_VPA);
362 // \todo GUIGUI Retrieve the player's appearence during the avatar selection.
363 // Get Visual Properties From the character selection window.
364 else
368 // Rebuild interface
369 buildInSceneInterface ();
371 // Add observer on invisible property
372 CInterfaceManager *pIM = CInterfaceManager::getInstance();
374 CCDBNodeLeaf *node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:IS_INVISIBLE", false);
375 if (node)
377 ICDBNode::CTextId textId;
378 node->addObserver(&_InvisibleObs, textId);
382 // Add an observer on skill points
383 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
385 _SkillPointObs[i].SpType= i;
386 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
387 if(node)
389 ICDBNode::CTextId textId;
390 node->addObserver(_SkillPointObs+i, textId);
394 // Add an observer on Fames
395 for( uint i=(uint)PVP_CLAN::BeginClans; i<=(uint)PVP_CLAN::EndClans; ++i )
397 uint32 factionIndex = PVP_CLAN::getFactionIndex((PVP_CLAN::TPVPClan)i);
398 uint32 fameIndexInDatabase = CStaticFames::getInstance().getDatabaseIndex(factionIndex);
399 string sDBPath = toString("SERVER:FAME:PLAYER%d:VALUE",fameIndexInDatabase);
401 CFameObserver * fameObs = new CFameObserver();
402 if( fameObs )
404 fameObs->FactionIndex = factionIndex;
405 CCDBNodeLeaf * node = NLGUI::CDBManager::getInstance()->getDbProp(sDBPath, false);
406 if(node)
408 ICDBNode::CTextId textId;
409 node->addObserver(fameObs, textId);
411 _FamesObs.push_back(fameObs);
415 // Add an observer on Mission Journal
416 CNPCIconCache::getInstance().addObservers();
418 // Initialize the camera distance.
419 View.cameraDistance(ClientCfg.CameraDistance);
421 // char and account time properties
422 CSkillManager *pSM = CSkillManager::getInstance();
423 if( pSM )
425 pSM->tryToUnblockTitleFromCharOldness( CharFirstConnectedTime );
426 pSM->tryToUnblockTitleFromCharPlayedTime( CharPlayedTime );
429 // Entity created.
430 return true;
431 }// build //
434 //-----------------------------------------------
435 // eyesHeight :
436 // \todo GUIGUI : do it better in mount mode
437 //-----------------------------------------------
438 float CUserEntity::eyesHeight()
440 if(!_OnMount)
441 return _EyesHeight * _CharacterScalePos;
442 else
443 return _EyesHeight * _CharacterScalePos;
444 }// eyesHeight //
447 /////////////////////////////////////////////////
448 /////////////////////////////////////////////////
449 /////////////// VISUAL PROPERTIES ///////////////
450 /////////////////////////////////////////////////
451 /////////////////////////////////////////////////
452 //-----------------------------------------------
453 // updateVisualPropertyPos :
454 // Update Entity Position.
455 //-----------------------------------------------
456 void CUserEntity::updateVisualPropertyPos(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */, const NLMISC::TGameCycle &/* pI */)
458 }// updateVisualPropertyPos //
460 //-----------------------------------------------
461 // updateVisualPropertyOrient :
462 // Update Entity Orientation.
463 //-----------------------------------------------
464 void CUserEntity::updateVisualPropertyOrient(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */)
466 }// updateVisualPropertyOrient //
469 void CUserEntity::updateVisualPropertyTargetList(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */, uint /* listIndex */)
473 void CUserEntity::updateVisualPropertyVisualFX(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
475 applyVisualFX(prop);
478 //-----------------------------------------------
479 // updateVisualPropertyBehaviour :
480 // Update Entity Behaviour.
481 //-----------------------------------------------
482 void CUserEntity::updateVisualPropertyBehaviour(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
484 // Compute the behaviour.
485 CBehaviourContext bc;
486 bc.Behav = MBEHAV::CBehaviour(prop);
487 bc.BehavTime = TimeInSec;
488 if(VerboseAnimUser)
490 nlinfo("UE::updateVPBeha: '%d(%s)'.", (sint)bc.Behav.Behaviour, MBEHAV::behaviourToString(bc.Behav.Behaviour).c_str());
492 CCDBNodeLeaf *targetList0 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_0));
493 CCDBNodeLeaf *targetList1 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_1));
494 CCDBNodeLeaf *targetList2 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_2));
495 CCDBNodeLeaf *targetList3 = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_3));
496 if (targetList0 && targetList1 && targetList2 && targetList3)
498 uint64 vp[4] =
500 (uint64) targetList0->getValue64(),
501 (uint64) targetList1->getValue64(),
502 (uint64) targetList2->getValue64(),
503 (uint64) targetList3->getValue64()
505 bc.Targets.unpack(vp, 4);
507 applyBehaviour(bc);
508 }// updateVisualPropertyBehaviour //
510 //-----------------------------------------------
511 // updateVisualPropertyName :
512 // Update Entity Name.
513 //-----------------------------------------------
514 void CUserEntity::updateVisualPropertyName(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
516 uint32 oldNameId = _NameId;
518 CPlayerCL::updateVisualPropertyName(gameCycle, prop);
520 // Name changed ?
521 /* if (oldNameId != _NameId)
523 CInterfaceManager *pIM = CInterfaceManager::getInstance();
524 CInterfaceElement *element = CWidgetManager::getInstance()->getElementFromId("ui:interface:mailbox:content:html");
525 if (element)
527 CGroupHTML *html = dynamic_cast<CGroupHTML*>(element);
528 if (html)
529 html->browse("home");
533 }// updateVisualPropertyName //
535 //-----------------------------------------------
536 // updateVisualPropertyTarget :
537 // Update Entity Target.
538 //-----------------------------------------------
539 void CUserEntity::updateVisualPropertyTarget(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */)
541 // Don't override the Player Target, cause client side entirely => no lag.
542 //targetSlot((CLFECOMMON::TCLEntityId)prop);
543 }// updateVisualPropertyTarget //
545 //-----------------------------------------------
546 // updateVisualPropertyMode :
547 // New mode received -> immediately change the mode for the user.
548 // \warning Read the position or orientation from the database when reading the mode (no more updated in updateVisualPropertyPos and updateVisualPropertyOrient).
549 //-----------------------------------------------
550 void CUserEntity::updateVisualPropertyMode(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
552 // Combat Float Check
553 if((MBEHAV::EMode)prop == MBEHAV::COMBAT_FLOAT)
555 nlwarning("UE:updateVPMode: the user should never have the COMBAT_FLOAT mode.");
556 return;
558 // New Mode Received.
559 _TheoreticalMode = (MBEHAV::EMode)prop;
560 // Change the user mode.
561 mode(_TheoreticalMode);
562 }// updateVisualPropertyMode //
564 //-----------------------------------------------
565 // updateVisualPropertyVpa :
566 // Update Entity Visual Property A
567 //-----------------------------------------------
568 void CUserEntity::updateVisualPropertyVpa(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
570 CPlayerCL::updateVisualPropertyVpa(gameCycle, prop);
572 // Special for user: Disable Character Lod, because always want it at full rez.
573 if(!_Skeleton.empty())
575 _Skeleton.setLodCharacterShape(-1);
578 updateVisualDisplay();
579 }// updateVisualPropertyVpa //
581 //-----------------------------------------------
582 // updateVisualPropertyVpb :
583 // Update Entity Visual Property B
584 //-----------------------------------------------
585 void CUserEntity::updateVisualPropertyVpb(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
587 CPlayerCL::updateVisualPropertyVpb(gameCycle, prop);
588 updateVisualDisplay();
589 }// updateVisualPropertyVpb //
591 //-----------------------------------------------
592 // updateVisualPropertyVpc :
593 // Update Entity Visual Property C
594 //-----------------------------------------------
595 void CUserEntity::updateVisualPropertyVpc(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
597 CPlayerCL::updateVisualPropertyVpc(gameCycle, prop);
598 updateVisualDisplay();
599 }// updateVisualPropertyVpc //
601 //-----------------------------------------------
602 // updateVisualPropertyEntityMounted :
603 // Update Entity Mount
604 //-----------------------------------------------
605 void CUserEntity::updateVisualPropertyEntityMounted(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
607 if(isFighting())
608 CPlayerCL::updateVisualPropertyEntityMounted(gameCycle, prop);
609 else
610 _Mount = (CLFECOMMON::TCLEntityId)prop;
611 }// updateVisualPropertyEntityMounted //
613 //-----------------------------------------------
614 // updateVisualPropertyRiderEntity :
615 // Update Entity Rider
616 //-----------------------------------------------
617 void CUserEntity::updateVisualPropertyRiderEntity(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
619 if(isFighting())
620 CPlayerCL::updateVisualPropertyRiderEntity(gameCycle, prop);
621 else
622 _Rider = (CLFECOMMON::TCLEntityId)prop;
623 }// updateVisualPropertyRiderEntity //
625 //-----------------------------------------------
626 //-----------------------------------------------
627 void CUserEntity::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
629 CPlayerCL::updateVisualPropertyPvpMode(gameCycle, prop);
630 // Additionaly, inform interface of the change
631 CInterfaceManager *pIM= CInterfaceManager::getInstance();
632 // For PVP ZoneFaction
633 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_MODE");
634 if(pDB)
636 sint32 val= pDB->getValue32();
637 pDB->setValue32(val+1);
639 // For Any PVP change
640 pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
641 if(pDB)
643 sint32 val= pDB->getValue32();
644 pDB->setValue32(val+1);
648 //-----------------------------------------------
649 //-----------------------------------------------
650 void CUserEntity::updateVisualPropertyOutpostInfos(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
652 CPlayerCL::updateVisualPropertyOutpostInfos(gameCycle, prop);
653 // For Any PVP change
654 CInterfaceManager *pIM= CInterfaceManager::getInstance();
655 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
656 if(pDB)
658 sint32 val= pDB->getValue32();
659 pDB->setValue32(val+1);
663 //-----------------------------------------------
664 //-----------------------------------------------
665 void CUserEntity::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
667 CPlayerCL::updateVisualPropertyPvpClan(gameCycle, prop);
668 // For Any PVP change
669 CInterfaceManager *pIM= CInterfaceManager::getInstance();
670 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_PVP_CHANGE_ANY");
671 if(pDB)
673 sint32 val= pDB->getValue32();
674 pDB->setValue32(val+1);
679 /////////////////////////////////////////////////
680 /////////////////////////////////////////////////
681 /////////////////////////////////////////////////
682 /////////////////////////////////////////////////
685 //-----------------------------------------------
686 // mode :
687 // Method called to change the mode (Combat/Mount/etc.).
688 // \todo GUIGUI : apply stage in combat modes instead of just removing them.
689 // \todo GUIGUI : go or teleport the player to the mode position (see how to manage it).
690 //-----------------------------------------------
691 bool CUserEntity::mode(MBEHAV::EMode m)
693 if(Verbose & VerboseAnim)
694 nlinfo("UE::mode: old mode '%s(%d)' new mode '%s(%d)'.", MBEHAV::modeToString(_Mode).c_str(), _Mode, MBEHAV::modeToString(m).c_str(), m);
695 // Nothing to do if the mode is the same as the previous one.
696 if(m == _Mode)
697 return true;
698 // Release the old Mode.
699 switch(_Mode)
701 // Leave COMBAT Mode
702 case MBEHAV::COMBAT:
703 case MBEHAV::COMBAT_FLOAT:
705 // If there are some stage not complete -> remove them
706 if(_Stages._StageSet.size() != 0)
707 _Stages._StageSet.clear();
709 break;
710 // Leave MOUNTED Mode
711 case MBEHAV::MOUNT_NORMAL:
712 case MBEHAV::MOUNT_SWIM:
714 if ( m == MBEHAV::REST )
716 // can't go afk while mounting
717 return false;
720 // if changing mode for another mount mode, do nothing
721 if (m != MBEHAV::MOUNT_NORMAL && m != MBEHAV::MOUNT_SWIM)
723 // Display the mount again.
724 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
725 if(mount)
727 // Set the mount.
728 mount->rider(CLFECOMMON::INVALID_SLOT);
729 mount->_Stages._StageSet.clear();
730 mount->setMode(MBEHAV::NORMAL);
731 mount->computeAutomaton();
732 mount->computeAnimSet();
733 mount->setAnim(CAnimationStateSheet::Idle);
734 if(mount->getPrimitive())
735 mount->getPrimitive()->setOcclusionMask(MaskColNpc); // the mount is an npc
736 mount->_ForbidClipping = false;
739 _Mount = CLFECOMMON::INVALID_SLOT;
740 // Restore the user Primitive
741 if(_Primitive)
743 _Primitive->setRadius( std::min(0.5f, (float)(RYZOM_ENTITY_SIZE_MAX/2)) );
744 _Primitive->setHeight(2);
746 _OnMount = false;
748 // Shift the player position (not to stand inside the mount)
749 CVectorD unmountShift;
750 unmountShift.sphericToCartesian(1.0, frontYaw() + NLMISC::Pi/2, 0);
751 pacsPos(pos() + unmountShift);
753 // Restore running if necessary
754 if (!_Run && _RunWhenAble)
756 switchVelocity();
760 if (_Mode == MBEHAV::MOUNT_SWIM && ( m == MBEHAV::COMBAT || m == MBEHAV::COMBAT_FLOAT))
762 //TODO : display "you can't fight while swimming"
763 return true;
766 break;
767 // Leave DEATH Mode
768 case MBEHAV::DEATH:
769 // Restore the last view.
770 viewMode(viewMode());
771 break;
772 case MBEHAV::SWIM:
773 if( m == MBEHAV::COMBAT || m == MBEHAV::COMBAT_FLOAT)
775 //TODO : display "you can't fight while swimming"
776 return true;
778 break;
779 default:
780 nlwarning("Invalid behaviour change.");
783 // Reset Parent, unless we stay in mount mode
784 if ((_Mode != MBEHAV::MOUNT_SWIM && _Mode != MBEHAV::MOUNT_NORMAL)
785 || (m != MBEHAV::MOUNT_SWIM && m != MBEHAV::MOUNT_NORMAL)
788 parent(CLFECOMMON::INVALID_SLOT);
791 // Change the Mode for the user ( if user sits down or stands up we wait in order to play the sit/stand transition anim)
792 if( m != MBEHAV::SIT && _Mode != MBEHAV::SIT )
793 _Mode = m;
794 _ModeWanted = m;
796 // Initialize the new Mode.
797 switch(m)
799 // Combat mode
800 case MBEHAV::COMBAT:
801 case MBEHAV::COMBAT_FLOAT:
803 C64BitsParts rot;
805 // Compute the angle
806 const string propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_ORIENTATION);
807 rot.i64[0] = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
808 _TargetAngle = rot.f[0];
810 // Initialize controls for the combat.
811 UserControls.startCombat();
813 // Context help
814 contextHelp ("action_bar");
816 break;
818 // Mount Normal or mount swim
819 case MBEHAV::MOUNT_NORMAL:
821 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(parent()));
822 if(mount)
824 mount->_Stages.removePosWithNoMode();
825 dir(mount->dir());
828 case MBEHAV::MOUNT_SWIM:
830 // Hide the mount unless we come from another mounted mode
831 if (_Mode != MBEHAV::MOUNT_SWIM && _Mode != MBEHAV::MOUNT_NORMAL)
833 if(_Mount != CLFECOMMON::INVALID_SLOT) // if _Mount is still invalid, the following code will be done in updatePos()
835 setOnMount();
838 // refresh target
839 UserEntity->selection(_Selection);
841 break;
843 // Dead mode.
844 case MBEHAV::DEATH:
845 setDead();
846 if(isSwimming())
847 _Mode = MBEHAV::SWIM_DEATH;
848 break;
850 // Normal or Default mode.
851 case MBEHAV::NORMAL:
852 _CurrentBehaviour.Behaviour = MBEHAV::IDLE;
853 default:
855 setAlive();
856 viewMode(viewMode());
857 break;
860 bool doSetAnim = true;
861 // if user sits down or stands up we set transition anim before changing animset
862 if( m == MBEHAV::SIT )
864 setAnim(CAnimationStateSheet::SitMode);
865 _Mode = m;
866 doSetAnim = false;
868 else
869 if( _Mode == MBEHAV::SIT && m!=MBEHAV::DEATH )
871 setAnim(CAnimationStateSheet::SitEnd);
872 _Mode = m;
873 doSetAnim = false;
876 // Show/Hide all or parts of the body.
877 updateVisualDisplay();
878 if( ClientCfg.AutomaticCamera )
880 // Set the direction as the front.
881 dir(front());
883 // Compute the current automaton
884 computeAutomaton();
885 // Update the animation set according to the mode.
886 computeAnimSet();
888 if( doSetAnim )
890 // Animset changed -> update current animation
891 setAnim(CAnimationStateSheet::Idle);
894 // Changing the mode well done.
895 return true;
896 }// mode //
900 * Mount the mount in _Mount
902 void CUserEntity::setOnMount()
904 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
905 if(mount)
907 // Update primitives for the mount and the rider.
908 if(_Primitive && mount->getPrimitive())
910 _Primitive->setRadius( std::min(mount->getPrimitive()->getRadius(), (float)(RYZOM_ENTITY_SIZE_MAX/2)) );
911 _Primitive->setHeight(mount->getPrimitive()->getHeight());
912 mount->getPrimitive()->setOcclusionMask(MaskColNone); // Remove collisions.
914 // Now on a mount
915 _OnMount = true;
916 // Link the mount and the user.
917 parent(_Mount);
918 // Refresh the View Mode
919 viewMode(viewMode());
920 // Close the crafting window if open
921 closeFaberCastWindow();
923 // Keep run in mind
924 _RunWhenAble = _Run;
926 mount->_ForbidClipping = true;
932 //-----------------------------------------------
933 // getVelocity :
934 // compute and return the entity velocity
935 //-----------------------------------------------
936 CVector CUserEntity::getVelocity() const
938 static const CVector lateral(0,0,1);
939 static CVector velocity;
940 velocity = front() * _FrontVelocity + ( lateral ^ front()) * _LateralVelocity;
941 velocity.normalize();
942 // User is Dead
943 if(isDead())
945 velocity *= 0.0;
947 // User is sitting
948 else if(isSit())
950 velocity *= 0.0;
952 // User is swimming
953 else if(isSwimming())
955 // We are a Ring DM so speed up a litle
958 // Forward Run or Walk
959 if(_FrontVelocity > 0.0f)
961 if(_Run)
962 velocity *= 3.0;
963 else
964 velocity *= 1.0;
966 // Lateral or Backward Walk
967 else
968 velocity *= 1.0;
970 if (_R2CharMode == R2::TCharMode::Editer || _R2CharMode == R2::TCharMode::Dm)
972 velocity *= (float(ClientCfg.DmRun) / 6.0f); // velocity max = max run / 2
976 else if(isRiding())
978 // Forward Run or Walk
979 if(_FrontVelocity > 0.0f)
981 if(_Run)
982 velocity *= getMountRunVelocity();
983 else
984 velocity *= getMountWalkVelocity();
986 // Lateral or Backward Walk (currently, not used)
987 else
988 velocity *= 0.66f;//getMountWalkVelocity();
990 else
992 // Forward Run or Walk
993 if(_FrontVelocity > 0.0f)
994 velocity *= currentVelocity();
995 // Lateral or Backward Walk
996 else
997 velocity *= _WalkVelocity;
999 return velocity;
1000 }// getVelocity //
1002 //-----------------------------------------------
1003 // speed :
1004 // Return the Entity Current Speed.
1005 //-----------------------------------------------
1006 double CUserEntity::speed() const // virtual
1008 return CPlayerCL::speed();
1009 // return (double)getVelocity().norm();
1010 }// speed //
1012 //-----------------------------------------------
1013 // applyMotion :
1014 // Apply the motion to the entity.
1015 //-----------------------------------------------
1016 void CUserEntity::applyMotion(CEntityCL *target)
1018 // default each frame
1019 _ForceLookSlot= CLFECOMMON::INVALID_SLOT;
1021 bool lastHasMoved = _HasMoved;
1022 _HasMoved = false;
1023 // Remove Positions in stages
1024 _Stages.removePosWithNoMode();
1025 // Remove Positions in stages for the mount.
1026 CCharacterCL *mount = 0;
1027 if(parent() != CLFECOMMON::INVALID_SLOT)
1029 mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(parent()));
1030 if(mount)
1031 mount->_Stages.removePosWithNoMode();
1033 // NO PRIMITIVE -> NO MOVE
1034 if(_Primitive == 0)
1035 return;
1036 // BAD CONNECTION -> NO MOVE
1037 if(NetMngr.getConnectionQuality() == false)
1038 return;
1039 // MS per TICK <=0 -> NO MOVE
1040 if(NetMngr.getMsPerTick() <= 0)
1041 return;
1042 // Compute Speed Vector
1043 NLMISC::CVectorD speed;
1044 if(_MoveToSlot != CLFECOMMON::INVALID_SLOT)
1046 // Check the Target.
1047 if(target == 0 || target == this)
1048 return;
1049 // Compute the vector between the user and the target.
1050 CVectorD dir2targ = target->pos() - pos();
1051 dir2targ.z = 0.0;
1052 if(dir2targ == CVectorD::Null)
1053 dir2targ = front();
1054 const double angToTarget = atan2(dir2targ.y, dir2targ.x);
1055 CVectorD aimingPos = target->getAttackerPos(angToTarget, attackRadius() + ClientCfg.AttackDist);
1056 // Aiming Position
1057 CVectorD dirToAimingPos = aimingPos-pos();
1058 dirToAimingPos.z = 0.0;
1059 const double distToAimingPos = dirToAimingPos.norm();
1061 // Decide if the target is reached or not
1062 bool targetReached= false;
1063 if(distToAimingPos > 0.5)
1065 // Because of Tryker Counter, may increase the Threshold, when the player is stalled
1066 if(distToAimingPos<_MoveToDist)
1068 // If the player is stalled too much time, abort the move and launch the action
1069 float actualSpeed= float((_Position - _LastFramePos).norm() / DT);
1071 // if player effectively runs twice slower, start colision timer
1072 if( actualSpeed*2 < getMaxSpeed() )
1074 if(!_MoveToColStartTime)
1075 _MoveToColStartTime= T1;
1077 // else ok, reset colision timer
1078 else
1079 _MoveToColStartTime= 0;
1081 // if too much time stalled, stop run.
1082 if(_MoveToColStartTime && (T1 - _MoveToColStartTime >= ClientCfg.MoveToTimeToStopStall) )
1083 targetReached= true;
1085 else
1086 _MoveToColStartTime= 0;
1088 else
1089 targetReached= true;
1091 // If the target is reached
1092 if(targetReached)
1094 // stop and execute action
1095 speed = CVectorD::Null;
1096 moveToAction(target);
1098 // else continue follow
1099 else
1101 // Look at the entity. delay it after pacs evalCollision(), for correct orientation
1102 _ForceLookSlot= target->slot();
1103 // but still estimate now an approximative front (may be used between now and applyForceLook())
1104 forceLookEntity(dir2targ, false);
1106 // Normalize
1107 dirToAimingPos.normalize();
1108 // Set the Velocity Direction
1109 speed = dirToAimingPos*distToAimingPos;
1110 speed /= DT;
1111 if(speed.norm() > getMaxSpeed())
1112 speed = dirToAimingPos*getMaxSpeed();
1115 else if(follow())
1117 // Check the Target.
1118 if(target == 0 || target == this)
1119 return;
1120 // If the target is moving, orientate the user to the target.
1121 // if(target->hasMoved())
1123 // Compute the vector between the user and the target.
1124 CVectorD dir2targ = target->pos() - pos();
1125 dir2targ.z = 0.0;
1126 if(dir2targ != CVectorD::Null)
1128 // Look at the entity. delay it after pacs evalCollision(), for correct orientation
1129 _ForceLookSlot= target->slot();
1130 // but still estimate now an approximative front (may be used between now and applyForceLook())
1131 forceLookEntity(dir2targ, false);
1134 // Compute the angle in the world to attack the target.
1135 const double angToTarget = frontYaw();
1136 // Get the best position to attack the target with the given angle.
1137 const CVectorD aimingPos = target->getAttackerPos(angToTarget, attackRadius() + ClientCfg.AttackDist);
1138 // Aiming Position
1139 CVectorD dirToAimingPos = aimingPos-pos();
1140 dirToAimingPos.z = 0.0;
1141 const double distToAimingPos = dirToAimingPos.norm();
1142 // If the User was not moving and the distance to re-move is not enough big, stay idle
1143 if(lastHasMoved
1144 || (isFighting() && distToAimingPos >= 0.5)
1145 || (!isFighting() && distToAimingPos >= 3.0))
1147 dirToAimingPos.normalize();
1148 // User is in combat mode -> follow as close as possible.
1149 if(isFighting())
1151 // Target is moving
1152 if(target->hasMoved())
1154 // Get closer if too far.
1155 if(distToAimingPos >= 0.2)
1157 // Set the Velocity Direction
1158 speed = dirToAimingPos*distToAimingPos;
1159 speed /= DT;
1160 if(speed.norm() > getMaxSpeed())
1161 speed = dirToAimingPos*getMaxSpeed();
1163 else
1165 // Try to have the same speed as the target.
1166 if(target->getSpeed() > getMaxSpeed())
1167 speed = target->dir()* ((float)getMaxSpeed());
1168 else
1169 speed = target->dir()* ((float)target->getSpeed());
1172 // Target is not moving.
1173 else
1175 // Get closer if too far.
1176 if(distToAimingPos >= 0.1)
1178 // Set the Velocity Direction
1179 speed = dirToAimingPos*distToAimingPos;
1180 speed /= DT;
1181 if(speed.norm() > getMaxSpeed())
1182 speed = dirToAimingPos*getMaxSpeed();
1184 else
1185 speed = CVectorD::Null;
1188 // User is not in combat mode -> follow not so close.
1189 else
1191 // Too far, get closer as fast as possible
1192 if(distToAimingPos >= 3.0)
1194 // Set the Velocity Direction
1195 speed = dirToAimingPos*distToAimingPos;
1196 speed /= DT;
1197 if(speed.norm() > getMaxSpeed())
1198 speed = dirToAimingPos*getMaxSpeed();
1200 // Just far enough, adjust the user speed to the target
1201 else if(target->hasMoved() && distToAimingPos >= 1.0)
1203 // Try to have the same speed as the target.
1204 if(target->getSpeed() > getMaxSpeed())
1205 speed = target->dir()* ((float)getMaxSpeed());
1206 else
1207 speed = target->dir()* ((float)target->getSpeed());
1209 // Too close, Stop
1210 else
1211 speed = CVectorD::Null;
1214 // User was stop and the next pos is to close to begin to move.
1215 else
1216 speed = CVectorD::Null;
1218 else
1220 speed = getVelocity()*_SpeedFactor.getValue();
1221 _SpeedFactor.addFactorValue(0.005f);
1224 // SPEED VECTOR NULL -> NO MOVE
1225 if(speed == CVectorD::Null)
1226 return;
1228 // First Person View
1229 if(UserControls.isInternalView())
1231 // If the server is slow, the client move slower too (only needed in FPV).
1232 double modif = (100.0f/(float)NetMngr.getMsPerTick());
1233 // don't increase speed
1234 clamp(modif, 0.0, 1.0);
1235 speed *= modif;
1236 // Move
1237 _HasMoved = true;
1238 _Primitive->move(speed, dynamicWI);
1239 if(mount && mount->getPrimitive())
1240 mount->getPrimitive()->move(speed, dynamicWI);
1242 // Third Person View
1243 else
1245 double modif = (100.0f/(float)NetMngr.getMsPerTick());
1246 clamp(modif, 0.0, 1.0);
1247 speed *= modif;
1248 speed += pos();
1249 sint64 x = (sint64)((sint32)(speed.x * 1000.0));
1250 sint64 y = (sint64)((sint32)(speed.y * 1000.0));
1251 sint64 z = (sint64)((sint32)(speed.z * 1000.0));
1252 const uint time = 10; // = 1sec because the speed is in Meter per Sec.
1253 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSX, x, 0);
1254 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSY, y);
1255 _Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSZ, z);
1256 // Move the Mount
1257 if(mount)
1259 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSX, x, 0);
1260 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSY, y);
1261 mount->_Stages.addStage(NetMngr.getCurrentClientTick()+time, CLFECOMMON::PROPERTY_POSZ, z);
1266 }// applyMotion //
1269 //---------------------------------------------------
1270 //---------------------------------------------------
1271 void CUserEntity::applyForceLook()
1273 if(_ForceLookSlot!=CLFECOMMON::INVALID_SLOT)
1275 CEntityCL *target= EntitiesMngr.entity(_ForceLookSlot);
1276 if(target && target!=this)
1278 // Compute the vector between the user and the target.
1279 CVectorD dir2targ = target->pos() - pos();
1280 dir2targ.z = 0.0;
1281 if(dir2targ == CVectorD::Null)
1282 dir2targ = front();
1283 // Look at the entity
1284 forceLookEntity(dir2targ, true);
1290 //---------------------------------------------------
1291 // updatePosCombatFloatChanged :
1292 //---------------------------------------------------
1293 void CUserEntity::updatePosCombatFloatChanged(CEntityCL * /* target */) // virtual
1295 if(viewMode() == FirstPV)
1297 pos(_FirstPos);
1299 }// updatePosCombatFloatChanged //
1302 //-----------------------------------------------
1303 // forceIndoorFPV //
1304 // Return true if the user is indoor and the CFG want to force the FPV Indoor.
1305 //-----------------------------------------------
1306 bool CUserEntity::forceIndoorFPV()
1308 return (ClientCfg.ForceIndoorFPV && indoor());
1309 }// forceIndoorFPV //
1312 //-----------------------------------------------
1313 // enableFollow :
1314 //-----------------------------------------------
1315 void CUserEntity::disableFollow()
1317 if (_FollowMode==false)
1318 return;
1320 _FollowMode = false;
1322 // Send the message to the server.
1323 CBitMemStream out;
1324 if(GenericMsgHeaderMngr.pushNameToStream("TARGET:NO_FOLLOW", out))
1325 NetMngr.push(out);
1326 else
1327 nlwarning("UE:follow: unknown message named 'TARGET:NO_FOLLOW'.");
1329 }// follow //
1332 //-----------------------------------------------
1333 // enableFollow :
1334 //-----------------------------------------------
1335 void CUserEntity::enableFollow(bool resetCameraRot)
1337 if (_FollowMode == true)
1338 return;
1340 if( _Mode == MBEHAV::DEATH )
1341 return;
1343 _FollowMode = true;
1345 // Send the message to the server.
1346 CBitMemStream out;
1347 if(GenericMsgHeaderMngr.pushNameToStream("TARGET:FOLLOW", out))
1348 NetMngr.push(out);
1349 else
1350 nlwarning("UE:follow: unknown message named 'TARGET:FOLLOW'.");
1352 // Disable any autowalk (else when follow re-disabled, it will effect, which is weird)
1353 UserControls.autowalkState(false);
1355 // disable afk mode
1356 setAFK(false);
1358 // if want to reset camera rotation
1359 if(resetCameraRot)
1360 startForceLookEntity(targetSlot());
1362 }// follow //
1365 //-----------------------------------------------
1366 // resetAnyMoveTo
1367 //-----------------------------------------------
1368 void CUserEntity::resetAnyMoveTo()
1370 // if we cancel a MoveToPhrase action, MUST dec the action counter, and hide the Action Icon
1371 if(_MoveToAction==CUserEntity::CombatPhrase || _MoveToAction==CUserEntity::ExtractRM)
1373 // the clientExecute has not been called in case of "ExtractRM autoFind"
1374 bool autoFindExtractRM= _MoveToAction==CUserEntity::ExtractRM && _MoveToPhraseMemoryLine == std::numeric_limits<uint>::max();
1375 if(!autoFindExtractRM)
1377 CSPhraseManager *pPM= CSPhraseManager::getInstance();
1378 pPM->cancelClientExecute(_MoveToPhraseCyclic);
1382 // reset to no moveTo
1383 _MoveToSlot = CLFECOMMON::INVALID_SLOT;
1384 _MoveToAction = CUserEntity::None;
1385 _MoveToDist = 0.0;
1386 _MoveToColStartTime= 0;
1389 //-----------------------------------------------
1390 // moveToCheckStartDist :
1391 // Check if the user is not already well placed.
1392 //-----------------------------------------------
1393 void CUserEntity::moveToCheckStartDist(CLFECOMMON::TCLEntityId slot, double dist, TMoveToAction /* action */)
1395 if(_MoveToSlot != CLFECOMMON::INVALID_SLOT)
1397 // Start a new Force Look entity
1398 startForceLookEntity(_MoveToSlot);
1400 // Disable any autowalk (else when moveTo re-disabled, it will effect, which is weird)
1401 UserControls.autowalkState(false);
1403 // disable afk mode
1404 setAFK(false);
1406 // if sufficiently near, launch the action
1407 CEntityCL *target = EntitiesMngr.entity(slot);
1408 if(target)
1410 CVectorD dir2targ = target->pos() - pos();
1411 dir2targ.z = 0.0;
1412 if((dir2targ==CVectorD::Null) || (dir2targ.norm() < dist))
1414 moveToAction(target);
1418 }// moveToCheckStartDist //
1420 //-----------------------------------------------
1421 // moveTo :
1422 // Method to move to someone else.
1423 //-----------------------------------------------
1424 void CUserEntity::moveTo(CLFECOMMON::TCLEntityId slot, double dist, TMoveToAction action)
1426 resetAnyMoveTo();
1428 // setup new state
1429 _MoveToSlot = slot;
1430 _MoveToDist = dist;
1431 _MoveToAction = action;
1433 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1434 }// moveTo //
1436 //-----------------------------------------------
1437 // moveToMission :
1438 // Method to move to someone else for a mission. NB: if prec action was a CombatPhrase action, action counter are decremented
1439 //-----------------------------------------------
1440 void CUserEntity::moveToMission(CLFECOMMON::TCLEntityId slot, double dist, uint32 id)
1442 resetAnyMoveTo();
1444 // setup new state
1445 _MoveToSlot = slot;
1446 _MoveToDist = dist;
1447 _MoveToAction = CUserEntity::Mission;
1448 _MoveToMissionId = id;
1450 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1451 }// moveToMission //
1453 //-----------------------------------------------
1454 // moveToMissionRing :
1455 // Method to move to someone else for a ring mission. NB: if prec action was a CombatPhrase action, action counter are decremented
1456 //-----------------------------------------------
1457 void CUserEntity::moveToMissionRing(CLFECOMMON::TCLEntityId slot, double dist, uint32 id)
1459 resetAnyMoveTo();
1461 // setup new state
1462 _MoveToSlot = slot;
1463 _MoveToDist = dist;
1464 _MoveToAction = CUserEntity::MissionRing;
1465 _MoveToMissionId = id;
1467 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1468 }// moveToMissionRing //
1470 //-----------------------------------------------
1471 // moveTo :
1472 // Method to move to someone else.
1473 //-----------------------------------------------
1474 void CUserEntity::moveToCombatPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool phraseCyclic)
1476 resetAnyMoveTo();
1478 // setup new state
1479 _MoveToSlot = slot;
1480 _MoveToDist = dist;
1481 _MoveToAction = CUserEntity::CombatPhrase;
1482 _MoveToPhraseMemoryLine= phraseMemoryLine;
1483 _MoveToPhraseMemorySlot= phraseMemorySlot;
1484 _MoveToPhraseCyclic= phraseCyclic;
1486 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1489 //-----------------------------------------------
1490 // Method to move to someone else, for foraging extraction
1491 // The caller MUST call after CSPhraseManager::clientExecute(), to increment action counter
1492 //-----------------------------------------------
1493 void CUserEntity::moveToExtractionPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool cyclic)
1495 // Test if forage tool in hand, otherwise auto-equip with it
1496 bool validForageToolInHand = false;
1497 CInventoryManager * inv = CInventoryManager::getInstance();
1498 if ( !inv )
1500 return;
1502 uint32 rightHandSheet = inv->getRightHandItemSheet();
1503 if ( rightHandSheet )
1505 validForageToolInHand = inv->isForageToolItem( rightHandSheet );
1507 if ( !validForageToolInHand )
1509 // Find a forage tool in the bag
1510 uint i;
1511 for ( i=0; i<MAX_BAGINV_ENTRIES; ++i )
1513 uint32 itemSheet = inv->getBagItem(i).getSheetID();
1514 if ( itemSheet && inv->isForageToolItem(itemSheet) )
1516 break;
1519 if ( i != MAX_BAGINV_ENTRIES && ClientCfg.AutoEquipTool )
1521 // remember last used weapon(s)
1522 rememberWeaponsInHand();
1524 // Clear hands
1525 inv->unequip( "LOCAL:INVENTORY:HAND:1" );
1526 inv->unequip( "LOCAL:INVENTORY:HAND:0" );
1528 // Equip hands
1529 string bagPath = toString( "LOCAL:INVENTORY:BAG:%u", i );
1530 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:0" );
1532 else
1534 // No forage tool in bag
1535 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiForageToolMissing"), "CHK" );
1536 return;
1540 resetAnyMoveTo();
1542 // setup new state
1543 _MoveToSlot = slot;
1544 _MoveToDist = dist;
1545 _MoveToAction = CUserEntity::ExtractRM;
1546 _MoveToPhraseMemoryLine= phraseMemoryLine;
1547 _MoveToPhraseMemorySlot= phraseMemorySlot;
1548 _MoveToPhraseCyclic= cyclic;
1550 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1553 //-----------------------------------------------
1554 // Method to begin a spire construction
1555 // The caller MUST call after CSPhraseManager::clientExecute(), to increment action counter
1556 //-----------------------------------------------
1557 void CUserEntity::moveToTotemBuildingPhrase(CLFECOMMON::TCLEntityId slot, double dist, uint phraseMemoryLine, uint phraseMemorySlot, bool cyclic)
1559 resetAnyMoveTo();
1561 // setup new state
1562 _MoveToSlot = slot;
1563 _MoveToDist = dist;
1564 _MoveToAction = CUserEntity::BuildTotem;
1565 _MoveToPhraseMemoryLine= phraseMemoryLine;
1566 _MoveToPhraseMemorySlot= phraseMemorySlot;
1567 _MoveToPhraseCyclic= cyclic;
1569 moveToCheckStartDist(_MoveToSlot, _MoveToDist, _MoveToAction);
1572 //-----------------------------------------------
1573 // moveToAction :
1574 // Launch the Action Once the Move is done.
1575 // \param CEntityCL * : pointer on the destination entity.
1576 // \warning entity pointer must be valid(allocated).
1577 //-----------------------------------------------
1578 void CUserEntity::moveToAction(CEntityCL *ent)
1580 // Check entity pointer
1581 nlassert(ent);
1583 UserControls.needReleaseForward();
1585 // Get the interface instance.
1586 CInterfaceManager *IM = CInterfaceManager::getInstance();
1587 switch(_MoveToAction)
1589 // Attack
1590 case CUserEntity::Attack:
1591 UserEntity->attack();
1592 break;
1593 // Quartering
1594 case CUserEntity::Quarter:
1595 if((ent->properties()).harvestable())
1596 CAHManager::getInstance()->runActionHandler("context_quartering", 0);
1597 break;
1598 // Loot
1599 case CUserEntity::Loot:
1600 if((ent->properties()).lootable())
1601 CAHManager::getInstance()->runActionHandler("context_loot", 0);
1602 break;
1603 // Pick Up
1604 case CUserEntity::PickUp:
1605 nlwarning("UE:checkMoveTo: not yet implemented");
1606 break;
1607 case CUserEntity::ExtractRM:
1608 extractRM();
1609 break;
1610 // Trade Item
1611 case CUserEntity::TradeItem:
1612 CAHManager::getInstance()->runActionHandler("context_trade_item", 0);
1613 break;
1614 // Trade Phrase
1615 case CUserEntity::TradePhrase:
1616 CAHManager::getInstance()->runActionHandler("context_trade_phrase", 0);
1617 break;
1618 // Trade Pact
1619 case CUserEntity::TradePact:
1620 CAHManager::getInstance()->runActionHandler("context_trade_pact", 0);
1621 break;
1622 // Mission
1623 case CUserEntity::Mission:
1625 string param = toString("id=%d", _MoveToMissionId);
1626 CAHManager::getInstance()->runActionHandler("open_mission_option", 0, param);
1628 break;
1629 // Dynamic Mission
1630 case CUserEntity::DynamicMission:
1631 CAHManager::getInstance()->runActionHandler("context_dynamic_mission", 0);
1632 break;
1633 // Static Mission
1634 case CUserEntity::StaticMission:
1635 CAHManager::getInstance()->runActionHandler("context_choose_mission", 0);
1636 break;
1637 // Mission
1638 case CUserEntity::MissionRing:
1640 string param = toString("id=%d", _MoveToMissionId);
1641 CAHManager::getInstance()->runActionHandler("mission_ring", 0, param);
1643 break;
1644 // Create Guild
1645 case CUserEntity::CreateGuild:
1646 CAHManager::getInstance()->runActionHandler("context_create_guild", 0);
1647 break;
1648 // News
1649 case CUserEntity::News:
1650 CAHManager::getInstance()->runActionHandler("context_talk", 0);
1651 break;
1652 // Trade Teleport
1653 case CUserEntity::TradeTeleport:
1654 CAHManager::getInstance()->runActionHandler("context_trade_teleport", 0);
1655 break;
1656 // Trade Faction items
1657 case CUserEntity::TradeFaction:
1658 CAHManager::getInstance()->runActionHandler("context_trade_faction", 0);
1659 break;
1660 // Trade Cosmetic
1661 case CUserEntity::TradeCosmetic:
1662 CAHManager::getInstance()->runActionHandler("context_trade_cosmetic", 0);
1663 break;
1664 // Talk
1665 case CUserEntity::Talk:
1666 nlwarning("UE:checkMoveTo: not yet implemented");
1667 break;
1668 // CombatPhrase
1669 case CUserEntity::CombatPhrase:
1670 UserEntity->attackWithPhrase();
1671 break;
1672 // Mount
1673 case CUserEntity::Mount:
1675 string orderStr = "mount";
1676 string beastIndexStr = "@UI:GCM_BEAST_SELECTED";
1677 bool confirmFree = true;
1678 beastOrder(orderStr,beastIndexStr,confirmFree);
1679 break;
1681 // WebPage
1682 case CUserEntity::WebPage:
1683 CAHManager::getInstance()->runActionHandler("context_web_page", 0);
1684 break;
1685 // Outpost
1686 case CUserEntity::Outpost:
1687 CLuaManager::getInstance().executeLuaScript("game:outpostBCOpenStateWindow()", 0);
1688 break;
1689 // BuildTotem
1690 case CUserEntity::BuildTotem:
1691 buildTotem();
1692 break;
1693 // openArkUrl
1694 case CUserEntity::OpenArkUrl:
1695 CLuaManager::getInstance().executeLuaScript("getUI('ui:interface:web_transactions'):find('html'):browse(ArkTargetUrl)", 0);
1696 break;
1697 default:
1698 break;
1701 // Move To Done.
1702 resetAnyMoveTo();
1703 }// moveToAction //
1706 //-----------------------------------------------
1707 // sendToServer :
1708 // Send the position and orientation to the server.
1709 //-----------------------------------------------
1710 bool CUserEntity::sendToServer(CBitMemStream &out)
1712 if(GenericMsgHeaderMngr.pushNameToStream("POSITION", out))
1714 // Backup the position sent.
1715 if (_Primitive) _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1716 // Send Position & Orientation
1717 CPositionMsg positionMsg;
1718 positionMsg.X = (sint32)(pos().x * 1000);
1719 positionMsg.Y = (sint32)(pos().y * 1000);
1720 positionMsg.Z = (sint32)(pos().z * 1000);
1721 positionMsg.Heading = frontYaw();
1722 out.serial(positionMsg);
1723 return true;
1725 else
1727 nlwarning("UE:sendToServer: unknown message named 'POSITION'.");
1728 return false;
1730 }// sendToServer //
1732 //-----------------------------------------------
1733 // msgForCombatPos :
1734 // Fill the msg to know if the user is well placed to fight.
1735 //-----------------------------------------------
1736 bool CUserEntity::msgForCombatPos(NLMISC::CBitMemStream &out)
1738 static bool wellPosition = false;
1739 bool wellP = false;
1740 // if(isFighting())
1742 // Is the user well Placed
1743 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
1744 if(target)
1745 wellP = target->isPlacedToFight(UserEntity->pos(), front(), attackRadius() + ClientCfg.AttackDist);
1747 // If different from the last time
1748 if(wellPosition != wellP)
1750 wellPosition = wellP;
1751 // Send state to server.
1752 if(GenericMsgHeaderMngr.pushNameToStream("COMBAT:VALIDATE_MELEE", out))
1754 uint8 flag = wellP?1:0;
1755 out.serial(flag);
1756 return true;
1758 else
1759 nlwarning("UE:msgForCombatPos: unknown message named 'COMBAT:TOGGLE_COMBAT_FLOAT_MODE'.");
1761 // Do not send the msg.
1762 return false;
1763 }// msgForCombatPos //
1766 //-----------------------------------------------
1767 // checkPos :
1768 // Check the User Position according to the server code.
1769 // \todo GUIGUI : put checks on GPos
1770 // \todo GUIGUI : refact the check primitive when creating a PACS again
1771 //-----------------------------------------------
1772 void CUserEntity::checkPos()
1774 if(PACS && _Primitive)
1776 // Is in water ?
1777 if(GR)
1779 UGlobalPosition gPos;
1780 _Primitive->getGlobalPosition(gPos, dynamicWI);
1781 float waterHeight;
1782 if(GR->isWaterPosition(gPos, waterHeight))
1784 if(isSwimming() == false)
1786 // Player is Dead set the right mode (swim and dead)
1787 if(isDead())
1788 mode(MBEHAV::SWIM_DEATH);
1789 else
1791 // Do not swim when in combat mode.
1792 if(isFighting())
1794 _HasMoved = false;
1795 pacsPos(_LastPositionValidated, _LastGPosValidated);
1797 // \todo GUIGUI : if the move was cancelled, do not switch to swimming mode.
1798 else if (isRiding())
1800 mode(MBEHAV::MOUNT_SWIM);
1801 // also change mounted entity mode
1802 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
1803 if(mount)
1805 // Set the mount.
1806 mount->setMode(MBEHAV::MOUNT_SWIM);
1807 mount->computeAutomaton();
1808 mount->computeAnimSet();
1809 mount->setAnim(CAnimationStateSheet::Idle);
1812 else
1814 mode(MBEHAV::SWIM);
1819 else
1821 if(isSwimming())
1823 if(isDead())
1825 mode(MBEHAV::DEATH);
1827 else if (isRiding())
1829 mode(MBEHAV::MOUNT_NORMAL);
1830 // also change mounted entity mode
1831 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
1832 if(mount)
1834 // Set the mount.
1835 mount->setMode(MBEHAV::MOUNT_NORMAL);
1836 mount->computeAutomaton();
1837 mount->computeAnimSet();
1838 mount->setAnim(CAnimationStateSheet::Idle);
1841 else
1843 mode(MBEHAV::NORMAL);
1849 // Create the Primitive used to check if the move will be accepted by the server
1850 if(_CheckPrimitive == 0)
1852 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1853 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1854 _CheckPrimitive = PACS->addNonCollisionablePrimitive(_Primitive);
1855 _CheckPrimitive->setOcclusionMask(MaskColNone); // Collide with nothing
1856 _CheckPrimitive->setCollisionMask(MaskColNone); // Collide with nothing
1857 _LastPositionValidated = pos();
1859 if(_CheckPrimitive)
1861 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1862 CVectorD speed = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1864 _CheckPrimitive->move(speed, dynamicWI);
1865 if(PACS->evalNCPrimitiveCollision(1.0, _CheckPrimitive, dynamicWI) == false)
1866 nlwarning("UE:checkPos: _CheckPrimitive is a collisionable primitive !!!");
1868 CVectorD vectDist = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1869 if(vectDist.norm() > 0.001)
1871 // The server will not be able to reproduce this move, restoring the last one.
1872 _HasMoved = false;
1873 pacsPos(_LastPositionValidated,_LastGPosValidated);
1875 else
1877 _LastPositionValidated = _Primitive->getFinalPosition(dynamicWI);
1878 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1882 }// checkPos //
1885 //-----------------------------------------------
1886 // testPacsPos :
1887 // test the move from posToTest to current pos
1888 //-----------------------------------------------
1889 bool CUserEntity::testPacsPos( CVectorD& posToTest )
1891 if(PACS && _Primitive && _CheckPrimitive)
1893 _CheckPrimitive->setGlobalPosition(posToTest, dynamicWI);
1894 CVectorD speed = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1896 _CheckPrimitive->move(speed, dynamicWI);
1897 if(PACS->evalNCPrimitiveCollision(1.0, _CheckPrimitive, dynamicWI) == false)
1898 nlwarning("UE:testPacsPos: _CheckPrimitive is a collisionable primitive !!!");
1900 CVectorD vectDist = _Primitive->getFinalPosition(dynamicWI) - _CheckPrimitive->getFinalPosition(dynamicWI);
1901 if(vectDist.norm() > 0.001)
1903 return false;
1905 else
1906 return true;
1908 return false;
1910 } // testPacsPos //
1913 //-----------------------------------------------
1914 // tp :
1915 // Teleport the player (remove selection, re-init checkPos, etc.).
1916 //-----------------------------------------------
1917 void CUserEntity::tp(const CVectorD &dest)
1919 // Remove the selection.
1920 UserEntity->selection(CLFECOMMON::INVALID_SLOT);
1921 // Update PACS
1922 pacsPos(dest);
1923 // Update the primitive useful to check the user position.
1924 _LastPositionValidated = dest;
1925 // Get the Global position
1926 if(_Primitive)
1928 // this is the last PACS position validated too
1929 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1930 // consider this is the last position sent to server (since actually received!)
1931 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1932 // Set the new position of the 'check' primitive
1933 if(_CheckPrimitive)
1934 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1936 else
1937 nlwarning("UE:tp: the entity has a Null primitive.");
1938 // Return to interface mode.
1939 viewMode(UserEntity->viewMode());
1940 // User well oriented.
1941 dir(UserEntity->front());
1942 // Set Normal Mode after a teleport.
1943 mode(MBEHAV::NORMAL);
1944 // Set animation with idle.
1945 setAnim(CAnimationStateSheet::Idle);
1946 }// tp //
1948 //-----------------------------------------------
1949 // correctPos :
1950 // Teleport the player to correct his position.
1951 //-----------------------------------------------
1952 void CUserEntity::correctPos(const NLMISC::CVectorD &dest)
1954 nlinfo("UE:correctPos: new user position %f %f %f", dest.x, dest.y, dest.z);
1955 // Change the user poisition.
1956 UserEntity->pacsPos(dest);
1957 // Update the primitive useful to check the user position.
1958 _LastPositionValidated = dest;
1959 // Get the Global position
1960 if(_Primitive)
1962 // this is the last PACS position validated too
1963 _Primitive->getGlobalPosition(_LastGPosValidated, dynamicWI);
1964 // consider this is the last position sent to server (since actually received!)
1965 _Primitive->getGlobalPosition(_LastGPosSent, dynamicWI);
1966 // Set the new position of the 'check' primitive
1967 if(_CheckPrimitive)
1968 _CheckPrimitive->setGlobalPosition(_LastGPosSent, dynamicWI);
1970 else
1971 nlwarning("UE:correctPos: the entity has a Null primitive.");
1973 // Correct the pos of the mount, if riding
1974 if ( isRiding() )
1976 if ( _Mount < EntitiesMngr.entities().size() )
1978 CEntityCL *mount = EntitiesMngr.entities()[_Mount];
1979 if ( mount )
1980 mount->pacsPos( dest );
1983 }// correctPos //
1987 * Check if the mount is able to run, and force walking mode if not
1988 * (if the player clicked on run, set back to walk).
1990 void CUserEntity::checkMountAbleToRun()
1992 if ( isRiding() )
1994 if ( running() )
1996 // Make the mount walk if she's hungry
1997 if ( ! _MountHunger.canRun() )
1998 switchVelocity( false );
2000 else
2002 // Make the mount run if she's not hungry anymore
2003 if ( _RunWhenAble && _MountHunger.canRun() )
2004 switchVelocity( false );
2010 //-----------------------------------------------
2011 // Update the position of the entity after the motion.
2012 // \param t : Time for the position of the entity after the motion.
2013 // \param target : pointer on the current target.
2014 //-----------------------------------------------
2015 void CUserEntity::updatePos(const TTime &t, CEntityCL *target)
2017 // Update Mount if mounting (if _Mount was still not valid when mode received)
2018 if((_Mount != CLFECOMMON::INVALID_SLOT && isRiding()) && _OnMount==false)
2020 setOnMount();
2022 // External view are managed like other entities.
2023 if(!UserControls.isInternalView())
2025 // Update position according to the animations.
2026 CPlayerCL::updatePos(t, target);
2027 _LastFrameTime = t*0.001;
2028 // Set The FPV when indoor.
2029 if(forceIndoorFPV())
2030 viewMode(FirstPV, false);
2031 return;
2034 else
2036 // 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)
2037 updateAnimationState();
2040 // Compute the Time Step.
2041 double frameTimeRemaining = computeTimeStep(((double)t)*0.001);
2042 // Do not update animation if Client Light
2043 if (!ClientCfg.Light)
2045 // Attack Animation.
2046 if(_AnimAttackOn)
2048 if(animIndex(MOVE) != ::CAnimation::UnknownAnim)
2050 if(animOffset(MOVE) >= EAM->getAnimationLength(animId(MOVE)))
2052 _AnimAttackOn = false;
2053 updateVisualDisplay();
2058 // Increase the time in the current animation.
2059 double previousTimeOffset = animOffset(MOVE);
2060 double offset = previousTimeOffset + frameTimeRemaining;
2061 // Check Anim length
2062 double animLength = EAM->getAnimationLength(animId(MOVE));
2063 if(offset > animLength)
2064 animOffset(MOVE, animLength);
2065 else
2066 animOffset(MOVE, offset);
2067 // Manage Events that could be created by the animation (like sound).
2068 animEventsProcessing(previousTimeOffset, animOffset(MOVE));
2070 // Get the right pos from PACS.
2071 if(_Primitive && GR)
2073 UGlobalPosition gPos;
2074 _Primitive->getGlobalPosition(gPos, dynamicWI);
2076 // Set the new current pos.
2077 pos(GR->getGlobalPosition(gPos));
2079 // Set the direction.
2080 if( ClientCfg.AutomaticCamera )
2082 //dir(front());
2084 // Reset the TPV when outdoor if needed.
2085 if(_Primitive && GR)
2087 UGlobalPosition gPos;
2088 _Primitive->getGlobalPosition(gPos, dynamicWI);
2089 if(!GR->isInterior(gPos))
2090 if(!ClientCfg.FPV)
2091 viewMode(ThirdPV, false);
2093 // Current time is now the entity time too.
2094 _LastFrameTime = t*0.001;
2095 }// updatePos //
2097 //-----------------------------------------------
2098 // updateDisplay :
2099 // Update the PACS position after the evalCollision. The entity position is set too. This is fast.
2100 // If the entity position is too far from its PACS position, setGlobalPosition is called.
2101 // After this call, the position.z is valid.
2102 //-----------------------------------------------
2103 void CUserEntity::pacsFinalizeMove() // virtual
2105 if(_Primitive == 0)
2106 return;
2108 // Get the global position
2109 _FinalPacsPos = _Primitive->getFinalPosition(dynamicWI);
2111 // Get the global position
2112 UGlobalPosition gPos;
2113 _Primitive->getGlobalPosition(gPos, dynamicWI);
2114 if(gPos.InstanceId != -1)
2116 pos(_FinalPacsPos);
2117 if(_Mount != CLFECOMMON::INVALID_SLOT)
2119 CEntityCL *mount = EntitiesMngr.entity(_Mount);
2120 if(mount)
2121 mount->pacsPos(pos());
2124 else
2125 _SetGlobalPositionDone = false;
2126 }// pacsFinalizeMove //
2129 //-----------------------------------------------
2130 // applyBehaviour :
2131 // Apply the behaviour for the user.
2132 //-----------------------------------------------
2133 void CUserEntity::applyBehaviour(const CBehaviourContext &behaviourContext) // virtual
2135 const MBEHAV::CBehaviour &behaviour = behaviourContext.Behav;
2136 // Special code for the First Person View
2137 if((viewMode() == FirstPV) && behaviour.isCombat())
2139 // Backup the current behaviour.
2140 _CurrentBehaviour = behaviourContext.Behav;
2141 dir(front());
2143 // check if self-target
2144 bool selfSpell = false;
2145 if (behaviourContext.Targets.Targets.size() == 1)
2147 if (behaviourContext.Targets.Targets[0].TargetSlot == 0)
2149 selfSpell = true;
2152 bool isOffensif;
2153 switch(behaviour.Behaviour)
2155 case MBEHAV::CAST_OFF:
2156 case MBEHAV::CAST_OFF_FAIL:
2157 case MBEHAV::CAST_OFF_SUCCESS:
2158 case MBEHAV::CAST_OFF_LINK:
2159 isOffensif = true;
2160 break;
2161 default:
2162 isOffensif = false;
2163 break;
2166 // Default target hit dates
2167 static vector<double> targetHitDates;
2168 targetHitDates.clear();
2169 targetHitDates.resize(behaviourContext.Targets.Targets.size(), TimeInSec);
2171 // cast projectiles/impact linked to behaviour
2172 updateCurrentAttack();
2173 if (isCurrentBehaviourAttackEnd())
2175 // In First Person (don't care), use a default animation => will choose a default delay of 0.5
2176 performCurrentAttackEnd(behaviourContext, selfSpell && isOffensif,
2177 targetHitDates, CAnimationStateSheet::UnknownState);
2180 _RightFXActivated = false;
2181 _LeftFXActivated = false;
2182 if(EAM)
2184 animState (MOVE, CAnimationStateSheet::FirstPersonAttack);
2185 animIndex (MOVE, CAnimation::UnknownAnim);
2186 animOffset(MOVE, 0.0);
2187 _CurrentState = EAM->mState(_CurrentAutomaton, animState(MOVE));
2188 if(_CurrentState && _CurrentAnimSet[MOVE])
2190 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
2191 if(animStatePtr)
2193 animIndex(MOVE, animStatePtr->chooseAnim(_AnimJobSpecialisation, people(), getGender(), 0.0));
2194 if(animIndex(MOVE) != CAnimation::UnknownAnim)
2196 if(_PlayList)
2198 _PlayList->setAnimation (MOVE, animId(MOVE));
2199 _PlayList->setTimeOrigin (MOVE, ryzomGetLocalTime ()*0.001);//_LastFrameTime);
2200 _AnimAttackOn = true;
2201 updateVisualDisplay();
2207 // Start FX to play at the animation beginning like trails.
2208 if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK)
2210 startItemAttackFXs(_CurrentBehaviour.Range.ImpactIntensity != 0, _CurrentBehaviour.Range.ImpactIntensity);
2212 else
2214 startItemAttackFXs(_CurrentBehaviour.Combat.ImpactIntensity != 0 && _CurrentBehaviour.Combat.HitType != HITTYPE::Failed, _CurrentBehaviour.Combat.ImpactIntensity);
2216 // DeltaHP
2217 applyBehaviourFlyingHPs(behaviourContext, behaviour, targetHitDates);
2219 // In third person view (or camera mode), play the same way than for the others.
2220 else
2221 CPlayerCL::applyBehaviour(behaviourContext);
2222 }// applyBehaviour //
2224 //---------------------------------------------------
2225 // setDead :
2226 // Method to Flag the character as dead and do everything needed.
2227 //---------------------------------------------------
2228 void CUserEntity::setDead() // virtual
2230 // User is dead -> NO FOLLOW
2231 disableFollow();
2232 // Remove the selection.
2233 UserEntity->selection(CLFECOMMON::INVALID_SLOT);
2234 // Death Mode Control and view.
2235 UserControls.mode(CUserControls::DeathMode);
2236 // Play the FX for the death
2237 NL3D::UParticleSystemInstance deathFX = FXMngr.instantFX(ClientCfg.DeadFXName);
2238 if(!deathFX.empty())
2239 deathFX.setPos(selectBox().getCenter());
2241 // Last teleport is a death
2243 // get player's kami fame
2244 CInterfaceManager *pIM = CInterfaceManager::getInstance();
2245 sint8 kamiFame = 0;
2246 uint kamiFameIndex = CStaticFames::getInstance().getFactionIndex("kami");
2247 if (pIM && kamiFameIndex != CStaticFames::INVALID_FACTION_INDEX)
2249 CCDBNodeLeaf *pLeafKamiFame = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", kamiFameIndex - 1), false);
2250 if (pLeafKamiFame != NULL)
2251 kamiFame = pLeafKamiFame->getValue8();
2254 // get player's karavan fame
2255 sint8 karavanFame = 0;
2256 uint karavanFameIndex = CStaticFames::getInstance().getFactionIndex("karavan");
2257 if (pIM && karavanFameIndex != CStaticFames::INVALID_FACTION_INDEX)
2259 CCDBNodeLeaf *pLeafKaravanFame = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", karavanFameIndex - 1), false);
2260 if (pLeafKaravanFame != NULL)
2261 karavanFame = pLeafKaravanFame->getValue8();
2264 // set loading background depending on player's faction fame
2265 if (kamiFame > karavanFame)
2266 LoadingBackground = ResurectKamiBackground;
2267 else
2268 LoadingBackground = ResurectKaravanBackground;
2270 // disable or enable shadowing
2271 updateCastShadowMap();
2273 // enable death warning popup
2274 //CInterfaceManager * pIM = CInterfaceManager::getInstance();
2275 if( pIM )
2277 CAHManager::getInstance()->runActionHandler("set",NULL,"dblink=UI:VARIABLES:DEATH_WARNING_WANTED|value=1");
2279 }// setDead //
2281 //-----------------------------------------------
2282 // skillUp :
2283 // Skill Up
2284 //-----------------------------------------------
2285 void CUserEntity::skillUp()
2287 // Play the FX for the death
2288 NL3D::UParticleSystemInstance skillUpFX = FXMngr.instantFX(ClientCfg.SkillUpFXName);
2289 skillUpFX.setClusterSystem(getClusterSystem());
2290 if(!skillUpFX.empty())
2291 skillUpFX.setPos(pos());
2292 }// skillUp //
2294 //-----------------------------------------------
2295 // startColTimer :
2296 // After a few time, if the user is still in collision with someone else, remove collisions with other entitites.
2297 //-----------------------------------------------
2298 void CUserEntity::startColTimer()
2300 if(_ColOn)
2302 if(_ColRemoved==false)
2304 if((T1-_ColStartTime) > ClientCfg.TimeToRemoveCol)
2306 EntitiesMngr.removeColUserOther();
2307 _ColRemoved = true;
2311 else
2313 _ColOn = true;
2314 _ColStartTime = T1;
2316 }// startColTimer //
2318 //-----------------------------------------------
2319 // stopColTimer :
2320 // Called when the user is no more in collision with another entity.
2321 //-----------------------------------------------
2322 void CUserEntity::stopColTimer()
2324 // Restore collisions.
2325 if(_ColRemoved)
2327 EntitiesMngr.restoreColUserOther();
2328 _ColRemoved = false;
2330 _ColOn = false;
2331 }// stopColTimer //
2335 //-----------------------------------------------
2336 // getMaxSpeed
2337 // Return the current max speed for the entity in meter per sec
2338 // The method return a max according to the speed factor (given by the server)
2339 // It's also return a value according to the landscape (water)
2340 // Also managed mounts
2341 //-----------------------------------------------
2342 double CUserEntity::getMaxSpeed() const // virtual
2344 // Max Defined speed according to the current factor (slow, or speed increased)
2345 double maxSpeed = _SpeedFactor.getValue();
2346 // User is Dead
2347 if(isDead())
2349 maxSpeed *= 0.0;
2351 // User is sitting
2352 else if(isSit())
2354 maxSpeed *= 0.0;
2356 // User is swimming
2357 else if(isSwimming())
2360 // We are a Ring DM so speed up a litle
2361 if (_R2CharMode == R2::TCharMode::Editer || _R2CharMode == R2::TCharMode::Dm)
2363 maxSpeed *= ClientCfg.DmRun / 2;
2365 else
2367 // Run
2368 maxSpeed *= 3.0;
2371 else if(isRiding())
2373 // Run if mount not hungry, otherwise, use walk velocity
2374 if (_MountHunger.canRun())
2375 maxSpeed *= (double)getMountRunVelocity();
2376 else
2377 maxSpeed *= (double)getMountWalkVelocity();
2379 else
2381 // Run
2382 maxSpeed *= runVelocity();
2384 // Return the current max
2385 return maxSpeed;
2386 // return ClientCfg.Run * _SpeedFactor.getValue();
2387 }// getMaxSpeed //
2390 //-----------------------------------------------
2391 // snapToGround :
2392 // Snap the user to the ground.
2393 //-----------------------------------------------
2394 void CUserEntity::snapToGround()
2396 CEntityCL::snapToGround();
2398 updateSound (ryzomGetLocalTime ());
2399 }// // snapToGround //
2402 //-----------------------------------------------
2403 // updateSound :
2404 //-----------------------------------------------
2405 void CUserEntity::updateSound(const TTime &time)
2407 H_AUTO_USE ( RZ_Client_Update_Sound );
2409 // no sound manager, no need to update sound
2410 if (SoundMngr == NULL)
2411 return;
2413 if (!(StereoHMD && true)) // TODO: ClientCfg.Headphone
2415 // NOTE: StereoHMD+Headphone impl in main_loop.cpp
2416 SoundMngr->setListenerPos(pos());
2417 const CMatrix &camMat = MainCam.getMatrix();
2418 SoundMngr->setListenerOrientation(camMat.getJ(), camMat.getK());
2421 if (ClientCfg.Light)
2422 return;
2424 // step sound of self : refind the sound animations associated to the walk and run animation
2426 static bool computeAnim = true;
2427 static bool lastMode = false;
2428 static TTime previousTime = 0;
2429 static TTime stepTime = 0;
2430 static bool leftRight = false;
2431 static NLSOUND::CSoundAnimMarker *leftStep = 0;
2432 static NLSOUND::CSoundAnimMarker *rightStep = 0;
2434 // TODO : Remove when bug corrected:
2435 if (_Gender == GSGENDER::unknown)
2437 nlwarning("CUserEntity::updateSound : The gender is unknown, forcing it to male");
2438 _Gender = GSGENDER::male;
2441 // force recompute of anim if walk/run change.
2442 computeAnim = computeAnim || (lastMode != _Run);
2444 // Check the sound animation to find the time between to step
2445 if(computeAnim && SoundMngr && _CurrentAnimSet[MOVE] != 0) // && _SoundId[MOVE] != NLSOUND::CSoundAnimationNoId)
2447 lastMode = _Run;
2448 TAnimStateId mode = _Run ? CAnimationStateSheet::Run : CAnimationStateSheet::Walk;
2449 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState (mode);
2450 if (animStatePtr)
2452 ::CAnimation::TAnimId animId = animStatePtr->chooseAnim (_AnimJobSpecialisation, people(), getGender(), 0);
2453 if (animId != ::CAnimation::UnknownAnim)
2455 const ::CAnimation *anim = animStatePtr->getAnimation (animId);
2456 if(anim)
2458 // Select the sound ID
2459 _SoundId[MOVE] = anim->soundId();
2461 if (_SoundId[MOVE] != NLSOUND::CSoundAnimationNoId)
2463 // retrieve the anim
2464 NLSOUND::CSoundAnimManager *mgr = NLSOUND::CSoundAnimManager::instance();
2466 string name = mgr->idToName(_SoundId[MOVE]);
2468 if (!name.empty())
2470 NLSOUND::CSoundAnimation *sanim = mgr->findAnimation(name);
2471 if (sanim->countMarkers() != 2)
2473 static set<string> warnOnce;
2474 if (warnOnce.find(sanim->getName()) == warnOnce.end())
2476 nlwarning("Sound animation '%s' has not 2 markers, not a biped ? (Display Once)", sanim->getName().c_str());
2477 warnOnce.insert(sanim->getName());
2480 else
2482 stepTime = TTime((sanim->getMarker(1)->getTime() - sanim->getMarker(0)->getTime()) * 1000);
2483 rightStep = sanim->getMarker(0);
2484 leftStep = sanim->getMarker(1);
2485 computeAnim = false;
2494 if( SoundMngr && _Mode == MBEHAV::NORMAL)
2496 if (!getVelocity().isNull())
2498 if (stepTime > 0 && time-previousTime>=stepTime)
2500 previousTime = time;
2502 // set the sound 1 meter below listener
2503 _SoundContext.Position = pos() + CVector(0,0,-1);
2504 _SoundContext.RelativeGain = SoundMngr->getUserEntitySoundLevel();;
2506 uint32 matId= getGroundType();
2507 //nldebug("Current material = %u", matId);
2508 _SoundContext.Args[0] = matId;
2510 if (leftRight)
2512 if (leftStep)
2513 // TODO : find the correct cluster
2514 leftStep->play(SoundMngr->getMixer(), NULL, _SoundContext);
2516 else
2518 if (rightStep)
2519 // TODO : find the correct cluster
2520 rightStep->play(SoundMngr->getMixer(), NULL, _SoundContext);
2522 // recompute a new sound anim
2523 computeAnim = true;
2526 leftRight = !leftRight;
2529 else
2531 // resets the counter
2532 previousTime = 0;
2535 }// updateSound //
2538 //-----------------------------------------------
2539 // rotate :
2540 // rotate the body on the left or right (front changes).
2541 //-----------------------------------------------
2542 void CUserEntity::rotate(float ang)
2544 // Rotate the body.
2545 CMatrix m;
2546 m.identity();
2547 m.rotateZ(ang);
2548 front(m * front().normed());
2549 }// rotate //
2552 //-----------------------------------------------
2553 // rotHeadVertically :
2554 // rotate the head vertically.
2555 //-----------------------------------------------
2556 void CUserEntity::rotHeadVertically(float ang)
2558 setHeadPitch(_HeadPitch+ang);
2559 }// rotHeadVertically //
2562 //-----------------------------------------------
2563 // setHeadPitch(double hp)
2564 //-----------------------------------------------
2565 void CUserEntity::setHeadPitch(double hp)
2567 _HeadPitch = hp;
2568 // epsilon to avoid gimbal lock
2569 clamp(_HeadPitch, -Pi/2 + 0.01, Pi/2 - 0.5);
2572 //---------------------------------------------------
2573 // slotRemoved :
2574 // To Inform about an entity removed (to remove from selection for example).
2575 // This will remove the entity from the target.
2576 // \param slot : Slot of the entity that will be removed.
2577 //---------------------------------------------------
2578 void CUserEntity::slotRemoved(const CLFECOMMON::TCLEntityId &slot)
2580 // parent call
2581 CPlayerCL::slotRemoved(slot);
2583 // reset also selection
2584 if(selection() == slot)
2585 selection(CLFECOMMON::INVALID_SLOT);
2586 }// slotRemoved //
2588 //---------------------------------------------------
2589 // selection :
2590 // Change the entity selected.
2591 // \param slot : slot now selected (CLFECOMMON::INVALID_SLOT for an empty selection).
2592 // \warning Can be different from the entity targeted (in combat mode for example).
2593 //---------------------------------------------------
2594 void CUserEntity::selection(const CLFECOMMON::TCLEntityId &slot) // virtual
2596 //allows reselection in Ring client: even if the selected slots is equal to the selection,
2597 //the client must send the messages.
2598 if ((_Selection == slot) && !ClientCfg.R2EDEnabled)
2599 return;
2601 // The selection will be the entity to watch
2602 WatchedEntitySlot = slot;
2603 disableFollow();
2605 // Send the entity selected to the server.
2606 NetMngr.pushTarget(slot);
2609 // Target the slot on client, don't wait NetWork response
2610 targetSlot(slot);
2611 _TargetSlotNoLag= slot;
2613 if (ClientCfg.R2EDEnabled)
2615 R2::getEditor().inGameSelection(slot);
2619 // Change the current selection so un color the current selection.
2620 CEntityCL *sel = EntitiesMngr.entity(_Selection);
2621 if(sel != NULL)
2622 sel->visualSelectionStop(); // Blink off == restore to normal
2624 // Set the entity selected
2625 _Selection = slot;
2627 // Update visual selection and interface
2628 if ( sel && sel->isForageSource() )
2629 sel->buildInSceneInterface(); // remove focus on previous selected source
2630 sel = EntitiesMngr.entity(_Selection);
2631 if(sel != NULL)
2633 sel->visualSelectionStart();
2634 if ( sel->isForageSource() )
2635 sel->buildInSceneInterface(); // set focus on new selected source
2639 // **** Update Target interface
2640 //get the new target slot and set it in the database
2641 CInterfaceManager *pIM = CInterfaceManager::getInstance();
2642 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:SLOT")->setValue64(slot);
2644 // Get the new target UID, and set in Database
2645 uint tgtSlot= _Selection;
2646 uint32 tgtEntityId= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
2647 CEntityCL *entity = NULL;
2648 if (tgtSlot!=CLFECOMMON::INVALID_SLOT)
2650 entity = EntitiesMngr.entity(tgtSlot);
2651 if (entity)
2652 tgtEntityId= entity->dataSetId();
2655 // Set the User Target
2656 CCDBNodeLeaf *prop = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:UID", false);
2657 if(prop)
2658 prop->setValue32(tgtEntityId);
2660 // Bar Manager. Update now the Target View (so it takes VP if data available or 0... but result is immediate)
2661 CBarManager::getInstance()->setLocalTarget(tgtEntityId);
2663 // **** Update DB for InGameMenu
2664 // clear the entries for mission option
2665 for(uint k = 0; k < NUM_MISSION_OPTIONS; ++k)
2667 CCDBNodeLeaf *missionOption = NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:TITLE", (int) k), false);
2668 if (missionOption)
2670 missionOption->setValue32(0);
2672 CCDBNodeLeaf *playerGiftNeeded = NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:PLAYER_GIFT_NEEDED", (int) k), false);
2673 if (playerGiftNeeded)
2675 playerGiftNeeded->setValue32(0);
2678 /* TODO ULU : Add RP tags */
2680 // update pvp tags
2681 if ((tgtSlot!=CLFECOMMON::INVALID_SLOT) && entity)
2683 CPlayerCL *pPlayer = dynamic_cast<CPlayerCL*>(entity);
2685 if (pPlayer)
2687 /*// Pvp Mode
2688 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:mode"));
2689 if (tagMode)
2691 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2692 tagMode->setTexture("pvp_orange.tga");
2693 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2694 tagMode->setTexture("pvp_red.tga");
2695 else
2696 tagMode->setTexture("alpha_10.tga");
2699 /*// Pvp available actions (attack, heal, both)
2700 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:actions"));
2701 if (tagMode)
2703 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2704 tag->setTexture("pvp_orange.tga");
2705 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2706 tag->setTexture("pvp_red.tga");
2707 else
2708 tag->setTexture("alpha_10.tga");
2714 // clear web page
2715 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_URL", false);
2716 if(prop) prop->setValue32(0);
2717 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_TITLE", false);
2718 if(prop) prop->setValue32(0);
2720 // clear mission ring
2721 for(uint k = 0; k < BOTCHATTYPE::MaxR2MissionEntryDatabase; ++k)
2723 prop= NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSION_RING:%d:TITLE", k), false);
2724 if(prop) prop->setValue32(0);
2727 // clear programs
2728 prop= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:PROGRAMMES", false);
2729 if(prop) prop->setValue32(0);
2730 prop= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:TARGET:CONTEXT_MENU:PROGRAMMES");
2731 if(prop) prop->setValue32(0);
2732 // increment db counter for context menu
2733 pIM->incLocalSyncActionCounter();
2735 }// selection //
2737 //---------------------------------------------------
2738 // moveToAttack :
2739 // Method to place the user to attack the target and attack.
2740 //---------------------------------------------------
2741 void CUserEntity::moveToAttack()
2743 // **** For clarity, try to launch a "default attack" found in the memory bar instead of an "anonymous" action
2744 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2745 uint32 memLine, memSlot;
2746 CEntityCL *target= EntitiesMngr.entity(selection());
2748 CInventoryManager *inv = CInventoryManager::getInstance();
2750 // auto-equip with valid weapon
2751 if( ClientCfg.AutoEquipTool )
2753 if(inv)
2755 // if no valid weapons in had -> auto-equip with last used weapons
2756 bool validWeaponInHand = true;
2757 uint32 rightHandSheet = inv->getRightHandItemSheet();
2758 if(rightHandSheet)
2760 validWeaponInHand = inv->isMeleeWeaponItem(rightHandSheet) || inv->isRangeWeaponItem(rightHandSheet);
2762 if( !validWeaponInHand )
2764 autoEquipWithLastUsedWeapons();
2767 // remember last used weapon(s)
2768 rememberWeaponsInHand();
2772 if(target && pPM->findDefaultAttack(memLine, memSlot))
2774 // launch instead a phrase execution with this phrase
2775 executeCombatWithPhrase(target, memLine, memSlot, true);
2777 // **** Else launch an anonymous "default attack"
2778 else
2780 // melee or range?
2781 bool melee = true;
2782 if(inv)
2784 uint32 rightHandSheet = inv->getRightHandItemSheet();
2785 if(rightHandSheet)
2786 melee = inv->isMeleeWeaponItem(rightHandSheet);
2789 // Move to target if melee weapon
2790 if(melee)
2791 moveTo(selection(), 2.0, CUserEntity::Attack);
2792 // Just attack if range weapon.
2793 else
2794 attack();
2796 }// moveToAttack //
2798 //---------------------------------------------------
2799 // attack :
2800 // Method to attack the target.
2801 //---------------------------------------------------
2802 void CUserEntity::attack()
2804 // execute the default attack
2805 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2806 pPM->executeDefaultAttack();
2808 bool melee = true;
2809 CInventoryManager *inv = CInventoryManager::getInstance();
2810 if(inv)
2812 uint32 rightHandSheet = inv->getRightHandItemSheet();
2813 if(rightHandSheet)
2814 melee = inv->isMeleeWeaponItem(rightHandSheet);
2817 // If option ON, follow when attacking.
2818 if(ClientCfg.FollowOnAtk)
2820 // Follow only if attacking with a melee weapon
2821 if(melee)
2822 // enable, but don't reset camera rotation
2823 enableFollow(false);
2825 }// attack //
2827 //---------------------------------------------------
2828 // attack :
2829 // Method to attack the target, with a special phrase
2830 //---------------------------------------------------
2831 void CUserEntity::attackWithPhrase()
2833 if( !canEngageCombat() )
2834 return;
2836 CSPhraseManager *pPM= CSPhraseManager::getInstance();
2838 // validate the execution on server
2839 pPM->sendExecuteToServer(_MoveToPhraseMemoryLine, _MoveToPhraseMemorySlot, _MoveToPhraseCyclic);
2841 // If the Attack is a "Next", and not a cycle, do a default attack
2842 if(!_MoveToPhraseCyclic)
2843 pPM->executeDefaultAttack();
2845 // If option ON, follow when attacking.
2846 if(ClientCfg.FollowOnAtk)
2848 bool melee = true;
2849 CInventoryManager *inv = CInventoryManager::getInstance();
2850 if(inv)
2852 uint32 rightHandSheet = inv->getRightHandItemSheet();
2853 if(rightHandSheet)
2854 melee = inv->isMeleeWeaponItem(rightHandSheet);
2856 // Follow only if attacking with a melee weapon
2857 if(melee)
2858 // enable, but don't reset camera rotation
2859 enableFollow(false);
2862 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
2863 _MoveToAction= CUserEntity::None;
2866 //-----------------------------------------------
2867 // assist :
2868 // your current target become the target of your current one.
2869 //-----------------------------------------------
2870 void CUserEntity::assist()
2872 assist(targetSlot());
2873 }// assist //
2875 //-----------------------------------------------
2877 void CUserEntity::assist(uint slot)
2879 // Check the current target
2880 if(slot == CLFECOMMON::INVALID_SLOT || slot == _Slot)
2881 return;
2882 // Check the target
2883 CEntityCL *target = EntitiesMngr.entity(slot);
2884 if(target == 0)
2885 return;
2886 // Check the new slot.
2887 CLFECOMMON::TCLEntityId newSlot = target->targetSlot();
2888 if(newSlot == CLFECOMMON::INVALID_SLOT || newSlot == _Slot)
2889 return;
2890 // Select the new target.
2891 selection(newSlot);
2894 //---------------------------------------------------
2895 // disengage :
2896 // Method to disengage the target.
2897 //---------------------------------------------------
2898 void CUserEntity::disengage()
2900 // Set the database in local.
2901 if(ClientCfg.Local)
2903 IngameDbMngr.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_MODE), MBEHAV::NORMAL); // Mode
2904 IngameDbMngr.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_TARGET_ID), _Selection); // Target
2905 UserEntity->updateVisualProperty(0, CLFECOMMON::PROPERTY_MODE);
2906 UserEntity->updateVisualProperty(0, CLFECOMMON::PROPERTY_TARGET_ID);
2907 return;
2910 // Disengage MSG.
2911 const string msgName = "COMBAT:DISENGAGE";
2912 CBitMemStream out;
2913 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2914 NetMngr.push(out);
2915 else
2916 nlwarning("UE::disengage: unknown message named '%s'.", msgName.c_str());
2918 // Change the current mode.
2919 mode(MBEHAV::NORMAL);
2920 }// disengage //
2922 //-----------------------------------------------
2923 // sit :
2924 // Ask for the client to sit/stand ('true' to sit).
2925 //-----------------------------------------------
2926 bool CUserEntity::sit(bool s)
2928 bool ok= false;
2930 // SIT
2931 if(s)
2933 if(canSit() == true)
2935 // disable afk mode
2936 setAFK(false);
2938 // Sit MSG.
2939 if(mode(MBEHAV::SIT))
2941 // autowalk disabled
2942 UserControls.autowalkState(false);
2944 static const string msgName = "COMMAND:SIT";
2945 CBitMemStream out;
2946 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2948 out.serial(s);
2949 NetMngr.push(out);
2951 else
2952 nlwarning("UE:sit: unknown message named '%s'.", msgName.c_str());
2954 // mode changed
2955 ok= true;
2957 // display sit msg
2958 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2959 string msg = CI18N::get("msgUserIsSitting");
2960 string cat = getStringCategory(msg, msg);
2961 pIM->displaySystemInfo(msg, cat);
2965 // STAND
2966 else
2968 if(_Mode == MBEHAV::SIT)
2970 if(mode(MBEHAV::NORMAL))
2972 static const string msgName = "COMMAND:SIT";
2973 CBitMemStream out;
2974 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
2976 out.serial(s);
2977 NetMngr.push(out);
2979 else
2980 nlwarning("UE:sit: unknown message named '%s'.", msgName.c_str());
2982 // mode changed
2983 ok= true;
2985 // display stand msg
2986 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2987 string msg = CI18N::get("msgUserIsStanding");
2988 string cat = getStringCategory(msg, msg);
2989 pIM->displaySystemInfo(msg, cat);
2994 // if mode changed, Write to the UI database, to update views
2995 if(ok)
2997 CInterfaceManager *pIM= CInterfaceManager::getInstance();
2998 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_STAND", false);
2999 if(node)
3000 node->setValue32(_Mode != MBEHAV::SIT);
3003 // mode changed
3004 return ok;
3005 }// sit //
3007 //-----------------------------------------------
3008 // canSit :
3009 // Return true if the user can sit.
3010 //-----------------------------------------------
3011 bool CUserEntity::canSit() const
3013 // If the user is not already sitting or is on a mount
3014 if(!isSit() && (!isRiding()) && !isDead() && !isSwimming())
3016 return true;
3018 else
3019 return false;
3020 }// canSit //
3022 //-----------------------------------------------
3023 // setAFK
3024 //-----------------------------------------------
3025 void CUserEntity::setAFK(bool b, string afkTxt)
3027 if( isAFK() == b ) return;
3029 if (b)
3031 if( isDead() || isRiding() || moveTo() || follow() )
3032 return;
3034 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3035 //sint64 start = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TSTART")->getValue64();
3036 //sint64 end = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TEND")->getValue64();
3037 //sint64 type = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64();
3038 //sint64 num = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_NUMBER")->getValue64();
3039 if( pIM )
3041 if( NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64() != 0 )
3042 return;
3045 if( !isSit() && !isSwimming() )
3047 if( !mode(MBEHAV::REST) )
3048 return;
3051 else
3053 if( !isSit() && !isSwimming() )
3055 if (isDead())
3056 return;
3057 else
3058 mode(MBEHAV::NORMAL);
3062 // send afk state
3063 static const string msgName = "COMMAND:AFK";
3064 CBitMemStream out;
3065 if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
3067 out.serial(b);
3068 NetMngr.push(out);
3070 else
3071 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgName.c_str());
3073 // custom afk txt
3074 ucstring ucstr; // TODO: UTF-8 (serial)
3075 ucstr.fromUtf8( afkTxt );
3076 CBitMemStream outTxt;
3077 static const string msgNameTxt = "STRING:AFK_TXT";
3078 if( GenericMsgHeaderMngr.pushNameToStream(msgNameTxt,outTxt) )
3080 outTxt.serial( ucstr );
3081 NetMngr.push( outTxt );
3083 else
3085 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgNameTxt.c_str());
3089 }// setAFK //
3091 //-----------------------------------------------
3092 // rollDice
3093 //-----------------------------------------------
3094 void CUserEntity::rollDice(sint16 min, sint16 max, bool local)
3096 if (local)
3098 // no need to broadcast over network here
3099 static NLMISC::CRandom* dice = (NLMISC::CRandom*)NULL;
3100 if (!dice)
3102 dice = new NLMISC::CRandom;
3103 dice->srand(CTickEventHandler::getGameCycle());
3105 sint16 roll = min + (sint16)dice->rand(max-min);
3107 string msg = CI18N::get("msgRollDiceLocal");
3108 strFindReplace(msg, "%min", toString(min));
3109 strFindReplace(msg, "%max", toString(max));
3110 strFindReplace(msg, "%roll", toString(roll));
3112 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3114 pIM->displaySystemInfo(msg, getStringCategory(msg, msg));
3115 return;
3117 const string msgName = "COMMAND:RANDOM";
3118 CBitMemStream out;
3119 if (GenericMsgHeaderMngr.pushNameToStream(msgName, out))
3121 out.serial(min);
3122 out.serial(max);
3123 NetMngr.push(out);
3125 else
3126 nlwarning("CUserEntity:rollDice: unknown message named '%s'.", msgName.c_str());
3127 }// rollDice //
3129 //---------------------------------------------------
3130 // canEngageCombat :
3131 // return true if user can engage melee combat, else return false and display system msg
3132 //---------------------------------------------------
3133 bool CUserEntity::canEngageCombat()
3135 if( isSit() )
3137 // display "you can't fight while sitting" message)
3138 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3139 string msg = CI18N::get("msgCantFightSit");
3140 string cat = getStringCategory(msg, msg);
3141 pIM->displaySystemInfo(msg, cat);
3143 return false;
3146 if( isSwimming() )
3148 // display "you can't fight while swiming" message)
3149 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3150 string msg = CI18N::get("msgCantFightSwim");
3151 string cat = getStringCategory(msg, msg);
3152 pIM->displaySystemInfo(msg, cat);
3154 return false;
3157 if ( isRiding() )
3159 // display "you can't fight while swimming" message)
3160 CInterfaceManager *pIM= CInterfaceManager::getInstance();
3161 string msg = CI18N::get("msgCantFightRide");
3162 string cat = getStringCategory(msg, msg);
3163 pIM->displaySystemInfo(msg, cat);
3165 return false;
3168 return true;
3169 } // canEngageCombat //
3172 //---------------------------------------------------
3173 // viewMode :
3174 // Change the View (First/Third Person View).
3175 //---------------------------------------------------
3176 void CUserEntity::viewMode(CUserEntity::TView viewMode, bool changeView)
3178 switch(viewMode)
3180 // First Person View
3181 case FirstPV:
3182 if(changeView)
3183 ClientCfg.FPV = true;
3184 if(_Mount != CLFECOMMON::INVALID_SLOT)
3186 CEntityCL *mount = EntitiesMngr.entity(_Mount);
3187 if(mount)
3188 mount->displayable(false);
3190 _HiddenMount = _Mount;
3192 // Change Controls.
3193 if( isRiding() )
3195 bool autoWalk = UserControls.autowalkState();
3196 UserControls.mode(CUserControls::MountMode);
3197 if( autoWalk )
3198 UserControls.autowalkState( true );
3200 else
3201 UserControls.mode(CUserControls::InterfaceMode);
3202 break;
3204 // Third Person View
3205 case ThirdPV:
3206 if(changeView)
3207 ClientCfg.FPV = false;
3209 if(_HiddenMount != CLFECOMMON::INVALID_SLOT)
3211 CEntityCL *mount = EntitiesMngr.entity(_HiddenMount);
3212 if(mount)
3213 mount->displayable(true);
3215 _HiddenMount = CLFECOMMON::INVALID_SLOT;
3217 // Change Controls.
3218 UserControls.mode(CUserControls::ThirdMode);
3219 break;
3221 // Unknown
3222 default:
3223 nlwarning("UE:viewMode: Unknown View Asked '%d'.", (sint)viewMode);
3224 return;
3227 // Change the current View like asked.
3228 _ViewMode = viewMode;
3230 // disable or enable shadowing
3231 updateCastShadowMap();
3232 }// viewMode //
3234 //-----------------------------------------------
3235 // toggleCamera :
3236 // Toggle Camera (First/Third Person)
3237 //-----------------------------------------------
3238 void CUserEntity::toggleCamera()
3240 // You cannot change the camera view when dead.
3241 if(isDead())
3242 return;
3243 // Only if not inside a building.
3244 if(!UserEntity->forceIndoorFPV())
3246 // Leave the 1st Person Mode -> Enter the 3rd Person View Mode
3247 if (UserEntity->viewMode() == CUserEntity::FirstPV)
3248 UserEntity->viewMode(CUserEntity::ThirdPV);
3249 // Leave the 3rd Person Mode -> Enter the 1st Person View Mode
3250 else
3251 UserEntity->viewMode(CUserEntity::FirstPV);
3253 }// toggleCamera //
3255 //-----------------------------------------------
3256 // forceCameraFirstPerson :
3257 // Force Camera to First Person View
3258 //-----------------------------------------------
3259 void CUserEntity::forceCameraFirstPerson()
3261 // You cannot change the camera view when dead.
3262 if(isDead())
3263 return;
3264 // Only if not inside a building.
3265 if(!UserEntity->forceIndoorFPV())
3267 if (UserEntity->viewMode() != CUserEntity::FirstPV)
3268 //Enter the 1st Person View Mode
3269 UserEntity->viewMode(CUserEntity::FirstPV);
3271 }// forceCameraFirstPerson //
3273 //---------------------------------------------------
3274 // getScale :
3275 // Return the entity scale. (return 1.0 if there is any problem).
3276 // \todo GUIGUI : do we have to take care of the user's race kwnowing it can favour him ?
3277 //---------------------------------------------------
3278 float CUserEntity::getScale() const // virtual
3280 // Default Scale.
3281 return 1.0f;
3282 }// getScale //
3284 //---------------------------------------------------
3285 // removeCheckPrimitive :
3286 // Remove the check primitive
3287 //---------------------------------------------------
3288 void CUserEntity::removeCheckPrimitive()
3290 if(PACS && _CheckPrimitive)
3291 PACS->removePrimitive(_CheckPrimitive);
3292 _CheckPrimitive = 0;
3295 //---------------------------------------------------
3296 // removePrimitive :
3297 // Remove the primitive
3298 //---------------------------------------------------
3299 void CUserEntity::removePrimitive() // virtual (will not be called by ~CEntityCL())
3301 // Remove the Primitive used for check
3302 removeCheckPrimitive();
3304 // Remove the other primitive
3305 CPlayerCL::removePrimitive();
3306 }// removePrimitive //
3308 //---------------------------------------------------
3309 // computePrimitive :
3310 // Create a primitive for the entity.
3311 //---------------------------------------------------
3312 void CUserEntity::computePrimitive() // virtual
3314 // Initialize the primitive.
3315 if(initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::Slide, (UMovePrimitive::TTrigger)(UMovePrimitive::OverlapTrigger | UMovePrimitive::EnterTrigger), MaskColPlayer, MaskColPlayer | MaskColNpc | MaskColDoor))
3316 _Primitive->insertInWorldImage(dynamicWI);
3317 // Set the position.
3318 pacsPos(pos());
3319 }// computePrimitive //
3322 //---------------------------------------------------
3323 // isBusy :
3324 // Return if the user is already busy (combat/bo chat/loot/ etc.).
3325 //---------------------------------------------------
3326 bool CUserEntity::isBusy() const
3328 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3329 // Check Trade.
3331 // TODO : put the right DB entry !
3333 CCDBNodeLeaf *nod = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:INVENTORY:EXCHANGE:BEGUN", false);
3334 if(nod)
3336 if(nod->getValueBool())
3337 return true;
3339 // else
3340 // nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:EXCHANGE:BEGUN'.");
3343 // Check Loot
3344 static const uint nbSlot = 4;
3345 uint i;
3346 for(i=0; i<nbSlot; ++i)
3348 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::pickup, i), false);
3349 if(nod)
3351 if(nod->getValue32() != 0)
3352 return true;
3354 else
3355 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::pickup, i);
3358 // Check Harvest
3359 for(i=0; i<nbSlot; ++i)
3361 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::harvest, i), false);
3362 if(nod)
3364 if(nod->getValue32() != 0)
3365 return true;
3367 else
3368 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::harvest, i);
3372 // Check Bot chat.
3373 CBotChatPage * currPage = CBotChatManager::getInstance()->getCurrPage();
3374 if( currPage!= NULL )
3376 return true;
3379 // Not Busy
3380 return false;
3381 }// isBusy //
3384 //---------------------------------------------------
3385 // updateVisualDisplay :
3386 // Show/Hide all or parts of the user body.
3387 // todo GUIGUI : it's bad for the _Face to be a separated instance
3388 //---------------------------------------------------
3389 void CUserEntity::updateVisualDisplay()
3391 // We need a skeleton.
3392 if(_Skeleton.empty())
3393 return;
3395 // 1st person View
3396 if(UserControls.isInternalView() || View.forceFirstPersonView())
3398 // Hide the mount
3399 if (_Mount != CLFECOMMON::INVALID_SLOT)
3401 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
3402 if(mount)
3403 mount->displayable(false);
3405 _HiddenMount = _Mount;
3407 else if (_HiddenMount != CLFECOMMON::INVALID_SLOT)
3409 // not on mount anymore, but still in FPV
3410 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_HiddenMount));
3411 if(mount)
3412 mount->displayable(true);
3414 _HiddenMount = CLFECOMMON::INVALID_SLOT;
3417 // Hide all user body parts.
3418 for(uint i=0; i<_Instances.size(); ++i)
3419 if(!_Instances[i].Current.empty())
3421 _Instances[i].Current.hide();
3422 _Instances[i].hideStaticFXs();
3424 // Hide the face
3425 if(!_Face.Current.empty())
3426 _Face.Current.hide();
3428 // We want to display weapons in 1st person view when attacking.
3429 if(modeWithHiddenItems() == false)
3431 if( _Mode == MBEHAV::COMBAT_FLOAT || _Mode == MBEHAV::COMBAT )
3433 if( _ObjectsVisible )
3435 // Show just Few parts
3436 if(!_Instances[SLOTTYPE::HANDS_SLOT].Current.empty())
3438 _Instances[SLOTTYPE::HANDS_SLOT].Current.show();
3439 _Instances[SLOTTYPE::HANDS_SLOT].showStaticFXs();
3441 if(!_Instances[SLOTTYPE::ARMS_SLOT].Current.empty())
3443 _Instances[SLOTTYPE::ARMS_SLOT].Current.show();
3444 _Instances[SLOTTYPE::ARMS_SLOT].showStaticFXs();
3446 if(!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current.empty())
3448 _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current.show();
3449 _Instances[SLOTTYPE::RIGHT_HAND_SLOT].showStaticFXs();
3451 if(!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Current.empty())
3453 _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current.show();
3454 _Instances[SLOTTYPE::LEFT_HAND_SLOT].showStaticFXs();
3460 else
3462 // Show the mount
3463 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
3464 if(mount)
3466 mount->displayable(true);
3468 showOrHideBodyParts( objectsVisible() );
3471 // Show the face
3473 if(!_Face.Current.empty())
3474 _Face.Current.show();
3477 }// updateVisualDisplay //
3479 void CUserEntity::lightOn()
3481 _LightOn = false;
3482 light();
3485 //---------------------------------------------------
3486 // light:
3487 // Show/Hide the user light.
3488 //---------------------------------------------------
3489 void CUserEntity::light()
3491 // Create the light
3492 if(_Light.empty())
3494 // Check there is a skeleton and a bone to stick the light before to create it.
3495 if(!_Skeleton.empty() && _NameBoneId!=-1)
3497 _Light = Scene->createPointLight();
3498 if(!_Light.empty())
3500 // front of the player
3501 _Light.setPos(0.f,0.3f,0.f);
3502 // Setup the light
3503 _Light.setupAttenuation(12.0f, 20.0f);
3504 // Attach the light
3505 _Skeleton.stickObject(_Light, _NameBoneId);
3506 // The player light is the only one who can interact with Lightmapped objects
3507 _Light.setInfluenceLightMap(true);
3509 // TestYoyo
3511 NL3D::UInstance inst;
3512 inst= Scene->createInstance("box.shape");
3513 if(!inst.empty())
3515 inst.setScale(0.2f, 0.2f, 0.2f);
3516 inst.parent(_Light);
3521 else
3522 nlwarning("UE:light: there is no skeleton or Name Bone to stick the light.");
3524 // Turn On/Off the Light
3525 _LightOn = !_LightOn;
3526 if(!_Light.empty())
3528 if(_LightOn)
3529 _Light.show();
3530 else
3531 _Light.hide();
3533 }// light //
3535 //---------------------------------------------------
3536 // CSpeedFactor::init :
3537 // Initialize the Observer for the Speed Factor.
3538 //---------------------------------------------------
3539 void CUserEntity::CSpeedFactor::init()
3541 _Value = 1.0f; // Default speed factor is 1.
3542 _ServerFactor = 1.0f;
3543 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3544 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3545 if(pNodeLeaf)
3547 /* if(!pNodeLeaf->addToLeaves(this))
3548 nlwarning("UE:SP:init: cannot add the observer");*/
3549 ICDBNode::CTextId textId;
3550 pNodeLeaf->addObserver(this, textId);
3551 if ( pNodeLeaf->getValue64() != 0 )
3552 _Value = ((float)pNodeLeaf->getValue64())/100.0f; // may have been received before
3554 else
3555 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3556 }// CSpeedFactor::init //
3558 //---------------------------------------------------
3559 // CMountHunger::init :
3560 //---------------------------------------------------
3561 void CUserEntity::CMountHunger::init()
3564 //---------------------------------------------------
3565 // CMountSpeeds::init :
3566 //---------------------------------------------------
3567 void CUserEntity::CMountSpeeds::init()
3569 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3570 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3571 BOMB_IF( ! pNodeLeaf, "MOUNT_WALK_SPEED not found", return );
3572 if(pNodeLeaf)
3574 ICDBNode::CTextId textId;
3575 pNodeLeaf->addObserver(this, textId);
3576 _WalkSpeed = ((float)pNodeLeaf->getValue32()) / 1000.0f; // may have been received before
3578 pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3579 BOMB_IF( ! pNodeLeaf, "MOUNT_RUN_SPEED not found", return );
3580 if(pNodeLeaf)
3582 ICDBNode::CTextId textId;
3583 pNodeLeaf->addObserver(this, textId);
3584 _RunSpeed = ((float)pNodeLeaf->getValue32()) / 1000.0f; // may have been received before
3588 //---------------------------------------------------
3590 void CUserEntity::CSpeedFactor::release()
3592 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3593 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3594 if(pNodeLeaf)
3596 /* if(!pNodeLeaf->addToLeaves(this))
3597 nlwarning("UE:SP:init: cannot add the observer");*/
3598 ICDBNode::CTextId textId;
3599 pNodeLeaf->removeObserver(this, textId);
3601 else
3602 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3603 }// CSpeedFactor::init //
3605 void CUserEntity::CMountHunger::release()
3608 void CUserEntity::CMountSpeeds::release()
3610 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3611 CCDBNodeLeaf *pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3612 BOMB_IF( ! pNodeLeaf, "MOUNT_WALK_SPEED not found", return );
3613 if(pNodeLeaf)
3615 ICDBNode::CTextId textId;
3616 pNodeLeaf->removeObserver(this, textId);
3618 pNodeLeaf = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3619 BOMB_IF( ! pNodeLeaf, "MOUNT_RUN_SPEED not found", return );
3620 if(pNodeLeaf)
3622 ICDBNode::CTextId textId;
3623 pNodeLeaf->removeObserver(this, textId);
3628 //---------------------------------------------------
3629 // CSpeedFactor::update :
3630 // Callback called to update the speed factor.
3631 //---------------------------------------------------
3632 void CUserEntity::CSpeedFactor::update(ICDBNode *node) // virtual
3634 CCDBNodeLeaf *leaf = safe_cast<CCDBNodeLeaf *>(node);
3635 _Value = ((float)leaf->getValue64())/100.0f;
3636 //nlinfo("SpeedFactor changed to %f / %" NL_I64 "u", _Value, leaf->getValue64());
3638 // clamp the value (2.0 is the egg item or the level 6 speed up power up, nothing should be faster)
3639 // commented because ring editor speed is in fact faster
3640 //if(_Value > 2.0f)
3642 //nlwarning("HACK: you try to change the speed factor to %f", _Value);
3643 //nlstop;
3644 //_Value = 2.0f;
3646 }// CSpeedFactor::update //
3650 * Return true if the mount can run. Precondition: UserEntity->isRiding().
3652 bool CUserEntity::CMountHunger::canRun() const
3654 CEntityCL *mountEntity = UserEntity->getMountEntity();
3655 if ( ! mountEntity )
3656 return false;
3658 // Find the mount's db leaf and check hunger
3659 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3660 CCDBNodeBranch *animalsNode = safe_cast<CCDBNodeBranch*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3661 BOMB_IF( ! animalsNode, "! animalsNode", return false; );
3662 uint nbAnimals = (uint)animalsNode->getNbNodes();
3663 for ( uint i=0; i!=nbAnimals; ++i )
3665 ICDBNode *beastNode = animalsNode->getNode( i );
3666 CCDBNodeLeaf *uidLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "UID" ) ));
3667 if ( ((CLFECOMMON::TClientDataSetIndex)uidLeaf->getValue32()) == mountEntity->dataSetId() )
3669 CCDBNodeLeaf *hungerLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "HUNGER" ) ));
3670 return (hungerLeaf->getValue32() != (sint)ANIMAL_TYPE::DbHungryValue);
3673 return true;
3678 //---------------------------------------------------
3679 // CMountSpeeds::update :
3680 // Callback called to update the mount speed.
3681 //---------------------------------------------------
3682 void CUserEntity::CMountSpeeds::update(ICDBNode * /* node */) // virtual
3684 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3685 _WalkSpeed = ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_WALK_SPEED")->getValue32())) / 1000.0f;
3686 _RunSpeed = ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_RUN_SPEED")->getValue32())) / 1000.0f;
3691 * Return the mount entity if the user is riding, otherwise NULL
3693 CEntityCL* CUserEntity::getMountEntity()
3695 if ( _Mount < EntitiesMngr.entities().size() )
3697 return EntitiesMngr.entities()[_Mount];
3699 return NULL;
3703 * Return the DB entry for the specified user's animal (NULL if not found)
3705 CCDBNodeBranch *CUserEntity::getBeastDBEntry( CLFECOMMON::TClientDataSetIndex uid )
3707 // Find animal entry corresponding to datasetId
3708 CInterfaceManager *pIM = CInterfaceManager::getInstance();
3709 CCDBNodeBranch *animalsNode = safe_cast<CCDBNodeBranch*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3710 BOMB_IF( ! animalsNode, "! animalsNode", return NULL );
3711 uint nbAnimals = (uint)animalsNode->getNbNodes();
3712 for ( uint i=0; i!=nbAnimals; ++i )
3714 ICDBNode *beastNode = animalsNode->getNode( i );
3715 CCDBNodeLeaf *pNodeLeaf = safe_cast<CCDBNodeLeaf*>(beastNode->getNode( ICDBNode::CTextId( "UID" ) ));
3716 if ( pNodeLeaf && (pNodeLeaf->getValue32() == (sint32)uid) )
3717 return (CCDBNodeBranch*)beastNode;
3719 return NULL;
3723 //---------------------------------------------------
3724 // displayDebug :
3725 // Display Debug Information.
3726 //---------------------------------------------------
3727 void CUserEntity::displayDebug(float x, float &y, float lineStep) // virtual
3729 CPlayerCL::displayDebug(x, y, lineStep);
3730 }// displayDebug //
3732 //---------------------------------------------------
3733 // displayModifiers :
3734 // Display dmg/heal numbers above the head.
3735 //---------------------------------------------------
3736 void CUserEntity::displayModifiers() // virtual
3738 if(!UserControls.isInternalView())
3739 CPlayerCL::displayModifiers();
3740 }// displayModifiers //
3742 //---------------------------------------------------
3743 // isVisible :
3744 // Return 'true' is the entity is displayed.
3745 //---------------------------------------------------
3746 bool CUserEntity::isVisible() const // virtual
3748 return !UserControls.isInternalView();
3749 }// isVisible //
3756 //---------------------------------------------------
3757 // readWrite :
3758 // Read/Write Variables from/to the stream.
3759 //---------------------------------------------------
3760 void CUserEntity::readWrite(NLMISC::IStream &f)
3762 CPlayerCL::readWrite(f);
3764 // PROTECTED
3765 f.serial(_SpeedFactor);
3766 f.serial(_FrontVelocity);
3767 f.serial(_LateralVelocity);
3768 CVector dummyHead;
3769 f.serial(dummyHead);
3770 f.serial(_HeadPitch);
3771 f.serial(_EyesHeight);
3772 f.serial(_Run);
3773 f.serial(_WalkVelocity);
3774 f.serial(_RunVelocity);
3775 f.serial(_CurrentVelocity);
3776 f.serial(_Selection);
3777 f.serial(_Trader);
3778 f.serial(_Interlocutor);
3779 f.serial(_Selectable);
3781 // PRIVATE
3782 f.serial(_OnMount);
3783 f.serial(_AnimAttackOn);
3784 // f.serialEnum(_ViewMode);
3785 }// readWrite //
3787 //---------------------------------------------------
3788 // load :
3789 // To call after a read from a stream to re-initialize the entity.
3790 //---------------------------------------------------
3791 void CUserEntity::load() // virtual
3793 CInterfaceManager *IM = CInterfaceManager::getInstance ();
3794 // Insert the user into PACS at his saved position
3795 pacsPos(pos());
3797 // update
3798 if(!_WaitForAppearance)
3800 // Visual properties A
3801 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
3802 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
3804 }// load //
3807 //---------------------------------------------------
3808 void CUserEntity::CInvisibleObserver::update(ICDBNode* node)
3810 UserEntity->buildInSceneInterface();
3813 //---------------------------------------------------
3814 void CUserEntity::CSkillPointsObserver::update(ICDBNode* node )
3816 if (FarTP.isFarTPInProgress() || IngameDbMngr.initInProgress()) // prevent from displaying at the beginning of a FarTP (due to RESET_BANK or CDB resetData())
3817 return;
3819 CInterfaceManager *pIM = CInterfaceManager::getInstance ();
3820 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
3821 if (leaf)
3823 sint32 oldValue = leaf->getOldValue32();
3824 if (oldValue != 0)
3826 sint delta = leaf->getValue32()-oldValue;
3827 string deltaStr = toString("%+d", delta);
3829 // get the sp title
3830 const string &spTitle = CI18N::get(toString("uiSkillPointsBold%d",SpType));
3832 // run the popup
3833 CAHManager::getInstance()->runActionHandler("message_popup", NULL, "text1="+deltaStr+"|text0="+spTitle);
3835 // Context help
3836 contextHelp ("skill_point");
3842 //---------------------------------------------------
3843 // CFameObserver::update
3844 //---------------------------------------------------
3845 void CUserEntity::CFameObserver::update(ICDBNode* node )
3847 CSkillManager *pSM = CSkillManager::getInstance();
3848 CCDBNodeLeaf *leaf = dynamic_cast<CCDBNodeLeaf*>(node);
3849 if (leaf)
3851 sint32 fameValue = leaf->getValue32();
3852 pSM->tryToUnblockTitleFromMinFames( FactionIndex, fameValue );
3853 pSM->tryToUnblockTitleFromMaxFames( FactionIndex, fameValue );
3858 //---------------------------------------------------
3859 void CUserEntity::makeTransparent(bool t)
3861 CPlayerCL::makeTransparent(t);
3863 uint32 opaMin= getOpacityMin();
3864 uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor));
3866 getFace()->makeInstanceTransparent(opacity, (uint8)opaMin);
3867 }// makeTransparent //
3869 //---------------------------------------------------
3870 void CUserEntity::setDiffuse(bool onOff, NLMISC::CRGBA diffuse)
3872 CPlayerCL::setDiffuse(onOff, diffuse);
3873 getFace()->setDiffuse(onOff, diffuse);
3879 // Helper for CUserEntity::extractRM()
3880 bool findExtractionActionInMemory( CSPhraseManager *pm, CSBrickManager *bm, uint memoryLine, uint& index )
3882 uint x;
3883 for ( x=0; x!=PHRASE_MAX_MEMORY_SLOT; ++x )
3885 uint32 phraseSlot = pm->getMemorizedPhrase( memoryLine, x );
3886 const CSPhraseCom& phraseCom = pm->getPhrase( phraseSlot );
3887 if ( ! phraseCom.empty() )
3889 CSBrickSheet *brickSheet = bm->getBrick( phraseCom.Bricks[0] );
3890 if ( brickSheet->isForageExtraction() && (!brickSheet->MandatoryFamilies.empty()) ) // assumes care root bricks have not mandatories
3892 index = x;
3893 return true;
3897 return false;
3900 //---------------------------------------------------
3901 void CUserEntity::extractRM()
3903 CSPhraseManager *pm = CSPhraseManager::getInstance();
3904 uint index;
3905 uint memoryLine;
3906 bool autoFindPhrase = (_MoveToPhraseMemoryLine == std::numeric_limits<uint>::max());
3907 if ( ! autoFindPhrase )
3909 // Use clicked phrase
3910 memoryLine = _MoveToPhraseMemoryLine;
3911 index = _MoveToPhraseMemorySlot;
3913 else
3915 // Find the first extraction phrase in the memory bar
3916 CSBrickManager *bm = CSBrickManager::getInstance();
3917 memoryLine = pm->getSelectedMemoryLineDB();
3918 if ( ! findExtractionActionInMemory( pm, bm, memoryLine, index ) )
3920 // Search in other memory bar lines (because the auto-equip does not set the current line at once)
3921 memoryLine = std::numeric_limits<uint>::max();
3922 uint nbLines = pm->getNbMemoryLines();
3923 for ( uint j=0; j!=nbLines; ++j )
3925 if ( j == memoryLine )
3926 continue;
3927 if ( findExtractionActionInMemory( pm, bm, j, index ) )
3929 memoryLine = j;
3930 break;
3936 if ( memoryLine != std::numeric_limits<uint>::max() )
3938 // Open the forage (but not for care actions). Necessary for the case of redoing an extraction after a Drop All on the same source.
3939 uint32 phraseId = pm->getMemorizedPhrase( memoryLine, index );
3940 if ( phraseId != 0 )
3942 const CSPhraseCom& phraseCom = pm->getPhrase( phraseId );
3943 if ( ! phraseCom.empty() )
3945 CSBrickSheet *rootBrick = CSBrickManager::getInstance()->getBrick( phraseCom.Bricks[0] );
3946 if ( rootBrick )
3948 if ( rootBrick->IndexInFamily == 1 ) // only extracting actions
3949 CTempInvManager::getInstance()->open( TEMP_INV_MODE::Forage );
3954 // Cast the extraction. if autoFindPhrase, clientExecute() not already called.
3955 if ( autoFindPhrase )
3957 // decide now if cyclic or not
3958 _MoveToPhraseCyclic= true;
3959 if(pm->avoidCyclicForPhrase(pm->getMemorizedPhrase(memoryLine, index)))
3960 _MoveToPhraseCyclic= false;
3962 // execute on client now
3963 pm->clientExecute( memoryLine, index, _MoveToPhraseCyclic);
3966 // execute on server
3967 pm->sendExecuteToServer( memoryLine, index, _MoveToPhraseCyclic );
3969 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
3970 _MoveToAction= CUserEntity::None;
3972 else
3974 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiExtractionPhraseMissing"), "CHK" );
3975 return;
3980 // ***************************************************************************
3981 bool CUserEntity::canCastShadowMap() const
3983 if(!CCharacterCL::canCastShadowMap())
3984 return false;
3986 // don't cast shadow in first person, but in death mode (third person actually...)
3987 return viewMode() != FirstPV || UserControls.mode() == CUserControls::DeathMode;
3991 // ***************************************************************************
3992 void CUserEntity::forceLookEntity(const NLMISC::CVectorD &dir2targIn, bool updateHeadPitch, bool /* start */)
3994 CVectorD dir2targ= dir2targIn;
3995 float frontYawBefore = 0.f;
3996 float frontYawAfter;
3998 // Third person: bkup current yaw
3999 if(viewMode()==ThirdPV)
4001 frontYawBefore = frontYaw();
4005 // **** Look at the entity
4006 dir2targ.normalize();
4007 front(dir2targ, false, false);
4010 // **** FirstPerson
4011 if(viewMode() == FirstPV)
4013 if(updateHeadPitch && _FollowForceHeadPitch)
4015 // rotate the head to the target
4016 CEntityCL *target = EntitiesMngr.entity(targetSlot());
4017 if(target)
4019 // Both Z must be correct
4020 snapToGround();
4021 target->snapToGround();
4023 // don't update to the real head position each frame (else jitter too much cause of target anim)
4024 CVector targetPos= target->pos() + CVector(0,0,_FollowHeadOffset);
4026 // then look at this target
4027 CVector dirToTarget = targetPos - (pos()+CVector(0,0, UserEntity->eyesHeight()));
4028 if((dirToTarget.x != 0.0f) || (dirToTarget.y!=0.0f))
4030 dirToTarget.normalize();
4031 setHeadPitch(atan2(dirToTarget.z, sqrt(sqr(dirToTarget.x)+sqr(dirToTarget.y))));
4034 // TestYoyo
4035 /*if(ClientCfg.Fly!=0.f)
4037 nlinfo("Uy: %.3f. Hp: %.3f. UPos:(%.3f,%.3f,%.3f). TPos:(%.3f,%.3f,%.3f)",
4038 UserEntity->frontYaw(), UserEntity->getHeadPitch(), pos().x, pos().y, pos().z,
4039 targetPos.x, targetPos.y, targetPos.z);
4040 static float uy=0.f;
4041 static float hp=0.f;
4042 if( fabs(fmod(UserEntity->frontYaw()-uy, 2*Pi))>ClientCfg.Fly ||
4043 fabs(fmod(UserEntity->getHeadPitch()-hp, 2*Pi))>ClientCfg.Fly )
4045 nlinfo("YOYOBREAK: ^^^^^^^^^^");
4047 uy=UserEntity->frontYaw();
4048 hp=UserEntity->getHeadPitch();
4053 // **** Third person
4054 else if(viewMode()==ThirdPV)
4056 // keep the current effective yaw. ONLY if no SmoothResetCameraYaw forced
4057 if(!UserControls.isResetSmoothCDYForced())
4059 frontYawAfter = frontYaw();
4060 float deltaYaw= frontYawAfter - frontYawBefore;
4062 // compensate rotation (NB: it stops also any SmoothReset)
4063 UserControls.appendCameraDeltaYaw(-deltaYaw);
4067 // when looking to an entity, if automatic camera, center the view on it.
4068 if( ClientCfg.AutomaticCamera /*&& start*/ )
4070 UserControls.resetSmoothCameraDeltaYaw();
4075 // ***************************************************************************
4076 void CUserEntity::startForceLookEntity(CLFECOMMON::TCLEntityId slot)
4078 // Start a new follow: force head pitch to follow by default
4079 _FollowForceHeadPitch= true;
4080 // if a follow is started in first person, reset CameraYaw
4081 if(viewMode()==FirstPV)
4082 UserControls.resetCameraDeltaYaw();
4084 // reorient now (important else may have a time shift because of resetCameraDeltaYaw above)
4085 CEntityCL *target = EntitiesMngr.entity(slot);
4086 if(target)
4088 /* For complex reason, the target may not be still snapped on the ground. snap it now
4089 - this is because in common case, entities are snap only if they are visible (for speed up)
4090 => depends on camera
4091 - but in this case, the camera depends on real entity position (headPitch in first person)
4093 target->snapToGround();
4095 // For FirstPerson targeting. Get the current target head offset
4096 _FollowHeadOffset= 0.f;
4097 CVector headPos;
4098 if(target->getHeadPos(headPos))
4100 _FollowHeadOffset= headPos.z - float(target->pos().z);
4103 // Look at the entity
4104 CVectorD dir2targ = target->pos() - pos();
4105 dir2targ.z = 0.0;
4106 if(dir2targ!=CVectorD::Null)
4108 forceLookEntity(dir2targ, true, true);
4114 // ***************************************************************************
4115 void CUserEntity::stopForceHeadPitchInFollow()
4117 _FollowForceHeadPitch= false;
4120 // ***************************************************************************
4121 void CUserEntity::switchVelocity(bool userRequest)
4123 if (ClientCfg.R2EDEnabled && R2::getEditor().getMode() == R2::CEditor::EditionMode)
4125 // when in the R2 editor, force to run all the time
4126 _Run = true;
4128 else
4130 _Run = !_Run;
4132 _CurrentVelocity = _Run ? runVelocity() : walkVelocity();
4134 if (userRequest)
4136 _RunWhenAble = _Run;
4139 // display message : your are running, you are walking
4140 CInterfaceManager *pIM= CInterfaceManager::getInstance();
4141 string msg;
4142 if( _Run )
4143 msg = CI18N::get("msgUserIsRunning");
4144 else
4145 msg = CI18N::get("msgUserIsWalking");
4146 string cat = getStringCategory(msg, msg);
4147 pIM->displaySystemInfo(msg, cat);
4149 // Write to the UI database, to update views
4150 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_RUNNING", false);
4151 if(node)
4152 node->setValue32(_Run);
4155 //-----------------------------------------------
4156 // autoEquipWithLastUsedWeapons
4158 //-----------------------------------------------
4159 void CUserEntity::autoEquipWithLastUsedWeapons()
4161 CInventoryManager *inv = CInventoryManager::getInstance();
4162 if ( !inv )
4164 return;
4167 // Clear hands
4168 inv->unequip( "LOCAL:INVENTORY:HAND:1" );
4169 inv->unequip( "LOCAL:INVENTORY:HAND:0" );
4171 uint ir,il;
4172 // Equip right hand
4173 if( _PreviousRightHandItem.Sheet != 0 )
4175 // find item in bag with same properties than last used one in right hand
4176 for ( ir=0; ir<MAX_BAGINV_ENTRIES; ++ir )
4178 if( _PreviousRightHandItem.Sheet == inv->getBagItem(ir).getSheetID() &&
4179 _PreviousRightHandItem.Quality == inv->getBagItem(ir).getQuality() &&
4180 _PreviousRightHandItem.Weight == inv->getBagItem(ir).getWeight() &&
4181 _PreviousRightHandItem.NameId == inv->getBagItem(ir).getNameId() )
4183 break;
4186 if ( ir != MAX_BAGINV_ENTRIES )
4188 // Equip right hand
4189 string bagPath = toString( "LOCAL:INVENTORY:BAG:%u", ir );
4190 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:0" );
4192 // Equip left hand if needed
4193 if( _PreviousLeftHandItem.Sheet != 0 )
4195 for ( il=0; il<MAX_BAGINV_ENTRIES; ++il )
4197 if( il != ir &&
4198 _PreviousLeftHandItem.Sheet == inv->getBagItem(il).getSheetID() &&
4199 _PreviousLeftHandItem.Quality == inv->getBagItem(il).getQuality() &&
4200 _PreviousLeftHandItem.Weight == inv->getBagItem(il).getWeight() &&
4201 _PreviousLeftHandItem.NameId == inv->getBagItem(il).getNameId() )
4203 break;
4206 if ( il != MAX_BAGINV_ENTRIES )
4208 bagPath = toString( "LOCAL:INVENTORY:BAG:%u", il );
4209 inv->equip( bagPath, "LOCAL:INVENTORY:HAND:1" );
4212 return;
4217 // TODO : choose the best one
4222 // ***************************************************************************
4223 void CUserEntity::executeCombatWithPhrase(CEntityCL *target, uint32 memoryLine, uint32 memoryIndex, bool cyclic)
4225 nlassert(target);
4226 CSPhraseManager *pPM= CSPhraseManager::getInstance();
4228 // is a melee combat?
4229 bool meleeCombat = false;
4230 // empty hand => yes!
4231 meleeCombat= true;
4232 uint32 rightHandSheet = getInventory().getRightHandItemSheet();
4233 if(rightHandSheet)
4234 meleeCombat = getInventory().isMeleeWeaponItem(rightHandSheet);
4236 // If melee combat, and if the user entity is not well placed for fight, or if it has changed his target
4237 if( meleeCombat &&
4239 !target->isPlacedToFight(pos(), front(), attackRadius() + ClientCfg.AttackDist) ||
4240 target->slot()!=_LastExecuteCombatSlot
4244 _LastExecuteCombatSlot= target->slot();
4246 // Cancel any follow
4247 disableFollow();
4249 // Launch the moveToCombatPhrase, canceling any old action client execution.
4250 // NB: this will also force him to look at the entity
4251 moveToCombatPhrase(target->slot(), 2.0, memoryLine, memoryIndex, cyclic);
4253 // And after (order is important), start the phrase execution on client
4254 pPM->clientExecute(memoryLine, memoryIndex, cyclic);
4256 else
4258 // Cancel any moveTo(), because don't want to continue reaching the prec entity
4259 // VERY important if previous MoveTo was a SPhrase MoveTo (because cancelClientExecute() must be called)
4260 resetAnyMoveTo();
4262 // start client execution: NB: start client execution even if it
4263 pPM->clientExecute(memoryLine, memoryIndex, cyclic);
4265 // inform Server of phrase cast
4266 pPM->sendExecuteToServer(memoryLine, memoryIndex, cyclic);
4268 if( !meleeCombat && !cyclic )
4270 pPM->executeDefaultAttack();
4275 // ***************************************************************************
4276 void CUserEntity::beginCast(const MBEHAV::CBehaviour &behaviour)
4278 if(viewMode()==ThirdPV)
4280 // backup front yaw
4281 float frontYawBefore = frontYaw();
4282 // begin cast
4283 CCharacterCL::beginCast( behaviour );
4284 // compensate the front change using a camera move
4285 float frontYawAfter = frontYaw();
4286 float deltaYaw= frontYawAfter - frontYawBefore;
4287 UserControls.appendCameraDeltaYaw(-deltaYaw);
4288 // if automatic camera, center the view behind the user
4289 if( ClientCfg.AutomaticCamera )
4291 UserControls.resetSmoothCameraDeltaYaw();
4294 else
4296 // in first person mode, reset the delta yaw
4297 UserControls.resetCameraDeltaYaw();
4298 CCharacterCL::beginCast( behaviour );
4302 // ***************************************************************************
4303 void CUserEntity::updatePreCollision(const NLMISC::TTime &time, CEntityCL *target)
4305 CPlayerCL::updatePreCollision(time, target);
4307 // test each frame if the mode has changed
4308 if(SoundMngr)
4310 // Play/stop music if comes from or goes to dead
4311 bool isDead = _Mode == MBEHAV::DEATH || _Mode == MBEHAV::SWIM_DEATH;
4313 // must start music?
4314 if (isDead && SoundMngr->getEventMusicPlayed() != ClientCfg.DeathMusic)
4316 SoundMngr->playEventMusic(ClientCfg.DeathMusic, 0, true);
4319 // must end music?
4320 if (!isDead && SoundMngr->getEventMusicPlayed() == ClientCfg.DeathMusic)
4322 SoundMngr->stopEventMusic(ClientCfg.DeathMusic, CSoundManager::LoadingMusicXFade);
4327 // ***************************************************************************
4328 void CUserEntity::buildTotem()
4330 const string msgName = "TOTEM:BUILD";
4331 CBitMemStream out;
4332 if( GenericMsgHeaderMngr.pushNameToStream( msgName, out ) )
4334 NetMngr.push( out );
4335 nlinfo( "sending TOTEM:build message to server" );
4339 // ***************************************************************************
4340 void CUserEntity::setR2CharMode(R2::TCharMode mode)
4342 if (mode == R2::TCharMode::Editer || mode == R2::TCharMode::Dm)
4344 _R2CharMode= mode;
4345 walkVelocity(ClientCfg.DmWalk);
4346 runVelocity(ClientCfg.DmRun);
4347 View.setCameraDistanceMaxForDm();
4348 CEntityCL *user = EntitiesMngr.entity(0);
4349 NLPACS::UMovePrimitive* prim = user?user->getPrimitive():0;
4350 if (prim)
4352 prim->setObstacle(false);
4356 else if (mode == R2::TCharMode::Player || mode == R2::TCharMode::Tester)
4358 _R2CharMode= mode;
4359 walkVelocity(ClientCfg.Walk);
4360 runVelocity(ClientCfg.Run);
4361 View.setCameraDistanceMaxForPlayer();
4362 CEntityCL *user = EntitiesMngr.entity(0);
4363 NLPACS::UMovePrimitive* prim = user?user->getPrimitive():0;
4364 if (prim)
4366 prim->setObstacle(true);
4369 else
4371 nlwarning("R2Ed: Error Char Mode not handled %u", (uint32)mode.getValue());
4375 bool CUserEntity::isInNpcControl() const
4377 CInterfaceManager* pIM = CInterfaceManager::getInstance();
4378 CCDBNodeLeaf *sheet = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4379 return sheet && NLMISC::CSheetId(sheet->getValue32())!=NLMISC::CSheetId::Unknown;
4383 void CUserEntity::updateNpcContolSpeed()
4385 CInterfaceManager* pIM = CInterfaceManager::getInstance();
4386 CCDBNodeLeaf *sheet = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4387 CCDBNodeLeaf *walk = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:WALK", false);
4388 CCDBNodeLeaf *run = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:RUN", false);
4389 if (!sheet || !walk || !run)
4391 return;
4394 static NLMISC::CSheetId oldSheet = NLMISC::CSheetId::Unknown;
4395 static float oldRun=0.f;
4396 static float oldWalk=0.f;
4398 NLMISC::CSheetId sheetId(sheet->getValue32());
4399 float newRun = float(run->getValue32()) / 1000.0f;
4400 float newWalk = float(walk->getValue32()) / 1000.0f;
4402 if (sheetId == oldSheet && oldRun == newRun && oldWalk == newWalk )
4404 return;
4407 oldSheet = sheetId;
4408 oldRun = newRun;
4409 oldWalk = newWalk;
4411 if (sheetId != NLMISC::CSheetId::Unknown)
4413 walkVelocity(newWalk);
4414 runVelocity(newRun);
4416 else
4418 setR2CharMode(_R2CharMode);
4423 //-----------------------------------------------
4424 // cancelAllPhrases
4425 //-----------------------------------------------
4426 void CUserEntity::cancelAllPhrases()
4428 CBitMemStream out;
4429 if(GenericMsgHeaderMngr.pushNameToStream("PHRASE:CANCEL_ALL", out))
4431 NetMngr.push(out);
4433 else
4435 nlwarning("<CUserEntity::cancelAllPhrases> unknown message name '%s'", "PHRASE:CANCEL_ALL");
4440 //-----------------------------------------------
4441 // canChangeFront
4442 //-----------------------------------------------
4443 bool CUserEntity::canChangeFront()
4445 return !(_CurrentBehaviour.Behaviour == MBEHAV::EXTRACTING
4446 || (_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK && _Mode==MBEHAV::COMBAT && !UserControls.isMoving())
4447 || (_CurrentBehaviour.Behaviour >= MBEHAV::MAGIC_CASTING_BEHAVIOUR_BEGIN && _CurrentBehaviour.Behaviour <= MBEHAV::MAGIC_CASTING_BEHAVIOUR_END));
4451 //-----------------------------------------------
4452 // rememberWeaponsInHand
4454 //-----------------------------------------------
4455 void CUserEntity::rememberWeaponsInHand()
4457 CInventoryManager * inv = CInventoryManager::getInstance();
4458 if( !inv )
4460 return;
4463 // keep right hand item
4464 CItemImage * rightItemImg = inv->getHandItem(0);
4465 if( rightItemImg )
4467 if( inv->isMeleeWeaponItem(rightItemImg->getSheetID()) || inv->isRangeWeaponItem(rightItemImg->getSheetID()) )
4469 _PreviousRightHandItem = CItemSnapshot(*rightItemImg);
4471 // keep left hand item too (could be ammo, second weapon, etc ..)
4472 CItemImage * leftItemImg = inv->getHandItem(1);
4473 if( leftItemImg )
4475 _PreviousLeftHandItem = CItemSnapshot(*leftItemImg);
4477 else
4479 _PreviousLeftHandItem = CItemSnapshot();
4486 //-----------------------------------------------
4487 // snapshot of a CItemImage
4489 //-----------------------------------------------
4490 CUserEntity::CItemSnapshot::CItemSnapshot( const CItemImage& i )
4492 Sheet = i.getSheetID();
4493 Quality = i.getQuality();
4494 Quantity = i.getQuantity();
4495 UserColor = i.getUserColor();
4496 Price = i.getPrice();
4497 Weight = i.getWeight();
4498 NameId = i.getNameId();
4499 InfoVersion = i.getInfoVersion();
4502 sint CUserEntity::getLevel() const
4504 CInterfaceManager *pIM = CInterfaceManager::getInstance();
4505 sint level = -1;
4506 for(uint i=0;i<EGSPD::CSPType::EndSPType;i++)
4508 CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false);
4509 if(node)
4511 level = std::max(level, (sint) node->getValue32());
4514 return level;
4517 //-----------------------------------------------
4518 // interlocutor
4519 //-----------------------------------------------
4520 void CUserEntity::interlocutor( const CLFECOMMON::TCLEntityId &slot)
4522 CLFECOMMON::TCLEntityId prevInterlocutor = _Interlocutor;
4523 _Interlocutor = slot;
4525 // Refresh (hide or unhide) the icon for the interlocutor NPC
4526 if (prevInterlocutor != CLFECOMMON::INVALID_SLOT)
4527 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(prevInterlocutor);
4528 if (_Interlocutor != CLFECOMMON::INVALID_SLOT)
4529 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(_Interlocutor);
4532 //-----------------------------------------------
4533 // trader
4534 //-----------------------------------------------
4535 void CUserEntity::trader(const CLFECOMMON::TCLEntityId &slot)
4537 CLFECOMMON::TCLEntityId prevTrader = _Trader;
4538 _Trader = slot;
4540 // Refresh (hide or unhide) the icon for the trader NPC
4541 if (prevTrader != CLFECOMMON::INVALID_SLOT)
4542 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(prevTrader);
4543 if (_Trader != CLFECOMMON::INVALID_SLOT)
4544 EntitiesMngr.refreshInsceneInterfaceOfFriendNPC(_Trader);