1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include "nel/misc/vectord.h"
30 #include "nel/misc/matrix.h"
31 #include "nel/misc/quat.h"
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"
43 #include "nel/pacs/u_global_position.h"
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"
55 #include "debug_client.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"
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"
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"
81 #include "nel/sound/sound_anim_manager.h"
82 #include "nel/sound/sound_animation.h"
83 #include "nel/sound/sound_anim_marker.h"
85 #include "r2/editor.h"
94 using namespace NLMISC
;
95 using namespace NLPACS
;
98 using NL3D::UVisualCollisionManager
;
99 using NL3D::UTextContext
;
105 extern UScene
*Scene
;
106 extern UVisualCollisionManager
*CollisionManager
;
107 extern CEntityAnimationManager
*EAM
;
108 extern UTextContext
*TextContext
;
109 extern NL3D::UCamera MainCam
;
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
;
130 // Hierarchical timer
131 H_AUTO_DECL ( RZ_Client_Update_Sound
)
136 //string chooseRandom( const vector<string>& sounds, uint32& previousIndex );
138 //-----------------------------------------------
141 //-----------------------------------------------
142 CUserEntity::CUserEntity()
147 _RunWhenAble
= false;
148 _WalkVelocity
= 1.0f
;
150 _CurrentVelocity
= _WalkVelocity
;
152 _FrontVelocity
= 0.0f
;
153 _LateralVelocity
= 0.0f
;
155 _SpeedServerAdjust
= 1.0f
;
157 // \todo GUIGUI : do it more generic.
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.
168 // Your are not on a mount at the beginning.
170 _HiddenMount
= CLFECOMMON::INVALID_SLOT
;
172 _AnimAttackOn
= false;
175 _PermanentDeath
= false;
179 // The user is not in collision with someone else.
181 // Collisions are not removed.
184 // No Move To at the beginning.
185 _MoveToSlot
= CLFECOMMON::INVALID_SLOT
;
186 _MoveToAction
= CUserEntity::None
;
188 _MoveToColStartTime
= 0;
190 _FollowForceHeadPitch
= false;
192 _ForceLookSlot
= CLFECOMMON::INVALID_SLOT
;
193 _LastExecuteCombatSlot
= CLFECOMMON::INVALID_SLOT
;
195 _R2CharMode
= R2::TCharMode::Player
;
200 //-----------------------------------------------
203 //-----------------------------------------------
204 CUserEntity::~CUserEntity()
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);
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);
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);
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();
256 //-----------------------------------------------
258 // Initialize properties of the entity (according to the class).
259 //-----------------------------------------------
260 void CUserEntity::initProperties()
262 properties().selectable(true);
263 }// initProperties //
266 //-----------------------------------------------
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
);
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'.");
286 pushInfoStr("User Sheet is a valid '.race_stats'.");
288 if(IngameDbMngr
.getNodePtr())
290 CCDBNodeBranch
*nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(IngameDbMngr
.getNodePtr()->getNode(0));
293 _DBEntry
= dynamic_cast<CCDBNodeBranch
*>(nodeRoot
->getNode(_Slot
));
295 pushDebugStr("Cannot get a pointer on the DB entry.");
301 if(ClientCfg
.RunAtTheBeginning
!= _Run
)
304 // Set the up of the user.
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.
325 // Initialize the observer for the speed factor and mount stuff
330 // Create the user playlist
333 // Initialize the internal time.
334 _LastFrameTime
= ((double)T1
) * 0.001;
336 // Set the gender in local mode.
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;
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
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.
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);
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);
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();
404 fameObs
->FactionIndex
= factionIndex
;
405 CCDBNodeLeaf
* node
= NLGUI::CDBManager::getInstance()->getDbProp(sDBPath
, false);
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();
425 pSM
->tryToUnblockTitleFromCharOldness( CharFirstConnectedTime
);
426 pSM
->tryToUnblockTitleFromCharPlayedTime( CharPlayedTime
);
434 //-----------------------------------------------
436 // \todo GUIGUI : do it better in mount mode
437 //-----------------------------------------------
438 float CUserEntity::eyesHeight()
441 return _EyesHeight
* _CharacterScalePos
;
443 return _EyesHeight
* _CharacterScalePos
;
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
)
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
;
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_1
));
495 CCDBNodeLeaf
*targetList3
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_TARGET_LIST_1
));
496 if (targetList0
&& targetList1
&& targetList2
&& targetList3
)
500 (uint64
) targetList0
->getValue64(),
501 (uint64
) targetList1
->getValue64(),
502 (uint64
) targetList2
->getValue64(),
503 (uint64
) targetList3
->getValue64()
505 bc
.Targets
.unpack(vp
, 4);
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
);
521 /* if (oldNameId != _NameId)
523 CInterfaceManager *pIM = CInterfaceManager::getInstance();
524 CInterfaceElement *element = CWidgetManager::getInstance()->getElementFromId("ui:interface:mailbox:content:html");
527 CGroupHTML *html = dynamic_cast<CGroupHTML*>(element);
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.");
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
)
608 CPlayerCL::updateVisualPropertyEntityMounted(gameCycle
, prop
);
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
)
620 CPlayerCL::updateVisualPropertyRiderEntity(gameCycle
, prop
);
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");
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");
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");
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");
673 sint32 val
= pDB
->getValue32();
674 pDB
->setValue32(val
+1);
679 /////////////////////////////////////////////////
680 /////////////////////////////////////////////////
681 /////////////////////////////////////////////////
682 /////////////////////////////////////////////////
685 //-----------------------------------------------
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.
698 // Release the old Mode.
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();
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
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
));
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
743 _Primitive
->setRadius( std::min(0.5f
, (float)(RYZOM_ENTITY_SIZE_MAX
/2)) );
744 _Primitive
->setHeight(2);
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
)
760 if (_Mode
== MBEHAV::MOUNT_SWIM
&& ( m
== MBEHAV::COMBAT
|| m
== MBEHAV::COMBAT_FLOAT
))
762 //TODO : display "you can't fight while swimming"
769 // Restore the last view.
770 viewMode(viewMode());
773 if( m
== MBEHAV::COMBAT
|| m
== MBEHAV::COMBAT_FLOAT
)
775 //TODO : display "you can't fight while swimming"
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
)
796 // Initialize the new Mode.
801 case MBEHAV::COMBAT_FLOAT
:
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();
814 contextHelp ("action_bar");
818 // Mount Normal or mount swim
819 case MBEHAV::MOUNT_NORMAL
:
821 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(parent()));
824 mount
->_Stages
.removePosWithNoMode();
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()
839 UserEntity
->selection(_Selection
);
847 _Mode
= MBEHAV::SWIM_DEATH
;
850 // Normal or Default mode.
852 _CurrentBehaviour
.Behaviour
= MBEHAV::IDLE
;
856 viewMode(viewMode());
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
);
869 if( _Mode
== MBEHAV::SIT
&& m
!=MBEHAV::DEATH
)
871 setAnim(CAnimationStateSheet::SitEnd
);
876 // Show/Hide all or parts of the body.
877 updateVisualDisplay();
878 if( ClientCfg
.AutomaticCamera
)
880 // Set the direction as the front.
883 // Compute the current automaton
885 // Update the animation set according to the mode.
890 // Animset changed -> update current animation
891 setAnim(CAnimationStateSheet::Idle
);
894 // Changing the mode well done.
900 * Mount the mount in _Mount
902 void CUserEntity::setOnMount()
904 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_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.
916 // Link the mount and the user.
918 // Refresh the View Mode
919 viewMode(viewMode());
920 // Close the crafting window if open
921 closeFaberCastWindow();
926 mount
->_ForbidClipping
= true;
932 //-----------------------------------------------
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();
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
)
966 // Lateral or Backward Walk
970 if (_R2CharMode
== R2::TCharMode::Editer
|| _R2CharMode
== R2::TCharMode::Dm
)
972 velocity
*= (float(ClientCfg
.DmRun
) / 6.0f
); // velocity max = max run / 2
978 // Forward Run or Walk
979 if(_FrontVelocity
> 0.0f
)
982 velocity
*= getMountRunVelocity();
984 velocity
*= getMountWalkVelocity();
986 // Lateral or Backward Walk (currently, not used)
988 velocity
*= 0.66f
;//getMountWalkVelocity();
992 // Forward Run or Walk
993 if(_FrontVelocity
> 0.0f
)
994 velocity
*= currentVelocity();
995 // Lateral or Backward Walk
997 velocity
*= _WalkVelocity
;
1002 //-----------------------------------------------
1004 // Return the Entity Current Speed.
1005 //-----------------------------------------------
1006 double CUserEntity::speed() const // virtual
1008 return CPlayerCL::speed();
1009 // return (double)getVelocity().norm();
1012 //-----------------------------------------------
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
;
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()));
1031 mount
->_Stages
.removePosWithNoMode();
1033 // NO PRIMITIVE -> NO MOVE
1036 // BAD CONNECTION -> NO MOVE
1037 if(NetMngr
.getConnectionQuality() == false)
1039 // MS per TICK <=0 -> NO MOVE
1040 if(NetMngr
.getMsPerTick() <= 0)
1042 // Compute Speed Vector
1043 NLMISC::CVectorD speed
;
1044 if(_MoveToSlot
!= CLFECOMMON::INVALID_SLOT
)
1046 // Check the Target.
1047 if(target
== 0 || target
== this)
1049 // Compute the vector between the user and the target.
1050 CVectorD dir2targ
= target
->pos() - pos();
1052 if(dir2targ
== CVectorD::Null
)
1054 const double angToTarget
= atan2(dir2targ
.y
, dir2targ
.x
);
1055 CVectorD aimingPos
= target
->getAttackerPos(angToTarget
, attackRadius() + ClientCfg
.AttackDist
);
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
1079 _MoveToColStartTime
= 0;
1081 // if too much time stalled, stop run.
1082 if(_MoveToColStartTime
&& (T1
- _MoveToColStartTime
>= ClientCfg
.MoveToTimeToStopStall
) )
1083 targetReached
= true;
1086 _MoveToColStartTime
= 0;
1089 targetReached
= true;
1091 // If the target is reached
1094 // stop and execute action
1095 speed
= CVectorD::Null
;
1096 moveToAction(target
);
1098 // else continue follow
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);
1107 dirToAimingPos
.normalize();
1108 // Set the Velocity Direction
1109 speed
= dirToAimingPos
*distToAimingPos
;
1111 if(speed
.norm() > getMaxSpeed())
1112 speed
= dirToAimingPos
*getMaxSpeed();
1117 // Check the Target.
1118 if(target
== 0 || target
== this)
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();
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
);
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
1144 || (isFighting() && distToAimingPos
>= 0.5)
1145 || (!isFighting() && distToAimingPos
>= 3.0))
1147 dirToAimingPos
.normalize();
1148 // User is in combat mode -> follow as close as possible.
1152 if(target
->hasMoved())
1154 // Get closer if too far.
1155 if(distToAimingPos
>= 0.2)
1157 // Set the Velocity Direction
1158 speed
= dirToAimingPos
*distToAimingPos
;
1160 if(speed
.norm() > getMaxSpeed())
1161 speed
= dirToAimingPos
*getMaxSpeed();
1165 // Try to have the same speed as the target.
1166 if(target
->getSpeed() > getMaxSpeed())
1167 speed
= target
->dir()* ((float)getMaxSpeed());
1169 speed
= target
->dir()* ((float)target
->getSpeed());
1172 // Target is not moving.
1175 // Get closer if too far.
1176 if(distToAimingPos
>= 0.1)
1178 // Set the Velocity Direction
1179 speed
= dirToAimingPos
*distToAimingPos
;
1181 if(speed
.norm() > getMaxSpeed())
1182 speed
= dirToAimingPos
*getMaxSpeed();
1185 speed
= CVectorD::Null
;
1188 // User is not in combat mode -> follow not so close.
1191 // Too far, get closer as fast as possible
1192 if(distToAimingPos
>= 3.0)
1194 // Set the Velocity Direction
1195 speed
= dirToAimingPos
*distToAimingPos
;
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());
1207 speed
= target
->dir()* ((float)target
->getSpeed());
1211 speed
= CVectorD::Null
;
1214 // User was stop and the next pos is to close to begin to move.
1216 speed
= CVectorD::Null
;
1220 speed
= getVelocity()*_SpeedFactor
.getValue();
1221 _SpeedFactor
.addFactorValue(0.005f
);
1224 // SPEED VECTOR NULL -> NO MOVE
1225 if(speed
== CVectorD::Null
)
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);
1238 _Primitive
->move(speed
, dynamicWI
);
1239 if(mount
&& mount
->getPrimitive())
1240 mount
->getPrimitive()->move(speed
, dynamicWI
);
1242 // Third Person View
1245 double modif
= (100.0f
/(float)NetMngr
.getMsPerTick());
1246 clamp(modif
, 0.0, 1.0);
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
);
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
);
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();
1281 if(dir2targ
== CVectorD::Null
)
1283 // Look at the entity
1284 forceLookEntity(dir2targ
, true);
1290 //---------------------------------------------------
1291 // updatePosCombatFloatChanged :
1292 //---------------------------------------------------
1293 void CUserEntity::updatePosCombatFloatChanged(CEntityCL
* /* target */) // virtual
1295 if(viewMode() == FirstPV
)
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 //-----------------------------------------------
1314 //-----------------------------------------------
1315 void CUserEntity::disableFollow()
1317 if (_FollowMode
==false)
1320 _FollowMode
= false;
1322 // Send the message to the server.
1324 if(GenericMsgHeaderMngr
.pushNameToStream("TARGET:NO_FOLLOW", out
))
1327 nlwarning("UE:follow: unknown message named 'TARGET:NO_FOLLOW'.");
1332 //-----------------------------------------------
1334 //-----------------------------------------------
1335 void CUserEntity::enableFollow(bool resetCameraRot
)
1337 if (_FollowMode
== true)
1340 if( _Mode
== MBEHAV::DEATH
)
1345 // Send the message to the server.
1347 if(GenericMsgHeaderMngr
.pushNameToStream("TARGET:FOLLOW", out
))
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);
1358 // if want to reset camera rotation
1360 startForceLookEntity(targetSlot());
1365 //-----------------------------------------------
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
;
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);
1406 // if sufficiently near, launch the action
1407 CEntityCL
*target
= EntitiesMngr
.entity(slot
);
1410 CVectorD dir2targ
= target
->pos() - pos();
1412 if((dir2targ
==CVectorD::Null
) || (dir2targ
.norm() < dist
))
1414 moveToAction(target
);
1418 }// moveToCheckStartDist //
1420 //-----------------------------------------------
1422 // Method to move to someone else.
1423 //-----------------------------------------------
1424 void CUserEntity::moveTo(CLFECOMMON::TCLEntityId slot
, double dist
, TMoveToAction action
)
1431 _MoveToAction
= action
;
1433 moveToCheckStartDist(_MoveToSlot
, _MoveToDist
, _MoveToAction
);
1436 //-----------------------------------------------
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
)
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
)
1464 _MoveToAction
= CUserEntity::MissionRing
;
1465 _MoveToMissionId
= id
;
1467 moveToCheckStartDist(_MoveToSlot
, _MoveToDist
, _MoveToAction
);
1468 }// moveToMissionRing //
1470 //-----------------------------------------------
1472 // Method to move to someone else.
1473 //-----------------------------------------------
1474 void CUserEntity::moveToCombatPhrase(CLFECOMMON::TCLEntityId slot
, double dist
, uint phraseMemoryLine
, uint phraseMemorySlot
, bool phraseCyclic
)
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();
1502 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
1503 if ( rightHandSheet
)
1505 validForageToolInHand
= inv
->isForageToolItem( rightHandSheet
);
1507 if ( !validForageToolInHand
)
1509 // Find a forage tool in the bag
1511 for ( i
=0; i
<MAX_BAGINV_ENTRIES
; ++i
)
1513 uint32 itemSheet
= inv
->getBagItem(i
).getSheetID();
1514 if ( itemSheet
&& inv
->isForageToolItem(itemSheet
) )
1519 if ( i
!= MAX_BAGINV_ENTRIES
&& ClientCfg
.AutoEquipTool
)
1521 // remember last used weapon(s)
1522 rememberWeaponsInHand();
1525 inv
->unequip( "LOCAL:INVENTORY:HAND:1" );
1526 inv
->unequip( "LOCAL:INVENTORY:HAND:0" );
1529 string bagPath
= toString( "LOCAL:INVENTORY:BAG:%u", i
);
1530 inv
->equip( bagPath
, "LOCAL:INVENTORY:HAND:0" );
1534 // No forage tool in bag
1535 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiForageToolMissing"), "CHK" );
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
)
1564 _MoveToAction
= CUserEntity::BuildTotem
;
1565 _MoveToPhraseMemoryLine
= phraseMemoryLine
;
1566 _MoveToPhraseMemorySlot
= phraseMemorySlot
;
1567 _MoveToPhraseCyclic
= cyclic
;
1569 moveToCheckStartDist(_MoveToSlot
, _MoveToDist
, _MoveToAction
);
1572 //-----------------------------------------------
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
1583 UserControls
.needReleaseForward();
1585 // Get the interface instance.
1586 CInterfaceManager
*IM
= CInterfaceManager::getInstance();
1587 switch(_MoveToAction
)
1590 case CUserEntity::Attack
:
1591 UserEntity
->attack();
1594 case CUserEntity::Quarter
:
1595 if((ent
->properties()).harvestable())
1596 CAHManager::getInstance()->runActionHandler("context_quartering", 0);
1599 case CUserEntity::Loot
:
1600 if((ent
->properties()).lootable())
1601 CAHManager::getInstance()->runActionHandler("context_loot", 0);
1604 case CUserEntity::PickUp
:
1605 nlwarning("UE:checkMoveTo: not yet implemented");
1607 case CUserEntity::ExtractRM
:
1611 case CUserEntity::TradeItem
:
1612 CAHManager::getInstance()->runActionHandler("context_trade_item", 0);
1615 case CUserEntity::TradePhrase
:
1616 CAHManager::getInstance()->runActionHandler("context_trade_phrase", 0);
1619 case CUserEntity::TradePact
:
1620 CAHManager::getInstance()->runActionHandler("context_trade_pact", 0);
1623 case CUserEntity::Mission
:
1625 string param
= toString("id=%d", _MoveToMissionId
);
1626 CAHManager::getInstance()->runActionHandler("mission_option", 0, param
);
1630 case CUserEntity::DynamicMission
:
1631 CAHManager::getInstance()->runActionHandler("context_dynamic_mission", 0);
1634 case CUserEntity::StaticMission
:
1635 CAHManager::getInstance()->runActionHandler("context_choose_mission", 0);
1638 case CUserEntity::MissionRing
:
1640 string param
= toString("id=%d", _MoveToMissionId
);
1641 CAHManager::getInstance()->runActionHandler("mission_ring", 0, param
);
1645 case CUserEntity::CreateGuild
:
1646 CAHManager::getInstance()->runActionHandler("context_create_guild", 0);
1649 case CUserEntity::News
:
1650 CAHManager::getInstance()->runActionHandler("context_talk", 0);
1653 case CUserEntity::TradeTeleport
:
1654 CAHManager::getInstance()->runActionHandler("context_trade_teleport", 0);
1656 // Trade Faction items
1657 case CUserEntity::TradeFaction
:
1658 CAHManager::getInstance()->runActionHandler("context_trade_faction", 0);
1661 case CUserEntity::TradeCosmetic
:
1662 CAHManager::getInstance()->runActionHandler("context_trade_cosmetic", 0);
1665 case CUserEntity::Talk
:
1666 nlwarning("UE:checkMoveTo: not yet implemented");
1669 case CUserEntity::CombatPhrase
:
1670 UserEntity
->attackWithPhrase();
1673 case CUserEntity::Mount
:
1675 string orderStr
= "mount";
1676 string beastIndexStr
= "@UI:GCM_BEAST_SELECTED";
1677 bool confirmFree
= true;
1678 beastOrder(orderStr
,beastIndexStr
,confirmFree
);
1682 case CUserEntity::WebPage
:
1683 CAHManager::getInstance()->runActionHandler("context_web_page", 0);
1686 case CUserEntity::Outpost
:
1687 CLuaManager::getInstance().executeLuaScript("game:outpostBCOpenStateWindow()", 0);
1690 case CUserEntity::BuildTotem
:
1702 //-----------------------------------------------
1704 // Send the position and orientation to the server.
1705 //-----------------------------------------------
1706 bool CUserEntity::sendToServer(CBitMemStream
&out
)
1708 if(GenericMsgHeaderMngr
.pushNameToStream("POSITION", out
))
1710 // Backup the position sent.
1711 if (_Primitive
) _Primitive
->getGlobalPosition(_LastGPosSent
, dynamicWI
);
1712 // Send Position & Orientation
1713 CPositionMsg positionMsg
;
1714 positionMsg
.X
= (sint32
)(pos().x
* 1000);
1715 positionMsg
.Y
= (sint32
)(pos().y
* 1000);
1716 positionMsg
.Z
= (sint32
)(pos().z
* 1000);
1717 positionMsg
.Heading
= frontYaw();
1718 out
.serial(positionMsg
);
1723 nlwarning("UE:sendToServer: unknown message named 'POSITION'.");
1728 //-----------------------------------------------
1729 // msgForCombatPos :
1730 // Fill the msg to know if the user is well placed to fight.
1731 //-----------------------------------------------
1732 bool CUserEntity::msgForCombatPos(NLMISC::CBitMemStream
&out
)
1734 static bool wellPosition
= false;
1738 // Is the user well Placed
1739 CEntityCL
*target
= EntitiesMngr
.entity(UserEntity
->targetSlot());
1741 wellP
= target
->isPlacedToFight(UserEntity
->pos(), front(), attackRadius() + ClientCfg
.AttackDist
);
1743 // If different from the last time
1744 if(wellPosition
!= wellP
)
1746 wellPosition
= wellP
;
1747 // Send state to server.
1748 if(GenericMsgHeaderMngr
.pushNameToStream("COMBAT:VALIDATE_MELEE", out
))
1750 uint8 flag
= wellP
?1:0;
1755 nlwarning("UE:msgForCombatPos: unknown message named 'COMBAT:TOGGLE_COMBAT_FLOAT_MODE'.");
1757 // Do not send the msg.
1759 }// msgForCombatPos //
1762 //-----------------------------------------------
1764 // Check the User Position according to the server code.
1765 // \todo GUIGUI : put checks on GPos
1766 // \todo GUIGUI : refact the check primitive when creating a PACS again
1767 //-----------------------------------------------
1768 void CUserEntity::checkPos()
1770 if(PACS
&& _Primitive
)
1775 UGlobalPosition gPos
;
1776 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
1778 if(GR
->isWaterPosition(gPos
, waterHeight
))
1780 if(isSwimming() == false)
1782 // Player is Dead set the right mode (swim and dead)
1784 mode(MBEHAV::SWIM_DEATH
);
1787 // Do not swim when in combat mode.
1791 pacsPos(_LastPositionValidated
, _LastGPosValidated
);
1793 // \todo GUIGUI : if the move was cancelled, do not switch to swimming mode.
1794 else if (isRiding())
1796 mode(MBEHAV::MOUNT_SWIM
);
1797 // also change mounted entity mode
1798 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
1802 mount
->setMode(MBEHAV::MOUNT_SWIM
);
1803 mount
->computeAutomaton();
1804 mount
->computeAnimSet();
1805 mount
->setAnim(CAnimationStateSheet::Idle
);
1821 mode(MBEHAV::DEATH
);
1823 else if (isRiding())
1825 mode(MBEHAV::MOUNT_NORMAL
);
1826 // also change mounted entity mode
1827 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
1831 mount
->setMode(MBEHAV::MOUNT_NORMAL
);
1832 mount
->computeAutomaton();
1833 mount
->computeAnimSet();
1834 mount
->setAnim(CAnimationStateSheet::Idle
);
1839 mode(MBEHAV::NORMAL
);
1845 // Create the Primitive used to check if the move will be accepted by the server
1846 if(_CheckPrimitive
== 0)
1848 _Primitive
->getGlobalPosition(_LastGPosSent
, dynamicWI
);
1849 _Primitive
->getGlobalPosition(_LastGPosValidated
, dynamicWI
);
1850 _CheckPrimitive
= PACS
->addNonCollisionablePrimitive(_Primitive
);
1851 _CheckPrimitive
->setOcclusionMask(MaskColNone
); // Collide with nothing
1852 _CheckPrimitive
->setCollisionMask(MaskColNone
); // Collide with nothing
1853 _LastPositionValidated
= pos();
1857 _CheckPrimitive
->setGlobalPosition(_LastGPosSent
, dynamicWI
);
1858 CVectorD speed
= _Primitive
->getFinalPosition(dynamicWI
) - _CheckPrimitive
->getFinalPosition(dynamicWI
);
1860 _CheckPrimitive
->move(speed
, dynamicWI
);
1861 if(PACS
->evalNCPrimitiveCollision(1.0, _CheckPrimitive
, dynamicWI
) == false)
1862 nlwarning("UE:checkPos: _CheckPrimitive is a collisionable primitive !!!");
1864 CVectorD vectDist
= _Primitive
->getFinalPosition(dynamicWI
) - _CheckPrimitive
->getFinalPosition(dynamicWI
);
1865 if(vectDist
.norm() > 0.001)
1867 // The server will not be able to reproduce this move, restoring the last one.
1869 pacsPos(_LastPositionValidated
,_LastGPosValidated
);
1873 _LastPositionValidated
= _Primitive
->getFinalPosition(dynamicWI
);
1874 _Primitive
->getGlobalPosition(_LastGPosValidated
, dynamicWI
);
1881 //-----------------------------------------------
1883 // test the move from posToTest to current pos
1884 //-----------------------------------------------
1885 bool CUserEntity::testPacsPos( CVectorD
& posToTest
)
1887 if(PACS
&& _Primitive
&& _CheckPrimitive
)
1889 _CheckPrimitive
->setGlobalPosition(posToTest
, dynamicWI
);
1890 CVectorD speed
= _Primitive
->getFinalPosition(dynamicWI
) - _CheckPrimitive
->getFinalPosition(dynamicWI
);
1892 _CheckPrimitive
->move(speed
, dynamicWI
);
1893 if(PACS
->evalNCPrimitiveCollision(1.0, _CheckPrimitive
, dynamicWI
) == false)
1894 nlwarning("UE:testPacsPos: _CheckPrimitive is a collisionable primitive !!!");
1896 CVectorD vectDist
= _Primitive
->getFinalPosition(dynamicWI
) - _CheckPrimitive
->getFinalPosition(dynamicWI
);
1897 if(vectDist
.norm() > 0.001)
1909 //-----------------------------------------------
1911 // Teleport the player (remove selection, re-init checkPos, etc.).
1912 //-----------------------------------------------
1913 void CUserEntity::tp(const CVectorD
&dest
)
1915 // Remove the selection.
1916 UserEntity
->selection(CLFECOMMON::INVALID_SLOT
);
1919 // Update the primitive useful to check the user position.
1920 _LastPositionValidated
= dest
;
1921 // Get the Global position
1924 // this is the last PACS position validated too
1925 _Primitive
->getGlobalPosition(_LastGPosValidated
, dynamicWI
);
1926 // consider this is the last position sent to server (since actually received!)
1927 _Primitive
->getGlobalPosition(_LastGPosSent
, dynamicWI
);
1928 // Set the new position of the 'check' primitive
1930 _CheckPrimitive
->setGlobalPosition(_LastGPosSent
, dynamicWI
);
1933 nlwarning("UE:tp: the entity has a Null primitive.");
1934 // Return to interface mode.
1935 viewMode(UserEntity
->viewMode());
1936 // User well oriented.
1937 dir(UserEntity
->front());
1938 // Set Normal Mode after a teleport.
1939 mode(MBEHAV::NORMAL
);
1940 // Set animation with idle.
1941 setAnim(CAnimationStateSheet::Idle
);
1944 //-----------------------------------------------
1946 // Teleport the player to correct his position.
1947 //-----------------------------------------------
1948 void CUserEntity::correctPos(const NLMISC::CVectorD
&dest
)
1950 nlinfo("UE:correctPos: new user position %f %f %f", dest
.x
, dest
.y
, dest
.z
);
1951 // Change the user poisition.
1952 UserEntity
->pacsPos(dest
);
1953 // Update the primitive useful to check the user position.
1954 _LastPositionValidated
= dest
;
1955 // Get the Global position
1958 // this is the last PACS position validated too
1959 _Primitive
->getGlobalPosition(_LastGPosValidated
, dynamicWI
);
1960 // consider this is the last position sent to server (since actually received!)
1961 _Primitive
->getGlobalPosition(_LastGPosSent
, dynamicWI
);
1962 // Set the new position of the 'check' primitive
1964 _CheckPrimitive
->setGlobalPosition(_LastGPosSent
, dynamicWI
);
1967 nlwarning("UE:correctPos: the entity has a Null primitive.");
1969 // Correct the pos of the mount, if riding
1972 if ( _Mount
< EntitiesMngr
.entities().size() )
1974 CEntityCL
*mount
= EntitiesMngr
.entities()[_Mount
];
1976 mount
->pacsPos( dest
);
1983 * Check if the mount is able to run, and force walking mode if not
1984 * (if the player clicked on run, set back to walk).
1986 void CUserEntity::checkMountAbleToRun()
1992 // Make the mount walk if she's hungry
1993 if ( ! _MountHunger
.canRun() )
1994 switchVelocity( false );
1998 // Make the mount run if she's not hungry anymore
1999 if ( _RunWhenAble
&& _MountHunger
.canRun() )
2000 switchVelocity( false );
2006 //-----------------------------------------------
2007 // Update the position of the entity after the motion.
2008 // \param t : Time for the position of the entity after the motion.
2009 // \param target : pointer on the current target.
2010 //-----------------------------------------------
2011 void CUserEntity::updatePos(const TTime
&t
, CEntityCL
*target
)
2013 // Update Mount if mounting (if _Mount was still not valid when mode received)
2014 if((_Mount
!= CLFECOMMON::INVALID_SLOT
&& isRiding()) && _OnMount
==false)
2018 // External view are managed like other entities.
2019 if(!UserControls
.isInternalView())
2021 // Update position according to the animations.
2022 CPlayerCL::updatePos(t
, target
);
2023 _LastFrameTime
= t
*0.001;
2024 // Set The FPV when indoor.
2025 if(forceIndoorFPV())
2026 viewMode(FirstPV
, false);
2032 // 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)
2033 updateAnimationState();
2036 // Compute the Time Step.
2037 double frameTimeRemaining
= computeTimeStep(((double)t
)*0.001);
2038 // Do not update animation if Client Light
2039 if (!ClientCfg
.Light
)
2041 // Attack Animation.
2044 if(animIndex(MOVE
) != ::CAnimation::UnknownAnim
)
2046 if(animOffset(MOVE
) >= EAM
->getAnimationLength(animId(MOVE
)))
2048 _AnimAttackOn
= false;
2049 updateVisualDisplay();
2054 // Increase the time in the current animation.
2055 double previousTimeOffset
= animOffset(MOVE
);
2056 double offset
= previousTimeOffset
+ frameTimeRemaining
;
2057 // Check Anim length
2058 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
2059 if(offset
> animLength
)
2060 animOffset(MOVE
, animLength
);
2062 animOffset(MOVE
, offset
);
2063 // Manage Events that could be created by the animation (like sound).
2064 animEventsProcessing(previousTimeOffset
, animOffset(MOVE
));
2066 // Get the right pos from PACS.
2067 if(_Primitive
&& GR
)
2069 UGlobalPosition gPos
;
2070 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
2072 // Set the new current pos.
2073 pos(GR
->getGlobalPosition(gPos
));
2075 // Set the direction.
2076 if( ClientCfg
.AutomaticCamera
)
2080 // Reset the TPV when outdoor if needed.
2081 if(_Primitive
&& GR
)
2083 UGlobalPosition gPos
;
2084 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
2085 if(!GR
->isInterior(gPos
))
2087 viewMode(ThirdPV
, false);
2089 // Current time is now the entity time too.
2090 _LastFrameTime
= t
*0.001;
2093 //-----------------------------------------------
2095 // Update the PACS position after the evalCollision. The entity position is set too. This is fast.
2096 // If the entity position is too far from its PACS position, setGlobalPosition is called.
2097 // After this call, the position.z is valid.
2098 //-----------------------------------------------
2099 void CUserEntity::pacsFinalizeMove() // virtual
2104 // Get the global position
2105 _FinalPacsPos
= _Primitive
->getFinalPosition(dynamicWI
);
2107 // Get the global position
2108 UGlobalPosition gPos
;
2109 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
2110 if(gPos
.InstanceId
!= -1)
2113 if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
2115 CEntityCL
*mount
= EntitiesMngr
.entity(_Mount
);
2117 mount
->pacsPos(pos());
2121 _SetGlobalPositionDone
= false;
2122 }// pacsFinalizeMove //
2125 //-----------------------------------------------
2127 // Apply the behaviour for the user.
2128 //-----------------------------------------------
2129 void CUserEntity::applyBehaviour(const CBehaviourContext
&behaviourContext
) // virtual
2131 const MBEHAV::CBehaviour
&behaviour
= behaviourContext
.Behav
;
2132 // Special code for the First Person View
2133 if((viewMode() == FirstPV
) && behaviour
.isCombat())
2135 // Backup the current behaviour.
2136 _CurrentBehaviour
= behaviourContext
.Behav
;
2139 // check if self-target
2140 bool selfSpell
= false;
2141 if (behaviourContext
.Targets
.Targets
.size() == 1)
2143 if (behaviourContext
.Targets
.Targets
[0].TargetSlot
== 0)
2149 switch(behaviour
.Behaviour
)
2151 case MBEHAV::CAST_OFF
:
2152 case MBEHAV::CAST_OFF_FAIL
:
2153 case MBEHAV::CAST_OFF_SUCCESS
:
2154 case MBEHAV::CAST_OFF_LINK
:
2162 // Default target hit dates
2163 static vector
<double> targetHitDates
;
2164 targetHitDates
.clear();
2165 targetHitDates
.resize(behaviourContext
.Targets
.Targets
.size(), TimeInSec
);
2167 // cast projectiles/impact linked to behaviour
2168 updateCurrentAttack();
2169 if (isCurrentBehaviourAttackEnd())
2171 // In First Person (don't care), use a default animation => will choose a default delay of 0.5
2172 performCurrentAttackEnd(behaviourContext
, selfSpell
&& isOffensif
,
2173 targetHitDates
, CAnimationStateSheet::UnknownState
);
2176 _RightFXActivated
= false;
2177 _LeftFXActivated
= false;
2180 animState (MOVE
, CAnimationStateSheet::FirstPersonAttack
);
2181 animIndex (MOVE
, CAnimation::UnknownAnim
);
2182 animOffset(MOVE
, 0.0);
2183 _CurrentState
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
2184 if(_CurrentState
&& _CurrentAnimSet
[MOVE
])
2186 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
2189 animIndex(MOVE
, animStatePtr
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), 0.0));
2190 if(animIndex(MOVE
) != CAnimation::UnknownAnim
)
2194 _PlayList
->setAnimation (MOVE
, animId(MOVE
));
2195 _PlayList
->setTimeOrigin (MOVE
, ryzomGetLocalTime ()*0.001);//_LastFrameTime);
2196 _AnimAttackOn
= true;
2197 updateVisualDisplay();
2203 // Start FX to play at the animation beginning like trails.
2204 if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
2206 startItemAttackFXs(_CurrentBehaviour
.Range
.ImpactIntensity
!= 0, _CurrentBehaviour
.Range
.ImpactIntensity
);
2210 startItemAttackFXs(_CurrentBehaviour
.Combat
.ImpactIntensity
!= 0 && _CurrentBehaviour
.Combat
.HitType
!= HITTYPE::Failed
, _CurrentBehaviour
.Combat
.ImpactIntensity
);
2213 applyBehaviourFlyingHPs(behaviourContext
, behaviour
, targetHitDates
);
2215 // In third person view (or camera mode), play the same way than for the others.
2217 CPlayerCL::applyBehaviour(behaviourContext
);
2218 }// applyBehaviour //
2220 //---------------------------------------------------
2222 // Method to Flag the character as dead and do everything needed.
2223 //---------------------------------------------------
2224 void CUserEntity::setDead() // virtual
2226 // User is dead -> NO FOLLOW
2228 // Remove the selection.
2229 UserEntity
->selection(CLFECOMMON::INVALID_SLOT
);
2230 // Death Mode Control and view.
2231 UserControls
.mode(CUserControls::DeathMode
);
2232 // Play the FX for the death
2233 NL3D::UParticleSystemInstance deathFX
= FXMngr
.instantFX(ClientCfg
.DeadFXName
);
2234 if(!deathFX
.empty())
2235 deathFX
.setPos(selectBox().getCenter());
2237 // Last teleport is a death
2239 // get player's kami fame
2240 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2242 uint kamiFameIndex
= CStaticFames::getInstance().getFactionIndex("kami");
2243 if (pIM
&& kamiFameIndex
!= CStaticFames::INVALID_FACTION_INDEX
)
2245 CCDBNodeLeaf
*pLeafKamiFame
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", kamiFameIndex
- 1), false);
2246 if (pLeafKamiFame
!= NULL
)
2247 kamiFame
= pLeafKamiFame
->getValue8();
2250 // get player's karavan fame
2251 sint8 karavanFame
= 0;
2252 uint karavanFameIndex
= CStaticFames::getInstance().getFactionIndex("karavan");
2253 if (pIM
&& karavanFameIndex
!= CStaticFames::INVALID_FACTION_INDEX
)
2255 CCDBNodeLeaf
*pLeafKaravanFame
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:FAME:PLAYER%d:VALUE", karavanFameIndex
- 1), false);
2256 if (pLeafKaravanFame
!= NULL
)
2257 karavanFame
= pLeafKaravanFame
->getValue8();
2260 // set loading background depending on player's faction fame
2261 if (kamiFame
> karavanFame
)
2262 LoadingBackground
= ResurectKamiBackground
;
2264 LoadingBackground
= ResurectKaravanBackground
;
2266 // disable or enable shadowing
2267 updateCastShadowMap();
2269 // enable death warning popup
2270 //CInterfaceManager * pIM = CInterfaceManager::getInstance();
2273 CAHManager::getInstance()->runActionHandler("set",NULL
,"dblink=UI:VARIABLES:DEATH_WARNING_WANTED|value=1");
2277 //-----------------------------------------------
2280 //-----------------------------------------------
2281 void CUserEntity::skillUp()
2283 // Play the FX for the death
2284 NL3D::UParticleSystemInstance skillUpFX
= FXMngr
.instantFX(ClientCfg
.SkillUpFXName
);
2285 skillUpFX
.setClusterSystem(getClusterSystem());
2286 if(!skillUpFX
.empty())
2287 skillUpFX
.setPos(pos());
2290 //-----------------------------------------------
2292 // After a few time, if the user is still in collision with someone else, remove collisions with other entitites.
2293 //-----------------------------------------------
2294 void CUserEntity::startColTimer()
2298 if(_ColRemoved
==false)
2300 if((T1
-_ColStartTime
) > ClientCfg
.TimeToRemoveCol
)
2302 EntitiesMngr
.removeColUserOther();
2312 }// startColTimer //
2314 //-----------------------------------------------
2316 // Called when the user is no more in collision with another entity.
2317 //-----------------------------------------------
2318 void CUserEntity::stopColTimer()
2320 // Restore collisions.
2323 EntitiesMngr
.restoreColUserOther();
2324 _ColRemoved
= false;
2331 //-----------------------------------------------
2333 // Return the current max speed for the entity in meter per sec
2334 // The method return a max according to the speed factor (given by the server)
2335 // It's also return a value according to the landscape (water)
2336 // Also managed mounts
2337 //-----------------------------------------------
2338 double CUserEntity::getMaxSpeed() const // virtual
2340 // Max Defined speed according to the current factor (slow, or speed increased)
2341 double maxSpeed
= _SpeedFactor
.getValue();
2353 else if(isSwimming())
2356 // We are a Ring DM so speed up a litle
2357 if (_R2CharMode
== R2::TCharMode::Editer
|| _R2CharMode
== R2::TCharMode::Dm
)
2359 maxSpeed
*= ClientCfg
.DmRun
/ 2;
2369 // Run if mount not hungry, otherwise, use walk velocity
2370 if (_MountHunger
.canRun())
2371 maxSpeed
*= (double)getMountRunVelocity();
2373 maxSpeed
*= (double)getMountWalkVelocity();
2378 maxSpeed
*= runVelocity();
2380 // Return the current max
2382 // return ClientCfg.Run * _SpeedFactor.getValue();
2386 //-----------------------------------------------
2388 // Snap the user to the ground.
2389 //-----------------------------------------------
2390 void CUserEntity::snapToGround()
2392 CEntityCL::snapToGround();
2394 updateSound (ryzomGetLocalTime ());
2395 }// // snapToGround //
2398 //-----------------------------------------------
2400 //-----------------------------------------------
2401 void CUserEntity::updateSound(const TTime
&time
)
2403 H_AUTO_USE ( RZ_Client_Update_Sound
);
2405 // no sound manager, no need to update sound
2406 if (SoundMngr
== NULL
)
2409 if (!(StereoHMD
&& true)) // TODO: ClientCfg.Headphone
2411 // NOTE: StereoHMD+Headphone impl in main_loop.cpp
2412 SoundMngr
->setListenerPos(pos());
2413 const CMatrix
&camMat
= MainCam
.getMatrix();
2414 SoundMngr
->setListenerOrientation(camMat
.getJ(), camMat
.getK());
2417 if (ClientCfg
.Light
)
2420 // step sound of self : refind the sound animations associated to the walk and run animation
2422 static bool computeAnim
= true;
2423 static bool lastMode
= false;
2424 static TTime previousTime
= 0;
2425 static TTime stepTime
= 0;
2426 static bool leftRight
= false;
2427 static NLSOUND::CSoundAnimMarker
*leftStep
= 0;
2428 static NLSOUND::CSoundAnimMarker
*rightStep
= 0;
2430 // TODO : Remove when bug corrected:
2431 if (_Gender
== GSGENDER::unknown
)
2433 nlwarning("CUserEntity::updateSound : The gender is unknown, forcing it to male");
2434 _Gender
= GSGENDER::male
;
2437 // force recompute of anim if walk/run change.
2438 computeAnim
= computeAnim
|| (lastMode
!= _Run
);
2440 // Check the sound animation to find the time between to step
2441 if(computeAnim
&& SoundMngr
&& _CurrentAnimSet
[MOVE
] != 0) // && _SoundId[MOVE] != NLSOUND::CSoundAnimationNoId)
2444 TAnimStateId mode
= _Run
? CAnimationStateSheet::Run
: CAnimationStateSheet::Walk
;
2445 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState (mode
);
2448 ::CAnimation::TAnimId animId
= animStatePtr
->chooseAnim (_AnimJobSpecialisation
, people(), getGender(), 0);
2449 if (animId
!= ::CAnimation::UnknownAnim
)
2451 const ::CAnimation
*anim
= animStatePtr
->getAnimation (animId
);
2454 // Select the sound ID
2455 _SoundId
[MOVE
] = anim
->soundId();
2457 if (_SoundId
[MOVE
] != NLSOUND::CSoundAnimationNoId
)
2459 // retrieve the anim
2460 NLSOUND::CSoundAnimManager
*mgr
= NLSOUND::CSoundAnimManager::instance();
2462 string name
= mgr
->idToName(_SoundId
[MOVE
]);
2466 NLSOUND::CSoundAnimation
*sanim
= mgr
->findAnimation(name
);
2467 if (sanim
->countMarkers() != 2)
2469 static set
<string
> warnOnce
;
2470 if (warnOnce
.find(sanim
->getName()) == warnOnce
.end())
2472 nlwarning("Sound animation '%s' has not 2 markers, not a biped ? (Display Once)", sanim
->getName().c_str());
2473 warnOnce
.insert(sanim
->getName());
2478 stepTime
= TTime((sanim
->getMarker(1)->getTime() - sanim
->getMarker(0)->getTime()) * 1000);
2479 rightStep
= sanim
->getMarker(0);
2480 leftStep
= sanim
->getMarker(1);
2481 computeAnim
= false;
2490 if( SoundMngr
&& _Mode
== MBEHAV::NORMAL
)
2492 if (!getVelocity().isNull())
2494 if (stepTime
> 0 && time
-previousTime
>=stepTime
)
2496 previousTime
= time
;
2498 // set the sound 1 meter below listener
2499 _SoundContext
.Position
= pos() + CVector(0,0,-1);
2500 _SoundContext
.RelativeGain
= SoundMngr
->getUserEntitySoundLevel();;
2502 uint32 matId
= getGroundType();
2503 //nldebug("Current material = %u", matId);
2504 _SoundContext
.Args
[0] = matId
;
2509 // TODO : find the correct cluster
2510 leftStep
->play(SoundMngr
->getMixer(), NULL
, _SoundContext
);
2515 // TODO : find the correct cluster
2516 rightStep
->play(SoundMngr
->getMixer(), NULL
, _SoundContext
);
2518 // recompute a new sound anim
2522 leftRight
= !leftRight
;
2527 // resets the counter
2534 //-----------------------------------------------
2536 // rotate the body on the left or right (front changes).
2537 //-----------------------------------------------
2538 void CUserEntity::rotate(float ang
)
2544 front(m
* front().normed());
2548 //-----------------------------------------------
2549 // rotHeadVertically :
2550 // rotate the head vertically.
2551 //-----------------------------------------------
2552 void CUserEntity::rotHeadVertically(float ang
)
2554 setHeadPitch(_HeadPitch
+ang
);
2555 }// rotHeadVertically //
2558 //-----------------------------------------------
2559 // setHeadPitch(double hp)
2560 //-----------------------------------------------
2561 void CUserEntity::setHeadPitch(double hp
)
2564 const double bound
= Pi
/2 - 0.01; // epsilon to avoid gimbal lock
2565 clamp(_HeadPitch
, -bound
, bound
);
2568 //---------------------------------------------------
2570 // To Inform about an entity removed (to remove from selection for example).
2571 // This will remove the entity from the target.
2572 // \param slot : Slot of the entity that will be removed.
2573 //---------------------------------------------------
2574 void CUserEntity::slotRemoved(const CLFECOMMON::TCLEntityId
&slot
)
2577 CPlayerCL::slotRemoved(slot
);
2579 // reset also selection
2580 if(selection() == slot
)
2581 selection(CLFECOMMON::INVALID_SLOT
);
2584 //---------------------------------------------------
2586 // Change the entity selected.
2587 // \param slot : slot now selected (CLFECOMMON::INVALID_SLOT for an empty selection).
2588 // \warning Can be different from the entity targeted (in combat mode for example).
2589 //---------------------------------------------------
2590 void CUserEntity::selection(const CLFECOMMON::TCLEntityId
&slot
) // virtual
2592 //allows reselection in Ring client: even if the selected slots is equal to the selection,
2593 //the client must send the messages.
2594 if ((_Selection
== slot
) && !ClientCfg
.R2EDEnabled
)
2597 // The selection will be the entity to watch
2598 WatchedEntitySlot
= slot
;
2601 // Send the entity selected to the server.
2602 NetMngr
.pushTarget(slot
);
2605 // Target the slot on client, don't wait NetWork response
2607 _TargetSlotNoLag
= slot
;
2609 if (ClientCfg
.R2EDEnabled
)
2611 R2::getEditor().inGameSelection(slot
);
2615 // Change the current selection so un color the current selection.
2616 CEntityCL
*sel
= EntitiesMngr
.entity(_Selection
);
2618 sel
->visualSelectionStop(); // Blink off == restore to normal
2620 // Set the entity selected
2623 // Update visual selection and interface
2624 if ( sel
&& sel
->isForageSource() )
2625 sel
->buildInSceneInterface(); // remove focus on previous selected source
2626 sel
= EntitiesMngr
.entity(_Selection
);
2629 sel
->visualSelectionStart();
2630 if ( sel
->isForageSource() )
2631 sel
->buildInSceneInterface(); // set focus on new selected source
2635 // **** Update Target interface
2636 //get the new target slot and set it in the database
2637 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2638 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:SLOT")->setValue64(slot
);
2640 // Get the new target UID, and set in Database
2641 uint tgtSlot
= _Selection
;
2642 uint32 tgtEntityId
= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX
;
2643 CEntityCL
*entity
= NULL
;
2644 if (tgtSlot
!=CLFECOMMON::INVALID_SLOT
)
2646 entity
= EntitiesMngr
.entity(tgtSlot
);
2648 tgtEntityId
= entity
->dataSetId();
2651 // Set the User Target
2652 CCDBNodeLeaf
*prop
= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:TARGET:UID", false);
2654 prop
->setValue32(tgtEntityId
);
2656 // Bar Manager. Update now the Target View (so it takes VP if data available or 0... but result is immediate)
2657 CBarManager::getInstance()->setLocalTarget(tgtEntityId
);
2659 // **** Update DB for InGameMenu
2660 // clear the entries for mission option
2661 for(uint k
= 0; k
< NUM_MISSION_OPTIONS
; ++k
)
2663 CCDBNodeLeaf
*missionOption
= NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:TITLE", (int) k
), false);
2666 missionOption
->setValue32(0);
2668 CCDBNodeLeaf
*playerGiftNeeded
= NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:PLAYER_GIFT_NEEDED", (int) k
), false);
2669 if (playerGiftNeeded
)
2671 playerGiftNeeded
->setValue32(0);
2674 missionOption
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:TITLE", (int) k
), false);
2677 missionOption
->setValue32(0);
2679 playerGiftNeeded
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:TARGET:CONTEXT_MENU:MISSIONS_OPTIONS:%d:PLAYER_GIFT_NEEDED", (int) k
), false);
2680 if (playerGiftNeeded
)
2682 playerGiftNeeded
->setValue32(0);
2685 /* TODO ULU : Add RP tags */
2688 if ((tgtSlot
!=CLFECOMMON::INVALID_SLOT
) && entity
)
2690 CPlayerCL
*pPlayer
= dynamic_cast<CPlayerCL
*>(entity
);
2695 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:mode"));
2698 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2699 tagMode->setTexture("pvp_orange.tga");
2700 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2701 tagMode->setTexture("pvp_red.tga");
2703 tagMode->setTexture("alpha_10.tga");
2706 /*// Pvp available actions (attack, heal, both)
2707 CViewBitmap * tagMode = dynamic_cast<CViewBitmap*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:target:pvp_tags:actions"));
2710 if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction)
2711 tag->setTexture("pvp_orange.tga");
2712 else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged)
2713 tag->setTexture("pvp_red.tga");
2715 tag->setTexture("alpha_10.tga");
2722 prop
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_URL", false);
2723 if(prop
) prop
->setValue32(0);
2724 prop
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:WEB_PAGE_TITLE", false);
2725 if(prop
) prop
->setValue32(0);
2727 // clear mission ring
2728 for(uint k
= 0; k
< BOTCHATTYPE::MaxR2MissionEntryDatabase
; ++k
)
2730 prop
= NLGUI::CDBManager::getInstance()->getDbProp(toString("LOCAL:TARGET:CONTEXT_MENU:MISSION_RING:%d:TITLE", k
), false);
2731 if(prop
) prop
->setValue32(0);
2735 prop
= NLGUI::CDBManager::getInstance()->getDbProp("LOCAL:TARGET:CONTEXT_MENU:PROGRAMMES", false);
2736 if(prop
) prop
->setValue32(0);
2737 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:TARGET:CONTEXT_MENU:PROGRAMMES");
2738 if(prop
) prop
->setValue32(0);
2739 // increment db counter for context menu
2740 pIM
->incLocalSyncActionCounter();
2744 //---------------------------------------------------
2746 // Method to place the user to attack the target and attack.
2747 //---------------------------------------------------
2748 void CUserEntity::moveToAttack()
2750 // **** For clarity, try to launch a "default attack" found in the memory bar instead of an "anonymous" action
2751 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
2752 uint32 memLine
, memSlot
;
2753 CEntityCL
*target
= EntitiesMngr
.entity(selection());
2755 CInventoryManager
*inv
= CInventoryManager::getInstance();
2757 // auto-equip with valid weapon
2758 if( ClientCfg
.AutoEquipTool
)
2762 // if no valid weapons in had -> auto-equip with last used weapons
2763 bool validWeaponInHand
= true;
2764 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
2767 validWeaponInHand
= inv
->isMeleeWeaponItem(rightHandSheet
) || inv
->isRangeWeaponItem(rightHandSheet
);
2769 if( !validWeaponInHand
)
2771 autoEquipWithLastUsedWeapons();
2774 // remember last used weapon(s)
2775 rememberWeaponsInHand();
2779 if(target
&& pPM
->findDefaultAttack(memLine
, memSlot
))
2781 // launch instead a phrase execution with this phrase
2782 executeCombatWithPhrase(target
, memLine
, memSlot
, true);
2784 // **** Else launch an anonymous "default attack"
2791 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
2793 melee
= inv
->isMeleeWeaponItem(rightHandSheet
);
2796 // Move to target if melee weapon
2798 moveTo(selection(), 2.0, CUserEntity::Attack
);
2799 // Just attack if range weapon.
2805 //---------------------------------------------------
2807 // Method to attack the target.
2808 //---------------------------------------------------
2809 void CUserEntity::attack()
2811 // execute the default attack
2812 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
2813 pPM
->executeDefaultAttack();
2816 CInventoryManager
*inv
= CInventoryManager::getInstance();
2819 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
2821 melee
= inv
->isMeleeWeaponItem(rightHandSheet
);
2824 // If option ON, follow when attacking.
2825 if(ClientCfg
.FollowOnAtk
)
2827 // Follow only if attacking with a melee weapon
2829 // enable, but don't reset camera rotation
2830 enableFollow(false);
2834 //---------------------------------------------------
2836 // Method to attack the target, with a special phrase
2837 //---------------------------------------------------
2838 void CUserEntity::attackWithPhrase()
2840 if( !canEngageCombat() )
2843 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
2845 // validate the execution on server
2846 pPM
->sendExecuteToServer(_MoveToPhraseMemoryLine
, _MoveToPhraseMemorySlot
, _MoveToPhraseCyclic
);
2848 // If the Attack is a "Next", and not a cycle, do a default attack
2849 if(!_MoveToPhraseCyclic
)
2850 pPM
->executeDefaultAttack();
2852 // If option ON, follow when attacking.
2853 if(ClientCfg
.FollowOnAtk
)
2856 CInventoryManager
*inv
= CInventoryManager::getInstance();
2859 uint32 rightHandSheet
= inv
->getRightHandItemSheet();
2861 melee
= inv
->isMeleeWeaponItem(rightHandSheet
);
2863 // Follow only if attacking with a melee weapon
2865 // enable, but don't reset camera rotation
2866 enableFollow(false);
2869 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
2870 _MoveToAction
= CUserEntity::None
;
2873 //-----------------------------------------------
2875 // your current target become the target of your current one.
2876 //-----------------------------------------------
2877 void CUserEntity::assist()
2879 assist(targetSlot());
2882 //-----------------------------------------------
2884 void CUserEntity::assist(uint slot
)
2886 // Check the current target
2887 if(slot
== CLFECOMMON::INVALID_SLOT
|| slot
== _Slot
)
2890 CEntityCL
*target
= EntitiesMngr
.entity(slot
);
2893 // Check the new slot.
2894 CLFECOMMON::TCLEntityId newSlot
= target
->targetSlot();
2895 if(newSlot
== CLFECOMMON::INVALID_SLOT
|| newSlot
== _Slot
)
2897 // Select the new target.
2901 //---------------------------------------------------
2903 // Method to disengage the target.
2904 //---------------------------------------------------
2905 void CUserEntity::disengage()
2907 // Set the database in local.
2910 IngameDbMngr
.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_MODE
), MBEHAV::NORMAL
); // Mode
2911 IngameDbMngr
.setProp("Entities:E0:P" + toString(CLFECOMMON::PROPERTY_TARGET_ID
), _Selection
); // Target
2912 UserEntity
->updateVisualProperty(0, CLFECOMMON::PROPERTY_MODE
);
2913 UserEntity
->updateVisualProperty(0, CLFECOMMON::PROPERTY_TARGET_ID
);
2918 const string msgName
= "COMBAT:DISENGAGE";
2920 if(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
2923 nlwarning("UE::disengage: unknown message named '%s'.", msgName
.c_str());
2925 // Change the current mode.
2926 mode(MBEHAV::NORMAL
);
2929 //-----------------------------------------------
2931 // Ask for the client to sit/stand ('true' to sit).
2932 //-----------------------------------------------
2933 bool CUserEntity::sit(bool s
)
2940 if(canSit() == true)
2946 if(mode(MBEHAV::SIT
))
2948 // autowalk disabled
2949 UserControls
.autowalkState(false);
2951 static const string msgName
= "COMMAND:SIT";
2953 if(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
2959 nlwarning("UE:sit: unknown message named '%s'.", msgName
.c_str());
2965 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2966 string msg
= CI18N::get("msgUserIsSitting");
2967 string cat
= getStringCategory(msg
, msg
);
2968 pIM
->displaySystemInfo(msg
, cat
);
2975 if(_Mode
== MBEHAV::SIT
)
2977 if(mode(MBEHAV::NORMAL
))
2979 static const string msgName
= "COMMAND:SIT";
2981 if(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
2987 nlwarning("UE:sit: unknown message named '%s'.", msgName
.c_str());
2992 // display stand msg
2993 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
2994 string msg
= CI18N::get("msgUserIsStanding");
2995 string cat
= getStringCategory(msg
, msg
);
2996 pIM
->displaySystemInfo(msg
, cat
);
3001 // if mode changed, Write to the UI database, to update views
3004 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3005 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_STAND", false);
3007 node
->setValue32(_Mode
!= MBEHAV::SIT
);
3014 //-----------------------------------------------
3016 // Return true if the user can sit.
3017 //-----------------------------------------------
3018 bool CUserEntity::canSit() const
3020 // If the user is not already sitting or is on a mount
3021 if(!isSit() && (!isRiding()) && !isDead() && !isSwimming())
3029 //-----------------------------------------------
3031 //-----------------------------------------------
3032 void CUserEntity::setAFK(bool b
, string afkTxt
)
3034 if( isAFK() == b
) return;
3038 if( isDead() || isRiding() || moveTo() || follow() )
3041 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3042 //sint64 start = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TSTART")->getValue64();
3043 //sint64 end = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TEND")->getValue64();
3044 //sint64 type = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64();
3045 //sint64 num = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_NUMBER")->getValue64();
3048 if( NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:ACT_TYPE")->getValue64() != 0 )
3052 if( !isSit() && !isSwimming() )
3054 if( !mode(MBEHAV::REST
) )
3060 if( !isSit() && !isSwimming() )
3065 mode(MBEHAV::NORMAL
);
3070 static const string msgName
= "COMMAND:AFK";
3072 if(GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
3078 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgName
.c_str());
3081 ucstring ucstr
; // TODO: UTF-8 (serial)
3082 ucstr
.fromUtf8( afkTxt
);
3083 CBitMemStream outTxt
;
3084 static const string msgNameTxt
= "STRING:AFK_TXT";
3085 if( GenericMsgHeaderMngr
.pushNameToStream(msgNameTxt
,outTxt
) )
3087 outTxt
.serial( ucstr
);
3088 NetMngr
.push( outTxt
);
3092 nlwarning("CUserEntity:setAFK: unknown message named '%s'.", msgNameTxt
.c_str());
3098 //-----------------------------------------------
3100 //-----------------------------------------------
3101 void CUserEntity::rollDice(sint16 min
, sint16 max
, bool local
)
3105 // no need to broadcast over network here
3106 static NLMISC::CRandom
* dice
= (NLMISC::CRandom
*)NULL
;
3109 dice
= new NLMISC::CRandom
;
3110 dice
->srand(CTickEventHandler::getGameCycle());
3112 sint16 roll
= min
+ (sint16
)dice
->rand(max
-min
);
3114 string msg
= CI18N::get("msgRollDiceLocal");
3115 strFindReplace(msg
, "%min", toString(min
));
3116 strFindReplace(msg
, "%max", toString(max
));
3117 strFindReplace(msg
, "%roll", toString(roll
));
3119 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3121 pIM
->displaySystemInfo(msg
, getStringCategory(msg
, msg
));
3124 const string msgName
= "COMMAND:RANDOM";
3126 if (GenericMsgHeaderMngr
.pushNameToStream(msgName
, out
))
3133 nlwarning("CUserEntity:rollDice: unknown message named '%s'.", msgName
.c_str());
3136 //---------------------------------------------------
3137 // canEngageCombat :
3138 // return true if user can engage melee combat, else return false and display system msg
3139 //---------------------------------------------------
3140 bool CUserEntity::canEngageCombat()
3144 // display "you can't fight while sitting" message)
3145 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3146 string msg
= CI18N::get("msgCantFightSit");
3147 string cat
= getStringCategory(msg
, msg
);
3148 pIM
->displaySystemInfo(msg
, cat
);
3155 // display "you can't fight while swiming" message)
3156 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3157 string msg
= CI18N::get("msgCantFightSwim");
3158 string cat
= getStringCategory(msg
, msg
);
3159 pIM
->displaySystemInfo(msg
, cat
);
3166 // display "you can't fight while swimming" message)
3167 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3168 string msg
= CI18N::get("msgCantFightRide");
3169 string cat
= getStringCategory(msg
, msg
);
3170 pIM
->displaySystemInfo(msg
, cat
);
3176 } // canEngageCombat //
3179 //---------------------------------------------------
3181 // Change the View (First/Third Person View).
3182 //---------------------------------------------------
3183 void CUserEntity::viewMode(CUserEntity::TView viewMode
, bool changeView
)
3187 // First Person View
3190 ClientCfg
.FPV
= true;
3191 if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
3193 CEntityCL
*mount
= EntitiesMngr
.entity(_Mount
);
3195 mount
->displayable(false);
3197 _HiddenMount
= _Mount
;
3202 bool autoWalk
= UserControls
.autowalkState();
3203 UserControls
.mode(CUserControls::MountMode
);
3205 UserControls
.autowalkState( true );
3208 UserControls
.mode(CUserControls::InterfaceMode
);
3211 // Third Person View
3214 ClientCfg
.FPV
= false;
3216 if(_HiddenMount
!= CLFECOMMON::INVALID_SLOT
)
3218 CEntityCL
*mount
= EntitiesMngr
.entity(_HiddenMount
);
3220 mount
->displayable(true);
3222 _HiddenMount
= CLFECOMMON::INVALID_SLOT
;
3225 UserControls
.mode(CUserControls::ThirdMode
);
3230 nlwarning("UE:viewMode: Unknown View Asked '%d'.", (sint
)viewMode
);
3234 // Change the current View like asked.
3235 _ViewMode
= viewMode
;
3237 // disable or enable shadowing
3238 updateCastShadowMap();
3241 //-----------------------------------------------
3243 // Toggle Camera (First/Third Person)
3244 //-----------------------------------------------
3245 void CUserEntity::toggleCamera()
3247 // You cannot change the camera view when dead.
3250 // Only if not inside a building.
3251 if(!UserEntity
->forceIndoorFPV())
3253 // Leave the 1st Person Mode -> Enter the 3rd Person View Mode
3254 if (UserEntity
->viewMode() == CUserEntity::FirstPV
)
3255 UserEntity
->viewMode(CUserEntity::ThirdPV
);
3256 // Leave the 3rd Person Mode -> Enter the 1st Person View Mode
3258 UserEntity
->viewMode(CUserEntity::FirstPV
);
3262 //-----------------------------------------------
3263 // forceCameraFirstPerson :
3264 // Force Camera to First Person View
3265 //-----------------------------------------------
3266 void CUserEntity::forceCameraFirstPerson()
3268 // You cannot change the camera view when dead.
3271 // Only if not inside a building.
3272 if(!UserEntity
->forceIndoorFPV())
3274 if (UserEntity
->viewMode() != CUserEntity::FirstPV
)
3275 //Enter the 1st Person View Mode
3276 UserEntity
->viewMode(CUserEntity::FirstPV
);
3278 }// forceCameraFirstPerson //
3280 //---------------------------------------------------
3282 // Return the entity scale. (return 1.0 if there is any problem).
3283 // \todo GUIGUI : do we have to take care of the user's race kwnowing it can favour him ?
3284 //---------------------------------------------------
3285 float CUserEntity::getScale() const // virtual
3291 //---------------------------------------------------
3292 // removeCheckPrimitive :
3293 // Remove the check primitive
3294 //---------------------------------------------------
3295 void CUserEntity::removeCheckPrimitive()
3297 if(PACS
&& _CheckPrimitive
)
3298 PACS
->removePrimitive(_CheckPrimitive
);
3299 _CheckPrimitive
= 0;
3302 //---------------------------------------------------
3303 // removePrimitive :
3304 // Remove the primitive
3305 //---------------------------------------------------
3306 void CUserEntity::removePrimitive() // virtual (will not be called by ~CEntityCL())
3308 // Remove the Primitive used for check
3309 removeCheckPrimitive();
3311 // Remove the other primitive
3312 CPlayerCL::removePrimitive();
3313 }// removePrimitive //
3315 //---------------------------------------------------
3316 // computePrimitive :
3317 // Create a primitive for the entity.
3318 //---------------------------------------------------
3319 void CUserEntity::computePrimitive() // virtual
3321 // Initialize the primitive.
3322 if(initPrimitive(0.5f
, 2.0f
, 0.0f
, 0.0f
, UMovePrimitive::Slide
, (UMovePrimitive::TTrigger
)(UMovePrimitive::OverlapTrigger
| UMovePrimitive::EnterTrigger
), MaskColPlayer
, MaskColPlayer
| MaskColNpc
| MaskColDoor
))
3323 _Primitive
->insertInWorldImage(dynamicWI
);
3324 // Set the position.
3326 }// computePrimitive //
3329 //---------------------------------------------------
3331 // Return if the user is already busy (combat/bo chat/loot/ etc.).
3332 //---------------------------------------------------
3333 bool CUserEntity::isBusy() const
3335 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3338 // TODO : put the right DB entry !
3340 CCDBNodeLeaf
*nod
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:INVENTORY:EXCHANGE:BEGUN", false);
3343 if(nod
->getValueBool())
3347 // nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:EXCHANGE:BEGUN'.");
3351 static const uint nbSlot = 4;
3353 for(i=0; i<nbSlot; ++i)
3355 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::pickup, i), false);
3358 if(nod->getValue32() != 0)
3362 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::pickup, i);
3366 for(i=0; i<nbSlot; ++i)
3368 nod = NLGUI::CDBManager::getInstance()->getDbProp(NLMISC::toString("SERVER:INVENTORY:%d:%d:SHEET", INVENTORIES::harvest, i), false);
3371 if(nod->getValue32() != 0)
3375 nlwarning("UE:isBusy: Cannot get the nod 'SERVER:INVENTORY:%d:%d:SHEET'.", INVENTORIES::harvest, i);
3380 CBotChatPage
* currPage
= CBotChatManager::getInstance()->getCurrPage();
3381 if( currPage
!= NULL
)
3391 //---------------------------------------------------
3392 // updateVisualDisplay :
3393 // Show/Hide all or parts of the user body.
3394 // todo GUIGUI : it's bad for the _Face to be a separated instance
3395 //---------------------------------------------------
3396 void CUserEntity::updateVisualDisplay()
3398 // We need a skeleton.
3399 if(_Skeleton
.empty())
3403 if(UserControls
.isInternalView() || View
.forceFirstPersonView())
3406 if (_Mount
!= CLFECOMMON::INVALID_SLOT
)
3408 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
3410 mount
->displayable(false);
3412 _HiddenMount
= _Mount
;
3414 else if (_HiddenMount
!= CLFECOMMON::INVALID_SLOT
)
3416 // not on mount anymore, but still in FPV
3417 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_HiddenMount
));
3419 mount
->displayable(true);
3421 _HiddenMount
= CLFECOMMON::INVALID_SLOT
;
3424 // Hide all user body parts.
3425 for(uint i
=0; i
<_Instances
.size(); ++i
)
3426 if(!_Instances
[i
].Current
.empty())
3428 _Instances
[i
].Current
.hide();
3429 _Instances
[i
].hideStaticFXs();
3432 if(!_Face
.Current
.empty())
3433 _Face
.Current
.hide();
3435 // We want to display weapons in 1st person view when attacking.
3436 if(modeWithHiddenItems() == false)
3438 if( _Mode
== MBEHAV::COMBAT_FLOAT
|| _Mode
== MBEHAV::COMBAT
)
3440 if( _ObjectsVisible
)
3442 // Show just Few parts
3443 if(!_Instances
[SLOTTYPE::HANDS_SLOT
].Current
.empty())
3445 _Instances
[SLOTTYPE::HANDS_SLOT
].Current
.show();
3446 _Instances
[SLOTTYPE::HANDS_SLOT
].showStaticFXs();
3448 if(!_Instances
[SLOTTYPE::ARMS_SLOT
].Current
.empty())
3450 _Instances
[SLOTTYPE::ARMS_SLOT
].Current
.show();
3451 _Instances
[SLOTTYPE::ARMS_SLOT
].showStaticFXs();
3453 if(!_Instances
[SLOTTYPE::RIGHT_HAND_SLOT
].Current
.empty())
3455 _Instances
[SLOTTYPE::RIGHT_HAND_SLOT
].Current
.show();
3456 _Instances
[SLOTTYPE::RIGHT_HAND_SLOT
].showStaticFXs();
3458 if(!_Instances
[SLOTTYPE::LEFT_HAND_SLOT
].Current
.empty())
3460 _Instances
[SLOTTYPE::LEFT_HAND_SLOT
].Current
.show();
3461 _Instances
[SLOTTYPE::LEFT_HAND_SLOT
].showStaticFXs();
3470 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
3473 mount
->displayable(true);
3475 showOrHideBodyParts( objectsVisible() );
3480 if(!_Face.Current.empty())
3481 _Face.Current.show();
3484 }// updateVisualDisplay //
3486 //---------------------------------------------------
3488 // Show/Hide the user light.
3489 //---------------------------------------------------
3490 void CUserEntity::light()
3495 // Check there is a skeleton and a bone to stick the light before to create it.
3496 if(!_Skeleton
.empty() && _NameBoneId
!=-1)
3498 _Light
= Scene
->createPointLight();
3501 // front of the player
3502 _Light
.setPos(0.f
,0.3f
,0.f
);
3504 _Light
.setupAttenuation(12.0f
, 20.0f
);
3506 _Skeleton
.stickObject(_Light
, _NameBoneId
);
3507 // The player light is the only one who can interact with Lightmapped objects
3508 _Light
.setInfluenceLightMap(true);
3512 NL3D::UInstance inst;
3513 inst= Scene->createInstance("box.shape");
3516 inst.setScale(0.2f, 0.2f, 0.2f);
3517 inst.parent(_Light);
3523 nlwarning("UE:light: there is no skeleton or Name Bone to stick the light.");
3525 // Turn On/Off the Light
3526 _LightOn
= !_LightOn
;
3536 //---------------------------------------------------
3537 // CSpeedFactor::init :
3538 // Initialize the Observer for the Speed Factor.
3539 //---------------------------------------------------
3540 void CUserEntity::CSpeedFactor::init()
3542 _Value
= 1.0f
; // Default speed factor is 1.
3543 _ServerFactor
= 1.0f
;
3544 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3545 CCDBNodeLeaf
*pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3548 /* if(!pNodeLeaf->addToLeaves(this))
3549 nlwarning("UE:SP:init: cannot add the observer");*/
3550 ICDBNode::CTextId textId
;
3551 pNodeLeaf
->addObserver(this, textId
);
3552 if ( pNodeLeaf
->getValue64() != 0 )
3553 _Value
= ((float)pNodeLeaf
->getValue64())/100.0f
; // may have been received before
3556 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3557 }// CSpeedFactor::init //
3559 //---------------------------------------------------
3560 // CMountHunger::init :
3561 //---------------------------------------------------
3562 void CUserEntity::CMountHunger::init()
3565 //---------------------------------------------------
3566 // CMountSpeeds::init :
3567 //---------------------------------------------------
3568 void CUserEntity::CMountSpeeds::init()
3570 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3571 CCDBNodeLeaf
*pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3572 BOMB_IF( ! pNodeLeaf
, "MOUNT_WALK_SPEED not found", return );
3575 ICDBNode::CTextId textId
;
3576 pNodeLeaf
->addObserver(this, textId
);
3577 _WalkSpeed
= ((float)pNodeLeaf
->getValue32()) / 1000.0f
; // may have been received before
3579 pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3580 BOMB_IF( ! pNodeLeaf
, "MOUNT_RUN_SPEED not found", return );
3583 ICDBNode::CTextId textId
;
3584 pNodeLeaf
->addObserver(this, textId
);
3585 _RunSpeed
= ((float)pNodeLeaf
->getValue32()) / 1000.0f
; // may have been received before
3589 //---------------------------------------------------
3591 void CUserEntity::CSpeedFactor::release()
3593 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3594 CCDBNodeLeaf
*pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:SPEED_FACTOR", false);
3597 /* if(!pNodeLeaf->addToLeaves(this))
3598 nlwarning("UE:SP:init: cannot add the observer");*/
3599 ICDBNode::CTextId textId
;
3600 pNodeLeaf
->removeObserver(this, textId
);
3603 nlwarning("UE:SP:init: 'SERVER:USER:SPEED_FACTOR' does not exist.");
3604 }// CSpeedFactor::init //
3606 void CUserEntity::CMountHunger::release()
3609 void CUserEntity::CMountSpeeds::release()
3611 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3612 CCDBNodeLeaf
*pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_WALK_SPEED", false );
3613 BOMB_IF( ! pNodeLeaf
, "MOUNT_WALK_SPEED not found", return );
3616 ICDBNode::CTextId textId
;
3617 pNodeLeaf
->removeObserver(this, textId
);
3619 pNodeLeaf
= NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:USER:MOUNT_RUN_SPEED", false );
3620 BOMB_IF( ! pNodeLeaf
, "MOUNT_RUN_SPEED not found", return );
3623 ICDBNode::CTextId textId
;
3624 pNodeLeaf
->removeObserver(this, textId
);
3629 //---------------------------------------------------
3630 // CSpeedFactor::update :
3631 // Callback called to update the speed factor.
3632 //---------------------------------------------------
3633 void CUserEntity::CSpeedFactor::update(ICDBNode
*node
) // virtual
3635 CCDBNodeLeaf
*leaf
= safe_cast
<CCDBNodeLeaf
*>(node
);
3636 _Value
= ((float)leaf
->getValue64())/100.0f
;
3637 //nlinfo("SpeedFactor changed to %f / %" NL_I64 "u", _Value, leaf->getValue64());
3639 // clamp the value (2.0 is the egg item or the level 6 speed up power up, nothing should be faster)
3640 // commented because ring editor speed is in fact faster
3643 //nlwarning("HACK: you try to change the speed factor to %f", _Value);
3647 }// CSpeedFactor::update //
3651 * Return true if the mount can run. Precondition: UserEntity->isRiding().
3653 bool CUserEntity::CMountHunger::canRun() const
3655 CEntityCL
*mountEntity
= UserEntity
->getMountEntity();
3656 if ( ! mountEntity
)
3659 // Find the mount's db leaf and check hunger
3660 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3661 CCDBNodeBranch
*animalsNode
= safe_cast
<CCDBNodeBranch
*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3662 BOMB_IF( ! animalsNode
, "! animalsNode", return false; );
3663 uint nbAnimals
= (uint
)animalsNode
->getNbNodes();
3664 for ( uint i
=0; i
!=nbAnimals
; ++i
)
3666 ICDBNode
*beastNode
= animalsNode
->getNode( i
);
3667 CCDBNodeLeaf
*uidLeaf
= safe_cast
<CCDBNodeLeaf
*>(beastNode
->getNode( ICDBNode::CTextId( "UID" ) ));
3668 if ( ((CLFECOMMON::TClientDataSetIndex
)uidLeaf
->getValue32()) == mountEntity
->dataSetId() )
3670 CCDBNodeLeaf
*hungerLeaf
= safe_cast
<CCDBNodeLeaf
*>(beastNode
->getNode( ICDBNode::CTextId( "HUNGER" ) ));
3671 return (hungerLeaf
->getValue32() != (sint
)ANIMAL_TYPE::DbHungryValue
);
3679 //---------------------------------------------------
3680 // CMountSpeeds::update :
3681 // Callback called to update the mount speed.
3682 //---------------------------------------------------
3683 void CUserEntity::CMountSpeeds::update(ICDBNode
* /* node */) // virtual
3685 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3686 _WalkSpeed
= ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_WALK_SPEED")->getValue32())) / 1000.0f
;
3687 _RunSpeed
= ((float)(NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:MOUNT_RUN_SPEED")->getValue32())) / 1000.0f
;
3692 * Return the mount entity if the user is riding, otherwise NULL
3694 CEntityCL
* CUserEntity::getMountEntity()
3696 if ( _Mount
< EntitiesMngr
.entities().size() )
3698 return EntitiesMngr
.entities()[_Mount
];
3704 * Return the DB entry for the specified user's animal (NULL if not found)
3706 CCDBNodeBranch
*CUserEntity::getBeastDBEntry( CLFECOMMON::TClientDataSetIndex uid
)
3708 // Find animal entry corresponding to datasetId
3709 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
3710 CCDBNodeBranch
*animalsNode
= safe_cast
<CCDBNodeBranch
*>(NLGUI::CDBManager::getInstance()->getDB()->getNode( ICDBNode::CTextId( "SERVER:PACK_ANIMAL" ), false ));
3711 BOMB_IF( ! animalsNode
, "! animalsNode", return NULL
);
3712 uint nbAnimals
= (uint
)animalsNode
->getNbNodes();
3713 for ( uint i
=0; i
!=nbAnimals
; ++i
)
3715 ICDBNode
*beastNode
= animalsNode
->getNode( i
);
3716 CCDBNodeLeaf
*pNodeLeaf
= safe_cast
<CCDBNodeLeaf
*>(beastNode
->getNode( ICDBNode::CTextId( "UID" ) ));
3717 if ( pNodeLeaf
&& (pNodeLeaf
->getValue32() == (sint32
)uid
) )
3718 return (CCDBNodeBranch
*)beastNode
;
3724 //---------------------------------------------------
3726 // Display Debug Information.
3727 //---------------------------------------------------
3728 void CUserEntity::displayDebug(float x
, float &y
, float lineStep
) // virtual
3730 CPlayerCL::displayDebug(x
, y
, lineStep
);
3733 //---------------------------------------------------
3734 // displayModifiers :
3735 // Display dmg/heal numbers above the head.
3736 //---------------------------------------------------
3737 void CUserEntity::displayModifiers() // virtual
3739 if(!UserControls
.isInternalView())
3740 CPlayerCL::displayModifiers();
3741 }// displayModifiers //
3743 //---------------------------------------------------
3745 // Return 'true' is the entity is displayed.
3746 //---------------------------------------------------
3747 bool CUserEntity::isVisible() const // virtual
3749 return !UserControls
.isInternalView();
3757 //---------------------------------------------------
3759 // Read/Write Variables from/to the stream.
3760 //---------------------------------------------------
3761 void CUserEntity::readWrite(NLMISC::IStream
&f
)
3763 CPlayerCL::readWrite(f
);
3766 f
.serial(_SpeedFactor
);
3767 f
.serial(_FrontVelocity
);
3768 f
.serial(_LateralVelocity
);
3770 f
.serial(dummyHead
);
3771 f
.serial(_HeadPitch
);
3772 f
.serial(_EyesHeight
);
3774 f
.serial(_WalkVelocity
);
3775 f
.serial(_RunVelocity
);
3776 f
.serial(_CurrentVelocity
);
3777 f
.serial(_Selection
);
3779 f
.serial(_Interlocutor
);
3780 f
.serial(_Selectable
);
3784 f
.serial(_AnimAttackOn
);
3785 // f.serialEnum(_ViewMode);
3788 //---------------------------------------------------
3790 // To call after a read from a stream to re-initialize the entity.
3791 //---------------------------------------------------
3792 void CUserEntity::load() // virtual
3794 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
3795 // Insert the user into PACS at his saved position
3799 if(!_WaitForAppearance
)
3801 // Visual properties A
3802 sint64 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA
))->getValue64();
3803 updateVisualPropertyVpa(0, prop
); // Vpa udapte vpb and vpc too.
3808 //---------------------------------------------------
3809 void CUserEntity::CInvisibleObserver::update(ICDBNode
* node
)
3811 UserEntity
->buildInSceneInterface();
3814 //---------------------------------------------------
3815 void CUserEntity::CSkillPointsObserver::update(ICDBNode
* node
)
3817 if (FarTP
.isFarTPInProgress() || IngameDbMngr
.initInProgress()) // prevent from displaying at the beginning of a FarTP (due to RESET_BANK or CDB resetData())
3820 CInterfaceManager
*pIM
= CInterfaceManager::getInstance ();
3821 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
3824 sint32 oldValue
= leaf
->getOldValue32();
3827 sint delta
= leaf
->getValue32()-oldValue
;
3828 string deltaStr
= toString("%+d", delta
);
3831 const string
&spTitle
= CI18N::get(toString("uiSkillPointsBold%d",SpType
));
3834 CAHManager::getInstance()->runActionHandler("message_popup", NULL
, "text1="+deltaStr
+"|text0="+spTitle
);
3837 contextHelp ("skill_point");
3843 //---------------------------------------------------
3844 // CFameObserver::update
3845 //---------------------------------------------------
3846 void CUserEntity::CFameObserver::update(ICDBNode
* node
)
3848 CSkillManager
*pSM
= CSkillManager::getInstance();
3849 CCDBNodeLeaf
*leaf
= dynamic_cast<CCDBNodeLeaf
*>(node
);
3852 sint32 fameValue
= leaf
->getValue32();
3853 pSM
->tryToUnblockTitleFromMinFames( FactionIndex
, fameValue
);
3854 pSM
->tryToUnblockTitleFromMaxFames( FactionIndex
, fameValue
);
3859 //---------------------------------------------------
3860 void CUserEntity::makeTransparent(bool t
)
3862 CPlayerCL::makeTransparent(t
);
3864 uint32 opaMin
= getOpacityMin();
3865 uint8 opacity
= (uint8
)(opaMin
+ (255-opaMin
) * (1.0 - _TranspFactor
));
3867 getFace()->makeInstanceTransparent(opacity
, (uint8
)opaMin
);
3868 }// makeTransparent //
3870 //---------------------------------------------------
3871 void CUserEntity::setDiffuse(bool onOff
, NLMISC::CRGBA diffuse
)
3873 CPlayerCL::setDiffuse(onOff
, diffuse
);
3874 getFace()->setDiffuse(onOff
, diffuse
);
3880 // Helper for CUserEntity::extractRM()
3881 bool findExtractionActionInMemory( CSPhraseManager
*pm
, CSBrickManager
*bm
, uint memoryLine
, uint
& index
)
3884 for ( x
=0; x
!=PHRASE_MAX_MEMORY_SLOT
; ++x
)
3886 uint32 phraseSlot
= pm
->getMemorizedPhrase( memoryLine
, x
);
3887 const CSPhraseCom
& phraseCom
= pm
->getPhrase( phraseSlot
);
3888 if ( ! phraseCom
.empty() )
3890 CSBrickSheet
*brickSheet
= bm
->getBrick( phraseCom
.Bricks
[0] );
3891 if ( brickSheet
->isForageExtraction() && (!brickSheet
->MandatoryFamilies
.empty()) ) // assumes care root bricks have not mandatories
3901 //---------------------------------------------------
3902 void CUserEntity::extractRM()
3904 CSPhraseManager
*pm
= CSPhraseManager::getInstance();
3907 bool autoFindPhrase
= (_MoveToPhraseMemoryLine
== std::numeric_limits
<uint
>::max());
3908 if ( ! autoFindPhrase
)
3910 // Use clicked phrase
3911 memoryLine
= _MoveToPhraseMemoryLine
;
3912 index
= _MoveToPhraseMemorySlot
;
3916 // Find the first extraction phrase in the memory bar
3917 CSBrickManager
*bm
= CSBrickManager::getInstance();
3918 memoryLine
= pm
->getSelectedMemoryLineDB();
3919 if ( ! findExtractionActionInMemory( pm
, bm
, memoryLine
, index
) )
3921 // Search in other memory bar lines (because the auto-equip does not set the current line at once)
3922 memoryLine
= std::numeric_limits
<uint
>::max();
3923 uint nbLines
= pm
->getNbMemoryLines();
3924 for ( uint j
=0; j
!=nbLines
; ++j
)
3926 if ( j
== memoryLine
)
3928 if ( findExtractionActionInMemory( pm
, bm
, j
, index
) )
3937 if ( memoryLine
!= std::numeric_limits
<uint
>::max() )
3939 // Open the forage (but not for care actions). Necessary for the case of redoing an extraction after a Drop All on the same source.
3940 uint32 phraseId
= pm
->getMemorizedPhrase( memoryLine
, index
);
3941 if ( phraseId
!= 0 )
3943 const CSPhraseCom
& phraseCom
= pm
->getPhrase( phraseId
);
3944 if ( ! phraseCom
.empty() )
3946 CSBrickSheet
*rootBrick
= CSBrickManager::getInstance()->getBrick( phraseCom
.Bricks
[0] );
3949 if ( rootBrick
->IndexInFamily
== 1 ) // only extracting actions
3950 CTempInvManager::getInstance()->open( TEMP_INV_MODE::Forage
);
3955 // Cast the extraction. if autoFindPhrase, clientExecute() not already called.
3956 if ( autoFindPhrase
)
3958 // decide now if cyclic or not
3959 _MoveToPhraseCyclic
= true;
3960 if(pm
->avoidCyclicForPhrase(pm
->getMemorizedPhrase(memoryLine
, index
)))
3961 _MoveToPhraseCyclic
= false;
3963 // execute on client now
3964 pm
->clientExecute( memoryLine
, index
, _MoveToPhraseCyclic
);
3967 // execute on server
3968 pm
->sendExecuteToServer( memoryLine
, index
, _MoveToPhraseCyclic
);
3970 // Because sendExecuteToServer() has been called, must NOT cancelClientExecute() at resetAnyMoveTo()
3971 _MoveToAction
= CUserEntity::None
;
3975 CInterfaceManager::getInstance()->displaySystemInfo( CI18N::get("uiExtractionPhraseMissing"), "CHK" );
3981 // ***************************************************************************
3982 bool CUserEntity::canCastShadowMap() const
3984 if(!CCharacterCL::canCastShadowMap())
3987 // don't cast shadow in first person, but in death mode (third person actually...)
3988 return viewMode() != FirstPV
|| UserControls
.mode() == CUserControls::DeathMode
;
3992 // ***************************************************************************
3993 void CUserEntity::forceLookEntity(const NLMISC::CVectorD
&dir2targIn
, bool updateHeadPitch
, bool /* start */)
3995 CVectorD dir2targ
= dir2targIn
;
3996 float frontYawBefore
= 0.f
;
3997 float frontYawAfter
;
3999 // Third person: bkup current yaw
4000 if(viewMode()==ThirdPV
)
4002 frontYawBefore
= frontYaw();
4006 // **** Look at the entity
4007 dir2targ
.normalize();
4008 front(dir2targ
, false, false);
4012 if(viewMode() == FirstPV
)
4014 if(updateHeadPitch
&& _FollowForceHeadPitch
)
4016 // rotate the head to the target
4017 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot());
4020 // Both Z must be correct
4022 target
->snapToGround();
4024 // don't update to the real head position each frame (else jitter too much cause of target anim)
4025 CVector targetPos
= target
->pos() + CVector(0,0,_FollowHeadOffset
);
4027 // then look at this target
4028 CVector dirToTarget
= targetPos
- (pos()+CVector(0,0, UserEntity
->eyesHeight()));
4029 if((dirToTarget
.x
!= 0.0f
) || (dirToTarget
.y
!=0.0f
))
4031 dirToTarget
.normalize();
4032 setHeadPitch(atan2(dirToTarget
.z
, sqrt(sqr(dirToTarget
.x
)+sqr(dirToTarget
.y
))));
4036 /*if(ClientCfg.Fly!=0.f)
4038 nlinfo("Uy: %.3f. Hp: %.3f. UPos:(%.3f,%.3f,%.3f). TPos:(%.3f,%.3f,%.3f)",
4039 UserEntity->frontYaw(), UserEntity->getHeadPitch(), pos().x, pos().y, pos().z,
4040 targetPos.x, targetPos.y, targetPos.z);
4041 static float uy=0.f;
4042 static float hp=0.f;
4043 if( fabs(fmod(UserEntity->frontYaw()-uy, 2*Pi))>ClientCfg.Fly ||
4044 fabs(fmod(UserEntity->getHeadPitch()-hp, 2*Pi))>ClientCfg.Fly )
4046 nlinfo("YOYOBREAK: ^^^^^^^^^^");
4048 uy=UserEntity->frontYaw();
4049 hp=UserEntity->getHeadPitch();
4054 // **** Third person
4055 else if(viewMode()==ThirdPV
)
4057 // keep the current effective yaw. ONLY if no SmoothResetCameraYaw forced
4058 if(!UserControls
.isResetSmoothCDYForced())
4060 frontYawAfter
= frontYaw();
4061 float deltaYaw
= frontYawAfter
- frontYawBefore
;
4063 // compensate rotation (NB: it stops also any SmoothReset)
4064 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
4068 // when looking to an entity, if automatic camera, center the view on it.
4069 if( ClientCfg
.AutomaticCamera
/*&& start*/ )
4071 UserControls
.resetSmoothCameraDeltaYaw();
4076 // ***************************************************************************
4077 void CUserEntity::startForceLookEntity(CLFECOMMON::TCLEntityId slot
)
4079 // Start a new follow: force head pitch to follow by default
4080 _FollowForceHeadPitch
= true;
4081 // if a follow is started in first person, reset CameraYaw
4082 if(viewMode()==FirstPV
)
4083 UserControls
.resetCameraDeltaYaw();
4085 // reorient now (important else may have a time shift because of resetCameraDeltaYaw above)
4086 CEntityCL
*target
= EntitiesMngr
.entity(slot
);
4089 /* For complex reason, the target may not be still snapped on the ground. snap it now
4090 - this is because in common case, entities are snap only if they are visible (for speed up)
4091 => depends on camera
4092 - but in this case, the camera depends on real entity position (headPitch in first person)
4094 target
->snapToGround();
4096 // For FirstPerson targeting. Get the current target head offset
4097 _FollowHeadOffset
= 0.f
;
4099 if(target
->getHeadPos(headPos
))
4101 _FollowHeadOffset
= headPos
.z
- float(target
->pos().z
);
4104 // Look at the entity
4105 CVectorD dir2targ
= target
->pos() - pos();
4107 if(dir2targ
!=CVectorD::Null
)
4109 forceLookEntity(dir2targ
, true, true);
4115 // ***************************************************************************
4116 void CUserEntity::stopForceHeadPitchInFollow()
4118 _FollowForceHeadPitch
= false;
4121 // ***************************************************************************
4122 void CUserEntity::switchVelocity(bool userRequest
)
4124 if (ClientCfg
.R2EDEnabled
&& R2::getEditor().getMode() == R2::CEditor::EditionMode
)
4126 // when in the R2 editor, force to run all the time
4133 _CurrentVelocity
= _Run
? runVelocity() : walkVelocity();
4137 _RunWhenAble
= _Run
;
4140 // display message : your are running, you are walking
4141 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
4144 msg
= CI18N::get("msgUserIsRunning");
4146 msg
= CI18N::get("msgUserIsWalking");
4147 string cat
= getStringCategory(msg
, msg
);
4148 pIM
->displaySystemInfo(msg
, cat
);
4150 // Write to the UI database, to update views
4151 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:PLAYER_RUNNING", false);
4153 node
->setValue32(_Run
);
4156 //-----------------------------------------------
4157 // autoEquipWithLastUsedWeapons
4159 //-----------------------------------------------
4160 void CUserEntity::autoEquipWithLastUsedWeapons()
4162 CInventoryManager
*inv
= CInventoryManager::getInstance();
4169 inv
->unequip( "LOCAL:INVENTORY:HAND:1" );
4170 inv
->unequip( "LOCAL:INVENTORY:HAND:0" );
4174 if( _PreviousRightHandItem
.Sheet
!= 0 )
4176 // find item in bag with same properties than last used one in right hand
4177 for ( ir
=0; ir
<MAX_BAGINV_ENTRIES
; ++ir
)
4179 if( _PreviousRightHandItem
.Sheet
== inv
->getBagItem(ir
).getSheetID() &&
4180 _PreviousRightHandItem
.Quality
== inv
->getBagItem(ir
).getQuality() &&
4181 _PreviousRightHandItem
.Weight
== inv
->getBagItem(ir
).getWeight() &&
4182 _PreviousRightHandItem
.NameId
== inv
->getBagItem(ir
).getNameId() )
4187 if ( ir
!= MAX_BAGINV_ENTRIES
)
4190 string bagPath
= toString( "LOCAL:INVENTORY:BAG:%u", ir
);
4191 inv
->equip( bagPath
, "LOCAL:INVENTORY:HAND:0" );
4193 // Equip left hand if needed
4194 if( _PreviousLeftHandItem
.Sheet
!= 0 )
4196 for ( il
=0; il
<MAX_BAGINV_ENTRIES
; ++il
)
4199 _PreviousLeftHandItem
.Sheet
== inv
->getBagItem(il
).getSheetID() &&
4200 _PreviousLeftHandItem
.Quality
== inv
->getBagItem(il
).getQuality() &&
4201 _PreviousLeftHandItem
.Weight
== inv
->getBagItem(il
).getWeight() &&
4202 _PreviousLeftHandItem
.NameId
== inv
->getBagItem(il
).getNameId() )
4207 if ( il
!= MAX_BAGINV_ENTRIES
)
4209 bagPath
= toString( "LOCAL:INVENTORY:BAG:%u", il
);
4210 inv
->equip( bagPath
, "LOCAL:INVENTORY:HAND:1" );
4218 // TODO : choose the best one
4223 // ***************************************************************************
4224 void CUserEntity::executeCombatWithPhrase(CEntityCL
*target
, uint32 memoryLine
, uint32 memoryIndex
, bool cyclic
)
4227 CSPhraseManager
*pPM
= CSPhraseManager::getInstance();
4229 // is a melee combat?
4230 bool meleeCombat
= false;
4231 // empty hand => yes!
4233 uint32 rightHandSheet
= getInventory().getRightHandItemSheet();
4235 meleeCombat
= getInventory().isMeleeWeaponItem(rightHandSheet
);
4237 // If melee combat, and if the user entity is not well placed for fight, or if it has changed his target
4240 !target
->isPlacedToFight(pos(), front(), attackRadius() + ClientCfg
.AttackDist
) ||
4241 target
->slot()!=_LastExecuteCombatSlot
4245 _LastExecuteCombatSlot
= target
->slot();
4247 // Cancel any follow
4250 // Launch the moveToCombatPhrase, canceling any old action client execution.
4251 // NB: this will also force him to look at the entity
4252 moveToCombatPhrase(target
->slot(), 2.0, memoryLine
, memoryIndex
, cyclic
);
4254 // And after (order is important), start the phrase execution on client
4255 pPM
->clientExecute(memoryLine
, memoryIndex
, cyclic
);
4259 // Cancel any moveTo(), because don't want to continue reaching the prec entity
4260 // VERY important if previous MoveTo was a SPhrase MoveTo (because cancelClientExecute() must be called)
4263 // start client execution: NB: start client execution even if it
4264 pPM
->clientExecute(memoryLine
, memoryIndex
, cyclic
);
4266 // inform Server of phrase cast
4267 pPM
->sendExecuteToServer(memoryLine
, memoryIndex
, cyclic
);
4269 if( !meleeCombat
&& !cyclic
)
4271 pPM
->executeDefaultAttack();
4276 // ***************************************************************************
4277 void CUserEntity::beginCast(const MBEHAV::CBehaviour
&behaviour
)
4279 if(viewMode()==ThirdPV
)
4282 float frontYawBefore
= frontYaw();
4284 CCharacterCL::beginCast( behaviour
);
4285 // compensate the front change using a camera move
4286 float frontYawAfter
= frontYaw();
4287 float deltaYaw
= frontYawAfter
- frontYawBefore
;
4288 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
4289 // if automatic camera, center the view behind the user
4290 if( ClientCfg
.AutomaticCamera
)
4292 UserControls
.resetSmoothCameraDeltaYaw();
4297 // in first person mode, reset the delta yaw
4298 UserControls
.resetCameraDeltaYaw();
4299 CCharacterCL::beginCast( behaviour
);
4303 // ***************************************************************************
4304 void CUserEntity::updatePreCollision(const NLMISC::TTime
&time
, CEntityCL
*target
)
4306 CPlayerCL::updatePreCollision(time
, target
);
4308 // test each frame if the mode has changed
4311 // Play/stop music if comes from or goes to dead
4312 bool isDead
= _Mode
== MBEHAV::DEATH
|| _Mode
== MBEHAV::SWIM_DEATH
;
4314 // must start music?
4315 if (isDead
&& SoundMngr
->getEventMusicPlayed() != ClientCfg
.DeathMusic
)
4317 SoundMngr
->playEventMusic(ClientCfg
.DeathMusic
, 0, true);
4321 if (!isDead
&& SoundMngr
->getEventMusicPlayed() == ClientCfg
.DeathMusic
)
4323 SoundMngr
->stopEventMusic(ClientCfg
.DeathMusic
, CSoundManager::LoadingMusicXFade
);
4328 // ***************************************************************************
4329 void CUserEntity::buildTotem()
4331 const string msgName
= "TOTEM:BUILD";
4333 if( GenericMsgHeaderMngr
.pushNameToStream( msgName
, out
) )
4335 NetMngr
.push( out
);
4336 nlinfo( "sending TOTEM:build message to server" );
4340 // ***************************************************************************
4341 void CUserEntity::setR2CharMode(R2::TCharMode mode
)
4343 if (mode
== R2::TCharMode::Editer
|| mode
== R2::TCharMode::Dm
)
4346 walkVelocity(ClientCfg
.DmWalk
);
4347 runVelocity(ClientCfg
.DmRun
);
4348 View
.setCameraDistanceMaxForDm();
4349 CEntityCL
*user
= EntitiesMngr
.entity(0);
4350 NLPACS::UMovePrimitive
* prim
= user
?user
->getPrimitive():0;
4353 prim
->setObstacle(false);
4357 else if (mode
== R2::TCharMode::Player
|| mode
== R2::TCharMode::Tester
)
4360 walkVelocity(ClientCfg
.Walk
);
4361 runVelocity(ClientCfg
.Run
);
4362 View
.setCameraDistanceMaxForPlayer();
4363 CEntityCL
*user
= EntitiesMngr
.entity(0);
4364 NLPACS::UMovePrimitive
* prim
= user
?user
->getPrimitive():0;
4367 prim
->setObstacle(true);
4372 nlwarning("R2Ed: Error Char Mode not handled %u", (uint32
)mode
.getValue());
4376 bool CUserEntity::isInNpcControl() const
4378 CInterfaceManager
* pIM
= CInterfaceManager::getInstance();
4379 CCDBNodeLeaf
*sheet
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4380 return sheet
&& NLMISC::CSheetId(sheet
->getValue32())!=NLMISC::CSheetId::Unknown
;
4384 void CUserEntity::updateNpcContolSpeed()
4386 CInterfaceManager
* pIM
= CInterfaceManager::getInstance();
4387 CCDBNodeLeaf
*sheet
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:SHEET", false);
4388 CCDBNodeLeaf
*walk
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:WALK", false);
4389 CCDBNodeLeaf
*run
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:USER:NPC_CONTROL:RUN", false);
4390 if (!sheet
|| !walk
|| !run
)
4395 static NLMISC::CSheetId oldSheet
= NLMISC::CSheetId::Unknown
;
4396 static float oldRun
=0.f
;
4397 static float oldWalk
=0.f
;
4399 NLMISC::CSheetId
sheetId(sheet
->getValue32());
4400 float newRun
= float(run
->getValue32()) / 1000.0f
;
4401 float newWalk
= float(walk
->getValue32()) / 1000.0f
;
4403 if (sheetId
== oldSheet
&& oldRun
== newRun
&& oldWalk
== newWalk
)
4412 if (sheetId
!= NLMISC::CSheetId::Unknown
)
4414 walkVelocity(newWalk
);
4415 runVelocity(newRun
);
4419 setR2CharMode(_R2CharMode
);
4424 //-----------------------------------------------
4426 //-----------------------------------------------
4427 void CUserEntity::cancelAllPhrases()
4430 if(GenericMsgHeaderMngr
.pushNameToStream("PHRASE:CANCEL_ALL", out
))
4436 nlwarning("<CUserEntity::cancelAllPhrases> unknown message name '%s'", "PHRASE:CANCEL_ALL");
4441 //-----------------------------------------------
4443 //-----------------------------------------------
4444 bool CUserEntity::canChangeFront()
4446 return !(_CurrentBehaviour
.Behaviour
== MBEHAV::EXTRACTING
4447 || (_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
&& _Mode
==MBEHAV::COMBAT
&& !UserControls
.isMoving())
4448 || (_CurrentBehaviour
.Behaviour
>= MBEHAV::MAGIC_CASTING_BEHAVIOUR_BEGIN
&& _CurrentBehaviour
.Behaviour
<= MBEHAV::MAGIC_CASTING_BEHAVIOUR_END
));
4452 //-----------------------------------------------
4453 // rememberWeaponsInHand
4455 //-----------------------------------------------
4456 void CUserEntity::rememberWeaponsInHand()
4458 CInventoryManager
* inv
= CInventoryManager::getInstance();
4464 // keep right hand item
4465 CItemImage
* rightItemImg
= inv
->getHandItem(0);
4468 if( inv
->isMeleeWeaponItem(rightItemImg
->getSheetID()) || inv
->isRangeWeaponItem(rightItemImg
->getSheetID()) )
4470 _PreviousRightHandItem
= CItemSnapshot(*rightItemImg
);
4472 // keep left hand item too (could be ammo, second weapon, etc ..)
4473 CItemImage
* leftItemImg
= inv
->getHandItem(1);
4476 _PreviousLeftHandItem
= CItemSnapshot(*leftItemImg
);
4480 _PreviousLeftHandItem
= CItemSnapshot();
4487 //-----------------------------------------------
4488 // snapshot of a CItemImage
4490 //-----------------------------------------------
4491 CUserEntity::CItemSnapshot::CItemSnapshot( const CItemImage
& i
)
4493 Sheet
= i
.getSheetID();
4494 Quality
= i
.getQuality();
4495 Quantity
= i
.getQuantity();
4496 UserColor
= i
.getUserColor();
4497 Price
= i
.getPrice();
4498 Weight
= i
.getWeight();
4499 NameId
= i
.getNameId();
4500 InfoVersion
= i
.getInfoVersion();
4503 sint
CUserEntity::getLevel() const
4505 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
4507 for(uint i
=0;i
<EGSPD::CSPType::EndSPType
;i
++)
4509 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i
), false);
4512 level
= std::max(level
, (sint
) node
->getValue32());
4518 //-----------------------------------------------
4520 //-----------------------------------------------
4521 void CUserEntity::interlocutor( const CLFECOMMON::TCLEntityId
&slot
)
4523 CLFECOMMON::TCLEntityId prevInterlocutor
= _Interlocutor
;
4524 _Interlocutor
= slot
;
4526 // Refresh (hide or unhide) the icon for the interlocutor NPC
4527 if (prevInterlocutor
!= CLFECOMMON::INVALID_SLOT
)
4528 EntitiesMngr
.refreshInsceneInterfaceOfFriendNPC(prevInterlocutor
);
4529 if (_Interlocutor
!= CLFECOMMON::INVALID_SLOT
)
4530 EntitiesMngr
.refreshInsceneInterfaceOfFriendNPC(_Interlocutor
);
4533 //-----------------------------------------------
4535 //-----------------------------------------------
4536 void CUserEntity::trader(const CLFECOMMON::TCLEntityId
&slot
)
4538 CLFECOMMON::TCLEntityId prevTrader
= _Trader
;
4541 // Refresh (hide or unhide) the icon for the trader NPC
4542 if (prevTrader
!= CLFECOMMON::INVALID_SLOT
)
4543 EntitiesMngr
.refreshInsceneInterfaceOfFriendNPC(prevTrader
);
4544 if (_Trader
!= CLFECOMMON::INVALID_SLOT
)
4545 EntitiesMngr
.refreshInsceneInterfaceOfFriendNPC(_Trader
);