1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
9 // This program is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Affero General Public License as
11 // published by the Free Software Foundation, either version 3 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Affero General Public License for more details.
19 // You should have received a copy of the GNU Affero General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Update character position notes
26 * -------------------------------
28 * The update of the character position is done as followed :
35 * The characters know where they want to be in 2d (pos().x and pos().y valid).
42 * PACS::getGlobalPosition
43 * If real position too far from PACS position
44 * PACS::setGlobalPosition
45 * -- Here pos().z is estimated
47 * NL3D::visualCollisionEntities
48 * -- Here pos().z is good
52 * -- Here the character is setuped in the engine
61 #include "character_cl.h"
62 #include "pacs_client.h"
63 #include "net_manager.h"
64 #include "entity_animation_manager.h"
65 #include "time_client.h"
66 #include "ingame_database_manager.h"
67 #include "client_chat_manager.h"
68 #include "motion/user_controls.h"
69 #include "color_slot_manager.h"
70 #include "sheet_manager.h"
71 #include "debug_client.h"
72 #include "animation_misc.h"
73 #include "entities.h" // \todo GUIGUI : try to remove this dependency.
75 #include "string_manager_client.h"
76 #include "interface_v3/interface_manager.h"
77 #include "fx_manager.h"
78 #include "interface_v3/group_in_scene_user_info.h"
79 #include "interface_v3/group_in_scene_bubble.h"
80 #include "client_cfg.h"
81 #include "user_entity.h"
82 #include "projectile_manager.h"
83 #include "init_main_loop.h"
84 #include "nel/misc/cdb_branch.h"
85 #include "animation_fx_misc.h"
86 #include "attack_list.h"
87 #include "animation_fx_id_array.h"
88 #include "cursor_functions.h"
89 #include "interface_v3/bar_manager.h"
91 #include "client_sheets/item_fx_sheet.h"
92 #include "client_sheets/attack_id_sheet.h"
94 #include "nel/misc/geom_ext.h"
95 #include "nel/misc/random.h"
97 #include "nel/3d/u_scene.h"
98 #include "nel/3d/u_camera.h"
99 #include "nel/3d/u_driver.h"
100 #include "nel/3d/u_play_list.h"
101 #include "nel/3d/u_particle_system_instance.h"
102 #include "nel/3d/u_bone.h"
103 #include "nel/3d/u_track.h"
104 #include "nel/3d/u_instance.h"
105 #include "nel/3d/u_instance_material.h"
107 #include "nel/pacs/u_global_position.h"
109 #include "nel/sound/sound_anim_manager.h"
113 #include "game_share/intensity_types.h"
114 #include "game_share/multi_target.h"
115 #include "game_share/visual_fx.h"
116 #include "game_share/range_weapon_type.h"
120 #define new DEBUG_NEW
126 #define INVALID_POS CVectorD(-1.0,-1.0,-1.0)
127 #define INVALID_DIST -1.0
128 #define INVALID_TIME -1.0
129 //#define MAX_HEAD_H_ROTATION Pi/2.5
130 #define MAX_HEAD_H_ROTATION Pi/3.0
131 #define MAX_HEAD_V_ROTATION Pi/4.0
132 #define DEFAULT_BLEND_LENGTH 0
135 static const double AURA_SHUTDOWN_TIME
= 1.f
; // number of seconds for auras and links to shutdown
140 using namespace NLMISC
;
141 using namespace NL3D
;
142 using namespace NLPACS
;
143 using namespace NLSOUND
;
145 using namespace MBEHAV
;
146 using namespace CLFECOMMON
;
151 extern UScene
*Scene
;
152 extern UDriver
*Driver
;
153 extern CEntityAnimationManager
*EAM
;
154 extern CClientChatManager ChatMngr
;
155 extern UTextContext
*TextContext
;
156 extern UMaterial GenericMat
;
157 extern UCamera MainCam
;
164 std::string LastMethod
;
166 #define ADD_METHOD(header) \
169 LastMethod = #header;
171 #define CHECK(param) \
174 nlwarning("entity:%d: Test '%s'", _Slot, #param); \
175 nlwarning("entity:%d: Last Method called '%s'", _Slot, LastMethod.c_str()); \
178 #define METHOD_NAME(param) LastMethod = (param);
181 #define ADD_METHOD(header) \
185 #define METHOD_NAME(param)
193 const std::string
CCharacterCL::_EmptyString
;
194 const uint8
CCharacterCL::_BadHairIndex
= 0xFF;
196 H_AUTO_DECL ( RZ_Client_Character_CL_Update_Pos_Combat_Float
)
197 H_AUTO_DECL ( RZ_Client_Entity_CL_Update_Pos_Pacs
)
198 H_AUTO_DECL ( RZ_Client_Entity_CL_Update_Pos_Combat_Float
)
199 H_AUTO_DECL ( RZ_Client_Entity_CL_Update_Pos_Compute_Motion
)
206 //---------------------------------------------------
208 // Set the direction that should have the character at the end of the animation.
209 // \param vect : vector used to set the direction at the end of the animation.
210 //---------------------------------------------------
211 void CCharacterCL::dirEndAnim(const CVector
&vect
)
213 setVect(_DirEndAnim
, vect
, true, true);
217 //-----------------------------------------------
220 //-----------------------------------------------
221 CCharacterCL::CCharacterCL()
226 _FirstPos
= INVALID_POS
; // Initialize the first with a bad position.
227 _FirstTime
= INVALID_TIME
; // Initialize the time for the first position with a bad one.
228 dist2FirstPos(INVALID_DIST
); // Initialize the distance to the first position with a bad value.
229 _RunStartTimeNoPop
= INVALID_TIME
;
231 _DestPos
= INVALID_POS
;
232 _DestTime
= INVALID_TIME
;
233 dist2Dest(INVALID_DIST
);
235 _OldPos
= INVALID_POS
;
236 _OldPosTime
= INVALID_TIME
;
239 // Initialize the time for the last loop with the current time when entity created.
240 _LastFrameTime
= 0.0;
241 // The animation should be played from the begin to the end.
242 _AnimReversed
.resize(animTypeCount
, false);
243 // Id among all the animations for each slot
244 _AnimId
.resize(animTypeCount
, NL3D::UPlayList::empty
);
245 // Index in the state of the current animation for each slot.
246 _AnimIndex
.resize(animTypeCount
, CAnimation::UnknownAnim
);
247 // ID of the current sound animation for each slot.
248 _SoundId
.resize(animTypeCount
, -1);
249 // ID of the current animation state for each slot.
250 _AnimState
.resize(animTypeCount
, CAnimationStateSheet::Idle
);
251 // Time offest in the current animation for each slot.
252 _AnimOffset
.resize(animTypeCount
, 0.0);
253 // Subsidiary Key for the Animation State (emote).
254 _SubStateKey
= CAnimationStateSheet::UnknownState
;
255 // The character does not accept special "job animation" by default
256 _AnimJobSpecialisation
= 0;
259 _LodCharacterAnimEnabled
= false;
260 _LodCharacterMasterAnimSlot
= MOVE
;
262 // default POS scale to 1.
263 _CharacterScalePos
= 1.f
;
268 // Unknown gender at the entity creation.
269 _Gender
= GSGENDER::unknown
;
271 // The bone for the name is not known for the time
273 // No UTransform for the name needed if there is no name so not allocated for the time.
275 // default Clod apparition => force compute the bone
276 _NameCLodDeltaZ
= NameCLodDeltaZNotComputed
;
278 // There is no anim set for the time.
279 _CurrentAnimSet
.resize(animTypeCount
, 0);
281 // Same as the animation at the beginning.
282 _RotationFactor
= 1.f
;
287 _RightFXActivated
= false;
288 _LeftFXActivated
= false;
291 dirEndAnim(CVector(0.f
, 1.f
, 0.f
));
293 // No item associated at the beginning but there is a room for them.
294 _Items
.resize(SLOTTYPE::NB_SLOT
);
295 _HeadIdx
= CEntityCL::BadIndex
;
296 _FaceIdx
= CEntityCL::BadIndex
;
298 // No frame remaining forthe blend at the beginning.
301 // Scale for the skeleton according to the gabarit. Default : 1
302 _CustomScalePos
= 1.f
;
304 // Start with "unknown mode wanted by the server"
305 _ModeWanted
= MBEHAV::UNKNOWN_MODE
; //MBEHAV::NORMAL;
307 _Mount
= CLFECOMMON::INVALID_SLOT
;
308 _Rider
= CLFECOMMON::INVALID_SLOT
;
309 _TheoreticalMount
= CLFECOMMON::INVALID_SLOT
;
310 _TheoreticalRider
= CLFECOMMON::INVALID_SLOT
;
311 _OwnerPeople
= MOUNT_PEOPLE::Unknown
;
313 // Default is : entity has no bone for the head and Neck.
316 _IsThereAMode
= false;
317 _ImportantStepTime
= 0.0;
318 _StartDecreaseLCTImpact
= 0;
320 // Entity has no look and so is not displayable for the time.
323 // Index of the instance in the right hand (0xFFFFFFFF = no index).
324 _RHandInstIdx
= CEntityCL::BadIndex
;
325 // Index of the instance in the left hand (0xFFFFFFFF = no index).
326 _LHandInstIdx
= CEntityCL::BadIndex
;
330 // No Hair Index at the beginning.
331 _HairIndex
= _BadHairIndex
;
339 _InSceneUserInterface
= NULL
;
340 _CurrentBubble
= NULL
;
342 // Initialize the head offset with a Null Vector.
343 _HeadOffset
= CVector::Null
;
344 _HeadOffsetComputed
= false;
345 // Initialize the Run Factor
347 // Initialize the Speed
352 _CurrentAttack
= NULL
;
353 _CurrentAttackID
.Type
= CAttackIDSheet::Unknown
;
355 //_PelvisBoneId = -1;
367 _PvpMode
= PVP_MODE::None
;
371 _OutpostSide
= OUTPOSTENUMS::UnknownPVPSide
;
373 _SelectableBySpace
= true;
375 _LastSelectBoxComputeTime
= 0;
381 _BaseCustomScale
= 0.f
;
382 _StepCustomScale
= 0.f
;
383 _StartCustomScale
= ryzomGetLocalTime();
386 //-----------------------------------------------
388 // Default Destructor
389 // \warning : Do not remove sheets before the entity.
390 //-----------------------------------------------
391 CCharacterCL::~CCharacterCL()
393 // Delete the UTransform used to compute the Bone for the Name.
394 if(!_NameTransform
.empty())
398 if (!_Skeleton
.empty())
399 _Skeleton
.detachSkeletonSon(_NameTransform
);
400 Scene
->deleteTransform(_NameTransform
);
405 // No more sheet pointed.
408 // Release items (but not their mesh, because they are managed by _Instances)
409 for(uint k
= 0; k
< _Items
.size(); ++k
)
414 // Delete previous interface
415 releaseInSceneInterfaces();
419 _CurrentBubble
->unlink();
424 //-----------------------------------------------
425 // computePrimitive :
426 // Create (or re-create) a primitive.
427 //-----------------------------------------------
428 void CCharacterCL::computePrimitive()
430 // Initialize the primitive.
433 initPrimitive(_Sheet
->ColRadius
*getScale(), _Sheet
->ColHeight
*getScale(), _Sheet
->ColLength
, _Sheet
->ColWidth
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
, _Sheet
->ClipRadius
, _Sheet
->ClipHeight
);
437 initPrimitive(0.5f
, 2.0f
, 0.0f
, 0.0f
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
);
440 _Primitive
->insertInWorldImage(dynamicWI
);
443 }// computePrimitive //
446 //-----------------------------------------------
447 void CCharacterCL::removeAllAttachedFX()
449 _AttachedFXListForCurrentAnim
.clear();
450 _AttachedFXListToRemove
.clear();
452 for(uint k
= 0; k
< MaxNumAura
; ++k
)
460 //-----------------------------------------------
462 void CCharacterCL::releaseInSceneInterfaces()
464 if (_InSceneUserInterface
)
466 CWidgetManager::getInstance()->unMakeWindow(_InSceneUserInterface
);
467 if (_InSceneUserInterface
->getParent())
469 _InSceneUserInterface
->getParent()->delGroup(_InSceneUserInterface
);
473 delete _InSceneUserInterface
;
476 _InSceneUserInterface
= NULL
;
480 //-----------------------------------------------
481 // stopAttachedFXForCurrrentAnim
482 // Stop all attached fxs linked to current animation
483 //-----------------------------------------------
484 void CCharacterCL::stopAttachedFXForCurrrentAnim(bool stopLoopingFX
)
486 // shutdown fxs for current anim
487 std::list
<CAttachedFX::TSmartPtr
>::iterator itAttachedFx
= _AttachedFXListForCurrentAnim
.begin();
488 while(itAttachedFx
!= _AttachedFXListForCurrentAnim
.end())
490 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpItAttached
= itAttachedFx
;
492 if (!(!stopLoopingFX
&& (*tmpItAttached
)->AniFX
&& (*tmpItAttached
)->AniFX
->Sheet
->RepeatMode
== CAnimationFXSheet::Loop
)) // dont remove looping fx if it is requested
494 // test if emitters should be shutdown at the end of the anim
495 if ((*tmpItAttached
)->AniFX
&&
496 (*tmpItAttached
)->AniFX
->Sheet
->RepeatMode
!= CAnimationFXSheet::Respawn
&&
497 (*tmpItAttached
)->StickMode
!= CFXStickMode::SpawnPermanent
500 if(!(*tmpItAttached
)->FX
.empty())
502 if (!(*tmpItAttached
)->FX
.removeByID(NELID("STOP")) && !(*tmpItAttached
)->FX
.removeByID(NELID("main")))
504 (*tmpItAttached
)->FX
.activateEmitters(false);
508 _AttachedFXListToRemove
.splice(_AttachedFXListToRemove
.begin(), _AttachedFXListForCurrentAnim
, tmpItAttached
);
509 _AttachedFXListToRemove
.front()->TimeOutDate
+= TimeInSec
; // compute absolute timeout date
512 /** Removes fx that lives on more that a given count of animation
513 * Useful if framerate is low to avoid that fx overlap
515 itAttachedFx
= _AttachedFXListToRemove
.begin();
516 while(itAttachedFx
!= _AttachedFXListToRemove
.end())
518 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpItAttachedFX
= itAttachedFx
;
520 if ((*tmpItAttachedFX
)->AniFX
)
522 if ((*tmpItAttachedFX
)->MaxAnimCount
!= 0) // if there a limit to the number of animation during which the fx can live ?
524 (*tmpItAttachedFX
)->MaxAnimCount
-= 1;
525 if ((*tmpItAttachedFX
)->MaxAnimCount
== 0)
528 _AttachedFXListToRemove
.erase(tmpItAttachedFX
);
536 //-----------------------------------------------
538 //-----------------------------------------------
539 void CCharacterCL::applyColorSlot(SInstanceCL
&instance
, sint skin
, sint userColor
, sint hair
, sint eyes
)
541 CColorSlotManager::TIntCouple array
[4];
544 array
[0].first
= (uint
)0; array
[0].second
= (uint
)skin
;
547 array
[1].first
= (uint
)1; array
[1].second
= (uint
)userColor
;
550 array
[2].first
= (uint
)2; array
[2].second
= (uint
)hair
;
553 array
[3].first
= (uint
)3; array
[3].second
= (uint
)eyes
;
556 UInstance inst
= instance
.createLoadingFromCurrent();
559 instance
.setColors(skin
, userColor
, hair
, eyes
);
560 ColorSlotManager
.setInstanceSlot(inst
, array
, 4);
562 }// applyColorSlot //
565 //-----------------------------------------------
567 //-----------------------------------------------
568 void CCharacterCL::changeColors(sint userColor
, sint hair
, sint eyes
, sint part
) // virtual
570 // Color Just a part of the entity.
573 if((uint
)part
< _Instances
.size())
574 applyColorSlot(_Instances
[part
], skin(), userColor
, hair
, eyes
);
576 // Color the whole entity.
579 for(uint i
=0; i
<_Instances
.size(); i
++)
580 applyColorSlot(_Instances
[i
], skin(), userColor
, hair
, eyes
);
584 //-----------------------------------------------
585 // addColoredInstance :
586 //-----------------------------------------------
587 uint32
CCharacterCL::addColoredInstance(const std::string
&shapeName
, const std::string
&stickPoint
, sint texture
, uint32 instIdx
, sint color
)
590 uint32 idx
= addInstance(shapeName
, stickPoint
, texture
, instIdx
);
591 SInstanceCL
*instance
= idx2Inst(idx
);
593 applyColorSlot(*instance
, skin(), color
, _HairColor
, _EyesColor
);
595 nlwarning("CH::addColoredInstance: cannot create the instance for the shape '%s'.", shapeName
.c_str());
598 }// addColoredInstance //
601 //-----------------------------------------------
603 // \param slot: structure of the equipement.
604 // \param visualSlot: visual slot used by this item.
605 // \return uint32 : index of the instance or 0xFFFFFFFF.
606 // \todo GUIGUI : find a better choice to avoid all visualSlot checks
607 //-----------------------------------------------
608 uint32
CCharacterCL::buildEquipment(const CCharacterSheet::CEquipment
&slot
, SLOTTYPE::EVisualSlot visualSlot
, sint color
, uint32 instIdx
)
610 uint32 idx
= CEntityCL::BadIndex
;
612 // Do something only if the slot is not empty.
613 if(slot
.getItem().empty())
616 sint slotColor
= slot
.Color
;
618 // This is a reference on an item (the file is store with an UPPER case so check in UPPER CASE).
619 string ext
= CFile::getExtension(slot
.getItem());
620 if((ext
== "item") || (ext
== "sitem"))
622 // IS the item a valid one ?
624 if(itemId
.buildSheetId(NLMISC::toLowerAscii(slot
.getItem())))
626 // Is it stored in the database ?
627 CEntitySheet
*entitySheet
= SheetMngr
.get(itemId
);
630 _Items
[visualSlot
].Sheet
= dynamic_cast<CItemSheet
*>(entitySheet
);
631 if(_Items
[visualSlot
].Sheet
)
633 const CItemSheet
&itemSheet
= *(_Items
[visualSlot
].Sheet
);
635 // Compute the bind point
640 case SLOTTYPE::RIGHT_HAND_SLOT
:
641 if( itemSheet
.ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
642 bindBone
= "box_arme";
645 case SLOTTYPE::LEFT_HAND_SLOT
:
646 // Shields are not stick to the same point.
647 if(itemSheet
.getAnimSet() == "s")
648 bindBone
= "Box_bouclier";
650 bindBone
= "box_arme_gauche";
653 bindBone
= slot
.getBindPoint();
657 // Choose the right colour.
663 // Get the item color
664 slotColor
= itemSheet
.Color
;
665 // Bad item color -> set to 0
673 idx
= createItemInstance(itemSheet
, instIdx
, visualSlot
, bindBone
, slot
.Texture
, slotColor
);
676 nlwarning("CH::buildEquipment: the sheet '%s' is not an item one.", slot
.getItem().c_str());
679 nlwarning("CH::buildEquipment: the sheet '%s' is not stored in the database.", slot
.getItem().c_str());
682 nlwarning("CH::buildEquipment: item '%s' is not in the Sheet Id list.", slot
.getItem().c_str());
696 idx
= addColoredInstance(slot
.getItem(), slot
.getBindPoint(), slot
.Texture
, instIdx
, slotColor
);
701 }// buildEquipment //
703 //-----------------------------------------------
704 // computeSomeBoneId :
705 // Compute the bone for the name, head...
706 // \warning This method do not check the bone is valid, nor there is a Scene.
707 //-----------------------------------------------
708 void CCharacterCL::computeSomeBoneId()
710 // **** Get the Bone for the name.
711 _NameBoneId
= _Skeleton
.getBoneIdByName("name");
712 // Dummy found -> return the name position.
713 if(_NameBoneId
!= -1)
715 // Just to force the bone to be compute (else not computed if not used).
716 _NameTransform
= Scene
->createTransform();
717 if(!_NameTransform
.empty())
718 _Skeleton
.stickObject(_NameTransform
, _NameBoneId
);
720 // No Bone for the Name.
724 pushDebugStr("The Bone for the name is missing.");
725 #endif // FINAL_VERSION
728 // **** Get the head bone.
729 _HeadBoneId
= _Skeleton
.getBoneIdByName("Bip01 Head");
731 if(_HeadBoneId
== -1)
734 pushDebugStr("The Bone for the Head is missing.");
735 #endif // FINAL_VERSION
739 _TargetAnimCtrl
.EyePos
= CVector::Null
;
740 _Skeleton
.setBoneAnimCtrl(_HeadBoneId
, &_TargetAnimCtrl
);
743 // **** Get the "chest" bone. take spine1.
744 _ChestBoneId
= _Skeleton
.getBoneIdByName("Bip01 Spine1");
745 if(_ChestBoneId
== -1)
748 pushDebugStr("The Bone for the Chest 'Bip01 Spine1' is missing.");
749 #endif // FINAL_VERSION
751 }// computeSomeBoneId //
754 //-----------------------------------------------
756 // Create the play list for this entity.
757 //-----------------------------------------------
758 void CCharacterCL::createPlayList()
760 // Release the old animation playlist.
763 EAM
->deletePlayList(_PlayList
);
767 // Create the new animation playlist.
768 _PlayList
= EAM
->createPlayList();
771 pushDebugStr("Cannot create a playlist for the entity.");
775 // Initialize the new playlist.
777 _PlayList
->setSpeedFactor (MOVE
, 1.f
);
778 _PlayList
->setWrapMode (MOVE
, UPlayList::Clamp
);
780 _PlayList
->setSpeedFactor (ACTION
, 1.f
);
781 _PlayList
->setWrapMode (ACTION
, UPlayList::Clamp
);
782 }// createPlayList //
786 //-----------------------------------------------
788 // retrieve ground fxs for that entity
789 //-----------------------------------------------
790 const std::vector
<CGroundFXSheet
> *CCharacterCL::getGroundFX() const
792 return &(_Sheet
->GroundFX
);
795 //-----------------------------------------------
797 // Build the entity from a sheet.
798 //-----------------------------------------------
799 bool CCharacterCL::build(const CEntitySheet
*sheet
) // virtual
801 // Cast the sheet in the right type.
802 _Sheet
= dynamic_cast<const CCharacterSheet
*>(sheet
);
805 pushDebugStr("This is not a character sheet -> entity not initialized.");
810 Type
= (_Sheet
->Race
>= EGSPD::CPeople::Creature
) ? Fauna
: NPC
;
815 // Get the fauna name in the sheet
816 const char *creatureName
= STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet
->Id
);
817 if (!FINAL_VERSION
|| !NLMISC::startsWith(creatureName
, "<NotExist:"))
818 _EntityName
= creatureName
;
822 // Name and title will be send by the server
826 if(IngameDbMngr
.getNodePtr())
828 CCDBNodeBranch
*nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(IngameDbMngr
.getNodePtr()->getNode(0));
831 _DBEntry
= dynamic_cast<CCDBNodeBranch
*>(nodeRoot
->getNode(_Slot
));
833 pushDebugStr("Cannot get a pointer on the DB entry.");
837 if (!ClientCfg
.Light
&& !_Sheet
->getSkelFilename().empty())
839 // Create the Playlist for the entity.
842 // Compute the first automaton.
843 _CurrentAutomaton
= automatonType() + "_normal.automaton";
845 // Get the Character gender.
846 _Gender
= (GSGENDER::EGender
)_Sheet
->Gender
;
848 // Initialize the internal time.
849 _LastFrameTime
= ((double)T1
) * 0.001;
852 if(!ClientCfg
.Light
&& !_Sheet
->getSkelFilename().empty() && skeleton(_Sheet
->getSkelFilename()))
854 // Set the skeleton scale.
855 skeleton()->setScale(getScale(), getScale(), getScale());
857 // Can create all characters except NPC.
861 if(_Sheet
->EyesColor
>= SheetMngr
.nbEyesColor())
863 if (SheetMngr
.nbEyesColor() == 0)
866 _EyesColor
= rand()%SheetMngr
.nbEyesColor();
870 _EyesColor
= _Sheet
->EyesColor
;
874 if(_Sheet
->HairColor
>= SheetMngr
.nbHairColor())
876 if (SheetMngr
.nbHairColor() == 0)
879 _HairColor
= rand()%SheetMngr
.nbHairColor();
883 _HairColor
= _Sheet
->HairColor
;
886 // -- Dress the character --
890 buildEquipment(_Sheet
->Body
, SLOTTYPE::CHEST_SLOT
);
891 buildEquipment(_Sheet
->Arms
, SLOTTYPE::ARMS_SLOT
);
892 buildEquipment(_Sheet
->Hands
, SLOTTYPE::HANDS_SLOT
);
894 buildEquipment(_Sheet
->Legs
, SLOTTYPE::LEGS_SLOT
);
895 buildEquipment(_Sheet
->Feet
, SLOTTYPE::FEET_SLOT
);
897 _FaceIdx
= buildEquipment(_Sheet
->Face
, SLOTTYPE::FACE_SLOT
);
899 // -- Manage the Head --
901 if(!_Sheet
->Head
.getItem().empty())
904 _HeadIdx
= buildEquipment(_Sheet
->Head
, SLOTTYPE::HEAD_SLOT
, -1, _HeadIdx
);
906 SInstanceCL
*pInstFace
= getFace();
909 if(!pInstFace
->Current
.empty())
910 pInstFace
->Current
.hide();
912 pInstFace
->KeepHiddenWhenLoaded
= true;
919 if(_HairIndex
!= _BadHairIndex
)
920 _HeadIdx
= buildEquipment(_Sheet
->HairItemList
[_HairIndex
], SLOTTYPE::HEAD_SLOT
, -1, _HeadIdx
);
922 SInstanceCL
*pInstFace
= getFace();
924 if(!pInstFace
->Current
.empty())
925 pInstFace
->Current
.show();
929 _RHandInstIdx
= buildEquipment(_Sheet
->ObjectInRightHand
, SLOTTYPE::RIGHT_HAND_SLOT
, -1, _RHandInstIdx
); // In The Right Hand
930 _LHandInstIdx
= buildEquipment(_Sheet
->ObjectInLeftHand
, SLOTTYPE::LEFT_HAND_SLOT
, -1, _LHandInstIdx
); // In The Left Hand
932 // Look is now ready.
935 // Cannot build as long as the alternative look property not received, so hide the entity.
939 // Compute the animation set (after weapons are set to choose the right animation set).
941 // Check the animation set is correct.
942 if(_CurrentAnimSet
[MOVE
] == 0)
943 pushDebugStr("Bad animation set");
945 // Set the animation to idle.
946 setAnim(CAnimationStateSheet::Idle
);
948 // Compute the bone for the name.
951 // Compute pelvis bone
952 //_PelvisBoneId = _Skeleton.getBoneIdByName("Bip01 Pelvis");
954 // Setup Lod Character skeleton and shapes colors, if skeleton exist
955 // Get Lod Character Id from the sheet.
956 sint clodId
= getLodCharacterId(*Scene
, _Sheet
->getLodCharacterName());
959 // Setup Lod Character shapes, if enabled.
960 skeleton()->setLodCharacterShape(clodId
);
961 skeleton()->setLodCharacterDistance(_Sheet
->LodCharacterDistance
);
967 uint32 idx
= buildEquipment(_Sheet
->Body
, SLOTTYPE::CHEST_SLOT
);
968 // must set the scale for the BotObject too
969 if(idx
<_Instances
.size())
972 _Instances
[idx
].setScale(CVector(s
,s
,s
));
976 // Setup _CharacterScalePos
977 _CharacterScalePos
= _Sheet
->CharacterScalePos
;
979 // Adjust the custom scale position according to the entity scale.
980 _CustomScalePos
*= getScale();
982 // Create PACS Primitive.
983 initPrimitive(_Sheet
->ColRadius
*getScale(), _Sheet
->ColHeight
*getScale(), _Sheet
->ColLength
, _Sheet
->ColWidth
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
, _Sheet
->ClipRadius
, _Sheet
->ClipHeight
);
985 // Compute the element to be able to snap the entity to the ground.
986 computeCollisionEntity();
988 // Initialize properties of the entity (selectable/attackable/etc.).
991 // copy some properties (special bot objects). before buildInSceneInterface
992 _DisplayInRadar
= _Sheet
->DisplayInRadar
;
993 _DisplayOSDName
= _Sheet
->DisplayOSDName
;
994 _DisplayOSDBars
= _Sheet
->DisplayOSDBars
;
995 _DisplayOSDForceOver
= _Sheet
->DisplayOSDForceOver
;
996 _Traversable
= _Sheet
->Traversable
;
997 _CanTurn
= _Sheet
->Turn
;
999 _SelectableBySpace
= _Sheet
->SelectableBySpace
;
1001 // Rebuild interface
1002 buildInSceneInterface ();
1013 //-----------------------------------------------
1015 //-----------------------------------------------
1016 bool CCharacterCL::isKami() const
1020 return (_Sheet
->Race
== EGSPD::CPeople::Kami
);
1023 //-----------------------------------------------
1025 //-----------------------------------------------
1026 bool CCharacterCL::isUnknownRace() const
1030 return (_Sheet
->Race
== EGSPD::CPeople::Unknown
);
1033 //-----------------------------------------------
1034 // getAttackHeight :
1035 // Return the atk height.
1036 // \todo GUIGUI : height relative to attacker instead of global height
1037 //-----------------------------------------------
1038 CCharacterCL::TAtkHeight
CCharacterCL::getAttackHeight(CEntityCL
*target
, BODY::TBodyPart localisation
, BODY::TSide side
) const
1040 // Check there is a target.
1042 return CCharacterCL::AtkMiddle
;
1043 // Get the position for a bone.
1045 if(target
->getBoneHeight(localisation
, side
, height
))
1049 return CCharacterCL::AtkLow
;
1051 else if(height
> 2.0f
)
1052 return CCharacterCL::AtkHigh
;
1054 // Default is Middle atk.
1055 return CCharacterCL::AtkMiddle
;
1056 }// getAttackHeight //
1058 //-----------------------------------------------
1060 //-----------------------------------------------
1061 bool CCharacterCL::getBoneHeight(BODY::TBodyPart localisation
, BODY::TSide side
, float &height
) const // virtual
1063 // If there is no skeleton return false
1064 if(_Skeleton
.empty())
1066 // Get the Bone Name
1067 const char *boneName
= getBoneNameFromBodyPart(localisation
, side
);
1071 sint boneId
= _Skeleton
.getBoneIdByName(std::string(boneName
));
1074 if(_Skeleton
.isBoneComputed(boneId
) == false)
1076 NL3D::UBone bone
= _Skeleton
.getBone(boneId
);
1077 CMatrix BoneMat
= bone
.getLastWorldMatrixComputed();
1078 height
= (float)(BoneMat
.getPos().z
-pos().z
);
1081 else if(height
> 10.0f
)
1084 }// getBoneHeight //
1087 //-----------------------------------------------
1088 // lookAtItemsInHands :
1089 // Look at items in hands to change the animation set.
1090 // \return true if the mode is a mode where items in hands should be hidden
1091 //-----------------------------------------------
1092 bool CCharacterCL::modeWithHiddenItems() const
1094 return ((ClientCfg
.PutBackItems
&& !isFighting()) || isSit() || _Mode
==MBEHAV::SWIM
|| isRiding() || _Mode
==MBEHAV::SWIM_DEATH
|| _Mode
==MBEHAV::REST
);
1095 }// lookAtItemsInHands //
1098 //-----------------------------------------------
1100 //-----------------------------------------------
1101 string
CCharacterCL::automatonType() const // virtual
1103 return _Sheet
->getAutomaton();
1104 }// automatonType //
1106 //-----------------------------------------------
1107 // computeAutomaton :
1108 // Compute the current automaton for the entity.
1109 //-----------------------------------------------
1110 void CCharacterCL::computeAutomaton()
1112 _CurrentAutomaton
= automatonType() + "_" + NLMISC::toLowerAscii(MBEHAV::modeToString(_Mode
)) + ".automaton";
1113 }// computeAutomaton //
1116 //-----------------------------------------------
1118 // Compute the animation set to use according to weapons, mode and race.
1119 //-----------------------------------------------
1120 void CCharacterCL::computeAnimSet(sint32 fakeLeftHand
, sint32 fakeRightHand
)
1124 // Use the generic method to compute the animation set.
1125 if(!::computeAnimSet(_CurrentAnimSet
[MOVE
], _Mode
, _Sheet
->getAnimSetBaseName(), _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
, _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
, !modeWithHiddenItems()))
1127 nlwarning("CH:computeAnimSet:%d: pb when trying to compute the animset. Sheet Id '%u(%s)'.", _Slot
, _SheetId
.asInt(), _SheetId
.toString().c_str());
1130 }// computeAnimSet //
1132 //-----------------------------------------------
1134 // Adjust the Predicted Interval to fix some errors according to the distance.
1135 //-----------------------------------------------
1136 NLMISC::TGameCycle
CCharacterCL::adjustPI(float x
, float y
, float /* z */, const NLMISC::TGameCycle
&pI
)
1138 NLMISC::TGameCycle adjustedPI
= pI
;
1139 if(ClientCfg
.RestrainPI
&& adjustedPI
> 0)
1141 double dist
= (x
-UserEntity
->pos().x
)*(x
-UserEntity
->pos().x
) + (y
-UserEntity
->pos().y
)*(y
-UserEntity
->pos().y
);
1142 // If under 50m check Predicted Interval
1145 NLMISC::TGameCycle maxPi
= (NLMISC::TGameCycle
)(sqrt(dist
)/5.0)+1;
1146 if(adjustedPI
> maxPi
)
1149 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
1150 nlinfo("CH:updtVPPos:%d: dist'%f' PI'%d' newPI'%d'.", _Slot
, sqrt(dist
), pI
, adjustedPI
);
1157 //-----------------------------------------------
1158 // updateVisualPropertyPos :
1159 // Received a new position for the entity.
1160 // \warning Do not send position for the user
1161 //-----------------------------------------------
1162 void CCharacterCL::updateVisualPropertyPos(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
, const NLMISC::TGameCycle
&pI
)
1164 // Check the DB entry (the warning is already done in the build method).
1167 // Get The property 'Y'.
1168 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1171 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1174 // Get The property 'Z'.
1175 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1178 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1182 // Convert Database into a Position
1183 float x
= (float)(prop
)/1000.0f
;
1184 float y
= (float)(nodeY
->getValue64())/1000.0f
;
1185 float z
= (float)(nodeZ
->getValue64())/1000.0f
;
1187 #ifdef TMP_DEBUG_GUIGUI
1188 // Theoretical Position
1189 _TheoreticalPosition
= CVectorD((double)x
, (double)y
, (double)z
);
1190 #endif // TMP_DEBUG_GUIGUI
1192 // First position Managed -> set the PACS Position
1193 if(_FirstPosManaged
)
1195 pacsPos(CVectorD(x
, y
, z
));
1196 _FirstPosManaged
= false;
1200 // Wait for the entity to be spawned
1204 // Stock the position (except if this is the user mount because it's the user that control him not the server)
1205 if( !isRiding() || _Rider
!= 0)
1207 // Adjust the Predicted Interval to fix some "bug" into the Prediction Algo.
1208 NLMISC::TGameCycle adjustedPI
= adjustPI(x
, y
, z
, pI
);
1210 _Stages
.addStage(gameCycle
, PROPERTY_POSX
, prop
, adjustedPI
);
1211 _Stages
.addStage(gameCycle
, PROPERTY_POSY
, nodeY
->getValue64());
1212 _Stages
.addStage(gameCycle
, PROPERTY_POSZ
, nodeZ
->getValue64());
1214 }// updateVisualPropertyPos //
1216 //-----------------------------------------------
1217 // updateVisualPropertyOrient :
1218 // Received a new orientation.
1219 //-----------------------------------------------
1220 void CCharacterCL::updateVisualPropertyOrient(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1222 #ifdef TMP_DEBUG_GUIGUI
1223 // Backup the last orientation received.
1224 _TheoreticalOrientation
= *(float *)(&prop
);
1225 #endif // TMP_DEBUG_GUIGUI
1227 // New Mode Received.
1230 float ori
= *(float *)(&prop
);
1231 nlinfo("(%05d,%03d) CH::updateVPOri:%d: '%f' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, ori
);
1234 // if no skeleton we set the orientation
1235 if(_Skeleton
.empty())
1237 // server forces the entity orientation even if it cannot turn
1238 front(CVector((float)cos(_TheoreticalOrientation
), (float)sin(_TheoreticalOrientation
), 0.f
), true, true, true);
1239 dir(front(), false, false);
1241 _Primitive
->setOrientation(_TheoreticalOrientation
, dynamicWI
);
1245 if( !isRiding() || _Rider
!= 0 )
1247 // Add in right stage.
1248 _Stages
.addStage(gameCycle
, PROPERTY_ORIENTATION
, prop
);
1251 }// updateVisualPropertyOrient //
1253 //-----------------------------------------------
1254 // updateVisualPropertyMode :
1255 // New mode received.
1256 // \warning For the first mode, we must have received the position and orientation (but this should be the case).
1257 // \warning Read the position or orientation from the database when reading the mode (no more updated in updateVisualPropertyPos and updateVisualPropertyOrient).
1258 //-----------------------------------------------
1259 void CCharacterCL::updateVisualPropertyMode(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1262 nlinfo("(%05d,%03d) CH:updtVPMode:%d: '%s(%d)' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, modeToString((MBEHAV::EMode
)prop
).c_str(), (MBEHAV::EMode
)prop
);
1263 // New Mode Received : Set the Theoretical Current Mode if different.
1264 if(_TheoreticalMode
!= (MBEHAV::EMode
)(prop
& 0xffff))
1265 _TheoreticalMode
= (MBEHAV::EMode
)(prop
& 0xffff);
1268 nlwarning("CH:updtVPMode:%d: The mode '%s(%d)' sent is the same as the current one.", _Slot
, modeToString(_TheoreticalMode
).c_str(), _TheoreticalMode
);
1271 // If it is the first mode, set the mode.
1272 if(_Mode
== MBEHAV::UNKNOWN_MODE
)
1274 // SET THE FIRST POSITION
1275 //-----------------------
1276 // Check the DB entry (the warning is already done in the build method).
1279 // Get The property 'PROPERTY_POSX'.
1280 CCDBNodeLeaf
*nodeX
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSX
));
1283 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSX
);
1286 // Get The property 'PROPERTY_POSY'.
1287 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1290 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1293 // Get The property 'PROPERTY_POSZ'.
1294 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1297 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1300 // Next position will no longer be the first one.
1302 // Insert the primitive into the world.
1304 _Primitive
->insertInWorldImage(dynamicWI
);
1305 // float makes a few cm error
1306 double x
= (double)(nodeX
->getValue64())/1000.0;
1307 double y
= (double)(nodeY
->getValue64())/1000.0;
1308 double z
= (double)(nodeZ
->getValue64())/1000.0;
1309 // Set the primitive position.
1310 pacsPos(CVectorD(x
, y
, z
));
1311 // SET THE FIRST ORIENTATION
1312 //--------------------------
1313 // Get The property 'PROPERTY_ORIENTATION'.
1314 CCDBNodeLeaf
*nodeOri
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_ORIENTATION
));
1317 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot
, CLFECOMMON::PROPERTY_ORIENTATION
);
1321 parts
.i64
[0] = nodeOri
->getValue64();
1322 float angleZ
= parts
.f
[0];
1323 // server forces the entity orientation even if it cannot turn
1324 front(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
), true, true, true);
1325 dir(front(), false, false);
1326 _TargetAngle
= angleZ
;
1328 _Primitive
->setOrientation(angleZ
, dynamicWI
);
1329 // SET THE FIRST MODE
1330 //-------------------
1332 _Mode
= _TheoreticalMode
;
1333 _ModeWanted
= _TheoreticalMode
;
1334 if((_Mode
== MBEHAV::MOUNT_NORMAL
) && (_Rider
== CLFECOMMON::INVALID_SLOT
))
1336 _Mode
= MBEHAV::NORMAL
;
1337 _ModeWanted
= MBEHAV::MOUNT_NORMAL
;
1338 // See also updateVisualPropertyRiderEntity() for the case when _Rider is received after the mode
1341 setAnim(CAnimationStateSheet::Idle
);
1342 // Add the mode to the stage.
1343 _Stages
.addStage(gameCycle
, PROPERTY_MODE
, prop
);
1347 setAnim(CAnimationStateSheet::Idle
);
1349 // Not the first mode -> Add to a stage.
1352 // Add the mode to the stage.
1353 _Stages
.addStage(gameCycle
, PROPERTY_MODE
, prop
);
1354 // Float mode push the orientation
1355 if(_TheoreticalMode
== MBEHAV::COMBAT_FLOAT
)
1357 // Get The property 'PROPERTY_ORIENTATION'.
1358 CCDBNodeLeaf
*nodeOri
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_ORIENTATION
));
1361 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot
, CLFECOMMON::PROPERTY_ORIENTATION
);
1364 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_ORIENTATION
, nodeOri
->getValue64());
1366 // Any other mode push the position
1369 if(_TheoreticalMode
!= MBEHAV::MOUNT_NORMAL
)
1371 // Check the DB entry (the warning is already done in the build method).
1374 // Get The property 'PROPERTY_POSX'.
1375 CCDBNodeLeaf
*nodeX
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSX
));
1378 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSX
);
1381 // Get The property 'PROPERTY_POSY'.
1382 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1385 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1388 // Get The property 'PROPERTY_POSZ'.
1389 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1392 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1396 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSX
, nodeX
->getValue64());
1397 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSY
, nodeY
->getValue64());
1398 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSZ
, nodeZ
->getValue64());
1402 }// updateVisualPropertyMode //
1404 //-----------------------------------------------
1405 // updateVisualPropertyBehaviour :
1406 // New Behaviour received.
1407 //-----------------------------------------------
1408 void CCharacterCL::updateVisualPropertyBehaviour(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1410 // New Behaviour Received.
1411 CBehaviour
beh(prop
);
1413 nlinfo("(%05d,%03d) CH::updateVPBeha:%d: '%s(%d)' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, behaviourToString((EBehaviour
)beh
.Behaviour
).c_str(), (sint
)beh
.Behaviour
);
1415 // Add in right stage.
1416 _Stages
.addStage(gameCycle
, PROPERTY_BEHAVIOUR
, prop
);
1417 }// updateVisualPropertyBehaviour //
1419 void CCharacterCL::updateVisualPropertyTargetList(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
, uint listIndex
)
1421 // Add in right stage.
1422 _Stages
.addStage(gameCycle
, PROPERTY_TARGET_LIST_0
+ listIndex
, prop
);
1425 void CCharacterCL::updateVisualPropertyVisualFX(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1427 _Stages
.addStage(gameCycle
, PROPERTY_VISUAL_FX
, prop
);
1429 //-----------------------------------------------
1430 // updateVisualPropertyName :
1431 // Received the name Id.
1432 //-----------------------------------------------
1433 void CCharacterCL::updateVisualPropertyName(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1435 // Update the entity name (do not need to be managed with LCT).
1436 uint32 nameId
= *(uint32
*)(&prop
);
1438 // Store the name Id
1441 // STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId, this, &_Name);
1442 STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId
, this);
1444 //if(!getEntityName().empty())
1445 // nlwarning("CH::updateVPName:%d: name Id '%d' received but no name allocated.", _Slot, nameId);
1446 //else if(verboseVP(this))
1447 // nlinfo("(%05d,%03d) CH::updateVPName:%d: name '%s(%d)' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, getEntityName().toString().c_str(), nameId);
1449 updateMissionTarget();
1450 }// updateVisualPropertyName //
1452 //-----------------------------------------------
1453 // updateVisualPropertyTarget :
1454 // Received the new target for the entity
1455 // \todo GUIGUI : should be added in a stage.
1456 //-----------------------------------------------
1457 void CCharacterCL::updateVisualPropertyTarget(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1459 // New target Received.
1460 sint targ
= (sint
)prop
;
1463 nlinfo("(%05d,%03d) CH::updateVPTarget:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, targ
);
1465 // New entity target
1466 _TargetSlotNoLag
= (CLFECOMMON::TCLEntityId
)targ
;
1468 // Add in right stage.
1469 _Stages
.addStage(gameCycle
, PROPERTY_TARGET_ID
, prop
);
1470 }// updateVisualPropertyTarget //
1472 //-----------------------------------------------
1473 // updateVisualPropertyVpa :
1474 // Received the new target for the entity
1475 // \todo GUIGUI : should be added in a stage.
1476 //-----------------------------------------------
1477 void CCharacterCL::updateVisualPropertyVpa(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1479 // VPA only useful for NPC
1482 //nlwarning("CH:updtVPVpa:%d: VPA received but NOT an NPC. Sheet Id '%u(%s)'.", _Slot, _SheetId.asInt(), _SheetId.toString().c_str());
1486 // NO SKELETON -> NO VPA
1487 if(_Skeleton
.empty())
1490 // Get the alternative look property.
1491 SAltLookProp altLookProp
= *(SAltLookProp
*)(&prop
);
1492 // Display debug infos
1495 nlinfo("(%05d,%03d) CH:updtVPVpa:%d: TopColor(%d) BotColor(%d) RH(%d) LH(%d) Hat(%d) Seed(%d) HairColor(%d)",
1496 sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
,
1497 (uint
)altLookProp
.Element
.ColorTop
, (uint
)altLookProp
.Element
.ColorBot
,
1498 (uint
)altLookProp
.Element
.WeaponRightHand
, (uint
)altLookProp
.Element
.WeaponLeftHand
,
1499 (uint
)altLookProp
.Element
.Hat
, (uint
)altLookProp
.Element
.Seed
,
1500 (uint
)altLookProp
.Element
.ColorHair
);
1503 // Dress the character.
1506 // The entity is now visually ready.
1509 // Generate a new random.
1510 NLMISC::CRandom rnd
;
1511 rnd
.srand((sint
)altLookProp
.Element
.Seed
);
1513 // Retrieve the right sheet for clothes.
1514 _ClothesSheet
= _Sheet
;
1515 if(!_Sheet
->IdAlternativeClothes
.empty())
1517 sint32 num
= rnd
.rand()%(_Sheet
->IdAlternativeClothes
.size()+1);
1520 CSheetId
altClothesId(_Sheet
->getAlternativeClothes(num
-1));
1521 const CEntitySheet
*sheetAlt
= SheetMngr
.get(altClothesId
);
1522 if(dynamic_cast<const CCharacterSheet
*>(sheetAlt
))
1523 _ClothesSheet
= dynamic_cast<const CCharacterSheet
*>(sheetAlt
);
1528 if(_Sheet
->EyesColor
>= SheetMngr
.nbEyesColor())
1530 if (SheetMngr
.nbEyesColor() == 0)
1533 _EyesColor
= (sint8
)(rnd
.rand()%SheetMngr
.nbEyesColor());
1536 _EyesColor
= _Sheet
->EyesColor
;
1538 if (SheetMngr
.nbHairColor() == 0)
1541 _HairColor
= (sint8
)altLookProp
.Element
.ColorHair
%SheetMngr
.nbHairColor();
1543 if(!_Sheet
->HairItemList
.empty())
1545 sint32 num
= rnd
.rand()%_Sheet
->HairItemList
.size();
1546 if(num
>=0 && num
<_BadHairIndex
)
1547 _HairIndex
= (uint8
)num
;
1549 nlwarning("CH:updtVPVpa:%d: Bad Hair Index '%d'", _Slot
, num
);
1552 // -- Dress the character -- (all parts that should not change)
1553 /** tmp : remove all fx item
1554 * \TODO delete an item only if changed
1556 buildEquipment(_ClothesSheet
->Body
, SLOTTYPE::CHEST_SLOT
, altLookProp
.Element
.ColorTop
); // Chest
1557 buildEquipment(_ClothesSheet
->Arms
, SLOTTYPE::ARMS_SLOT
, altLookProp
.Element
.ColorArm
); // Arms
1558 buildEquipment(_ClothesSheet
->Hands
, SLOTTYPE::HANDS_SLOT
, altLookProp
.Element
.ColorGlove
); // Gloves
1559 buildEquipment(_ClothesSheet
->Legs
, SLOTTYPE::LEGS_SLOT
, altLookProp
.Element
.ColorBot
); // Legs
1560 buildEquipment(_ClothesSheet
->Feet
, SLOTTYPE::FEET_SLOT
, altLookProp
.Element
.ColorBoot
); // Boots
1562 _FaceIdx
= buildEquipment(_Sheet
->Face
, SLOTTYPE::FACE_SLOT
);
1564 // Entity is now dressed
1568 // -- Manage the Head --
1569 // Display the helm.
1570 if(altLookProp
.Element
.Hat
!=0 && !_ClothesSheet
->Head
.getItem().empty())
1573 _HeadIdx
= buildEquipment(_ClothesSheet
->Head
, SLOTTYPE::HEAD_SLOT
, altLookProp
.Element
.ColorHair
, _HeadIdx
);
1575 SInstanceCL
*pInstFace
= getFace();
1578 if(pInstFace
->Current
.empty() == false)
1579 pInstFace
->Current
.hide();
1581 pInstFace
->KeepHiddenWhenLoaded
= true;
1584 // Display the Hair.
1588 if(_HairIndex
!= _BadHairIndex
)
1589 _HeadIdx
= buildEquipment(_Sheet
->HairItemList
[_HairIndex
], SLOTTYPE::HEAD_SLOT
, altLookProp
.Element
.ColorHair
, _HeadIdx
);
1590 // Display the face.
1591 SInstanceCL
*pInstFace
= getFace();
1593 if(!pInstFace
->Current
.empty())
1594 pInstFace
->Current
.show();
1597 // -- Manage weapons -- (weapons can change)
1600 const CItemSheet
*newRightHand
= SheetMngr
.getItem(SLOTTYPE::RIGHT_HAND_SLOT
, (uint
)altLookProp
.Element
.WeaponRightHand
);
1601 if (newRightHand
!= _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
) // item changed ?
1603 // Remove the old Item in the right hand
1604 if(_RHandInstIdx
!= CEntityCL::BadIndex
)
1607 _RHandInstIdx
= addInstance("", "", -1, _RHandInstIdx
);
1609 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].release();
1612 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
= newRightHand
;
1615 if( newRightHand
->ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
1616 _RHandInstIdx
= createItemInstance(*newRightHand
, _RHandInstIdx
, SLOTTYPE::RIGHT_HAND_SLOT
, "box_arme", -1, -1);
1618 _RHandInstIdx
= createItemInstance(*newRightHand
, _RHandInstIdx
, SLOTTYPE::RIGHT_HAND_SLOT
, "", -1, -1);
1621 // update fx for right hand (trail may have been activated, or advantage fx)
1624 SInstanceCL
*instCLRH
= idx2Inst(_RHandInstIdx
);
1627 NL3D::UInstance itemInstance
= (!instCLRH
->Loading
.empty()) ? instCLRH
->Loading
: instCLRH
->Current
;
1628 if (!itemInstance
.empty())
1631 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].enableAdvantageFX(itemInstance
);
1632 if ( _CurrentBehaviour
.Behaviour
!= MBEHAV::EXTRACTING
)
1633 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].setTrailSize(altLookProp
.Element
.RTrail
);
1639 const CItemSheet
*newLeftHand
= SheetMngr
.getItem(SLOTTYPE::LEFT_HAND_SLOT
, (uint
)altLookProp
.Element
.WeaponLeftHand
);
1640 if (newLeftHand
!= _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
) // item changed ?
1642 // Remove the old Item in the left hand
1643 if(_LHandInstIdx
!= CEntityCL::BadIndex
)
1646 _LHandInstIdx
= addInstance("", "", -1, _LHandInstIdx
);
1648 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].release();
1651 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
= newLeftHand
;
1655 if(newLeftHand
->getAnimSet() == "s")
1656 bindBone
= "Box_bouclier";
1658 bindBone
= "box_arme_gauche";
1659 _LHandInstIdx
= createItemInstance(*newLeftHand
, _LHandInstIdx
, SLOTTYPE::LEFT_HAND_SLOT
, bindBone
, -1, -1);
1662 // update fx for left hand (trail may have been activated, or advantage fx)
1665 SInstanceCL
*instCLLH
= idx2Inst(_LHandInstIdx
);
1668 NL3D::UInstance itemInstance
= (!instCLLH
->Loading
.empty()) ? instCLLH
->Loading
: instCLLH
->Current
;
1669 if (!itemInstance
.empty())
1672 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].enableAdvantageFX(itemInstance
);
1673 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].setTrailSize((uint
) (2 * altLookProp
.Element
.LTrail
));
1679 // -- Update Animation -- (after all those changes animation could change).
1681 setAnim(animState(MOVE
));
1682 }// updateVisualPropertyVpb //
1684 //-----------------------------------------------
1685 // updateVisualPropertyVpb :
1686 //-----------------------------------------------
1687 void CCharacterCL::updateVisualPropertyVpb(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1689 // Get the alternative look property.
1690 SAltLookProp2 altLookProp
= *(SAltLookProp2
*)(&prop
);
1691 // Display debug infos
1694 nlinfo("(%05d,%03d) CH:updtVPVpb:%d: Scale(%d)", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
,
1695 (uint
)altLookProp
.PropertySubData
.Scale
);
1700 if (altLookProp
.PropertySubData
.Scale
==0)
1703 customScale
= (float)altLookProp
.PropertySubData
.Scale
/100.f
;
1705 if (!_ScaleDone
) // first time
1707 _CustomScale
= customScale
;
1713 _StartCustomScale
= ryzomGetLocalTime();
1714 _BaseCustomScale
= _CustomScale
;
1715 _StepCustomScale
= customScale
- _BaseCustomScale
;
1720 void CCharacterCL::scale()
1723 float oldCustomScale
= _CustomScale
;
1725 _CustomScale
= _BaseCustomScale
+ ((float(ryzomGetLocalTime() - _StartCustomScale
) * _StepCustomScale
) / 1000.f
);
1726 if ((_StepCustomScale
>= 0.f
&& _CustomScale
> _BaseCustomScale
+ _StepCustomScale
)
1727 || (_StepCustomScale
< 0.f
&& _CustomScale
< _BaseCustomScale
+ _StepCustomScale
))
1729 _StepCustomScale
= 0.f
;
1732 applyScale(oldCustomScale
);
1735 void CCharacterCL::applyScale(float oldCustomScale
)
1737 // Apply modification
1738 _CustomScalePos
/= oldCustomScale
;
1739 _CustomScalePos
*= _CustomScale
;
1740 // change the scale of the skeleton according to the new people
1741 USkeleton
* skel
= skeleton();
1744 skel
->setScale(getScale(), getScale(), getScale());
1746 // modify the stick bone scale to not propagate scale to child
1747 sint boneID
= skel
->getBoneIdByName("stick_1");
1750 UBone bone
= skel
->getBone(boneID
);
1751 CVector newBoneScale
= bone
.getScale() * oldCustomScale
/_CustomScale
;
1752 bone
.setScale( newBoneScale
);
1755 // must set the new scale for the BotObject too
1756 else if(!_Instances
.empty())
1758 float s
= getScale();
1759 _Instances
[0].setScale(CVector(s
,s
,s
));
1766 _Primitive
->getSize(width
, depth
);
1767 UMovePrimitive::TType primtype
= _Primitive
->getPrimitiveType();
1768 _Primitive
->setPrimitiveType(UMovePrimitive::_2DOrientedBox
);
1769 _Primitive
->setSize((width
/ oldCustomScale
) * _CustomScale
, (depth
/ oldCustomScale
) * _CustomScale
);
1770 _Primitive
->setPrimitiveType(primtype
);
1773 }// updateVisualPropertyVpb //
1775 //-----------------------------------------------
1776 // updateVisualPropertyEntityMounted :
1777 // Update Entity Mount
1778 //-----------------------------------------------
1779 void CCharacterCL::updateVisualPropertyEntityMounted(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1781 // New target Received.
1782 sint mountSlot
= (sint
)prop
;
1783 _TheoreticalMount
= (CLFECOMMON::TCLEntityId
)mountSlot
;
1785 nlinfo("(%05d,%03d) CH::updateVPMount:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, mountSlot
);
1787 // Add in right stage.
1788 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
, prop
);
1789 }// updateVisualPropertyEntityMounted //
1791 //-----------------------------------------------
1792 // updateVisualPropertyRiderEntity :
1793 // Update Entity Rider
1794 //-----------------------------------------------
1795 void CCharacterCL::updateVisualPropertyRiderEntity(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1797 // New target Received.
1798 sint riderSlot
= (sint
)prop
;
1799 _TheoreticalRider
= (CLFECOMMON::TCLEntityId
)riderSlot
;
1801 nlinfo("(%05d,%03d) CH::updateVPRider:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, riderSlot
);
1803 // Add in right stage.
1804 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
, prop
);
1805 }// updateVisualPropertyRiderEntity //
1807 //-----------------------------------------------
1808 // updateVisualPropertyBars :
1809 // Update Entity Bars
1810 //-----------------------------------------------
1811 void CCharacterCL::updateVisualPropertyBars(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
) // virtual
1813 CBarManager::CBarInfo barInfo
;
1815 // Encode HP to 7 bits
1816 barInfo
.Score
[SCORES::hit_points
] = (sint8
)((prop
&0x7ff) * 127 / 1023);
1817 // NB: barInfo are sint8, but no problem, since anything following is 7 bits.
1818 barInfo
.Score
[SCORES::stamina
] = (uint8
)((prop
>>11)&0x7f);
1819 barInfo
.Score
[SCORES::sap
] = (uint8
)((prop
>>18)&0x7f);
1820 barInfo
.Score
[SCORES::focus
] = (uint8
)((prop
>>25)&0x7f);
1822 // update The Bar manager
1823 CBarManager
*pBM
= CBarManager::getInstance();
1825 WHY gameCycle+1 ????? (yoyo)
1826 It's because sometimes I have a bug With target DB update and VP update. This is the scenario
1827 where I suppose the problem rises:
1828 tick=320: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1829 tick=321: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=321
1830 EGS::databaseUpdate(), DB updated, with timestamp=321!!!
1832 Thus I receives on client:
1833 first the VP with Hp=10, timestamp=321
1834 second the DB with Hp=49, timestamp=321 too => replaced => BUG
1835 NB: DB is typically sent at low frequency by FrontEnd, thus received later on client.
1837 Since databaseUpdate() is called every 2 ticks, adding +1 to VP timestamps solve easily the problem.
1839 NB: the problem occurs because tickUpdate() and databaseUpdate() are called typically with 1 tick shift (tickUpdate()
1840 on even ticks, and databaseUpdate() on odd ticks for instance).
1842 NB: moreover, tickupdate() is called every 8 (or 16) ticks, and databaseUpdate() every 2 ticks. So there is one more
1844 318: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1845 319: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=319
1846 EGS::databaseUpdate(), BUT decide to send only a small subset of DB (because lot of things to send)
1847 => our TargetHP is not updated
1848 320: nothing. tickupdate() is not called, since every 8 ticks
1849 321: EGS::databaseUpdate(), update TargetHP, with timestamp=321 !!!!! => Bug
1851 (remind that we cannot store a timestamp for each DB property, else would be too big to store and to send...)
1853 BTW, this last bug should be very rare, so don't care.
1855 pBM
->updateBars(dataSetId(), barInfo
, gameCycle
+1,
1856 CBarManager::HpFlag
| CBarManager::StaFlag
| CBarManager::SapFlag
| CBarManager::FocusFlag
);
1858 }// updateVisualPropertyBars //
1860 //-----------------------------------------------
1861 // updateVisualPropertyGuildSymbol :
1863 //-----------------------------------------------
1864 void CCharacterCL::updateVisualPropertyGuildSymbol(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1866 _GuildSymbol
= prop
;
1868 // maybe need to rebuild the in scene interface
1869 if(_InSceneUserInterface
&& _InSceneUserInterface
->needGuildSymbolId())
1870 buildInSceneInterface();
1871 } // updateVisualPropertyGuildSymbol //
1873 //-----------------------------------------------
1874 // updateVisualPropertyGuildNameID :
1876 //-----------------------------------------------
1877 void CCharacterCL::updateVisualPropertyGuildNameID(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1879 _GuildNameId
= uint32(prop
);
1881 // maybe need to rebuild the in scene interface
1882 if(_InSceneUserInterface
&& _InSceneUserInterface
->needGuildNameId())
1883 buildInSceneInterface();
1884 } // updateVisualPropertyGuildNameID //
1886 //-----------------------------------------------
1887 // updateVisualPropertyEventFactionID :
1889 //-----------------------------------------------
1890 void CCharacterCL::updateVisualPropertyEventFactionID(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1892 // ODD Hack by Ulukyn
1893 //_EventFactionId = uint32(prop);
1894 _PvpMode
= uint32(prop
);
1896 buildInSceneInterface();
1901 uint numEntity
= (uint
)EntitiesMngr
.entities().size();
1902 for (i
=0; i
<numEntity
; i
++)
1904 CEntityCL
*entity
= EntitiesMngr
.entity(i
);
1907 CCharacterCL
*character
= dynamic_cast<CCharacterCL
*>(entity
);
1910 if( character
->getPvpMode() != 0 && !character
->isUser())
1911 character
->buildInSceneInterface ();
1916 } // updateVisualPropertyEventFactionID //
1918 //-----------------------------------------------
1919 // updateVisualPropertyPvpMode :
1921 //-----------------------------------------------
1922 void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1924 //_PvpMode = uint32(prop);
1925 //buildInSceneInterface();
1927 } // updateVisualPropertyPvpMode //
1929 //-----------------------------------------------
1930 // updateVisualPropertyPvpClan :
1932 //-----------------------------------------------
1933 void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1935 _LeagueId
= uint32(prop
);
1937 buildInSceneInterface();
1942 uint numEntity
= (uint
)EntitiesMngr
.entities().size();
1943 for (i
=0; i
<numEntity
; i
++)
1945 CEntityCL
*entity
= EntitiesMngr
.entity(i
);
1948 CCharacterCL
*character
= dynamic_cast<CCharacterCL
*>(entity
);
1951 if( character
->getPvpMode() != 0 && !character
->isUser())
1952 character
->buildInSceneInterface ();
1957 } // updateVisualPropertyPvpClan //
1959 //-----------------------------------------------
1960 // updateVisualPropertyStatus :
1961 // Update Entity Status
1962 //-----------------------------------------------
1963 void CCharacterCL::updateVisualPropertyStatus(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&/* prop */) // virtual
1965 nlinfo("CH:updtVPStatus:%d: received.", _Slot
);
1966 }// updateVisualPropertyStatus //
1968 //-----------------------------------------------
1969 // updateVisualPropertyContextual :
1970 // Update Entity Status
1971 //-----------------------------------------------
1972 void CCharacterCL::updateVisualPropertyContextual(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1974 bool precAttackable
= _Properties
.attackable();
1977 CEntityCL::updateVisualPropertyContextual(gameCycle
, prop
);
1979 // if attack modified, and npc/fauna, must rebuild the in scene interface,
1980 // cause sheets 'Attackable' property not always correclty filled
1981 if( (isNPC()||isFauna()) && precAttackable
!=_Properties
.attackable())
1982 buildInSceneInterface();
1986 //-----------------------------------------------
1987 // updateVisualPropertyOwnerPeople :
1989 //-----------------------------------------------
1990 void CCharacterCL::updateVisualPropertyOwnerPeople(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1992 if( _OwnerPeople
!= MOUNT_PEOPLE::TMountPeople(prop
) )
1995 float oldPeopleScaleFactor
;
1996 switch( _OwnerPeople
)
1998 case MOUNT_PEOPLE::Fyros
: oldPeopleScaleFactor
= ClientCfg
.FyrosScale
; break;
1999 case MOUNT_PEOPLE::Matis
: oldPeopleScaleFactor
= ClientCfg
.MatisScale
; break;
2000 case MOUNT_PEOPLE::Tryker
: oldPeopleScaleFactor
= ClientCfg
.TrykerScale
; break;
2001 case MOUNT_PEOPLE::Zorai
: oldPeopleScaleFactor
= ClientCfg
.ZoraiScale
; break;
2003 oldPeopleScaleFactor
= 1.f
;
2005 _CustomScalePos
/= oldPeopleScaleFactor
;
2007 // set the new scale pos
2008 float newPeopleScaleFactor
;
2009 _OwnerPeople
= MOUNT_PEOPLE::TMountPeople(prop
);
2010 switch( _OwnerPeople
)
2012 case MOUNT_PEOPLE::Fyros
: newPeopleScaleFactor
= ClientCfg
.FyrosScale
; break;
2013 case MOUNT_PEOPLE::Matis
: newPeopleScaleFactor
= ClientCfg
.MatisScale
; break;
2014 case MOUNT_PEOPLE::Tryker
: newPeopleScaleFactor
= ClientCfg
.TrykerScale
; break;
2015 case MOUNT_PEOPLE::Zorai
: newPeopleScaleFactor
= ClientCfg
.ZoraiScale
; break;
2017 newPeopleScaleFactor
= 1.f
;
2019 _CustomScalePos
*= newPeopleScaleFactor
;
2021 // change the scale of the skeleton according to the new people
2022 USkeleton
* skel
= skeleton();
2025 skel
->setScale(getScale(), getScale(), getScale());
2027 // modify the stick bone scale to not propagate scale to child
2028 sint boneID
= skel
->getBoneIdByName("stick_1");
2031 UBone bone
= skel
->getBone(boneID
);
2032 CVector newBoneScale
= bone
.getScale() * oldPeopleScaleFactor
/newPeopleScaleFactor
;
2033 bone
.setScale( newBoneScale
);
2038 } // updateVisualPropertyOwnerPeople //
2042 //-----------------------------------------------
2043 // updateVisualPropertyOutpostInfos :
2045 //-----------------------------------------------
2046 void CCharacterCL::updateVisualPropertyOutpostInfos(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
2048 _OutpostId
= ((uint16
)prop
)&0x7FFF;
2049 uint16 side
= (((uint16
)prop
)&0x8000)>>15;
2050 _OutpostSide
= (OUTPOSTENUMS::TPVPSide
)side
;
2052 nldebug("<CCharacterCL::updateVisualPropertyOutpostInfos> prop = %d, id=%d side=%d",(uint16
)prop
,_OutpostId
,_OutpostSide
);
2054 buildInSceneInterface();
2056 } // updateVisualPropertyOutpostInfos //
2059 //-----------------------------------------------
2061 // Get The Entity Skin
2062 //-----------------------------------------------
2063 sint
CCharacterCL::skin() const // virtual
2065 return _Sheet
->Skin
;
2069 //-----------------------------------------------
2071 // Initialize properties of the entity (according to the class).
2072 //-----------------------------------------------
2073 void CCharacterCL::initProperties()
2075 properties().selectable(_Sheet
->Selectable
);
2076 properties().talkableTo(_Sheet
->Talkable
);
2077 properties().attackable(_Sheet
->Attackable
);
2078 properties().givable(_Sheet
->Givable
);
2079 properties().mountable(_Sheet
->Mountable
);
2080 properties().invitable(false); // You cannot group with a bot.
2081 properties().afk(false);
2083 switch(_Sheet
->HLState
)
2085 case LHSTATE::LOOTABLE
:
2086 properties().lootable(true); // You can loot the creature
2087 properties().harvestable(false); // You cannot harvest the creature
2089 case LHSTATE::HARVESTABLE
:
2090 properties().lootable(false); // You cannot loot the creature
2091 properties().harvestable(true); // You can harvest the creature
2093 case LHSTATE::LOOTABLE_HARVESTABLE
:
2094 properties().lootable(true); // You can loot the creature
2095 properties().harvestable(true); // You can harvest the creature
2099 properties().lootable(false); // You cannot loot the creature
2100 properties().harvestable(false); // You cannot harvest the creature
2103 }// initProperties //
2106 //-----------------------------------------------
2107 // computeTimeStep :
2108 // Compute the elapsed time since last call.
2109 // \param currentTime : current time in sec.
2110 // \return double : elapsed time.
2111 //-----------------------------------------------
2112 double CCharacterCL::computeTimeStep(const double ¤tTime
)
2114 // Last Time greater than Current Time.
2115 if(_LastFrameTime
> currentTime
)
2117 nlwarning("CCharacterCL::computeTimeStep : Time since last frame is negative (%f). Set _LastFrameTime with currentTime", _LastFrameTime
- currentTime
);
2118 _LastFrameTime
= currentTime
;
2121 // Time since last Time >= 0
2122 return currentTime
- _LastFrameTime
;
2123 }// computeTimeStep //
2126 //-----------------------------------------------
2128 // \todo GUIGUI : to do an average speed, there is a problem if time become very small because small frame or _LastFrameTime increase when looping
2129 //-----------------------------------------------
2130 double CCharacterCL::computeSpeed()
2133 double t
= _DestTime
- _LastFrameTime
;
2134 if(dist2Dest() <= 0.0) // Already at destination.
2136 else if(_LastFrameTime
== 0.0) // First frame
2138 else if(t
< 0.0001) // We should already be at destination.
2139 spd
= 1000.0;//-1.0;
2142 spd
= dist2Dest()/t
;
2143 if(spd
>0 && spd
<0.001)
2152 //-----------------------------------------------
2153 // computeSpeedFactor :
2154 // Compute and return the speed factor to apply to the animation.
2155 // \param speedToDest : evaluted speed to destination.
2156 // \return double : the speed factor to use for the current animation.
2157 // \todo GUIGUI : review this scale problem and optimize it.
2158 //-----------------------------------------------
2159 double CCharacterCL::computeSpeedFactor(double speedToDest
)
2162 double speedFactor
= 1.0;
2164 // \todo GUIGUI : optimize emotes, currently it's badly designed.
2165 const CAnimationState
*animStatePtr
;
2166 // If the current animation is an emote, get the right animation state.
2167 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
2168 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
2170 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
2173 // Get the animation
2174 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
2177 // If the animation is a move (not idle, turn, etc.).
2180 // Move : adjust speed factor according to the animation.
2181 if(_CurrentState
->Move
)
2183 // Check for oo speed.
2184 if(speedToDest
!= -1)
2186 // Compute the animation average speed according to the scale.
2187 double animSpeed
= EAM
->getAnimationAverageSpeed(anim
->id())*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
2189 speedFactor
= speedToDest
/ animSpeed
;
2191 nlwarning("The animation is a move but animation speed is %f !", animSpeed
);
2193 // \todo GUIGUI : unlimited speed, perhaps return a special value.
2194 // We should be arrived so speed is maximum.
2196 speedFactor
= 1000.0;
2198 // The current animation is a rotation so adjust the rotation speed according to the angle.
2199 if(_CurrentState
->Rotation
&& _RotationFactor
> 0.0)
2201 speedFactor
/= _RotationFactor
;
2202 speedFactor
= std::min(speedFactor
, 1.5);
2203 speedFactor
= std::max(speedFactor
, 0.5);
2205 // This animation must be play in a maximum time when there is someting to do after.
2206 // (must be done after the rotation speed adjustment)
2207 if(_CurrentState
->MaxAnimDuration
> 0.00f
)
2209 if(dist2Dest() >= 0.0)
2211 double animLength
= EAM
->getAnimationLength(anim
->id());
2212 double speedFactorBackup
= speedFactor
;
2213 speedFactor
= animLength
/_CurrentState
->MaxAnimDuration
;
2214 // If the animation speed should have been greater, let it play faster.
2215 if(speedFactor
< speedFactorBackup
)
2216 speedFactor
= speedFactorBackup
;
2219 // Panic mode (too late => accelerate)
2220 if(!_CurrentState
->Move
&& _ImportantStepTime
!=0.0)
2222 const float beginPanic
= 1.5f
; // start panic when too late of 1.5 seconds
2223 const float endPanic
= 5.f
; // max panic factor at 5 seconds of interval
2224 const float maxPanicSpeedFactor
= 10.f
;
2225 float t
= float(TimeInSec
- _ImportantStepTime
);
2228 float lerp
= (t
-beginPanic
)/(endPanic
-beginPanic
);
2229 clamp(lerp
, 0.f
, 1.f
);
2230 float panicSF
= lerp
*maxPanicSpeedFactor
;
2231 if(panicSF
>speedFactor
)
2232 speedFactor
= panicSF
;
2235 // Special panic mode because there is a position just after
2236 // NB: don't accelerate animations that can be breaked because of move
2237 // But still allow acceleration for mode animation transition (_Mode!=_ModeWanted)
2238 if( !_CurrentState
->Move
&& _RunStartTimeNoPop
!=INVALID_TIME
&&
2239 (!_CurrentState
->BreakableOnMove
|| _Mode
!=_ModeWanted
) )
2241 const float maxPanicSpeedFactor
= 1.5f
;
2242 // compare time of rest of animation, to remain time to the first move
2243 float remainAnimTime
= float(EAM
->getAnimationLength(anim
->id()) - animOffset(MOVE
));
2244 remainAnimTime
= max(remainAnimTime
, 0.f
);
2246 // if already too late, then maximize speed
2247 if(TimeInSec
>=_RunStartTimeNoPop
)
2249 panicSF
= maxPanicSpeedFactor
;
2251 // else target the animation speed so it ends at estimated start of move
2254 panicSF
= float(remainAnimTime
/(_RunStartTimeNoPop
-TimeInSec
));
2255 panicSF
= min(panicSF
, maxPanicSpeedFactor
);
2257 // only if greater than prec
2258 if(panicSF
>speedFactor
)
2259 speedFactor
= panicSF
;
2264 if(_Slot==WatchedEntitySlot && EntitiesMngr.isLogingStageChange())
2266 sint64 refLT= EntitiesMngr.getLogStageChangeStartLocalTime();
2267 NLMISC::createDebug();
2268 NLMISC::DebugLog->displayRawNL("** Entity %d: (t=%3d) animState %s: %.2f/%.2f. move: %d. rstnp: %d. sf: %.2f",
2269 (sint32)_Slot, sint32(T1-refLT),
2270 CAnimationState::getAnimationStateName(animStatePtr->state()).c_str(),
2271 float(animOffset(MOVE)), float(EAM->getAnimationLength(anim->id())),
2272 uint(_CurrentState->Move), _RunStartTimeNoPop==INVALID_TIME?-1:sint32(sint64(_RunStartTimeNoPop*1000)-refLT),
2281 // \todo GUIGUI : corriger pour enlever le speed factor min.
2282 // Adjuste speed factor
2283 if(speedFactor
< 0.5)
2286 // Check negative or null speed factor.
2287 if(speedFactor
<= 0.0)
2289 nlwarning("CCharacterCL::computeSpeedFactor: speedFactor = %f and this should never be <= 0.", speedFactor
);
2293 // Return the speed factor.
2295 }// computeSpeedFactor //
2297 //-----------------------------------------------
2298 // endAnimTransition :
2299 // Call it at the end of the current animation to choose the next one.
2300 //-----------------------------------------------
2301 void CCharacterCL::endAnimTransition()
2303 // One more animation played.
2306 // Hide the entity if needed.
2310 // Debug Animation for the selection
2311 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
2312 nlinfo("CH:endAnimTransition:%d: current animation finished.", _Slot
);
2313 // If the animation is a rotation, set the character direction he should have at the end of the animation.
2314 if(_CurrentState
->Rotation
)
2318 nldebug("<CCharacterCL::endAnimTransition> rotation : set dir as end anim dir");
2323 // Fit the current direction to the target when attacking.
2324 if(_CurrentState
->Attack
)
2328 // If user is in range attack and not moving, set dir to target
2331 if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
&& _Mode
==MBEHAV::COMBAT
&& !UserControls
.isMoving())
2333 dir( dirToTarget() );
2335 float frontYawBefore
= frontYaw();
2337 float frontYawAfter
= frontYaw();
2338 float deltaYaw
= frontYawAfter
- frontYawBefore
;
2339 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
2341 if( ClientCfg
.AutomaticCamera
)
2343 UserControls
.resetSmoothCameraDeltaYaw();
2348 // If the next mode in the automaton != Current Mode
2349 if(_CurrentState
->NextMode
!= _Mode
)
2351 // Undo previous behaviour
2352 if (_Mode
== MBEHAV::DEATH
)
2354 // Restore collisions.
2357 // TODO: Without this dynamic cast
2358 if (dynamic_cast<CPlayerCL
*>(this))
2359 _Primitive
->setOcclusionMask(MaskColPlayer
);
2361 _Primitive
->setOcclusionMask(MaskColNpc
);
2365 if (ClientCfg
.UsePACSForAll
&& _Primitive
)
2366 _Primitive
->setCollisionMask(MaskColNone
);
2369 switch(_CurrentState
->NextMode
)
2372 case MBEHAV::COMBAT
:
2373 case MBEHAV::COMBAT_FLOAT
:
2374 if(ClientCfg
.UsePACSForAll
&& _Primitive
)
2375 _Primitive
->setCollisionMask(MaskColPlayer
| MaskColNpc
| MaskColDoor
); // Collision with player and npc.
2378 case MBEHAV::MOUNT_NORMAL
:
2380 // Remove collisions if needed.
2381 if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
2384 _Primitive
->setOcclusionMask(MaskColNone
);
2392 case MBEHAV::UNKNOWN_MODE
:
2395 // NO BREAK is Normal here
2397 case MBEHAV::NORMAL
:
2398 // _Mount = CLFECOMMON::INVALID_SLOT;
2399 // _Rider = CLFECOMMON::INVALID_SLOT;
2400 parent(CLFECOMMON::INVALID_SLOT
);
2403 front(CVector(1.f, 0.f, 0.f));
2410 // Change the current mode.
2411 if ( _ModeWanted
!= MBEHAV::UNKNOWN_MODE
)
2413 _Mode
= _CurrentState
->NextMode
;
2416 // nlinfo( "NO mode change from %u to %u, %s S%hu", _Mode, _CurrentState->NextMode, _SheetId.toString().c_str(), _Slot );
2417 // Change the current automaton.
2419 // Update the animation set according to the new automaton.
2422 // Select the Default Next Animation.
2423 setAnim(_CurrentState
->NextState
);
2424 }// endAnimTransition //
2426 //-----------------------------------------------
2428 //-----------------------------------------------
2429 CCharacterCL::TOnMove
CCharacterCL::onMove(const CAutomatonStateSheet
&curAnimState
)
2431 // Animation is breakable if the distance to destination is long enough (at least > 0).
2432 if(curAnimState
.BreakableOnMove
&& dist2Dest()>0.0)
2434 // \todo GUIGUI : take the next position to current one (it could be possible this position was the same as the first).
2435 CVectorD dirToFirstPos
= _FirstPos
-pos();
2436 dirToFirstPos
.z
= 0.0;
2437 if(dirToFirstPos
!= CVectorD::Null
)
2438 dirToFirstPos
.normalize();
2441 nlwarning("CH:onMove:%d: First pos == pos -> take the dir(%f,%f,%f) instead.", _Slot
, dir().x
, dir().y
, dir().z
);
2442 dirToFirstPos
= dir();
2445 // Compute Angle between the front and the first position.
2446 double angToDest
= computeShortestAngle(atan2(front().y
, front().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2450 if(curAnimState
.OnMoveLeft
!= CAnimationStateSheet::UnknownState
)
2451 if((angToDest
>Pi
/3.0) && (angToDest
<2.0*Pi
/3.0))
2454 if(curAnimState
.OnMoveRight
!= CAnimationStateSheet::UnknownState
)
2455 if((angToDest
<-Pi
/3.0) && (angToDest
>-2.0*Pi
/3.0))
2458 if(curAnimState
.OnMoveBackward
!= CAnimationStateSheet::UnknownState
)
2459 if(fabs(angToDest
)>1.92)
2460 return OnMoveBackward
;
2462 // Forward (default)
2463 if(curAnimState
.OnMoveForward
!= CAnimationStateSheet::UnknownState
)
2465 // if(_MaxLoop || fabs(angToDest)<=1.92)
2466 return OnMoveForward
;
2469 nlwarning("CH:onMove:%d: the current state is breakable on Move and the dist to dest is not Null, but there is no animation to move.", _Slot
);
2475 //-----------------------------------------------
2477 //-----------------------------------------------
2478 CCharacterCL::TOnRot
CCharacterCL::onRotation(const CAutomatonStateSheet
&curAnimState
, CVector
&dirEndA
)
2480 // Turn and About Face
2481 if(curAnimState
.BreakableOnRotation
)
2483 CVector bodyDir
= dir();
2484 CVector destDir
= front();
2486 // when user is attacking, his dest is his target
2487 if( isUser() && _CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
&& _Mode
==MBEHAV::COMBAT
&& !UserControls
.isMoving() )
2489 destDir
= dirToTarget();
2492 // Compute the angle between the current heading and computed heading.
2493 double angToDest
= computeShortestAngle(atan2(bodyDir
.y
, bodyDir
.x
), atan2(destDir
.y
, destDir
.x
));
2494 // Rotation to the left.
2495 if(angToDest
>= (ClientCfg
.AnimatedAngleThreshold
*Pi
/180.0))
2500 // Rotation to the right.
2501 if(angToDest
<= -(ClientCfg
.AnimatedAngleThreshold
*Pi
/180.0))
2511 //-----------------------------------------------
2513 //-----------------------------------------------
2514 CCharacterCL::TOnBigBend
CCharacterCL::onBigBend(const CAutomatonStateSheet
&curAnimState
, CVector
&dirEndA
)
2516 // If the current direction is too different of the direction to the first destination -> play a rotation.
2517 if(curAnimState
.BrkOnBigBend
)
2519 CVector dirToFirstPos
;
2520 if(setVect(dirToFirstPos
, _FirstPos
- pos(), true, false))
2522 dirToFirstPos
= curAnimState
.getMatrix()*dirToFirstPos
;
2523 double angToDest
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2524 // Rotation to the left.
2525 if(angToDest
>= 1.5)
2527 dirEndA
= dirToFirstPos
;
2530 // Rotation to the right.
2531 if(angToDest
<= -1.5)
2533 dirEndA
= dirToFirstPos
;
2537 if(fabs(angToDest
) > 0.1)
2539 double newAngle
= atan2(dir().y
, dir().x
) + angToDest
/2.0;
2540 dir(CVector((float)cos(newAngle
), (float)sin(newAngle
), dir().z
));
2550 //-----------------------------------------------
2552 //-----------------------------------------------
2553 bool CCharacterCL::onBadHeading(const CAutomatonStateSheet
&curAnimState
)
2556 if(curAnimState
.BreakableOnBadHeading
&& !_MaxLoop
)
2558 CVector dirToFirstPos
;
2559 if(setVect(dirToFirstPos
, _FirstPos
- pos(), true, false))
2561 // Compute the angle between the front and the next body direction.
2562 double angToDest
= computeShortestAngle(atan2(front().y
, front().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2563 if(curAnimState
.BadHeadingMin
<= curAnimState
.BadHeadingMax
)
2565 if((angToDest
<curAnimState
.BadHeadingMin
) || (angToDest
>curAnimState
.BadHeadingMax
))
2570 if((angToDest
<curAnimState
.BadHeadingMin
) && (angToDest
>curAnimState
.BadHeadingMax
))
2579 //-----------------------------------------------
2581 // Select a new animation for the entity.
2582 // \todo GUIGUI : better manage when there is no skeleton
2583 // \todo GUIGUI : simplify head control
2584 //-----------------------------------------------
2585 ADD_METHOD(void CCharacterCL::setAnim(TAnimStateKey newKey
, TAnimStateKey subKey
, uint animID
))
2589 // Debug Animation for the target
2590 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
2591 nlinfo("CH:setAnim:%d: state '%s'.", _Slot
, CAnimationState::getAnimationStateName (newKey
).c_str());
2594 // RELEASE THE OLD ANIMATION
2595 TAnimStateId lastAnimStateId
= animState(MOVE
); // Bkup the current anim state Id.
2597 // \todo GUIGUI : Faire une fonction pour savoir si on a changer de type d'animation.
2598 // Reset loop counter
2599 if((newKey
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
))
2602 // RESET THE CURRENT ANIMATION
2604 CAnimation::TAnimId buIndex
= animIndex(MOVE
);
2605 animIndex (MOVE
, CAnimation::UnknownAnim
); // Reset the current animation Index
2606 animState (MOVE
, CAnimationStateSheet::UnknownState
); // Reset the current animation state
2607 animOffset(MOVE
, 0.0); // Reset the current animation offset
2608 animIndex (MOVE_BLEND_OUT
, CAnimation::UnknownAnim
); // Reset the current animation Index
2609 animState (MOVE_BLEND_OUT
, CAnimationStateSheet::UnknownState
); // Reset the current animation state
2610 animOffset(MOVE_BLEND_OUT
, 0.0); // Reset the current animation offset
2611 _RotationFactor
= 1.0; // New animation -> default rotation factor for the time.
2612 _RightFXActivated
= false;
2613 _LeftFXActivated
= false;
2614 _RotationFactor
= 1.0f
; // Rotation factor as the animation for the time.
2615 _SubStateKey
= CAnimationStateSheet::UnknownState
; // No SubStateKey for the time.
2616 dirEndAnim(dir()); // For the time the direction at the end of the animation is the current direction.
2619 // If there is no animation set ->There is nothing we can do properly.
2620 if(_CurrentAnimSet
[MOVE
] == 0)
2625 if(!animationStateKey(MOVE
, newKey
))
2627 nlwarning("CH:setAnim:%d: automaton '%s': animation state key '%s' asked is not valid -> trying Idle.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (newKey
).c_str());
2628 if(!animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2629 nlwarning("CH:setAnim:%d: Idle is not warking too", _Slot
);
2633 double speedToDest
= computeSpeed();
2634 // Compute the direction to the first position (direction = front if there is not first pos)
2635 CVector dirToFirstPos
;
2636 if(dist2FirstPos() > 0.0)
2637 dirToFirstPos
= (_FirstPos
- pos()).normed();
2639 dirToFirstPos
= front();
2641 uint antiFreezeCounter
= 0;
2642 // Loop until the process find a steady state.
2644 // Get the state for the current animation.
2645 const CAutomatonStateSheet
*state
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
2648 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot
, CAnimationStateSheet::getAnimationStateName(animState(MOVE
)).c_str(), _CurrentAutomaton
.c_str());
2649 // No animation playing
2651 _PlayList
->setAnimation(MOVE
, UPlayList::empty
);
2654 const CAutomatonStateSheet
&curAnimState
= *state
;
2655 //--------------------//
2656 //--------------------//
2657 // ANTI-FREEZE SYSTEM //
2658 // If too many loop, display some infos
2659 if(antiFreezeCounter
> 10)
2662 nlwarning("CH:setAnim:anitFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
2663 nlwarning("CH:setAnim:anitFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
2664 nlwarning("CH:setAnim:anitFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
2665 nlwarning("CH:setAnim:anitFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
2666 nlwarning("CH:setAnim:anitFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
2667 nlwarning("CH:setAnim:anitFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationStateSheet::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
2669 // Once too many more time reached, leave the method.
2670 if(antiFreezeCounter
> 20)
2673 // Update antiFreezeCounter.
2674 ++antiFreezeCounter
;
2675 // ANTI-FREEZE SYSTEM //
2676 //--------------------//
2677 //--------------------//
2678 // Is there a mode in the queue
2679 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
2681 // Is the current mode not already the mode wanted.
2682 if((_Mode
!= _ModeWanted
) && (dist2Dest()==INVALID_DIST
))
2684 TAnimStateKey transition
;
2685 // Check Default Mode Connection.
2686 if(curAnimState
.getModeConnection(_ModeWanted
, transition
))
2688 // Mode Mount need to be synchro
2689 if(_ModeWanted
== MBEHAV::MOUNT_NORMAL
|| _ModeWanted
== MBEHAV::MOUNT_SWIM
)
2692 if(_Rider
!= CLFECOMMON::INVALID_SLOT
)
2694 if(animationStateKey(MOVE
, transition
))
2697 nlwarning("CH:setAnim:%d: Mode Wanted '%d' : automaton '%s': state '%s': Transition '%s' is not valid.", _Slot
, _ModeWanted
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName (transition
).c_str());
2700 else if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
2702 CEntityCL
*mountTmp
= EntitiesMngr
.entity(_Mount
);
2703 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(mountTmp
);
2706 if(mountTmp
->mode() == _ModeWanted
)
2708 if(animationStateKey(MOVE
, transition
))
2712 nlwarning("CH:setAnim:%d: automaton '%s': state '%s': MountDefaultModeTransition '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName (transition
).c_str());
2718 nlwarning("CH:setAnim:%d: Mode Wanted '%s' but the mount does not exist.", _Slot
, MBEHAV::modeToString(_ModeWanted
).c_str());
2722 // Other modes have the same code.
2725 if(animationStateKey(MOVE
, transition
))
2728 nlwarning("CH:setAnim:%d: Mode Wanted '%d' : automaton '%s': state '%s': Transition '%s' is not valid.", _Slot
, _ModeWanted
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName (transition
).c_str());
2735 // Should we stop the animation once at destination.
2736 if(curAnimState
.BrkAtDest
&& (dist2Dest() <= ClientCfg
.DestThreshold
))
2739 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2742 nlwarning("CH:setAnim:%d: automaton '%s': state '%s': BrkAtDest 'idle' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str());
2746 switch(onMove(curAnimState
))
2750 if(animationStateKey(MOVE
, curAnimState
.OnMoveForward
))
2753 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveForeward '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnMoveForward
).c_str());
2756 case OnMoveBackward
:
2757 if(animationStateKey(MOVE
, curAnimState
.OnMoveBackward
))
2760 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveBackward '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnMoveBackward
).c_str());
2764 if(animationStateKey(MOVE
, curAnimState
.OnMoveLeft
))
2767 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveLeft '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnMoveLeft
).c_str());
2771 if(animationStateKey(MOVE
, curAnimState
.OnMoveRight
))
2774 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveRight '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnMoveRight
).c_str());
2780 // On Rotation/About Face
2781 CVector
dirEndA(0.0f
, 0.0f
, 0.0f
);
2782 switch(onRotation(curAnimState
, dirEndA
))
2786 if(animationStateKey(MOVE
, curAnimState
.OnLeftRotation
))
2788 dirEndAnim(dirEndA
);
2792 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnLeftRotation '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnLeftRotation
).c_str());
2796 if(animationStateKey(MOVE
, curAnimState
.OnRightRotation
))
2798 dirEndAnim(dirEndA
);
2802 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnRightRotation '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnRightRotation
).c_str());
2809 if(curAnimState
.MaxLoop
&& curAnimState
.MaxLoop
<=_NbLoopAnim
)
2811 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2820 if(onBadHeading(curAnimState
))
2822 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2825 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': 'Idle' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str());
2829 switch(onBigBend(curAnimState
, dirEndA
))
2833 if(animationStateKey(MOVE
, curAnimState
.OnBigBendLeft
))
2835 dirEndAnim(dirEndA
);
2839 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnBigBendLeft '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnBigBendLeft
).c_str());
2843 if(animationStateKey(MOVE
, curAnimState
.OnBigBendRight
))
2845 dirEndAnim(dirEndA
);
2849 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnBigBendRight '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName(curAnimState
.OnBigBendRight
).c_str());
2855 // If the animation change according to a high speed and speed high enough or oo.
2856 if(ClientCfg
.BlendForward
== false)
2858 if(curAnimState
.OnMaxSpeed
.Breakable
)
2860 if(speedToDest
>= _CurrentAnimSet
[MOVE
]->speedToRun() || speedToDest
== -1.0)
2862 if(animationStateKey(MOVE
, curAnimState
.OnMaxSpeed
.NextStateKey
))
2865 nlwarning("CH::setAnim: Char '%d': automaton '%s': state '%s': OnMaxSpeed.NextStateKey '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName (curAnimState
.OnMaxSpeed
.NextStateKey
).c_str());
2870 // If the animation can change with a low speed.
2871 if(curAnimState
.OnMinSpeed
.Breakable
)
2873 // If the speed is low enough (check this is not -1 (infinite speed)) -> update the animation to play.
2874 if(speedToDest
!= -1.0 && speedToDest
<= _CurrentAnimSet
[MOVE
]->speedToWalk())
2876 if(animationStateKey(MOVE
, curAnimState
.OnMinSpeed
.NextStateKey
))
2879 nlwarning("CH::setAnim: Char '%d': automaton '%s': state '%s': OnMinSpeed.NextStateKey '%s' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str(), CAnimationState::getAnimationStateName (curAnimState
.OnMinSpeed
.NextStateKey
).c_str());
2885 // \todo GUIGUI : better manage automate change
2886 // Current animation is not of the same kind as the old one.
2887 bool sameAnim
= lastAnimStateId
== animState(MOVE
) && _OldAutomaton
== _CurrentAutomaton
;
2891 stopItemAttackFXs();
2892 stopAttachedFXForCurrrentAnim(true); // stop all anim fx, including looping fxs
2896 stopAttachedFXForCurrrentAnim(false); // stop all anim fxs, but do not stop looping fxs (because the same anim is repeating)
2899 // Compute the current animation state.
2900 _CurrentState
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
2901 // If the state does not exist.
2902 if(_CurrentState
== 0)
2904 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot
, CAnimationStateSheet::getAnimationStateName (animState(MOVE
)).c_str(), _CurrentAutomaton
.c_str());
2906 // No animation playing
2908 _PlayList
->setAnimation(MOVE
, UPlayList::empty
);
2910 // The state is valid.
2913 double angToDest
= 0.0;
2914 // If the new state is a rotation.
2915 if(_CurrentState
->Rotation
)
2916 angToDest
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(dirEndAnim().y
, dirEndAnim().x
));
2917 // Get the right animation state and choose an animation.
2919 // Get the animation state
2920 const CAnimationState
*animationState
= 0;
2921 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
2923 _SubStateKey
= subKey
;
2924 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
2927 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
2930 // Choose the animation
2931 CAnimation::TAnimId index
;
2935 index
= CAnimation::UnknownAnim
;
2936 animIndex(MOVE
, animationState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), angToDest
, index
));
2937 if(animID
!= NL3D::UAnimationSet::NotFound
)
2938 animId(MOVE
, animID
);
2941 // Should the objects in hands be displayed ?
2942 _ObjectsVisible
= animationState
->areObjectsVisible();
2943 showOrHideBodyParts( _ObjectsVisible
);
2945 // in case of user manage the internal view
2948 UserEntity
->updateVisualDisplay();
2952 // Initialize the animation
2953 if(animIndex(MOVE
) != CAnimation::UnknownAnim
)
2955 // If the new state is a rotation.
2956 if(_CurrentState
->Rotation
)
2958 // Get the animation rotation.
2959 double animAngle
= CAnimationMisc::getAnimationRotation(EAM
->getAnimationSet(), animId(MOVE
));
2960 // Compute the rotation factor.
2961 if(animAngle
!= 0.0)
2962 _RotationFactor
= fabs(angToDest
/animAngle
);
2964 _RotationFactor
= -1.0; // \todo GUIGUI : see which value we should use if we have a rot anim without rot and which should rotate character
2967 // If the animation is an atk or forage extraction -> Start all dynamic FXs.
2968 if (_CurrentBehaviour
.Behaviour
== MBEHAV::EXTRACTING
)
2970 // True Extract Animation only for Use AnimationState type
2971 // \todo yoyo: ugly?
2972 if( animState(MOVE
)==CAnimationStateSheet::UseLoop
)
2974 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].setTrailSize(_CurrentBehaviour
.ForageExtraction
.Level
);
2975 startItemAttackFXs(true, 1);
2978 else if (_CurrentState
->Attack
)
2980 if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
2982 startItemAttackFXs(_CurrentBehaviour
.Range
.ImpactIntensity
!= 0, _CurrentBehaviour
.Range
.ImpactIntensity
);
2986 startItemAttackFXs(_CurrentBehaviour
.Combat
.ImpactIntensity
!= 0 && _CurrentBehaviour
.Combat
.HitType
!= HITTYPE::Failed
, _CurrentBehaviour
.Combat
.ImpactIntensity
);
2991 // Initialize the new animation.
2994 // Blend the last animation and the new one.
2995 if(ClientCfg
.BlendFrameNumber
&& _BlendRemaining
<= 0 // Blend On ?
2996 && animIndex(ACTION
) != CAnimation::UnknownAnim
// Last Animation Valid ?
2997 && ((lastAnimStateId
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
)
2998 || (animState(MOVE
) == CAnimationStateSheet::Emote
)) // Last anim != or automaton != ?
2999 && !isRiding()) // No Blend on a mount. \todo GUIGUI trouver un meilleur moyen.
3001 // Get the rotation of the last animation for the blend.
3002 if(!_Skeleton
.empty())
3003 _OldRotQuat
= _Skeleton
.getRotQuat();
3004 _BlendRemaining
= ClientCfg
.BlendFrameNumber
;
3006 _PlayList
->setAnimation(ACTION
, animId(ACTION
));
3007 // Compute weight step.
3008 float w
= 1.f
/((float)(ClientCfg
.BlendFrameNumber
+1));
3009 // Set Old Anim Weight.
3010 _PlayList
->setWeight(ACTION
, 1.f
-w
);
3011 // Set New Anim Weight.
3012 _PlayList
->setWeight(MOVE
, w
);
3014 _AnimReversed
[ACTION
] = _AnimReversed
[MOVE
];
3017 _PlayList
->setAnimation (MOVE
, animId(MOVE
));
3018 // Blend between Walk and Run.
3019 if(ClientCfg
.BlendForward
&& (animState(MOVE
) == CAnimationStateSheet::Walk
))
3021 _CurrentAnimSet
[MOVE_BLEND_OUT
] = _CurrentAnimSet
[MOVE
];
3022 animState(MOVE_BLEND_OUT
, CAnimationStateSheet::Run
);
3023 const CAnimationState
*animationBlendState
= _CurrentAnimSet
[MOVE_BLEND_OUT
]->getAnimationState(animState(MOVE_BLEND_OUT
));
3024 if(animationBlendState
)
3026 animIndex(MOVE_BLEND_OUT
, animationBlendState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender()));
3027 _PlayList
->setAnimation(MOVE_BLEND_OUT
, animId(MOVE_BLEND_OUT
));
3028 _PlayList
->setWeight(MOVE_BLEND_OUT
, 1.0f
); // \todo GUIGUI : verify what is happening if animId is "empty".
3031 nlwarning("setAnim:%d: animationBlendState is Null.", _Slot
);
3034 // Set children animation.
3035 std::list
<CEntityCL
*>::iterator it
= _Children
.begin();
3036 while(it
!= _Children
.end())
3040 CCharacterCL
*child
= dynamic_cast<CCharacterCL
*>(*it
);
3043 // Set the Child as the parent.
3044 child
->computeAnimSet();
3045 child
->currentAutomaton() = _CurrentAutomaton
; // \todo GUIGUI : CA VA PAS MARCHER A CAUSE DU TYPE !!!
3046 child
->animOffset(MOVE
, animOffset(MOVE
));
3047 child
->animState(MOVE
, animState(MOVE
));
3048 child
->currentState(currentState());
3049 child
->animIndex(MOVE
, CAnimation::UnknownAnim
);
3050 const CAnimationState
*animStatePtr
= child
->currentAnimSet()[MOVE
]->getAnimationState(child
->animState(MOVE
));
3053 child
->animIndex(MOVE
, animStatePtr
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), angToDest
));
3054 child
->playList()->setAnimation(MOVE
, child
->animId(MOVE
));
3056 // TEMP : \todo GUIGUI : Pour le moment on enlever le Blend sur les montures.
3058 child
->playList()->setAnimation(ACTION
, UPlayList::empty
);
3059 child
->currentAnimSet()[ACTION
] = child
->currentAnimSet()[MOVE
];
3060 child
->animState (ACTION
, child
->animState (MOVE
));
3061 child
->animIndex (ACTION
, child
->animIndex (MOVE
));
3062 child
->animOffset(ACTION
, child
->animOffset(MOVE
));
3067 nlwarning("CH:setAnim:%d: Child is not a 'CCharacterCL'.", _Slot
);
3070 nlwarning("CH:setAnim:%d: Child not allocated.", _Slot
);
3077 // Get the animation.
3078 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState((animState(MOVE
) == CAnimationStateSheet::Emote
)?subKey
:animState(MOVE
));
3081 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
3084 _HideSkin
= anim
->hideAtEndAnim();
3086 _AnimReversed
[MOVE
] = anim
->isReverse();
3087 // Is the head controlable.
3088 _TargetAnimCtrl
.Enabled
= anim
->headControlable();
3089 // Select the sound ID
3090 _SoundId
[MOVE
] = anim
->soundId();
3091 // look in behaviour if there's a spell to play
3094 if (_CurrentAttackInfo
.Intensity
>= 1 && _CurrentAttackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
)
3096 MAGICFX::TSpellCastStage attackStage
;
3099 case CAnimationStateSheet::OffensiveCastBegin
:
3100 case CAnimationStateSheet::CurativeCastBegin
:
3101 case CAnimationStateSheet::MixedCastBegin
:
3102 case CAnimationStateSheet::AcidCastInit
:
3103 case CAnimationStateSheet::BlindCastInit
:
3104 case CAnimationStateSheet::ColdCastInit
:
3105 case CAnimationStateSheet::ElecCastInit
:
3106 case CAnimationStateSheet::FearCastInit
:
3107 case CAnimationStateSheet::FireCastInit
:
3108 case CAnimationStateSheet::HealHPCastInit
:
3109 case CAnimationStateSheet::MadCastInit
:
3110 case CAnimationStateSheet::PoisonCastInit
:
3111 case CAnimationStateSheet::RootCastInit
:
3112 case CAnimationStateSheet::RotCastInit
:
3113 case CAnimationStateSheet::ShockCastInit
:
3114 case CAnimationStateSheet::SleepCastInit
:
3115 case CAnimationStateSheet::SlowCastInit
:
3116 case CAnimationStateSheet::StunCastInit
:
3117 attackStage
= MAGICFX::CastBegin
;
3119 case CAnimationStateSheet::OffensiveCastLoop
:
3120 case CAnimationStateSheet::CurativeCastLoop
:
3121 case CAnimationStateSheet::MixedCastLoop
:
3122 case CAnimationStateSheet::AcidCastLoop
:
3123 case CAnimationStateSheet::BlindCastLoop
:
3124 case CAnimationStateSheet::ColdCastLoop
:
3125 case CAnimationStateSheet::ElecCastLoop
:
3126 case CAnimationStateSheet::FearCastLoop
:
3127 case CAnimationStateSheet::FireCastLoop
:
3128 case CAnimationStateSheet::HealHPCastLoop
:
3129 case CAnimationStateSheet::MadCastLoop
:
3130 case CAnimationStateSheet::PoisonCastLoop
:
3131 case CAnimationStateSheet::RootCastLoop
:
3132 case CAnimationStateSheet::RotCastLoop
:
3133 case CAnimationStateSheet::ShockCastLoop
:
3134 case CAnimationStateSheet::SleepCastLoop
:
3135 case CAnimationStateSheet::SlowCastLoop
:
3136 case CAnimationStateSheet::StunCastLoop
:
3137 attackStage
= MAGICFX::CastLoop
;
3139 case CAnimationStateSheet::OffensiveCastSuccess
:
3140 case CAnimationStateSheet::CurativeCastSuccess
:
3141 case CAnimationStateSheet::MixedCastSuccess
:
3142 case CAnimationStateSheet::AcidCastEnd
:
3143 case CAnimationStateSheet::BlindCastEnd
:
3144 case CAnimationStateSheet::ColdCastEnd
:
3145 case CAnimationStateSheet::ElecCastEnd
:
3146 case CAnimationStateSheet::FearCastEnd
:
3147 case CAnimationStateSheet::FireCastEnd
:
3148 case CAnimationStateSheet::HealHPCastEnd
:
3149 case CAnimationStateSheet::MadCastEnd
:
3150 case CAnimationStateSheet::PoisonCastEnd
:
3151 case CAnimationStateSheet::RootCastEnd
:
3152 case CAnimationStateSheet::RotCastEnd
:
3153 case CAnimationStateSheet::ShockCastEnd
:
3154 case CAnimationStateSheet::SleepCastEnd
:
3155 case CAnimationStateSheet::SlowCastEnd
:
3156 case CAnimationStateSheet::StunCastEnd
:
3157 attackStage
= MAGICFX::CastEnd
;
3159 case CAnimationStateSheet::OffensiveCastFail
:
3160 case CAnimationStateSheet::OffensiveCastFumble
:
3161 case CAnimationStateSheet::CurativeCastFail
:
3162 case CAnimationStateSheet::CurativeCastFumble
:
3163 case CAnimationStateSheet::MixedCastFail
:
3164 case CAnimationStateSheet::MixedCastFumble
:
3165 case CAnimationStateSheet::AcidCastFail
:
3166 case CAnimationStateSheet::BlindCastFail
:
3167 case CAnimationStateSheet::ColdCastFail
:
3168 case CAnimationStateSheet::ElecCastFail
:
3169 case CAnimationStateSheet::FearCastFail
:
3170 case CAnimationStateSheet::FireCastFail
:
3171 case CAnimationStateSheet::HealHPCastFail
:
3172 case CAnimationStateSheet::MadCastFail
:
3173 case CAnimationStateSheet::PoisonCastFail
:
3174 case CAnimationStateSheet::RootCastFail
:
3175 case CAnimationStateSheet::RotCastFail
:
3176 case CAnimationStateSheet::ShockCastFail
:
3177 case CAnimationStateSheet::SleepCastFail
:
3178 case CAnimationStateSheet::SlowCastFail
:
3179 case CAnimationStateSheet::StunCastFail
:
3180 attackStage
= MAGICFX::CastFail
;
3183 case CAnimationStateSheet::DefaultAtkLow
:
3184 case CAnimationStateSheet::DefaultAtkMiddle
:
3185 case CAnimationStateSheet::DefaultAtkHigh
:
3186 case CAnimationStateSheet::PowerfulAtkLow
:
3187 case CAnimationStateSheet::PowerfulAtkMiddle
:
3188 case CAnimationStateSheet::PowerfulAtkHigh
:
3189 case CAnimationStateSheet::AreaAtkLow
:
3190 case CAnimationStateSheet::AreaAtkMiddle
:
3191 case CAnimationStateSheet::AreaAtkHigh
:
3192 case CAnimationStateSheet::Attack1
:
3193 case CAnimationStateSheet::Attack2
:
3194 case CAnimationStateSheet::FirstPersonAttack
:
3195 attackStage
= MAGICFX::CastEnd
;
3198 attackStage
= MAGICFX::SpellCastStageCount
;
3201 const CAnimationFXSet
*afs
= NULL
;
3204 case MAGICFX::CastBegin
: afs
= &_CurrentAttack
->AttackBeginFX
; break;
3205 case MAGICFX::CastLoop
: afs
= &_CurrentAttack
->AttackLoopFX
; break;
3206 case MAGICFX::CastEnd
: afs
= &_CurrentAttack
->AttackEndFX
; break;
3207 case MAGICFX::CastFail
: afs
= &_CurrentAttack
->AttackFailFX
; break;
3210 playCastFX(afs
, _CurrentAttackInfo
.Intensity
);
3213 // start forage prospection anim fx(s)
3214 if ( (behaviour() == MBEHAV::PROSPECTING
) || (behaviour() == MBEHAV::PROSPECTING_END
) )
3216 const CAnimationFXSet
& fxSet
= anim
->getFXSet();
3217 std::vector
<UParticleSystemInstance
> fxInstances
;
3218 CAttachedFX::CBuildInfo bi
;
3219 CAttachedFX::CTargeterInfo ti
;
3220 for (uint k
= 0; k
< fxSet
.FX
.size(); ++k
)
3222 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3223 bi
.Sheet
= &(fxSet
.FX
[k
]);
3224 fx
->create(*this, bi
, ti
);
3225 if (!fx
->FX
.empty())
3228 attachFXInternal(fx
,FXListCurrentAnim
);
3231 // Set user param for size of prospection area
3233 fx
->FX
.setUserParam( 0, ((float)(_CurrentBehaviour
.ForageProspection
.Range
)) / 127.0f
);
3235 switch ( _CurrentBehaviour
.ForageProspection
.Angle
)
3237 case 0: up
[2] = up
[1] = up
[0] = 0.0f
; break;
3238 case 1: up
[0]=1.0f
; up
[1]=0.0f
; up
[2]=0.0f
; break;
3239 case 2: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=0.0f
; break;
3240 default: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=1.0f
; break;
3242 fx
->FX
.setUserParam( 1, up
[0] );
3243 fx
->FX
.setUserParam( 2, up
[1] );
3244 fx
->FX
.setUserParam( 3, up
[2] );
3245 //nlinfo( "Prospection %s %u %u", behaviour()==MBEHAV::PROSPECTING?"PROSPTG":(behaviour()==MBEHAV::PROSPECTING_END?"PRO_END":"OTHER"), _CurrentBehaviour.ForageProspection.Range, _CurrentBehaviour.ForageProspection.Angle );
3250 // Set user param for level of prospection
3252 switch ( _CurrentBehaviour
.ForageProspection
.Level
)
3254 case 0: up
[3] = up
[2] = up
[1] = up
[0] = 0.0f
; break;
3255 case 1: up
[0]=0.0f
; up
[1]=1.0f
; up
[2]=0.0f
; up
[3]=0.0f
; break;
3256 case 2: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=0.0f
; up
[3]=0.0f
; break;
3257 case 3: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=1.0f
; up
[3]=0.0f
; break;
3258 default: up
[3] = up
[2] = up
[1] = up
[0] = 1.0f
; break;
3260 fx
->FX
.setUserParam( 0, up
[0] );
3261 fx
->FX
.setUserParam( 1, up
[1] );
3262 fx
->FX
.setUserParam( 2, up
[2] );
3263 fx
->FX
.setUserParam( 3, up
[3] );
3268 // start standard anim fx
3271 CAttachedFX::CBuildInfo bi
;
3272 CAttachedFX::CTargeterInfo ti
;
3273 bi
.MaxNumAnimCount
= MAX_FX_ANIM_COUNT
;
3274 for (uint k
= 0; k
< anim
->getFXSet().FX
.size(); ++k
)
3276 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
3277 if (anim
->getFXSet().FX
[k
].Sheet
->RepeatMode
== CAnimationFXSheet::Loop
&& sameAnim
) continue;
3278 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3279 bi
.Sheet
= &(anim
->getFXSet().FX
[k
]);
3280 //bi.StickMode = &bi.Sheet->FX[k].StickMode;
3281 if (anim
->getFXSet().FX
[k
].Sheet
)
3283 bi
.StickMode
= &(anim
->getFXSet().FX
[k
].Sheet
->StickMode
);
3285 fx
->create(*this, bi
, ti
);
3286 if (!fx
->FX
.empty())
3288 attachFXInternal(fx
, FXListAuto
);
3295 nlwarning("CH:setAnim:%d: cannot get the pointer on the animation.", _Slot
);
3297 // No animation found -> check if it is just a transition or is there really an animation missing
3300 // INFO : Verbose mode for Animations of the selection.
3301 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
3302 nlinfo("CH:setAnim:%d: Anim Not Found, state '%s'.", _Slot
, CAnimationState::getAnimationStateName(animState(MOVE
)).c_str());
3304 // If the next animation or automaton is not the same -> this is a transition -> no animation needed.
3305 if(_CurrentState
->MoveState
!= _CurrentState
->NextState
|| (_CurrentState
->NextMode
!= _Mode
))
3307 // Choose the next animation.
3308 endAnimTransition();
3310 // Else -> Animation Missing.
3313 // Set the LOD Animation.
3314 setAnimLOD(((lastAnimStateId
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
)));
3318 //-----------------------------------------------
3320 //-----------------------------------------------
3321 void CCharacterCL::playCastFX(const CAnimationFXSet
*afs
, uint power
)
3324 if (power
<= 0 || power
> 5) return;
3325 static const float castFXUserParams
[MAGICFX::NUM_SPELL_POWER
][4] =
3327 { 0.f
, 0.f
, 0.f
, 0.f
},
3328 { 1.f
, 0.f
, 0.f
, 0.f
},
3329 { 1.f
, 1.f
, 0.f
, 0.f
},
3330 { 1.f
, 1.f
, 1.f
, 0.f
},
3331 { 1.f
, 1.f
, 1.f
, 1.f
}
3333 CAttachedFX::CBuildInfo bi
;
3334 CAttachedFX::CTargeterInfo ti
;
3335 for (uint k
= 0; k
< afs
->FX
.size(); ++k
)
3337 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3338 bi
.Sheet
= &afs
->FX
[k
];
3339 fx
->create(*this, bi
, ti
);
3340 if (!fx
->FX
.empty())
3342 for(uint l
= 0; l
< 4; ++l
)
3344 fx
->FX
.setUserParam(l
, castFXUserParams
[power
- 1][l
]);
3346 attachFXInternal(fx
, FXListCurrentAnim
);
3352 //-----------------------------------------------
3353 // showOrHideBodyParts
3354 //-----------------------------------------------
3355 void CCharacterCL::showOrHideBodyParts( bool objectsVisible
)
3357 // UnHide all user body parts.
3358 for(uint i
=0; i
<_Instances
.size(); ++i
)
3359 if(!_Instances
[i
].Current
.empty())
3360 _Instances
[i
].Current
.show();
3362 // hide or show the face
3363 if( _Items
[SLOTTYPE::HEAD_SLOT
].Sheet
&& _Items
[SLOTTYPE::HEAD_SLOT
].Sheet
->Family
== ITEMFAMILY::ARMOR
)
3366 SInstanceCL
*face
= getFace ();
3371 if(!face
->Current
.empty())
3372 face
->Current
.hide();
3374 face
->KeepHiddenWhenLoaded
= true;
3380 SInstanceCL
*face
= getFace ();
3385 if(!face
->Current
.empty())
3386 face
->Current
.show();
3388 face
->KeepHiddenWhenLoaded
= false;
3392 // get the instance index for right hand and left hand
3393 uint32 rHandInstIdx
;
3394 uint32 lHandInstIdx
;
3395 if( isPlayer() || isUser() )
3397 rHandInstIdx
= SLOTTYPE::RIGHT_HAND_SLOT
;
3398 lHandInstIdx
= SLOTTYPE::LEFT_HAND_SLOT
;
3400 // hide gloves(armor) if player has magician amplifier
3401 if( _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
&& (_Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
->ItemType
== ITEM_TYPE::MAGICIAN_STAFF
) )
3403 if( !_Instances
[SLOTTYPE::HANDS_SLOT
].Current
.empty() )
3404 _Instances
[SLOTTYPE::HANDS_SLOT
].Current
.hide();
3406 _Instances
[SLOTTYPE::HANDS_SLOT
].KeepHiddenWhenLoaded
= true;
3411 rHandInstIdx
= _RHandInstIdx
;
3412 lHandInstIdx
= _LHandInstIdx
;
3415 // if not already hidden and need to hide :
3416 if( !objectsVisible
)
3419 nlassert(SLOTTYPE::RIGHT_HAND_SLOT
< _Items
.size() && SLOTTYPE::LEFT_HAND_SLOT
< _Items
.size());
3420 if(rHandInstIdx
<_Instances
.size())
3421 if( !(_Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
&& _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
->NeverHideWhenEquipped
) )
3422 if(!_Instances
[rHandInstIdx
].Current
.empty())
3424 _Instances
[rHandInstIdx
].Current
.hide();
3425 _Instances
[rHandInstIdx
].hideStaticFXs();
3428 if(lHandInstIdx
<_Instances
.size())
3429 if( !(_Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
&& _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
->NeverHideWhenEquipped
) )
3430 if(!_Instances
[lHandInstIdx
].Current
.empty())
3432 _Instances
[lHandInstIdx
].Current
.hide();
3433 _Instances
[lHandInstIdx
].hideStaticFXs();
3439 if(rHandInstIdx
<_Instances
.size())
3440 if(!_Instances
[rHandInstIdx
].Current
.empty())
3442 _Instances
[rHandInstIdx
].Current
.show();
3443 _Instances
[rHandInstIdx
].showStaticFXs();
3446 if(lHandInstIdx
<_Instances
.size())
3447 if(!_Instances
[lHandInstIdx
].Current
.empty())
3449 _Instances
[lHandInstIdx
].Current
.show();
3450 _Instances
[lHandInstIdx
].showStaticFXs();
3455 //-----------------------------------------------
3457 // Set the LOD animation.
3458 //-----------------------------------------------
3459 ADD_METHOD(void CCharacterCL::setAnimLOD(bool changed
))
3460 // reset LOD animation.
3461 _LodCharacterAnimEnabled
= false;
3462 // Setup new LOD animation.
3465 // if the entity has a lod Character
3466 sint lodId
= skeleton()->getLodCharacterShape();
3470 _LodCharacterAnimEnabled
= true;
3471 _LodCharacterMasterAnimSlot
= MOVE
;
3472 _LodCharacterAnimTimeOffset
= 0;
3473 // do complex stuff only if the anim state has really changed.
3476 // get the anim state from the set.
3477 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
3478 // if animStatePtr found.
3481 // get Lod Character animation Name from the anim state.
3482 const string
&lodAnimName
= animStatePtr
->getLodCharacterAnimation();
3483 // Find the anim in the UScene LodCharacterManager
3484 sint animId
= Scene
->getCLodAnimIdByName(lodId
, lodAnimName
);
3485 // if Anim not found, get the "idle" anim, with the Id 0.
3488 // setup the skeleton.
3489 skeleton()->setLodCharacterAnimId(animId
);
3497 //-----------------------------------------------
3498 // updateAnimationState :
3499 // \todo GUIGUI : precalculate distance to destination when receiving Stages.
3500 // \todo GUIGUI : improve, we are setting often 'idle' to recompute orientation at the end of animation instead of doing directly the right one.
3501 //-----------------------------------------------
3502 ADD_METHOD(void CCharacterCL::updateAnimationState())
3503 // If the current state is invalid -> return.
3504 if(_CurrentState
== 0)
3507 // Get the Animation Length.
3508 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
3509 // Check Anim length
3510 if(animOffset(MOVE
) >= animLength
)
3512 // Choose the next animation.
3513 endAnimTransition();
3515 // Last animation not finished check for some chances to break current animation.
3519 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
3521 // Is the current mode not already the mode wanted.
3522 if((_Mode
!= _ModeWanted
) && (dist2Dest()==INVALID_DIST
))
3524 if((_ModeWanted
!= MBEHAV::MOUNT_NORMAL
&& _ModeWanted
!= MBEHAV::MOUNT_SWIM
) || (_Rider
!= CLFECOMMON::INVALID_SLOT
))
3526 // Is there a possible connection with the mode wanted.
3527 TAnimStateKey transition
;
3528 if(_CurrentState
->getModeConnection(_ModeWanted
, transition
))
3530 setAnim(transition
);
3536 //InfoLog->displayRawNL("(%d)_Rider=%d _Mount=%d _ModeWanted=%s _Mode=%s offset=%f length=%f",_Slot,_Rider,_Mount,MBEHAV::modeToString(_ModeWanted).c_str(),MBEHAV::modeToString(_Mode).c_str(),animOffset(MOVE),animLength);
3537 // anti-bug : sometimes _rider is not set yet and we stay in an infinite move
3538 _Rider
= _TheoreticalRider
;
3543 // Should we stop the animation once at destination.
3544 if(_CurrentState
->BrkAtDest
&& dist2Dest() <= ClientCfg
.DestThreshold
)
3547 setAnim(CAnimationStateSheet::Idle
);
3552 switch(onMove(*_CurrentState
))
3556 setAnim(_CurrentState
->OnMoveForward
);
3559 case OnMoveBackward
:
3560 setAnim(_CurrentState
->OnMoveBackward
);
3564 setAnim(_CurrentState
->OnMoveLeft
);
3568 setAnim(_CurrentState
->OnMoveRight
);
3576 switch(onRotation(*_CurrentState
, tmp
))
3580 setAnim(CAnimationStateSheet::Idle
);
3584 setAnim(CAnimationStateSheet::Idle
);
3591 if(onBadHeading(*_CurrentState
))
3593 setAnim(CAnimationStateSheet::Idle
);
3598 switch(onBigBend(*_CurrentState
, tmp
))
3600 // Big Bend Left and Right
3603 setAnim(_CurrentState
->MoveState
);
3609 // \todo GUIGUI : changer de place cette partie je pense.
3610 // Adjust the direction to fit the front
3611 if( !(isUser() && ClientCfg
.AutomaticCamera
==false) )
3613 if(_CurrentState
->AdjustOri
)
3615 // Adjust before the half of the attack animation.
3616 const double animL
= animLength
/2.0;
3617 // Half already reatch, dir should be as the front now
3618 if(animOffset(MOVE
) > animL
)
3622 double ang
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(front().y
, front().x
));
3625 double ang2
= (animOffset(MOVE
)/animL
)*ang
/animL
;
3626 double angleZ
= ang2
+atan2(dir().y
, dir().x
);
3627 dir(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
));
3633 }// updateAnimationState //
3635 //-----------------------------------------------
3637 //-----------------------------------------------
3638 ADD_METHOD(double CCharacterCL::computeMotion(const double &oldMovingTimeOffset
, TAnimationType channel
) const)
3639 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Compute_Motion
)
3641 // Check the state is valid.
3642 if(_CurrentState
== 0)
3645 // Calculate movement for given animation segment.
3646 if(_CurrentState
->Move
)
3648 // Animation is unknown, no move in it.
3649 if(animIndex(channel
) == CAnimation::UnknownAnim
)
3652 CVector oldPos
, newPos
;
3653 uint animID
= animId(channel
);
3654 if(EAM
->interpolate(animID
, oldMovingTimeOffset
, oldPos
))
3656 if(EAM
->interpolate(animID
, animOffset(channel
), newPos
))
3658 CVector mov
= newPos
- oldPos
;
3659 // Scale it by the CharacterScalePos, if needed, according to the animation.
3660 bool mustApplyCharacterScalePosFactor
= true;
3661 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[channel
]->getAnimationState(animState(channel
));
3664 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(channel
));
3666 mustApplyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
3668 // scale it according to the species.
3669 if(mustApplyCharacterScalePosFactor
)
3670 mov
*= _CharacterScalePos
;
3671 // Scale according to the gabarit.
3672 mov
*= _CustomScalePos
;
3673 // Return a significant move.
3674 double distDone
= mov
.norm();
3675 if(distDone
>0.0 && distDone
<ClientCfg
.SignificantDist
)
3676 distDone
= ClientCfg
.SignificantDist
;
3681 // Miss the position's track.
3682 nlwarning("CCharacterCL::computeMotion : Animation should have a track for the position.");
3685 // The animation is not one to move.
3688 }// computeMotion //
3690 //-----------------------------------------------
3692 //-----------------------------------------------
3693 void CCharacterCL::beginCast(const MBEHAV::CBehaviour
&behaviour
)
3695 // if the player has a target, force him to face this target
3696 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot());
3697 if(target
&& !target
->isUser() )
3699 CVectorD dirToTarget
= target
->pos() - pos();
3701 dirToTarget
.normalize();
3702 front( dirToTarget
);
3706 switch(behaviour
.Behaviour
)
3710 // OFFENSIVE CAST BEGIN
3711 case MBEHAV::CAST_OFF
:
3712 setAnim(CAnimationStateSheet::OffensiveCastInit
);
3714 // CURATIVE CAST BEGIN
3715 case MBEHAV::CAST_CUR
:
3716 setAnim(CAnimationStateSheet::CurativeCastInit
);
3719 case MBEHAV::CAST_MIX
:
3720 setAnim(CAnimationStateSheet::MixedCastInit
);
3724 setAnim(CAnimationStateSheet::Idle
);
3727 case MBEHAV::CAST_ACID
:
3728 setAnim(CAnimationStateSheet::AcidCastInit
);
3730 case MBEHAV::CAST_BLIND
:
3731 setAnim(CAnimationStateSheet::BlindCastInit
);
3733 case MBEHAV::CAST_COLD
:
3734 setAnim(CAnimationStateSheet::ColdCastInit
);
3736 case MBEHAV::CAST_ELEC
:
3737 setAnim(CAnimationStateSheet::ElecCastInit
);
3739 case MBEHAV::CAST_FEAR
:
3740 setAnim(CAnimationStateSheet::FearCastInit
);
3742 case MBEHAV::CAST_FIRE
:
3743 setAnim(CAnimationStateSheet::FireCastInit
);
3745 case MBEHAV::CAST_HEALHP
:
3746 setAnim(CAnimationStateSheet::HealHPCastInit
);
3748 case MBEHAV::CAST_MAD
:
3749 setAnim(CAnimationStateSheet::MadCastInit
);
3751 case MBEHAV::CAST_POISON
:
3752 setAnim(CAnimationStateSheet::PoisonCastInit
);
3754 case MBEHAV::CAST_ROOT
:
3755 setAnim(CAnimationStateSheet::RootCastInit
);
3757 case MBEHAV::CAST_ROT
:
3758 setAnim(CAnimationStateSheet::RotCastInit
);
3760 case MBEHAV::CAST_SHOCK
:
3761 setAnim(CAnimationStateSheet::ShockCastInit
);
3763 case MBEHAV::CAST_SLEEP
:
3764 setAnim(CAnimationStateSheet::SleepCastInit
);
3766 case MBEHAV::CAST_SLOW
:
3767 setAnim(CAnimationStateSheet::SlowCastInit
);
3769 case MBEHAV::CAST_STUN
:
3770 setAnim(CAnimationStateSheet::StunCastInit
);
3777 //-----------------------------------------------
3779 //-----------------------------------------------
3780 void CCharacterCL::endCast(const MBEHAV::CBehaviour
&behaviour
, const MBEHAV::CBehaviour
&lastBehaviour
)
3782 if( !(isUser() && isSit()) )
3784 switch(lastBehaviour
.Behaviour
)
3786 case MBEHAV::CAST_ACID
:
3787 setAnim(CAnimationStateSheet::AcidCastEnd
);
3789 case MBEHAV::CAST_BLIND
:
3790 setAnim(CAnimationStateSheet::BlindCastEnd
);
3792 case MBEHAV::CAST_COLD
:
3793 setAnim(CAnimationStateSheet::ColdCastEnd
);
3795 case MBEHAV::CAST_ELEC
:
3796 setAnim(CAnimationStateSheet::ElecCastEnd
);
3798 case MBEHAV::CAST_FEAR
:
3799 setAnim(CAnimationStateSheet::FearCastEnd
);
3801 case MBEHAV::CAST_FIRE
:
3802 setAnim(CAnimationStateSheet::FireCastEnd
);
3804 case MBEHAV::CAST_HEALHP
:
3805 setAnim(CAnimationStateSheet::HealHPCastEnd
);
3807 case MBEHAV::CAST_MAD
:
3808 setAnim(CAnimationStateSheet::MadCastEnd
);
3810 case MBEHAV::CAST_POISON
:
3811 setAnim(CAnimationStateSheet::PoisonCastEnd
);
3813 case MBEHAV::CAST_ROOT
:
3814 setAnim(CAnimationStateSheet::RootCastEnd
);
3816 case MBEHAV::CAST_ROT
:
3817 setAnim(CAnimationStateSheet::RotCastEnd
);
3819 case MBEHAV::CAST_SHOCK
:
3820 setAnim(CAnimationStateSheet::ShockCastEnd
);
3822 case MBEHAV::CAST_SLEEP
:
3823 setAnim(CAnimationStateSheet::SleepCastEnd
);
3825 case MBEHAV::CAST_SLOW
:
3826 setAnim(CAnimationStateSheet::SlowCastEnd
);
3828 case MBEHAV::CAST_STUN
:
3829 setAnim(CAnimationStateSheet::StunCastEnd
);
3832 setAnim(CAnimationStateSheet::Idle
);
3837 switch(behaviour
.Behaviour
)
3841 // OFFENSIVE CAST END
3842 case MBEHAV::CAST_OFF_FAIL
:
3843 setAnim(CAnimationStateSheet::OffensiveCastFail
);
3845 case MBEHAV::CAST_OFF_FUMBLE
:
3846 setAnim(CAnimationStateSheet::OffensiveCastFumble
);
3848 case MBEHAV::CAST_OFF_SUCCESS
:
3849 setAnim(CAnimationStateSheet::OffensiveCastSuccess
);
3851 case MBEHAV::CAST_OFF_LINK
:
3852 setAnim(CAnimationStateSheet::OffensiveCastLink
);
3854 // CURATIVE CAST END
3855 case MBEHAV::CAST_CUR_FAIL
:
3856 setAnim(CAnimationStateSheet::CurativeCastFail
);
3858 case MBEHAV::CAST_CUR_FUMBLE
:
3859 setAnim(CAnimationStateSheet::CurativeCastFumble
);
3861 case MBEHAV::CAST_CUR_SUCCESS
:
3862 setAnim(CAnimationStateSheet::CurativeCastSuccess
);
3864 case MBEHAV::CAST_CUR_LINK
:
3865 setAnim(CAnimationStateSheet::CurativeCastLink
);
3868 case MBEHAV::CAST_MIX_FAIL
:
3869 setAnim(CAnimationStateSheet::MixedCastFail
);
3871 case MBEHAV::CAST_MIX_FUMBLE
:
3872 setAnim(CAnimationStateSheet::MixedCastFumble
);
3874 case MBEHAV::CAST_MIX_SUCCESS
:
3875 setAnim(CAnimationStateSheet::MixedCastSuccess
);
3877 case MBEHAV::CAST_MIX_LINK
:
3878 setAnim(CAnimationStateSheet::MixedCastLink
);
3890 // *************************************************************************************************
3891 void CCharacterCL::updateCurrentAttack()
3893 // This is a behaviour for the magic.
3894 if(_CurrentBehaviour
.isMagic())
3896 _CurrentAttackID
.Type
= CAttackIDSheet::Magic
;
3897 _CurrentAttackID
.SpellInfo
.Mode
= (MAGICFX::TSpellMode
) _CurrentBehaviour
.Spell
.SpellMode
;
3898 _CurrentAttackID
.SpellInfo
.ID
= (MAGICFX::TMagicFx
) _CurrentBehaviour
.Spell
.SpellId
;
3900 // This is a behaviour for the combat.
3901 else if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
3903 _CurrentAttackID
.Type
= CAttackIDSheet::Range
;
3904 _CurrentAttackID
.RangeWeaponType
= (RANGE_WEAPON_TYPE::TRangeWeaponType
) _CurrentBehaviour
.Range
.WeaponType
;
3906 else if (_CurrentBehaviour
.isCombat())
3908 _CurrentAttackID
.Type
= CAttackIDSheet::Melee
;
3910 else if (_CurrentBehaviour
.isCreatureAttack())
3912 _CurrentAttackID
.Type
= CAttackIDSheet::Creature
;
3913 _CurrentAttackID
.CreatureAttackIndex
= _CurrentBehaviour
.Behaviour
== MBEHAV::CREATURE_ATTACK_0
? 0 : 1;
3917 // the behaviour does not generate an attack
3918 _CurrentAttack
= NULL
;
3919 _CurrentAttackID
.Type
= CAttackIDSheet::Unknown
;
3922 _CurrentAttack
= getAttack(_CurrentAttackID
);
3923 // update current attack infos
3924 if(_CurrentBehaviour
.isMagic())
3926 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Spell
.SpellIntensity
;
3927 // physical damge are irrelevant for magic
3928 _CurrentAttackInfo
.DamageType
= DMGTYPE::UNDEFINED
;
3929 _CurrentAttackInfo
.HitType
= HITTYPE::Undefined
;
3930 _CurrentAttackInfo
.Localisation
= BODY::UnknownBodyPart
;
3931 _CurrentAttackInfo
.PhysicalImpactIntensity
= 0;
3933 // This is a behaviour for the combat.
3934 else if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
3936 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Range
.ImpactIntensity
;
3937 _CurrentAttackInfo
.DamageType
= DMGTYPE::UNDEFINED
;
3938 _CurrentAttackInfo
.HitType
= HITTYPE::Undefined
;
3939 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.Range
.Localisation
;
3940 _CurrentAttackInfo
.PhysicalImpactIntensity
= 0;
3942 else if (_CurrentBehaviour
.isCombat())
3944 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Combat
.ImpactIntensity
;
3945 _CurrentAttackInfo
.DamageType
= (DMGTYPE::EDamageType
) _CurrentBehaviour
.Combat2
.DamageType
;
3946 _CurrentAttackInfo
.HitType
= (HITTYPE::THitType
) _CurrentBehaviour
.Combat
.HitType
;
3947 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.Combat
.Localisation
;
3948 _CurrentAttackInfo
.PhysicalImpactIntensity
= _CurrentBehaviour
.Combat
.ImpactIntensity
;
3950 else if (_CurrentBehaviour
.isCreatureAttack())
3952 // creature attack is the most general form
3953 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.CreatureAttack
.MagicImpactIntensity
;
3954 _CurrentAttackInfo
.DamageType
= (DMGTYPE::EDamageType
) _CurrentBehaviour
.CreatureAttack2
.DamageType
;
3955 _CurrentAttackInfo
.HitType
= (HITTYPE::THitType
) _CurrentBehaviour
.CreatureAttack2
.HitType
;
3956 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.CreatureAttack
.Localisation
;
3957 _CurrentAttackInfo
.PhysicalImpactIntensity
= _CurrentBehaviour
.CreatureAttack
.ImpactIntensity
;
3959 _CurrentAttackInfo
.Side
= (rand() & 1) ? BODY::Left
: BODY::Right
;
3963 // utility function for performCurrentAttackEnd
3964 inline static void getResistAndDistance(uint8 packedInfo
, bool isDirectAttack
, bool isCombat
, bool &resist
, uint
&distance
)
3966 // get the distance from attacker to defender (consider 0 if a direct attack)
3970 distance
= packedInfo
& 127;
3975 resist
= (packedInfo
& 128) != 0;
3978 // *********************************************************************************************
3979 void CCharacterCL::performCurrentAttackEnd(const CBehaviourContext
&bc
, bool directOffensifSpell
, vector
<double> &targetHitDates
, TAnimStateKey animForCombat
)
3981 if (!_CurrentAttack
) return;
3982 if (!_CurrentAttack
->Sheet
) return;
3983 if (bc
.Targets
.Targets
.empty())
3985 nlwarning ("No target available for current attack.");
3988 nlassert(targetHitDates
.size() == bc
.Targets
.Targets
.size());
3990 CAttackInfo attackInfo
= _CurrentAttackInfo
;
3992 const CAttackSheet
&sheet
= *_CurrentAttack
->Sheet
;
3995 // should cast FX for static Objects like towers be used ?
3996 bool usesStaticCastFX
= _Sheet
&& !_Sheet
->ProjectileCastRay
.empty();
3998 CProjectileBuild pb
;
4001 // ray of cast for static object with several cast points
4002 CVector castWorldOrigin
;
4003 CVector castWorldPos
;
4004 CVector additionnalOffset
= NLMISC::CVector::Null
;
4005 bool castRayValid
= false;
4006 uint mainCastFXIntensity
= attackInfo
.Intensity
;
4009 // *** Compute castStartTime (time at which the projectile is casted)
4011 // *** Compute castStartTime (time at which the projectile is casted)
4013 float delay
= 0.f
; // by default, no delay before impact
4014 double timeFactor
= 1;
4016 // Combat (range or melee) => no resist (yoyo: legacy code. Actually the server should set the resist bit to 0 in Target.Info)
4017 bool isCombat
= _CurrentBehaviour
.isCombat();
4019 // An attack is said 'direct' when no projectile is casted (Generally those are melee attack, but also pistol range attack for instance)
4020 // In this case, all targets are hit at the same time, no matter what date is supplied for them.
4021 bool isDirectAttack
= sheet
.ProjectileFX
.empty();
4028 // compute impact date. It is given by the trigger on right hand in the animation
4029 // Get the current animation id.
4030 if (sheet
.ForceUseProjectileDelay
)
4032 delay
= sheet
.ProjectileDelay
;
4035 if (!directOffensifSpell
)
4037 if ((_CurrentAttackID
.Type
!= CAttackIDSheet::Range
) && (_PlayList
!= NULL
))
4041 // if the animation has been correctly chosen
4042 if(animForCombat
!=CAnimationStateSheet::UnknownState
)
4044 // try to get the MeleeImpactDelay that is stored in the sheet (given by graphists)
4045 const CAnimationSet
*animSet
= currentAnimSet()[MOVE
];
4048 const CAnimationState
*animState
= animSet
->getAnimationState(animForCombat
);
4050 delay
= animState
->getMeleeImpactDelay();
4056 // see if weapon is known, and in this case, delay the impact by the same amount
4057 const CItemSheet
*sheet
= _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
;
4058 if (sheet
) delay
= sheet
->FX
.ImpactFXDelay
;
4061 // all target are reached at the same time
4066 // there's a projectile
4067 if (usesStaticCastFX
)
4069 delay
= sheet
.StaticObjectProjectileDelay
;
4073 if (!directOffensifSpell
)
4075 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4076 // projectile cast from a character
4077 delay
= sheet
.ProjectileDelay
;
4082 // then castStartTime is just start of behav + animation delay
4083 double castStartTime
;
4084 // Special for direct attack. because of lag sometimes behavTime<TimeInSec, which results
4085 // on too early played hits regarding animation played
4088 // start at max(behavTime, TimeInSec), but with a limit of bc.BehavTime+0.5 sec
4089 if(TimeInSec
> bc
.BehavTime
+0.5)
4090 castStartTime
= bc
.BehavTime
+ 0.5;
4091 else if(TimeInSec
> bc
.BehavTime
)
4092 castStartTime
= TimeInSec
;
4094 castStartTime
= bc
.BehavTime
;
4096 castStartTime
+= delay
;
4100 // In case of magic or range (with projectile), this is not required because the projectilemanager should take cares of
4101 // cast start time and current time (and therfore advance the projectile if required)
4102 castStartTime
= bc
.BehavTime
+ delay
;
4105 // *** Casts projectiles and Impacts
4108 // there's a projectile
4109 switch(sheet
.ProjectileMode
)
4111 // TODO: homin code for projectile look quite similar, maybe can merge ?
4114 getResistAndDistance(bc
.Targets
.Targets
[0].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4115 double mainStartDate
= castStartTime
;
4116 double mainEndDate
= mainStartDate
+ timeFactor
* double(distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
);
4117 CCharacterCL
*mainTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[0].TargetSlot
));
4120 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *mainTarget
);
4121 if (usesStaticCastFX
)
4123 // compute the cast pos
4124 computeBestCastRay(*mainTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4125 castRayValid
= true;
4128 // the target hit date
4129 targetHitDates
[0]= mainEndDate
;
4131 // Add the projectile to queue
4132 if (createCurrentAttackEndPart(pb
,
4139 sheet
.PlayImpactAnim
,
4141 sheet
.IsImpactLocalised
,
4146 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4149 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4150 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4151 // all subsequent projectiles are casted from the secondary target, from the first impact point, and have level 1
4152 for(uint k
= 1; k
< bc
.Targets
.Targets
.size(); ++k
)
4154 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4155 double secondaryEndDate
= mainEndDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4156 CCharacterCL
*secondaryTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4157 if (secondaryTarget
)
4159 // the target hit date
4160 targetHitDates
[k
]= secondaryEndDate
;
4162 // Add the projectile to queue
4163 if (mainTarget
->createCurrentAttackEndPart(pb
,
4166 &pb
.ProjectileAimingPoint
,
4169 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4170 sheet
.PlayImpactAnim
,
4172 sheet
.IsImpactLocalised
,
4176 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *secondaryTarget
);
4177 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4184 case MAGICFX::Chain
:
4186 double currDate
= castStartTime
;
4187 CCharacterCL
*currCaster
= this;
4188 const CFXStickMode
*projectileStartPoint
= NULL
; // by default, start at caster hand
4189 CFXStickMode currStickMode
;
4190 for(uint k
= 0; k
< bc
.Targets
.Targets
.size(); ++k
)
4194 // for secondary impacts, intensity is 0 or 1
4195 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4196 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4198 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4199 double nextDate
= currDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4200 CCharacterCL
*currTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4201 if (!currTarget
) break;
4204 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *currTarget
);
4207 if (k
== 0 && usesStaticCastFX
)
4209 // compute the initial cast pos for objects with multiple possible cast positions
4210 computeBestCastRay(*currTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4211 castRayValid
= true;
4214 // the target hit date
4215 targetHitDates
[k
]= nextDate
;
4217 // Add the projectile to queue
4218 if (currCaster
->createCurrentAttackEndPart(pb
,
4221 projectileStartPoint
,
4224 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4225 sheet
.PlayImpactAnim
,
4227 sheet
.IsImpactLocalised
,
4229 k
== 0 ? additionnalOffset
: CVector::Null
4232 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4234 currStickMode
= pb
.ProjectileAimingPoint
;
4235 projectileStartPoint
= &currStickMode
;
4236 currCaster
= currTarget
;
4237 if (!currCaster
) break;
4238 currDate
= nextDate
;
4242 case MAGICFX::Spray
:
4244 for(uint k
= 0; k
< bc
.Targets
.Targets
.size(); ++k
)
4246 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4247 double startDate
= castStartTime
;
4248 double endDate
= startDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4249 CCharacterCL
*currTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4252 // for secondary impacts, intensity is 0 or 1
4253 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4254 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4258 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *currTarget
);
4259 if (k
== 0 && usesStaticCastFX
)
4261 // compute the initial cast pos for objects with multiple possible cast positions
4262 computeBestCastRay(*currTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4263 castRayValid
= true;
4266 // the target hit date
4267 targetHitDates
[k
]= endDate
;
4269 // nb : only main target display the spell with full power
4270 if (createCurrentAttackEndPart(pb
,
4276 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4277 sheet
.PlayImpactAnim
,
4279 sheet
.IsImpactLocalised
,
4281 k
== 0 ? additionnalOffset
: CVector::Null
4284 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4294 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4295 if (usesStaticCastFX
&& castRayValid
)
4297 buildStaticObjectCastFX(castWorldOrigin
, castWorldPos
, *_CurrentAttack
->Sheet
, mainCastFXIntensity
);
4301 // *** Play damage shields when melee attack is done
4303 // TODO: to finalize (server code not done).
4304 if (ClientCfg.DamageShieldEnabled && _CurrentBehaviour.isCombat() && _CurrentBehaviour.todoNotRange())
4306 for(uint k = 0; k < bc.Targets.Targets.size(); ++k)
4308 uint power = bc.Targets.Targets[k].Info & 7;
4311 uint dmType = bc.Targets.Targets[k].Info >> 3;
4313 CAttackIDSheet damageShieldID;
4314 damageShieldID.Type = CAttackIDSheet::DamageShield;
4315 damageShieldID.DamageShieldType = dmType;
4316 CCharacterCL *currTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4317 if (!currTarget) continue;
4318 const CAttack *damageShieldReaction = currTarget->getAttack(damageShieldID);
4319 if (!damageShieldReaction) continue;
4320 CAttackInfo attackInfo;
4321 attackInfo.Intensity = power;
4322 attackInfo.PhysicalImpactIntensity = 0;
4323 attackInfo.Localisation = BODY::UnknownBodyPart;
4325 const CAttackSheet &damageShieldSheet = *damageShieldReaction->Sheet;
4326 computeTargetStickMode(damageShieldSheet, attackInfo, pb.ProjectileAimingPoint, *this);
4327 if (currTarget->createCurrentAttackEndPart(pb,
4328 damageShieldReaction,
4334 damageShieldSheet.PlayImpactAnim,
4339 // play "CastEnd" at the good date (if any ...)
4340 pb.CastAspect = &damageShieldReaction->AttackEndFX;
4341 pb.CastPower = power;
4342 pb.ForcePlayImpact = true;
4343 CProjectileManager::getInstance().addProjectileToQueue(pb);
4351 // *********************************************************************************************
4352 void CCharacterCL::buildStaticObjectCastFX(const NLMISC::CVector
&castWorldOrigin
, NLMISC::CVector
&castWorldPos
, const CAttackSheet
&/* sheet */, uint intensity
)
4354 if (intensity
== 0) return;
4355 const float *userParams
= CProjectileManager::getProjectileFXUserParams(intensity
);
4356 // create additionnal cast fxs on the tower or other static object (if any)
4357 // Build lookat matrix (with respect to axis) looking from castWorldOrigin to castWorldPos
4358 CVector dir
= castWorldPos
- castWorldOrigin
;
4359 CVector I
= dir
.normed();
4360 CVector K
= (CVector::K
- (I
* CVector::K
) * I
).normed();
4362 castMat
.setPos(castWorldPos
);
4363 castMat
.setRot(I
, K
^ I
, K
);
4364 CAttachedFX::CBuildInfo bi
;
4365 CAttachedFX::CTargeterInfo ti
;
4366 bi
.StaticMatrix
= &castMat
;
4367 const CAnimationFXSet
&afs
=_CurrentAttack
->AttackStaticObjectCastFX
;
4368 for (uint k
= 0; k
< afs
.FX
.size(); ++k
)
4370 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
4371 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
4372 bi
.Sheet
= &afs
.FX
[k
];
4373 fx
->create(*this, bi
, ti
);
4374 if (!fx
->FX
.empty())
4378 for(uint l
= 0; l
< 4; ++l
)
4380 fx
->FX
.setUserParam(l
, userParams
[l
]);
4388 // *********************************************************************************************
4389 void CCharacterCL::computeTargetStickMode(const CAttackSheet
&sheet
, const CAttackInfo
&attackInfo
, CFXStickMode
&dest
, CEntityCL
&target
)
4391 bool hasPhysicalImpact
= false;
4392 if (attackInfo
.Localisation
!= BODY::UnknownBodyPart
&&
4393 attackInfo
.PhysicalImpactIntensity
>= 1 &&
4394 attackInfo
.PhysicalImpactIntensity
<= MAGICFX::NUM_SPELL_POWER
&&
4395 attackInfo
.HitType
!= HITTYPE::Failed
&&
4396 attackInfo
.HitType
!= HITTYPE::Undefined
&&
4397 attackInfo
.DamageType
!= DMGTYPE::UNDEFINED
)
4399 hasPhysicalImpact
= true;
4402 if (sheet
.IsImpactLocalised
|| hasPhysicalImpact
)
4404 // get projectile impact point from localisation
4405 // & generate stick mode
4406 const char *targetBoneName
= target
.getBoneNameFromBodyPart(attackInfo
.Localisation
, attackInfo
.Side
);
4409 if (sheet
.DefaultAimingPoint
.Mode
== CFXStickMode::UserBoneOrientedTowardTargeter
)
4411 dest
.Mode
= CFXStickMode::UserBoneOrientedTowardTargeter
;
4415 dest
.Mode
= CFXStickMode::UserBone
;
4417 dest
.UserBoneName
= CStringMapper::map(targetBoneName
);
4422 // use default aiming point given in sheet
4423 dest
= sheet
.DefaultAimingPoint
;
4428 // *********************************************************************************************
4429 bool CCharacterCL::createCurrentAttackEndPart(CProjectileBuild
&destPB
,
4430 const CAttack
*currentAttack
,
4431 const CCharacterCL
&target
,
4432 const CFXStickMode
*sm
,
4436 bool playImpactAnim
,
4438 bool /* mainImpactIsLocalised */,
4439 const CAttackInfo
&attackInfo
,
4440 const NLMISC::CVector
&additionnalOffset
/*= NLMISC::CVector::Null*/
4443 if (!currentAttack
) return false;
4444 if (!currentAttack
->Sheet
) return false;
4447 const CAttackSheet
&sheet
= *currentAttack
->Sheet
;
4450 destPB
.StartDate
= spawnDate
;
4451 destPB
.EndDate
= hitDate
;
4452 // choose fx for projectile
4453 if (attackInfo
.Intensity
>= 1 && attackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
)
4455 destPB
.ProjectileAspect
= ¤tAttack
->ProjectileFX
;
4459 destPB
.ProjectileAspect
= NULL
;
4461 // choose fx for impact
4464 destPB
.ImpactAspect
= NULL
;
4468 if (attackInfo
.Intensity
>= 1 && attackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
) // impact has same intensity than projectile
4470 destPB
.ImpactAspect
= ¤tAttack
->ImpactFX
;
4474 destPB
.ImpactAspect
= NULL
;
4478 // FILL PROJECTILE BUILD
4479 destPB
.AttackInfo
= attackInfo
;
4480 destPB
.TargeterInfo
.Slot
= slot();
4483 destPB
.LocalizedImpact
= sheet
.IsImpactLocalised
;
4484 // If this is a secondary projectile, it may start from another location, which is the impact point of the previous projectile
4485 // (so it doesn't start from the caster hand, or any around settings that is read from the spell sheet)
4486 if (sm
) // start stickmode wanted ?
4488 destPB
.TargeterInfo
.StickMode
= *sm
;
4492 // if no stick mode is not forced, then use the one given in the projectile sheet
4493 if (destPB
.ProjectileAspect
&& !destPB
.ProjectileAspect
->FX
.empty())
4495 destPB
.TargeterInfo
.StickMode
= destPB
.ProjectileAspect
->FX
[0].Sheet
->StickMode
;
4499 // if no projectile is given, then uses the default casting point
4500 destPB
.TargeterInfo
.StickMode
= sheet
.DefaultCastingPoint
;
4503 destPB
.Mode
= sheet
.ProjectileMode
;
4504 destPB
.Target
.Slot
= target
.slot();
4505 destPB
.TargeterInfo
.StickOffset
= CVector::Null
;
4506 destPB
.PlayImpactAnim
= playImpactAnim
;
4507 destPB
.LetProjectileStickedOnTarget
= sheet
.LetProjectileStickedOnTarget
;
4508 destPB
.TargeterInfo
.DefaultPos
= pos().asVector();
4510 destPB
.MagicResist
= magicResist
;
4511 // offset if projectile is launched from a range weapon and projectile is sticked to box_arme
4512 if (destPB
.TargeterInfo
.StickMode
.Mode
== CFXStickMode::UserBone
&& sheet
.ApplyItemOffsetToWeaponBone
)
4514 // should be fired from the 'box_arme' bone, which means it is fired from a weapon
4515 if (CStringMapper::unmap(destPB
.TargeterInfo
.StickMode
.UserBoneName
) == "box_arme")
4517 NLMISC::CVector projectileOffset
= NLMISC::CVector::Null
;
4518 const CItemSheet
*is
= getRightHandItemSheet();
4521 destPB
.TargeterInfo
.StickOffset
= is
->FX
.AttackFXOffset
;
4523 destPB
.TargeterInfo
.StickOffset
+= additionnalOffset
;
4526 destPB
.TargeterInfo
.StickOffset
+= sheet
.AdditionnalStartOffset
;
4531 // *********************************************************************************************
4532 void CCharacterCL::computeBestCastRay(CEntityCL
&targetEntity
,
4533 const CFXStickMode
&targetStickMode
,
4534 NLMISC::CVector
&castWorldOrigin
,
4535 NLMISC::CVector
&castWorldPos
,
4536 NLMISC::CVector
&worldOffsetToCasterPivot
4539 // additionnal offset taken from sheet. Useful for towers that have no bones, but can fire projectiles anyway
4540 nlassert(_Sheet
&& !_Sheet
->ProjectileCastRay
.empty());
4541 // if several offsets are provided, then choose the one that has the smallest angle towards target
4543 CProjectileManager::evalFXPosition(&targetStickMode
, targetEntity
, target
);
4544 float maxDP3
= -FLT_MAX
;
4545 // NB : the offset is relative to object pivot, not to a bone
4546 CMatrix casterMatrix
;
4547 buildAlignMatrix(casterMatrix
);
4548 for(uint k
= 0; k
< _Sheet
->ProjectileCastRay
.size(); ++k
)
4550 CVector currCastWorldPos
= casterMatrix
* _Sheet
->ProjectileCastRay
[k
].Pos
;
4551 CVector currCastWorldOrigin
= casterMatrix
* _Sheet
->ProjectileCastRay
[k
].Origin
;
4552 float dp3
= (target
- currCastWorldPos
).normed() * (currCastWorldPos
- currCastWorldOrigin
).normed();
4556 worldOffsetToCasterPivot
= casterMatrix
.mulVector(_Sheet
->ProjectileCastRay
[k
].Pos
);
4557 castWorldOrigin
= currCastWorldOrigin
;
4558 castWorldPos
= currCastWorldPos
;
4563 // *********************************************************************************************
4564 bool CCharacterCL::isCurrentBehaviourAttackEnd() const
4566 switch(_CurrentBehaviour
.Behaviour
)
4568 case MBEHAV::CAST_OFF_SUCCESS
:
4569 case MBEHAV::CAST_OFF_LINK
:
4570 case MBEHAV::CAST_CUR_SUCCESS
:
4571 case MBEHAV::CAST_CUR_LINK
:
4572 case MBEHAV::CAST_MIX_SUCCESS
:
4573 case MBEHAV::CAST_MIX_LINK
:
4574 case MBEHAV::RANGE_ATTACK
:
4575 case MBEHAV::CREATURE_ATTACK_0
:
4576 case MBEHAV::CREATURE_ATTACK_1
:
4577 case MBEHAV::DEFAULT_ATTACK
:
4578 case MBEHAV::POWERFUL_ATTACK
:
4579 case MBEHAV::AREA_ATTACK
:
4588 // ***************************************************************************
4589 void CCharacterCL::applyBehaviourFlyingHPs(const CBehaviourContext
&bc
, const MBEHAV::CBehaviour
&behaviour
,
4590 const vector
<double> &targetHitDates
)
4592 nlassert(targetHitDates
.size()==bc
.Targets
.Targets
.size());
4594 if (behaviour
.DeltaHP
== 0 || bc
.Targets
.Targets
.empty())
4597 CRGBA
deltaHPColor(0, 0, 0);
4598 for (size_t i
=0; i
<bc
.Targets
.Targets
.size(); ++i
)
4600 if(bc
.Targets
.Targets
[i
].DeltaHP
== 0)
4603 CEntityCL
*target2
= EntitiesMngr
.entity(bc
.Targets
.Targets
[i
].TargetSlot
);
4607 if(bc
.Targets
.Targets
[i
].DeltaHP
< 0)
4609 // if the behaviour is casted by the user
4612 deltaHPColor
= ClientCfg
.SystemInfoParams
["dgm"].Color
;
4615 // if the behaviour is casted by an entity that target the user
4616 if( targetSlot() == 0 )
4618 CEntityCL
*actor
= EntitiesMngr
.entity(slot());
4621 // if actor is player : use pvp color
4622 if( actor
->isPlayer() )
4623 deltaHPColor
= ClientCfg
.SystemInfoParams
["dgp"].Color
;
4625 deltaHPColor
= ClientCfg
.SystemInfoParams
["dg"].Color
;
4630 deltaHPColor
= CRGBA(127,127,127);
4635 deltaHPColor
= CRGBA(0,220,0);
4638 target2
->addHPOutput(bc
.Targets
.Targets
[i
].DeltaHP
, deltaHPColor
, float(targetHitDates
[i
]-TimeInSec
));
4644 //-----------------------------------------------
4645 // Apply the behaviour.
4646 // \param behaviour : the behaviour to apply.
4647 //-----------------------------------------------
4648 void CCharacterCL::applyBehaviour(const CBehaviourContext
&bc
) // virtual
4650 // Backup the current behaviour.
4651 CBehaviour previousBehaviour
= _CurrentBehaviour
;
4652 _CurrentBehaviour
= bc
.Behav
;
4653 const CBehaviour
&behaviour
= bc
.Behav
;
4655 // check if self-target
4656 bool selfSpell
= false;
4657 if (bc
.Targets
.Targets
.size() == 1)
4659 if (bc
.Targets
.Targets
[0].TargetSlot
== 0)
4665 switch(behaviour
.Behaviour
)
4667 case MBEHAV::CAST_OFF
:
4668 case MBEHAV::CAST_OFF_FAIL
:
4669 case MBEHAV::CAST_OFF_SUCCESS
:
4670 case MBEHAV::CAST_OFF_LINK
:
4678 // Get a pointer on the target.
4679 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot());
4682 // ***** Choose Combat Animation to apply
4684 TAnimStateKey combatAnimState
= CAnimationStateSheet::UnknownState
;
4685 bool isMovingCombatAnimState
= false;
4686 // if the behaviour is for combat, compute now the animation to apply
4687 if( !behaviour
.isMagic() && ( behaviour
.isCombat() || behaviour
.isCreatureAttack() ) )
4689 // Atk Animation when moving
4690 if(_CurrentState
&& _CurrentState
->Move
&& (_CurrentState
->OnAtk
!= CAnimationStateSheet::UnknownState
))
4692 combatAnimState
= _CurrentState
->OnAtk
;
4693 isMovingCombatAnimState
= true;
4695 // Atk Animation when NOT moving
4698 // select the combat animation.
4699 switch(behaviour
.Behaviour
)
4701 case CREATURE_ATTACK_0
:
4702 combatAnimState
= CAnimationStateSheet::Attack1
;
4704 case CREATURE_ATTACK_1
:
4705 combatAnimState
= CAnimationStateSheet::Attack2
;
4707 // Default Animation
4708 case DEFAULT_ATTACK
:
4709 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4712 case CCharacterCL::AtkLow
:
4713 combatAnimState
= CAnimationStateSheet::DefaultAtkLow
;
4716 case CCharacterCL::AtkHigh
:
4717 combatAnimState
= CAnimationStateSheet::DefaultAtkHigh
;
4719 // MIDDLE or Default
4720 case CCharacterCL::AtkMiddle
:
4722 combatAnimState
= CAnimationStateSheet::DefaultAtkMiddle
;
4726 // Powerful Animation
4727 case POWERFUL_ATTACK
:
4728 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4731 case CCharacterCL::AtkLow
:
4732 combatAnimState
= CAnimationStateSheet::PowerfulAtkLow
;
4735 case CCharacterCL::AtkHigh
:
4736 combatAnimState
= CAnimationStateSheet::PowerfulAtkHigh
;
4738 // MIDDLE or Default
4739 case CCharacterCL::AtkMiddle
:
4741 combatAnimState
= CAnimationStateSheet::PowerfulAtkMiddle
;
4747 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4750 case CCharacterCL::AtkLow
:
4751 combatAnimState
= CAnimationStateSheet::AreaAtkLow
;
4754 case CCharacterCL::AtkHigh
:
4755 combatAnimState
= CAnimationStateSheet::AreaAtkHigh
;
4757 // MIDDLE or Default
4758 case CCharacterCL::AtkMiddle
:
4760 combatAnimState
= CAnimationStateSheet::AreaAtkMiddle
;
4766 combatAnimState
= CAnimationStateSheet::Attack1
;
4775 // ***** Compute Impact delays and cast missiles
4777 // Default target hit dates
4778 static vector
<double> targetHitDates
;
4779 targetHitDates
.clear();
4780 targetHitDates
.resize(bc
.Targets
.Targets
.size(), TimeInSec
);
4782 // Update Attack Projectiles and FXs
4783 updateCurrentAttack();
4784 if (isCurrentBehaviourAttackEnd())
4786 // retrieve target hit dates, so flying HPs have the correct ones
4787 performCurrentAttackEnd(bc
, selfSpell
&& isOffensif
, targetHitDates
, combatAnimState
);
4790 // INFO : display some debug information.
4791 if((VerboseAnimUser
&& _Slot
==0) || (VerboseAnimSelection
&& _Slot
== UserEntity
->selection()))
4792 nlinfo("CH:applyBeh:%d: '%d(%s)'", _Slot
, (sint
)behaviour
.Behaviour
, behaviourToString((EBehaviour
)behaviour
.Behaviour
).c_str());
4795 // ***** Apply the behaviour according to type
4797 // This is a behaviour for the magic.
4798 if(behaviour
.isMagic())
4800 // Execute the magic behaviour.
4801 switch(behaviour
.Behaviour
)
4805 case MBEHAV::CAST_OFF
:
4806 case MBEHAV::CAST_CUR
:
4807 case MBEHAV::CAST_MIX
:
4808 case MBEHAV::CAST_ACID
:
4809 case MBEHAV::CAST_BLIND
:
4810 case MBEHAV::CAST_COLD
:
4811 case MBEHAV::CAST_ELEC
:
4812 case MBEHAV::CAST_FEAR
:
4813 case MBEHAV::CAST_FIRE
:
4814 case MBEHAV::CAST_HEALHP
:
4815 case MBEHAV::CAST_MAD
:
4816 case MBEHAV::CAST_POISON
:
4817 case MBEHAV::CAST_ROOT
:
4818 case MBEHAV::CAST_ROT
:
4819 case MBEHAV::CAST_SHOCK
:
4820 case MBEHAV::CAST_SLEEP
:
4821 case MBEHAV::CAST_SLOW
:
4822 case MBEHAV::CAST_STUN
:
4823 beginCast(behaviour
);
4827 case MBEHAV::CAST_OFF_FAIL
:
4828 case MBEHAV::CAST_OFF_FUMBLE
:
4829 if (!selfSpell
) endCast(behaviour
, previousBehaviour
);
4831 case MBEHAV::CAST_OFF_SUCCESS
:
4832 case MBEHAV::CAST_OFF_LINK
:
4833 endCast(behaviour
, previousBehaviour
);
4835 case MBEHAV::CAST_CUR_FAIL
:
4836 case MBEHAV::CAST_CUR_FUMBLE
:
4837 endCast(behaviour
, previousBehaviour
);
4839 case MBEHAV::CAST_CUR_SUCCESS
:
4840 case MBEHAV::CAST_CUR_LINK
:
4841 endCast(behaviour
, previousBehaviour
);
4843 case MBEHAV::CAST_MIX_FAIL
:
4844 case MBEHAV::CAST_MIX_FUMBLE
:
4845 endCast(behaviour
, previousBehaviour
);
4847 case MBEHAV::CAST_MIX_SUCCESS
:
4848 case MBEHAV::CAST_MIX_LINK
:
4849 endCast(behaviour
, previousBehaviour
);
4855 applyBehaviourFlyingHPs(bc
, behaviour
, targetHitDates
);
4857 // This is a behaviour for the combat.
4858 else if(behaviour
.isCombat() || behaviour
.isCreatureAttack())
4860 float frontYawBefore
= 0.f
;
4861 float frontYawAfter
= 0.f
;
4863 // Atk Animation when NOT moving?
4864 if(target
&& !isMovingCombatAnimState
)
4866 // orientate to target
4867 CVectorD dirToTarget
= target
->pos() - pos();
4869 dirToTarget
.normalize();
4870 if( !(isUser() && ClientCfg
.AutomaticCamera
== false) )
4873 frontYawBefore
= frontYaw();
4874 front( dirToTarget
);
4879 // Apply the state animation chosen before
4880 if(combatAnimState
!=CAnimationStateSheet::UnknownState
)
4881 setAnim(combatAnimState
);
4883 // move camera so view doesn't change
4884 if( isUser() && frontYawBefore
!= 0.f
)
4886 frontYawAfter
= frontYaw();
4887 float deltaYaw
= frontYawAfter
- frontYawBefore
;
4890 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
4894 // reset yaw smoothly to center view behind user
4895 if( isUser() && target
&& !target
->isUser() && ClientCfg
.AutomaticCamera
)
4897 UserControls
.resetSmoothCameraDeltaYaw();
4901 applyBehaviourFlyingHPs(bc
, behaviour
, targetHitDates
);
4904 else if(behaviour
.isEmote())
4906 if(ClientCfg
.Light
==false && ClientCfg
.EAMEnabled
)
4912 // old code : fxs attached to emotes
4913 uint emoteIndex = behaviour.Behaviour-EMOTE_BEGIN;
4914 CTextEmotListSheet *pTELS = dynamic_cast<CTextEmotListSheet*>(SheetMngr.get(CSheetId("list.text_emotes")));
4917 if (emoteIndex < pTELS->TextEmotList.size())
4919 const CTextEmotListSheet::STextEmot &emot = pTELS->TextEmotList[emoteIndex];
4920 if (!emot.FXToSpawn.empty())
4922 // Compute the direction Matrix
4924 CVector vi = dir() ^ CVector::K;
4925 CVector vk = vi ^ dir();
4926 fxMatrix.setRot(vi, UserEntity->dir(), vk, true);
4927 fxMatrix.setPos(pos().asVector() + fxMatrix.getJ() * emot.FXSpawnDist);
4928 FXMngr.deferFX(emot.FXToSpawn, fxMatrix, emot.FXSpawnDelay);
4933 if(EAM
->getEmot(behaviour
.Behaviour
-EMOTE_BEGIN
, emot
))
4934 setAnim(CAnimationStateSheet::Emote
, emot
);
4936 nlwarning("CH:applyBeh:%d: Emot '%d' unknown.", _Slot
, behaviour
.Behaviour
-EMOTE_BEGIN
);
4943 switch(behaviour
.Behaviour
)
4946 case MBEHAV::LOOT_INIT
:
4947 setAnim(CAnimationStateSheet::LootInit
);
4950 case MBEHAV::LOOT_END
:
4951 setAnim(CAnimationStateSheet::LootEnd
);
4953 // Prospecting Begin
4954 case MBEHAV::PROSPECTING
:
4955 setAnim(CAnimationStateSheet::ProspectingInit
);
4958 case MBEHAV::PROSPECTING_END
:
4959 setAnim(CAnimationStateSheet::ProspectingEnd
);
4962 case MBEHAV::EXTRACTING
:
4965 if(behaviour
.DeltaHP
!= 0)
4966 target
->addHPOutput(behaviour
.DeltaHP
,CRGBA(0,220,0));
4967 // If receiving a new DeltaHP in the current extraction, don't reset the animation
4968 if ( previousBehaviour
.Behaviour
!= _CurrentBehaviour
.Behaviour
)
4969 setAnim(CAnimationStateSheet::UseInit
);
4972 case MBEHAV::EXTRACTING_END
:
4973 setAnim(CAnimationStateSheet::UseEnd
);
4977 setAnim(CAnimationStateSheet::CareInit
);
4980 case MBEHAV::CARE_END
:
4981 setAnim(CAnimationStateSheet::CareEnd
);
4984 // Begin to use a tool
4985 case MBEHAV::HARVESTING
:
4987 case MBEHAV::REPAIR
:
4988 case MBEHAV::REFINE
:
4989 case MBEHAV::TRAINING
:
4990 setAnim(CAnimationStateSheet::UseInit
);
4993 // End to use a tool
4994 case MBEHAV::HARVESTING_END
:
4995 case MBEHAV::FABER_END
:
4996 case MBEHAV::REPAIR_END
:
4997 case MBEHAV::REFINE_END
:
4998 case MBEHAV::TRAINING_END
:
4999 setAnim(CAnimationStateSheet::UseEnd
);
5003 case MBEHAV::STUNNED
:
5004 setAnim(CAnimationStateSheet::StunBegin
);
5008 case MBEHAV::STUN_END
:
5009 setAnim(CAnimationStateSheet::StunEnd
);
5016 // Unknown behaviour -> idle.
5017 case UNKNOWN_BEHAVIOUR
:
5019 nlwarning("CH::computeBehaviour : Entity in slot %d has an unknown behaviour %d to manage.", _Slot
, (sint
)behaviour
.Behaviour
);
5023 }// computeBehaviour //
5025 //-----------------------------------------------
5027 // Play an impact on the entity
5028 // \param impactType : 0=magic, 1=melee
5029 // \param type : see behaviour for spell
5030 // \param intensity : see behaviour for spell
5031 // \param id : see behaviour for spell
5032 //-----------------------------------------------
5033 void CCharacterCL::impact(uint
/* impactType */, uint type
, uint id
, uint intensity
) // virtual
5035 // Display Magic Debug Infos
5036 if(Verbose
& VerboseMagic
)
5037 nlinfo("CH:impact:%d: type: %d, id: %d, intensity: %d", _Slot
, type
, id
, intensity
);
5038 // No Intensity -> No Impact
5041 // Invalid Intensity -> No Impact
5042 else if(intensity
>5)
5044 nlwarning("CH:impact:%d: invalid intensity %u", _Slot
, intensity
);
5047 // RESIST : temp until resist is in the enum.
5051 NL3D::UInstance resistFX
= Scene
->createInstance("Sp_Resist_Lev5.ps");
5052 if(!resistFX
.empty())
5053 resistFX
.setPos(pos());
5056 // Compute the impact name
5058 if(id
< ClientCfg
.OffImpactFX
.size())
5059 impact
= ClientCfg
.OffImpactFX
[id
];
5064 NL3D::UInstance impactFX
= Scene
->createInstance(impact
);
5065 if(!impactFX
.empty())
5067 impactFX
.setPos(pos());
5068 UParticleSystemInstance instFX
;
5069 instFX
.cast (impactFX
);
5072 // UserParam | Intensity 1 | Intensity 2 | Intensity 3 | Intensity 4 | Intensity 5
5073 // 0 | 0 | 0 | 1 | 1 | 1
5074 // 1 | 0 | 1 | 1 | 1 | 1
5075 // 2 | 0 | 0 | 0 | 1 | 1
5076 // 3 | 0 | 0 | 0 | 0 | 1
5077 float userParam0
= 0.0f
;
5078 float userParam1
= 0.0f
;
5079 float userParam2
= 0.0f
;
5080 float userParam3
= 0.0f
;
5081 // WARNING : there is no break and this is correct.
5093 instFX
.setUserParam(0, userParam0
);
5094 instFX
.setUserParam(1, userParam1
);
5095 instFX
.setUserParam(2, userParam2
);
5096 instFX
.setUserParam(3, userParam3
);
5102 //-----------------------------------------------
5104 //-----------------------------------------------
5105 void CCharacterCL::meleeImpact(const CAttackInfo
&attack
)
5107 if (_Skeleton
.empty()) return;
5108 if (attack
.PhysicalImpactIntensity
< 1 || attack
.PhysicalImpactIntensity
> 5) return;
5109 if (attack
.HitType
== HITTYPE::Failed
) return;
5110 const char *boneName
= getBoneNameFromBodyPart(attack
.Localisation
, attack
.Side
);
5111 if (!boneName
) return;
5112 sint boneId
= _Skeleton
.getBoneIdByName(std::string(boneName
));
5113 if (boneId
== -1) return;
5115 // choose good fx depending on the kind of damage
5116 NL3D::UInstance instance
;
5117 switch(attack
.DamageType
)
5119 case DMGTYPE::BLUNT
: instance
= Scene
->createInstance("mel_impactblunt.ps"); break;
5120 case DMGTYPE::SLASHING
: instance
= Scene
->createInstance("mel_impactslashing.ps"); break;
5121 case DMGTYPE::PIERCING
: instance
= Scene
->createInstance("mel_impactpiercing.ps"); break;
5123 return; // other types not supported
5126 if (instance
.empty()) return;
5127 UParticleSystemInstance impact
;
5128 impact
.cast (instance
);
5131 Scene
->deleteInstance(instance
);
5134 // the 2 first user params of the fx are used to modulate intensity
5135 static const float intensityUP
[5][2] =
5143 impact
.setUserParam(0, intensityUP
[attack
.PhysicalImpactIntensity
- 1][0]);
5144 impact
.setUserParam(1, intensityUP
[attack
.PhysicalImpactIntensity
- 1][1]);
5145 impact
.setUserParam(2, (attack
.HitType
== HITTYPE::CriticalHit
|| attack
.HitType
== HITTYPE::CriticalHitResidual
) ? 1.f
: 0.f
);
5146 impact
.setUserParam(3, (attack
.HitType
== HITTYPE::HitResidual
|| attack
.HitType
== HITTYPE::CriticalHitResidual
) ? 1.f
: 0.f
);
5148 _Skeleton
.stickObject(impact
, boneId
);
5149 // delegate managment of the impact to the fx manager
5150 FXMngr
.fx2remove(impact
);
5154 //-----------------------------------------------
5156 // Play the magic impact on the entity
5157 // \param type : type of the impact (host/good/neutral).
5158 // \param intensity : intensity of the impact.
5159 //-----------------------------------------------
5160 void CCharacterCL::magicImpact(uint type
, uint intensity
) // virtual
5169 impact
= "Sp_Resist_Lev";
5173 impact
= "Sp_Bien_Cure_Lev";
5177 impact
= "Sp_Neutre_Protect_Lev";
5181 impact
= "Sp_Host_Hurt_Lev";
5184 nlwarning("CH:magicImpact:%d: Unknown type '%d'.", _Slot
, type
);
5191 case INTENSITY_TYPE::IMPACT_NONE
:
5194 case INTENSITY_TYPE::IMPACT_INSIGNIFICANT
:
5197 case INTENSITY_TYPE::IMPACT_VERY_WEAK
:
5200 case INTENSITY_TYPE::IMPACT_WEAK
:
5203 case INTENSITY_TYPE::IMPACT_AVERAGE
:
5206 case INTENSITY_TYPE::IMPACT_STRONG
:
5212 nlwarning("CH:magicImpact:%d: Unknown intensity '%d'.", _Slot
, intensity
);
5217 NL3D::UInstance resistFX
= Scene
->createInstance(impact
);
5218 if(!resistFX
.empty())
5219 resistFX
.setPos(pos());
5224 //-----------------------------------------------
5226 // Return the basic max speed for the entity in meter per sec
5227 //-----------------------------------------------
5228 double CCharacterCL::getMaxSpeed() const // virtual
5230 return _Sheet
->MaxSpeed
;
5234 //-----------------------------------------------
5236 // Method called to change the mode (Combat/Mount/etc.).
5237 //-----------------------------------------------
5238 bool CCharacterCL::mode(MBEHAV::EMode m
)
5241 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
5243 nlinfo("CH::mode:%d: m'%s(%d)', _ModeWanted'%s(%d)', _Mode'%s(%d)'", _Slot
,
5244 MBEHAV::modeToString(m
).c_str(), m
,
5245 MBEHAV::modeToString(_ModeWanted
).c_str(), _ModeWanted
,
5246 MBEHAV::modeToString(_Mode
).c_str(), _Mode
);
5248 // Is the mode wanted valid ?
5249 if(m
== MBEHAV::UNKNOWN_MODE
|| m
>= MBEHAV::NUMBER_OF_MODES
)
5251 nlwarning("CH::mode:%d: Invalid Mode Wanted '%s(%d)' -> keep '%s(%d)'.",
5252 _Slot
, MBEHAV::modeToString(m
).c_str(), m
, MBEHAV::modeToString(_Mode
).c_str(), _Mode
);
5255 // Set the mode wanted.
5257 if(_CurrentState
== 0)
5258 _Mode
= _ModeWanted
;
5263 //-----------------------------------------------
5265 // Apply stage modifications.
5266 // \todo GUIGUI ; ameliorer gestion mode.
5267 //-----------------------------------------------
5268 bool CCharacterCL::applyStage(CStage
&stage
)
5270 bool stageDone
= true;
5272 // If the Stage has a position, backup the position and the stage time.
5273 if(stage
.getPos(_OldPos
))
5276 _OldPosTime
= stage
.time();
5277 // Remove the property.
5278 stage
.removeProperty(PROPERTY_POSX
);
5279 stage
.removeProperty(PROPERTY_POSY
);
5280 stage
.removeProperty(PROPERTY_POSZ
);
5283 // Apply orientation.
5284 pair
<bool, sint64
> resultTeta
= stage
.property(PROPERTY_ORIENTATION
);
5285 if(resultTeta
.first
)
5288 parts
.i64
[0] = resultTeta
.second
;
5289 float angleZ
= parts
.f
[0];
5290 // server forces the entity orientation even if it cannot turn
5291 front(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
), true, true, true);
5293 _TargetAngle
= (float)angleZ
;
5294 // Remove the property.
5295 stage
.removeProperty(PROPERTY_ORIENTATION
);
5299 pair
<bool, sint64
> resultMode
= stage
.property(PROPERTY_MODE
);
5300 if(resultMode
.first
)
5302 // Get the mode from stage.
5304 parts
.i64
[0] = resultMode
.second
;
5305 uint8 mo
= parts
.u8
[0];
5306 // If the mode wanted is not the same, change the mode wanted.
5307 if(mo
!= _ModeWanted
)
5309 if(mode((MBEHAV::EMode
)mo
))
5311 //stageDone = false;
5312 if(_Mode
!= _ModeWanted
)
5315 stage
.removeProperty(PROPERTY_MODE
);
5318 stage
.removeProperty(PROPERTY_MODE
);
5320 // If the mode wanted is not the same as the current mode -> Stage not done.
5321 else if(_Mode
!= _ModeWanted
)
5323 // Property applied -> Remove the property form stage.
5325 stage
.removeProperty(PROPERTY_MODE
);
5329 pair
<bool, sint64
> resultBehaviour
= stage
.property(PROPERTY_BEHAVIOUR
);
5330 if(resultBehaviour
.first
)
5332 CBehaviourContext bc
;
5333 bc
.Behav
= CBehaviour(resultBehaviour
.second
);
5334 bc
.BehavTime
= stage
.time();
5335 // See if there's a list of target associated with that behaviour (for multitarget spells)
5336 uint64 spellTarget
[4];
5337 uint numTargets
= 0;
5338 for(uint k
= 0; k
< 4; ++k
)
5340 pair
<bool, sint64
> stProp
= stage
.property(PROPERTY_TARGET_LIST_0
+ k
);
5341 if (!stProp
.first
) break;
5342 spellTarget
[k
] = (uint64
) stProp
.second
;
5344 stage
.removeProperty(PROPERTY_TARGET_LIST_0
+ k
);
5349 // get the list of targets from the visual properties
5350 bc
.Targets
.unpack(spellTarget
, numTargets
);
5352 // Compute the beheviour.
5355 // Remove the property.
5356 stage
.removeProperty(PROPERTY_BEHAVIOUR
);
5359 // Apply the target.
5360 pair
<bool, sint64
> resultTarget
= stage
.property(PROPERTY_TARGET_ID
);
5361 if(resultTarget
.first
)
5363 // Change the entity target.
5364 targetSlot((CLFECOMMON::TCLEntityId
)resultTarget
.second
);
5365 // Remove the property.
5366 stage
.removeProperty(PROPERTY_TARGET_ID
);
5370 pair
<bool, sint64
> resultParent
= stage
.property(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
);
5371 if(resultParent
.first
)
5373 _Mount
= (CLFECOMMON::TCLEntityId
)resultParent
.second
;
5375 // Remove the property.
5376 stage
.removeProperty(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
);
5380 pair
<bool, sint64
> resultRider
= stage
.property(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
);
5381 if(resultRider
.first
)
5383 _Rider
= (CLFECOMMON::TCLEntityId
)resultRider
.second
;
5385 // Remove the property.
5386 stage
.removeProperty(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
);
5389 // visual fxs : links and auras
5390 pair
<bool, sint64
> resultVisualFX
= stage
.property(CLFECOMMON::PROPERTY_VISUAL_FX
);
5391 if (resultVisualFX
.first
)
5393 applyVisualFX(resultVisualFX
.second
);
5394 stage
.removeProperty(CLFECOMMON::PROPERTY_VISUAL_FX
);
5401 //-----------------------------------------------
5402 // applyCurrentStage :
5403 // Apply The Current Stage (first stage).
5404 //-----------------------------------------------
5405 bool CCharacterCL::applyCurrentStage()
5409 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5410 if(it
!= _Stages
._StageSet
.end())
5412 // Apply the Stage and remove it if stage done.
5413 if(applyStage((*it
).second
))
5414 _Stages
._StageSet
.erase(it
);
5419 nlwarning("CCharacterCL::applyCurrentStage: there is no stage.");
5421 // Update information from remaining stages.
5425 }// applyCurrentStage //
5428 //-----------------------------------------------
5429 // applyAllStagesToFirstPos :
5430 // Apply all stages to the first stage with a position.
5431 //-----------------------------------------------
5432 void CCharacterCL::applyAllStagesToFirstPos()
5435 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5436 while(it
!= _Stages
._StageSet
.end() && !(*it
).second
.getPos(stagePos
))
5439 if(!applyStage((*it
).second
))
5445 // Backup the iterator to remove
5446 CStageSet::TStageSet::iterator itTmp
= it
;
5449 // Remove the stage done.
5450 _Stages
._StageSet
.erase(itTmp
);
5453 // Apply the stage with the position.
5454 if(it
!= _Stages
._StageSet
.end())
5457 if(applyStage((*it
).second
))
5458 // Remove the stage done.
5459 _Stages
._StageSet
.erase(it
);
5462 nlwarning("CH:applyAllStagesToFirstPos:%d: There is no stage with a position.", _Slot
);
5464 // Upate information from remaining stages.
5466 }// applyAllStagesToFirstPos //
5469 //-----------------------------------------------
5471 // Play the time step for the loop and truncate to End Anim if Time Step too big.
5472 //-----------------------------------------------
5473 ADD_METHOD(void CCharacterCL::playToEndAnim(const double &startTimeOffset
, double &length
))
5475 double speedToDest
= speed();
5477 if(ClientCfg
.BlendForward
&& (animState(MOVE
) == CAnimationStateSheet::Walk
))
5479 uint animWalkId
= animId(MOVE
);
5480 uint animRunId
= animId(MOVE_BLEND_OUT
);
5481 double animWalkSpeed
= EAM
->getAnimationAverageSpeed(animWalkId
)*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
5482 double animRunSpeed
= EAM
->getAnimationAverageSpeed(animRunId
)*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
5483 if(animWalkSpeed
<=animRunSpeed
)
5485 double startTimeOffRun
= animOffset(MOVE_BLEND_OUT
);
5486 double animWalkLength
= EAM
->getAnimationLength(animWalkId
);
5487 double animRunLength
= EAM
->getAnimationLength(animRunId
);
5488 // Current Speed <= Walk Speed, so use the walk animation only.
5489 if(speed() <= animWalkSpeed
)
5492 double speedFactor
= speed()/animWalkSpeed
;
5493 double animTimeOffWalk
= animOffset(MOVE
) + length
*speedFactor
;
5494 if(animTimeOffWalk
> animWalkLength
)
5496 animOffset(MOVE
, animWalkLength
);
5497 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5498 length
= (animWalkLength
- startTimeOffset
) / speedFactor
;
5500 // Adjust Time Offset for the Run Channel
5503 animOffset(MOVE
, animTimeOffWalk
);
5504 animOffset(MOVE_BLEND_OUT
, animRunLength
*(animTimeOffWalk
/animWalkLength
));
5507 // Current Speed >= Run Speed, so use the run animation only.
5508 else if(speed() >= animRunSpeed
)
5511 double speedFactor
= speed()/animRunSpeed
;
5512 double animTimeOffRun
= animOffset(MOVE_BLEND_OUT
) + length
*speedFactor
;
5513 if(animTimeOffRun
> animRunLength
)
5515 animOffset(MOVE
, animWalkLength
);
5516 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5517 length
= (animRunLength
- startTimeOffRun
) / speedFactor
;
5519 // Adjust Time Offset for the Walk Channel
5522 animOffset(MOVE
, animWalkLength
*(animTimeOffRun
/animRunLength
));
5523 animOffset(MOVE_BLEND_OUT
, animTimeOffRun
);
5526 // Current Speed > Walk Speed & < Run Speed, so mix Walk and Run animation.
5529 double t1
= animRunSpeed
-animWalkSpeed
;
5530 double t2
= speed()-animWalkSpeed
;
5532 double mixLength
= runFactor()*animRunLength
+ (1.0-runFactor())*animWalkLength
;
5533 double animTimeOffWalk
= animOffset(MOVE
) + animWalkLength
/mixLength
*length
;
5534 if(animTimeOffWalk
> animWalkLength
)
5536 animOffset(MOVE
, animWalkLength
);
5537 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5538 length
= (animWalkLength
- startTimeOffset
) / (animWalkLength
/mixLength
);
5542 animOffset(MOVE
, animTimeOffWalk
);
5543 animOffset(MOVE_BLEND_OUT
, animRunLength
*animTimeOffWalk
/animWalkLength
); // Same percentage in the animation than the Walk one.
5549 //nlwarning("playToEndAnim:%d: animWalkSpeed > animRunSpeed", _Slot);
5551 // No Mix between Walk and Run.
5554 double speedFactor
= computeSpeedFactor(speedToDest
);
5555 // Compute the desired new time offset.
5556 double animTimeOffMove
= animOffset(MOVE
) + length
* speedFactor
;
5557 // Truncate animation time offset if it over-runs end of animation and change the loopTimeOffset too.
5558 double animationLength
= EAM
->getAnimationLength(animId(MOVE
));
5559 if(animTimeOffMove
> animationLength
)
5561 animOffset(MOVE
, animationLength
);
5562 length
= (animationLength
- startTimeOffset
) / speedFactor
;
5565 animOffset(MOVE
, animTimeOffMove
);
5566 }// playToEndAnim //
5568 //-----------------------------------------------
5570 // Call this method to give a time for each stage, compute distance to destination and some more information.
5571 // \todo GUIGUI : clean up
5572 //-----------------------------------------------
5573 void CCharacterCL::updateStages()
5575 H_AUTO ( RZ_Client_Entity_CL_updateStages
);
5577 _FirstPos
= INVALID_POS
; // No First Position
5578 _FirstTime
= INVALID_TIME
; //- No First Position
5579 dist2FirstPos(INVALID_DIST
); // No First Position
5580 _DestPos
= INVALID_POS
; // No Destination
5581 _DestTime
= INVALID_TIME
; // No Destination
5582 dist2Dest(INVALID_DIST
); // No Destination
5583 CVectorD posTmp
= pos();
5584 _IsThereAMode
= false;
5585 _ImportantStepTime
= 0.0;
5588 // ***** update predicted interval: if a new pos B is found after a pos A, then the interval B-A is known!
5589 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5590 CStageSet::TStageSet::iterator itPosPrec
= _Stages
._StageSet
.end();
5591 bool somePosFoundEarly
= false;
5592 while(it
!= _Stages
._StageSet
.end())
5594 // if this stage has a position
5595 if(it
->second
.isPresent(PROPERTY_POSITION
))
5597 somePosFoundEarly
= true;
5598 // then it's cool we can set the new accurate interval to the prec stage which has a pos
5599 if(itPosPrec
!=_Stages
._StageSet
.end())
5601 uint dgc
= it
->first
- itPosPrec
->first
;
5602 itPosPrec
->second
.predictedInterval(dgc
);
5611 // ***** Compute the current LCT Impact for this character
5612 // NB: used only in mode CClientConfig::StageUsePosOnlyLCT
5614 // If disabled, full LCT impact
5615 if(_StartDecreaseLCTImpact
==0)
5619 const sint32 decreaseTick
= 20; // 2 seconds
5620 // blend according to the start of decrease
5621 sint32 dt
= NetMngr
.getCurrentServerTick() - _StartDecreaseLCTImpact
;
5624 else if(dt
>=decreaseTick
)
5627 charLCTI
= ((decreaseTick
-dt
)*256)/decreaseTick
;
5628 // hence, at end of blend, charLCTI is 0
5632 // ***** Compute Stages to give them a time and get some information from those stages.
5633 // yoyo: use any stage with no LCT, until it is to be played AFTER a position
5634 bool stageForceLCTFound
= false;
5635 CStageSet::TStageSet::iterator itTmp
;
5636 it
= _Stages
._StageSet
.begin();
5637 while(it
!= _Stages
._StageSet
.end())
5639 CStage
&stage
= (*it
).second
;
5641 // *** retrieve position in stage if any
5642 CVectorD posInStage
;
5643 bool hasPos
= stage
.getPos(posInStage
);
5644 // check the first pos is correct
5645 if(hasPos
&& dist2Dest()==INVALID_DIST
)
5647 // Compute the distance to the first position
5648 double distToFirst
= (CVectorD(posInStage
.x
, posInStage
.y
, 0.0) - CVectorD( posTmp
.x
, posTmp
.y
, 0.0)).norm();
5649 double distToLimiter
= (CVectorD(posInStage
.x
, posInStage
.y
, 0.0) - CVectorD(_PositionLimiter
.x
, _PositionLimiter
.y
, 0.0)).norm();
5650 // Check if the first pos is Not the same as the current entity pos
5651 if((distToFirst
< ClientCfg
.DestThreshold
)
5652 || (distToLimiter
<= ClientCfg
.PositionLimiterRadius
))
5654 // The FIRST POSITION is the SAME as the CURRENT entity POSITION -> REMOVE POSITION in the stage
5655 stage
.removeProperty(CLFECOMMON::PROPERTY_POSX
);
5656 stage
.removeProperty(CLFECOMMON::PROPERTY_POSY
);
5657 stage
.removeProperty(CLFECOMMON::PROPERTY_POSZ
);
5660 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
5661 nlinfo("CH:updateStages:%d: Bad First, distToFirst(%f), ClientCfg.DestThreshold(%f), distToLimiter(%f), ClientCfg.PositionLimiterRadius(%f)", _Slot
,
5662 distToFirst
, ClientCfg
.DestThreshold
, distToLimiter
, ClientCfg
.PositionLimiterRadius
);
5666 // stage has pos? => force LCT for it and after
5667 stageForceLCTFound
= stageForceLCTFound
|| hasPos
;
5670 // *** Compute the estimated Time for the Stage.
5671 // Compute difference in Game Cycle Between the current Game Cycle and the current Stage.
5673 if(ClientCfg
.StageLCTUsage
==CClientConfig::StageUseAllLCT
)
5674 t
= (*it
).first
- NetMngr
.getCurrentClientTick();
5675 else if(ClientCfg
.StageLCTUsage
==CClientConfig::StageUseNoLCT
)
5676 t
= (*it
).first
- NetMngr
.getCurrentServerTick();
5679 // Update LCTImpact for the stage
5680 if(stageForceLCTFound
)
5682 // Force full impact for stage after or including a POS
5683 stage
.setLCTImpact(256);
5687 /* minimize the LCT impact with the current char one
5688 Hence, if the stage had a lct impact lowered, but LCT decrease was canceled,
5689 the stage will keep its LCT impact
5691 stage
.setLCTImpact(min(stage
.getLCTImpact(), charLCTI
));
5694 sint32 lcti
= stage
.getLCTImpact();
5698 t
= (*it
).first
- NetMngr
.getCurrentClientTick();
5701 t
= (*it
).first
- NetMngr
.getCurrentServerTick();
5705 sint32 twlct
= (*it
).first
- NetMngr
.getCurrentClientTick();
5706 sint32 twolct
= (*it
).first
- NetMngr
.getCurrentServerTick();
5707 t
= (twlct
*lcti
+ twolct
*(256-lcti
))>>8;
5710 // Compute the estimated Time for the Stage.
5711 stage
.time( (double)(NetMngr
.getMachineTimeAtTick() + t
*NetMngr
.getMsPerTick())/1000.0 );
5714 // *** Important step is used for "Panic mode" animation acceleration. skip the first stage
5715 if(_ImportantStepTime
==0.0 && it
!=_Stages
._StageSet
.begin())
5717 // Important steps are ones that takes times (pos, orientation, mode, animation etc....)
5718 if( stage
.isPresent(PROPERTY_POSITION
) ||
5719 stage
.isPresent(PROPERTY_MODE
) ||
5720 stage
.isPresent(PROPERTY_ORIENTATION
) ||
5721 stage
.isPresent(PROPERTY_ENTITY_MOUNTED_ID
) ||
5722 stage
.isPresent(PROPERTY_RIDER_ENTITY_ID
) ||
5723 stage
.isPresent(PROPERTY_BEHAVIOUR
))
5724 _ImportantStepTime
= stage
.time();
5728 // *** Compute dist2dest (until a mode) if has pos
5729 if((_IsThereAMode
==false) && hasPos
)
5731 // Set the destination pos and time.
5732 _DestPos
= posInStage
;
5733 _DestTime
= stage
.time() + (double)(stage
.predictedInterval()*NetMngr
.getMsPerTick())/1000.0;
5734 // Update First Pos.
5735 if(dist2Dest() == INVALID_DIST
)
5737 _FirstPos
= _DestPos
;
5738 _FirstTime
= stage
.time();
5739 // Compute the distance to the first position
5740 double distToFirst
= (CVectorD(_DestPos
.x
, _DestPos
.y
, 0.0) - CVectorD(posTmp
.x
,posTmp
.y
, 0.0)).norm();
5741 // Set the Distance to the Destination as the distance to the first position.
5742 dist2Dest(distToFirst
);
5743 // Set the distance to First Stage.
5744 dist2FirstPos(distToFirst
);
5746 // Increase distance to destination.
5748 dist2Dest(dist2Dest() + (CVectorD(_DestPos
.x
, _DestPos
.y
, 0.0) - CVectorD(posTmp
.x
, posTmp
.y
, 0.0)).norm());
5749 // Backup the last pos.
5752 // Stop if there is a mode in the stage.
5753 if(stage
.isPresent(CLFECOMMON::PROPERTY_MODE
))
5754 _IsThereAMode
= true;
5760 // REMOVE EMPTY STAGE (because only position and the same as the current one).
5762 _Stages
._StageSet
.erase(itTmp
);
5765 // If there is no mode in queue, mode wanted is the current mode
5766 // It must usually be the theorical one except for some mode used only by the client like SWIM
5768 _ModeWanted
= _Mode
;
5770 // ***** update _StartDecreaseLCTImpact
5771 // If a stage that force LCT has been found in the list
5772 if(stageForceLCTFound
)
5773 // Decrease of LCT is disabled
5774 _StartDecreaseLCTImpact
= 0;
5775 else if(_StartDecreaseLCTImpact
==0)
5776 // Start to decrease LCT
5777 _StartDecreaseLCTImpact
= NetMngr
.getCurrentServerTick();
5780 // ***** compute _RunStartTimeNoPop (see _RunStartTimeNoPop)
5781 _RunStartTimeNoPop
= INVALID_TIME
;
5782 // only if have some pos in the queue
5783 if(somePosFoundEarly
)
5786 double fpTime
= INVALID_TIME
;
5787 // if the first pos is computed, use it
5788 if(_FirstTime
!=INVALID_TIME
)
5790 d2fp
= dist2FirstPos();
5793 // else try to compute the first pos, WIHTOUT regarding if there is a mode or not
5794 // (because even mode anim can be accelerated....)
5797 it
= _Stages
._StageSet
.begin();
5798 while(it
!= _Stages
._StageSet
.end())
5800 CStage
&stage
= (*it
).second
;
5802 if(stage
.getPos(firstPos
))
5804 fpTime
= stage
.time() + (double)(stage
.predictedInterval()*NetMngr
.getMsPerTick())/1000.0;
5805 d2fp
= CVectorD(firstPos
.x
-pos().x
, firstPos
.y
-pos().y
, 0.0).norm();
5812 // with d2fp, fpTime, and maxSpeed, we can estimate the moment where the run should start
5813 if(fpTime
!=INVALID_TIME
)
5815 float maxSpeed
= (float)getMaxSpeed();
5818 // compute at which time the first move should begin so it doesn't have to accelerate
5819 _RunStartTimeNoPop
= fpTime
- d2fp
/maxSpeed
;
5826 //-----------------------------------------------
5828 // Return if the impact must be played.
5829 // \param anim : pointer on the current animation (MUST NOT BE NULL).
5830 // \param currentTime : current time in the animation.
5831 // \param triggerName : name of the trigger to check.
5832 // \param isActive : read (and can change) the state.
5833 // \param timeFactor : when to activate the impact if there is no track (value has to be between 0 and 1 to be valid).
5834 // \return bool : true if the trigger is valid.
5835 // \warning This method does not check if the animation is Null.
5836 //-----------------------------------------------
5837 ADD_METHOD(bool CCharacterCL::beginImpact(NL3D::UAnimation
*anim
, NL3D::TAnimationTime currentTime
, const std::string
&triggerName
, bool &isActive
, float timeFactor
))
5838 // Is the impact already activeated.
5842 // Try to find the impact trigger in the animation.
5843 UTrack
*Track
= anim
->getTrackByName(triggerName
.c_str());
5844 // No track -> just check with 2/3 animation
5847 if(Track
->interpolate(currentTime
, isActive
))
5850 nlwarning("CH:beginImpact:%d: Wrong type asked.", _Slot
);
5853 // No Track or pb with it so try with the animation length.
5854 float length
= (float)(anim
->getEndTime()-anim
->getBeginTime());
5855 isActive
= (animOffset(MOVE
) >= length
*timeFactor
);
5859 //-----------------------------------------------
5860 // animEventsProcessing :
5861 // Manage Events that could be created by the animation (like sound).
5862 // \param startTime : time to start processing events from the current animation.
5863 // \param stopTime : time to stop processing events from the current animation.
5864 // \todo GUIGUI : Optimize FXs launch when we would have time
5865 //-----------------------------------------------
5866 void CCharacterCL::animEventsProcessing(double startTime
, double stopTime
)
5868 if (_CurrentState
== 0)
5871 // \todo Vianney : temp le temps de savoir comment on joue les son pour le propre joueur
5872 // No sound for the player.
5873 if(_Slot
!= 0 || _Mode
!= MBEHAV::NORMAL
)
5875 // Retreive the surface material
5876 uint32 matId
= getGroundType();
5877 // Set the material id var
5878 _SoundContext
.Args
[0] = matId
;
5881 // Set the sound family var
5882 _SoundContext
.Args
[2] = _Sheet
->SoundFamily
;
5883 // Set the sound variation var
5884 _SoundContext
.Args
[3] = _Sheet
->SoundVariation
;
5888 // Set the sound family var
5889 _SoundContext
.Args
[2] = 0;
5890 // Set the sound variation var
5891 _SoundContext
.Args
[3] = 0;
5894 CSoundAnimManager
* sndMngr
= CSoundAnimManager::instance();
5895 if(_SoundContext
.Args
[2] != 999 && sndMngr
&& (_SoundId
[MOVE
] != CSoundAnimationNoId
))
5897 _SoundContext
.Position
= pos();
5898 // Look for the cluster(s) containing this character...
5899 std::vector
<NL3D::CCluster
*> clusters
;
5900 if (!_Instance
.empty())
5903 _Instance
.getLastParentClusters(clusters
);
5905 else if (!_Skeleton
.empty())
5908 _Skeleton
.getLastParentClusters(clusters
);
5910 CCluster
*pcluster
= 0;
5911 // use the first cluster if at leat one available
5912 if (!clusters
.empty())
5913 pcluster
= clusters
.front();
5914 sndMngr
->playAnimation(_SoundId
[MOVE
], (float) startTime
, (float) stopTime
, pcluster
, _SoundContext
);
5918 }// animEventsProcessing //
5920 //-----------------------------------------------
5921 // updatePreCollision :
5922 // Method called each frame to manage the entity.
5923 // \param time : current time of the frame.
5924 // \parem target : pointer on the current entity target.
5925 //-----------------------------------------------
5926 void CCharacterCL::updatePreCollision(const TTime
¤tTimeInMs
, CEntityCL
*target
) // virtual
5928 H_AUTO ( RZ_Client_Entity_CL_Update_Pre_Collision
);
5929 // Set the Last frame PACS Pos.
5931 _Primitive
->getGlobalPosition(_LastFramePACSPos
, dynamicWI
);
5932 // Set the previous position before changing the current one.
5933 _LastFramePos
= _Position
;
5935 // Turn towards the target when in COMBAT_FLOAT mode.
5936 if(_Mode
== MBEHAV::COMBAT_FLOAT
)
5938 H_AUTO ( RZ_Client_Entity_CL_Update_Combat
)
5940 // Check there is a valid target and it's not the entity itself.
5941 if(targetSlot() != CLFECOMMON::INVALID_SLOT
5942 && targetSlot() != slot()
5944 // Set the new entity direction
5945 front(target
->pos() - pos(), true, false);
5948 // Update Position if not a child & displayable.
5949 if(parent() == CLFECOMMON::INVALID_SLOT
&& _Displayable
)
5951 H_AUTO ( RZ_Client_Entity_CL_Update_Pos
)
5952 updatePos(currentTimeInMs
, target
);
5954 }// updatePreCollision //
5956 //-----------------------------------------------
5958 // Apply track on fxs
5959 // Check if some FX should be removed.
5960 //-----------------------------------------------
5961 inline void CCharacterCL::updateFX()
5967 //-----------------------------------------------
5968 // updateAttachedFX :
5969 // Apply track on animated fxs
5970 // Remove those that should be removed
5971 //-----------------------------------------------
5972 void CCharacterCL::updateAttachedFX()
5974 // build align matrix
5975 CMatrix alignMatrix
;
5976 buildAlignMatrix(alignMatrix
);
5978 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
5979 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
5981 if ((*itAttachedFxToStart
).DelayBeforeStart
< (float)(TimeInSec
- (*itAttachedFxToStart
).StartTime
))
5983 uint index
= (*itAttachedFxToStart
).MaxNumAnimCount
;
5984 (*itAttachedFxToStart
).MaxNumAnimCount
= 0;
5985 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
5986 fx
->create(*this, (*itAttachedFxToStart
), CAttachedFX::CTargeterInfo());
5987 if (!fx
->FX
.empty())
5989 _AuraFX
[index
] = fx
;
5991 itAttachedFxToStart
= _AttachedFXListToStart
.erase(itAttachedFxToStart
);
5995 ++itAttachedFxToStart
;
5999 // update tracks & pos for anim attachedfxs
6000 std::list
<CAttachedFX::TSmartPtr
>::iterator itAttachedFx
= _AttachedFXListForCurrentAnim
.begin();
6001 while(itAttachedFx
!= _AttachedFXListForCurrentAnim
.end())
6003 nlassert(*itAttachedFx
);
6004 CAttachedFX
&attachedFX
= **itAttachedFx
;
6005 attachedFX
.update(*this, alignMatrix
);
6006 if (!attachedFX
.FX
.empty()) attachedFX
.FX
.setUserMatrix(alignMatrix
);
6010 // Try to remove animation FXs still not removed.
6011 itAttachedFx
= _AttachedFXListToRemove
.begin();
6012 while(itAttachedFx
!= _AttachedFXListToRemove
.end())
6014 // If the FX is not present or valid -> remove the FX.
6015 bool mustDelete
= false;
6016 CAttachedFX
&attachedFX
= **itAttachedFx
;
6017 if (attachedFX
.SpawnTime
!= TimeInSec
)
6019 if(attachedFX
.FX
.empty() || !attachedFX
.FX
.isSystemPresent() || !attachedFX
.FX
.isValid())
6024 if (attachedFX
.TimeOutDate
!= 0)
6026 if (TimeInSec
>= attachedFX
.TimeOutDate
)
6033 // Remove from the list.
6034 itAttachedFx
= _AttachedFXListToRemove
.erase(itAttachedFx
);
6038 attachedFX
.update(*this, alignMatrix
);
6039 if (!attachedFX
.FX
.empty()) attachedFX
.FX
.setUserMatrix(alignMatrix
);
6044 // update the aura fx
6045 for(uint k
= 0; k
< MaxNumAura
; ++k
)
6049 if (_AuraFX
[k
]->TimeOutDate
!= 0.f
) // we use that flag to mark the aura as 'shutting down'
6051 if (TimeInSec
>= _AuraFX
[k
]->TimeOutDate
)
6057 float lifeRatio
= (float) ((_AuraFX
[k
]->TimeOutDate
- TimeInSec
) / AURA_SHUTDOWN_TIME
);
6058 if (!_AuraFX
[k
]->FX
.empty()) _AuraFX
[k
]->FX
.setUserParam(0, 1.f
- lifeRatio
);
6061 if (_AuraFX
[k
]) // not deleted yet ?
6063 // update position & orientation
6064 _AuraFX
[k
]->update(*this, alignMatrix
);
6069 // update the link fx
6072 _LinkFX
->update(*this, alignMatrix
);
6076 _StaticFX
->FX
->update(*this, alignMatrix
);
6083 //-----------------------------------------------
6085 //-----------------------------------------------
6086 void CCharacterCL::updateVisible (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6088 if (_StepCustomScale
!= 0.f
)
6091 // Changes the skeleton state
6092 if(!_Skeleton
.empty())
6096 // Changes the instance position.
6097 else if(!_Instance
.empty())
6102 // Snap the entity to the ground.
6104 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground
)
6108 // Apply the new entity position to the visual of the entity (apply x and z movement due to animation).
6109 if(parent() == CLFECOMMON::INVALID_SLOT
)
6111 H_AUTO ( RZ_Client_Entity_CL_Update_Display
)
6115 // Change the cluster of the entity.
6117 H_AUTO ( RZ_Client_Entity_CL_Update_Cluster
)
6121 // Update the LodCharacter Animation.
6122 if(_LodCharacterAnimEnabled
)
6124 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation
)
6126 // set this value to the skeleton
6128 skeleton()->setLodCharacterAnimTime(_LodCharacterAnimTimeOffset
);
6133 H_AUTO ( RZ_Client_Entity_CL_Update_FX
)
6138 if(!_HPModifiers
.empty())
6141 mod
.CHPModifier::operator= (*_HPModifiers
.begin());
6142 mod
.Time
= TimeInSec
+ mod
.DeltaT
;
6143 _HPDisplayed
.push_back(mod
);
6144 _HPModifiers
.erase(_HPModifiers
.begin());
6148 CEntityCL::updateVisible(currentTimeInMs
, target
);
6149 }// updateVisible //
6151 //-----------------------------------------------
6152 // updateSomeClipped :
6153 //-----------------------------------------------
6154 void CCharacterCL::updateSomeClipped (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6156 // Snap the entity to the ground.
6158 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground
)
6162 // Changes the skeleton position.
6163 if(!_Skeleton
.empty())
6165 _Skeleton
.setPos(pos());
6168 // Changes the instance position.
6169 else if(!_Instance
.empty())
6171 _Instance
.setPos(pos());
6175 if(!ClientCfg
.Light
)
6177 // Update texture Async Loading
6178 updateAsyncTexture();
6179 // Update lod Texture
6184 H_AUTO ( RZ_Client_Entity_CL_Update_FX
)
6189 // Remove Modifiers.
6190 _HPModifiers
.clear();
6191 _HPDisplayed
.clear();
6194 CEntityCL::updateSomeClipped(currentTimeInMs
, target
);
6195 }// updateSomeClipped //
6197 //-----------------------------------------------
6199 //-----------------------------------------------
6200 void CCharacterCL::updateClipped (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6202 // hide the scene interface
6203 if (_InSceneUserInterface
)
6205 if (_InSceneUserInterface
->getActive())
6206 _InSceneUserInterface
->setActive (false);
6210 if (_CurrentBubble
->getActive())
6211 _CurrentBubble
->setActive (false);
6215 CEntityCL::updateClipped(currentTimeInMs
, target
);
6216 }// updateClipped //
6218 //-----------------------------------------------
6219 // updateVisiblePostPos :
6220 // Update the entity after all positions done.
6221 //-----------------------------------------------
6222 void CCharacterCL::updateVisiblePostPos(const NLMISC::TTime
¤tTimeInMs
, CEntityCL
*target
) // virtual
6224 // Stuff to do only when alive.
6227 // Update the head direction.
6229 H_AUTO ( RZ_Client_Entity_CL_Update_Head_Direction
)
6230 updateHeadDirection(target
);
6234 H_AUTO ( RZ_Client_Entity_CL_Update_Blink
)
6235 updateBlink(currentTimeInMs
);
6239 // Update in scene interface
6240 if(_InSceneUserInterface
|| _CurrentBubble
)
6242 // Draw the entity Name if asked or under the cursor.
6243 bool showIS
= mustShowInsceneInterface( (!_Sheet
) || (_Sheet
->DisplayOSD
) );
6244 bool showBubble
= true;
6246 // Don't show bubble if lod
6247 if (!_Skeleton
.empty() && _Skeleton
.isDisplayedAsLodCharacter())
6252 // If the name of the character is unknown, no user info
6253 if (_EntityName
.empty() && _Title
.empty())
6256 // if mounted : don't display name
6257 if( _Rider
!= CLFECOMMON::INVALID_SLOT
)
6264 if (_InSceneUserInterface
)
6267 _InSceneUserInterface
->setActive (showIS
);
6271 // Update dynamic data
6272 _InSceneUserInterface
->updateDynamicData ();
6274 NLMISC::CVectorD pos
;
6275 if (getNamePos(pos
))
6277 // Check the pos validity
6278 if((isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
)) == false)
6280 nlwarning("CH:updateVisiblePostPos:%d: invalid pos %f %f %f", _Slot
, pos
.x
, pos
.y
, pos
.z
);
6283 _InSceneUserInterface
->Position
= pos
;
6287 pos
= (box().getMin() + box().getMax())/2;
6288 pos
.z
= box().getMax().z
;
6289 nlassert(isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
));
6290 _InSceneUserInterface
->Position
= pos
;
6298 showBubble
&= _CurrentBubble
->canBeShown();
6301 if (_CurrentBubble
->getActive() != showBubble
)
6302 _CurrentBubble
->setActive (showBubble
);
6308 if (_InSceneUserInterface
)
6309 offsetX
= - 10 - (_InSceneUserInterface
->getWReal() / 2);
6310 _CurrentBubble
->setOffsetX (offsetX
);
6312 NLMISC::CVectorD pos
;
6313 if (!getNamePos(pos
))
6315 pos
= (box().getMin() + box().getMax())/2;
6316 pos
.z
= box().getMax().z
;
6319 CRaceStatsSheet
*sheet
= const_cast<CRaceStatsSheet
*>(UserEntity
->playerSheet());
6320 float namePosZ
= sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZNormal
;
6321 if (pos
.z
> box().getMin().z
+ namePosZ
)
6322 pos
.z
= box().getMin().z
+ namePosZ
;
6323 nlassert(isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
));
6324 _CurrentBubble
->Position
= pos
;
6330 CEntityCL::updateVisiblePostPos(currentTimeInMs
, target
);
6331 }// updateVisiblePostPos //
6334 //-----------------------------------------------
6335 // updatePostCollision :
6336 // Method called each frame to manage the entity.
6337 // \param time : current time of the frame.
6338 // \parem target : pointer on the current entity target.
6339 //-----------------------------------------------
6340 void CCharacterCL::updatePostCollision(const TTime
&/* currentTimeInMs */, CEntityCL
* /* target */) // virtual
6342 H_AUTO ( RZ_Client_Entity_CL_Update_Post_Collision
)
6344 // Finalize PACS position
6346 H_AUTO ( RZ_Client_Entity_CL_Update_Finalize_Move
)
6348 // \todo GUIGUI : fait rapidement pour voir les autres se baigner pour la video, faire mieux.
6350 // changed : Malkav , also do this for mektoub (as they can swim)
6351 if(PACS
&& _Primitive
6352 && (isPlayer() || isNPC()
6353 || (_Sheet
&& (_Sheet
->Race
== EGSPD::CPeople::MektoubMount
|| _Sheet
->Race
== EGSPD::CPeople::MektoubPacker
))
6360 UGlobalPosition gPos
;
6361 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
6363 if(GR
->isWaterPosition(gPos
, waterHeight
))
6365 if(isSwimming()==false)
6369 _Mode
= MBEHAV::SWIM_DEATH
;
6371 else if (isRiding())
6373 _Mode
= MBEHAV::MOUNT_SWIM
;
6375 // also change mounted entity mode
6376 if (_Mount
!= CLFECOMMON::INVALID_SLOT
)
6378 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
6382 mount
->setMode(MBEHAV::MOUNT_SWIM
);
6383 mount
->computeAutomaton();
6384 mount
->computeAnimSet();
6385 mount
->setAnim(CAnimationStateSheet::Idle
);
6391 _Mode
= MBEHAV::SWIM
;
6394 // Compute the current automaton
6396 // Update the animation set according to the mode.
6398 // Animset changed -> update current animation
6399 setAnim(CAnimationStateSheet::Idle
);
6408 _Mode
= MBEHAV::DEATH
;
6410 else if (isRiding())
6412 _Mode
= MBEHAV::MOUNT_NORMAL
;
6413 // also change mounted entity mode
6414 if (_Mount
!= CLFECOMMON::INVALID_SLOT
)
6416 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
6420 mount
->setMode(MBEHAV::MOUNT_NORMAL
);
6421 mount
->computeAutomaton();
6422 mount
->computeAnimSet();
6423 mount
->setAnim(CAnimationStateSheet::Idle
);
6429 _Mode
= MBEHAV::NORMAL
;
6432 // Compute the current automaton
6434 // Update the animation set according to the mode.
6436 // Animset changed -> update current animation
6437 setAnim(CAnimationStateSheet::Idle
);
6443 }// updatePostCollision //
6446 //-----------------------------------------------
6448 //-----------------------------------------------
6449 double CCharacterCL::getTheMove(double loopTimeStep
, double oldMovingTimeOffset
, double oldMovingTimeOffsetRun
) const
6454 // A Real Move State
6455 if(_CurrentState
->Move
)
6457 // Get the covered distance from the animation.
6458 move
= getTheMove(loopTimeStep
, oldMovingTimeOffset
, MOVE
);
6459 if(runFactor() > 0.0)
6461 // Blend the 2 move (Walk & Run).
6462 move
= move
*(1.0-runFactor()) + getTheMove(loopTimeStep
, oldMovingTimeOffsetRun
, MOVE_BLEND_OUT
)*runFactor();
6463 // The move must be significant.
6464 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6465 move
= ClientCfg
.SignificantDist
;
6469 else if(_CurrentState
->Slide
)
6471 move
= speed() * loopTimeStep
;
6472 // The move must be significant.
6473 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6474 move
= ClientCfg
.SignificantDist
;
6481 move
= speed() * loopTimeStep
;
6482 // The move must be significant.
6483 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6484 move
= ClientCfg
.SignificantDist
;
6486 // Check the move is significant.
6487 CHECK(!((move
>0.0) && (move
<ClientCfg
.SignificantDist
)));
6488 // Return the move done by the entity since last time.
6491 //-----------------------------------------------
6493 //-----------------------------------------------
6494 double CCharacterCL::getTheMove(double loopTimeStep
, double oldMovingTimeOffset
, TAnimationType channel
) const
6497 // Compute a linear motion when the animation is missing.
6498 if(animIndex(channel
) == CAnimation::UnknownAnim
)
6500 double offsetT
= _DestTime
- _LastFrameTime
;
6503 // \todo GUIGUI : in this case, 'loopTimeStep' should not decrease so FIX IT.
6508 move
= dist2Dest() * (loopTimeStep
/ offsetT
);
6509 // The move must be significant.
6510 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6511 move
= ClientCfg
.SignificantDist
;
6514 // Get the motion done by the animation.
6516 move
= computeMotion(oldMovingTimeOffset
, channel
);
6518 CHECK(!(move
>0.0 && move
<ClientCfg
.SignificantDist
));
6522 //-----------------------------------------------
6523 // updatePosCombatFloat :
6524 //-----------------------------------------------
6525 void CCharacterCL::updatePosCombatFloat(double /* frameTimeRemaining */, CEntityCL
*target
) // virtual
6527 H_AUTO_USE ( RZ_Client_Character_CL_Update_Pos_Combat_Float
)
6529 // The target is valid
6532 // Get the position where the attacker should go to attack his target according to the attack angle.
6533 CVectorD dirToTarget
= target
->pos() - pos();
6534 dirToTarget
.z
= 0.0;
6536 || ((dirToTarget
!= CVectorD::Null
)
6537 && fabs(target
->pos().x
-target
->lastFramePos().x
)>0.01
6538 && fabs(target
->pos().y
-target
->lastFramePos().y
)>0.01))
6540 double angToTarget
= atan2(dirToTarget
.y
, dirToTarget
.x
);
6541 _DestPos
= target
->getAttackerPos(angToTarget
, attackRadius() + ClientCfg
.AttackDist
);
6544 _DestPos
= target
->getAttackerPos(_TargetAngle
, attackRadius() + ClientCfg
.AttackDist
);
6545 // Compute the distance to destination.
6546 CVectorD vectToDest
= _DestPos
- pos();
6548 // Distance to destination is big enough.
6549 if(vectToDest
.norm() > ClientCfg
.DestThreshold
)
6551 dist2Dest(vectToDest
.norm());
6552 // Compute the time to reach the destination at the max speed.
6553 double lengthOfTimeToDest
= 0.0; // 0 = No Speed Limit
6554 _FirstPos
= _DestPos
;
6555 _DestTime
= _LastFrameTime
+ lengthOfTimeToDest
+ ClientCfg
.ChaseReactionTime
;
6556 _FirstTime
= _DestTime
;
6558 // The time remaining will be enough to reach the destination
6559 if(frameTimeRemaining >= lengthOfTimeToDest)
6561 _FirstPos = _DestPos;
6562 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6563 _FirstTime = _DestTime;
6565 // The time remaining is not enough to reach the destination at max speed -> compute a first pos possible to reach.
6568 _FirstPos = pos() + vectToDest*frameTimeRemaining/lengthOfTimeToDest;
6569 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6570 _FirstTime = _LastFrameTime + frameTimeRemaining + ClientCfg.ChaseReactionTime;
6573 // Compute the distance to the first position.
6574 CVectorD tmp2computeDist2FirstPos
= _FirstPos
-pos();
6575 tmp2computeDist2FirstPos
.z
= 0.0;
6576 dist2FirstPos(tmp2computeDist2FirstPos
.norm());
6578 updatePosCombatFloatChanged(target
);
6580 // Destination is too close (will consider to be at destination.
6583 _FirstPos
= _DestPos
= pos();
6586 _FirstTime
= _DestTime
= _LastFrameTime
;
6589 // The target is not allocated.
6592 _FirstPos
= _DestPos
= pos();
6595 _FirstTime
= _DestTime
= _LastFrameTime
;
6597 }// updatePosCombatFloat //
6599 //-----------------------------------------------
6601 // Upadte the player position
6602 // \param time : Time for the position of the entity after the motion.
6603 // \param target : pointer on the current target.
6604 // \todo GUIGUI : compute it when receiving a new stage instead of every frame (should be faster).
6605 // \todo GUIGUI: recompute distance to destination even if the Stage not reached.
6606 //-----------------------------------------------
6607 ADD_METHOD(void CCharacterCL::updatePos(const TTime
¤tTimeInMs
, CEntityCL
*target
))
6608 _OldAutomaton
= _CurrentAutomaton
;
6609 // Compute the Time Step.
6610 double frameTimeRemaining
= computeTimeStep(((double)currentTimeInMs
)*0.001);
6612 // Update the LodCharacter Animation.
6613 if(_LodCharacterAnimEnabled
)
6615 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation
);
6616 // \todo GUIGUI : replace 'getSpeedFactor' by the correct speed factor !!
6617 // update lod anim time. multiply by speed factor of the most important slot.
6618 _LodCharacterAnimTimeOffset
+= DT
* _PlayList
->getSpeedFactor(_LodCharacterMasterAnimSlot
);
6623 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Set_Play_List
);
6624 // \todo GUIGUI : do something better for the blend (mounts there).
6625 if(isRiding() || ClientCfg
.BlendFrameNumber
== 0 || _BlendRemaining
<= 0)
6627 _BlendRemaining
= 0;
6628 _PlayList
->setAnimation(ACTION
, UPlayList::empty
);
6629 _PlayList
->setWeight(ACTION
, 0.0f
);
6630 if(runFactor() < 0.5 || (_CurrentAnimSet
[MOVE_BLEND_OUT
]==0))
6632 if(_CurrentAnimSet
[MOVE
])
6634 _CurrentAnimSet
[ACTION
] = _CurrentAnimSet
[MOVE
];
6635 animState (ACTION
, animState (MOVE
));
6636 animIndex (ACTION
, animIndex (MOVE
)); // This also call "animId" and set it.
6637 animOffset(ACTION
, animOffset(MOVE
));
6641 _CurrentAnimSet
[ACTION
] = 0;
6642 animState (ACTION
, CAnimationStateSheet::UnknownState
);
6643 animIndex (ACTION
, CAnimation::UnknownAnim
); // This also call "animId" and set it.
6644 animOffset(ACTION
, 0.0);
6649 _CurrentAnimSet
[ACTION
] = _CurrentAnimSet
[MOVE_BLEND_OUT
];
6650 animState (ACTION
, animState (MOVE_BLEND_OUT
));
6651 animIndex (ACTION
, animIndex (MOVE_BLEND_OUT
)); // This also call "animId" and set it.
6652 animOffset(ACTION
, animOffset(MOVE_BLEND_OUT
));
6654 _AnimReversed
[ACTION
] = false;
6658 double animLength
= EAM
->getAnimationLength(animId(ACTION
));
6659 // Check Anim length
6660 if(animOffset(ACTION
)+frameTimeRemaining
> animLength
)
6661 animOffset(ACTION
, animLength
);
6663 animOffset(ACTION
, animOffset(ACTION
)+frameTimeRemaining
);
6664 // Compute weight step.
6665 float w
= (float)_BlendRemaining
/(float)(ClientCfg
.BlendFrameNumber
+1);
6666 // Set Old Anim Weight.
6667 _PlayList
->setWeight(ACTION
, w
);
6668 // Set New Anim Weight.
6669 _PlayList
->setWeight(MOVE
, 1.f
-w
);
6672 uint antiFreezeCounter
= 0;
6673 // While the time Step is not Null.
6674 while(frameTimeRemaining
> 0)
6676 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_WhileStep
);
6678 //--------------------//
6679 //--------------------//
6680 // ANTI-FREEZE SYSTEM //
6681 // If too many loop, display some infos
6682 if(antiFreezeCounter
> 50)
6685 nlwarning("CH:updatePos:antiFreeze:%d: frameTimeRemaining '%f'", _Slot, frameTimeRemaining);
6686 nlwarning("CH:updatePos:antiFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
6687 nlwarning("CH:updatePos:antiFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
6688 nlwarning("CH:updatePos:antiFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
6689 nlwarning("CH:updatePos:antiFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
6690 nlwarning("CH:updatePos:antiFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
6691 nlwarning("CH:updatePos:antiFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationState::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
6693 // Once too many more time reached, leave the method.
6694 if(antiFreezeCounter
> 60)
6697 // Update antiFreezeCounter.
6698 ++antiFreezeCounter
;
6699 // ANTI-FREEZE SYSTEM //
6700 //--------------------//
6701 //--------------------//
6702 // \todo GUIGUI : improve dist2first and dist2dest
6705 // \todo GUIGUI : Bug with _TargetAngle in fight float, we overwrite here angle sent by the server ?
6706 // If the entity is too far (orientation not received yet), set the front vector as the moving direction.
6707 CVectorD distToUser
= pos()-UserEntity
->pos();
6709 if(distToUser
.norm()*1000.0 > CLFECOMMON::THRESHOLD_ORIENTATION
*0.9)
6711 if(_FirstPos
!= INVALID_POS
)
6713 CVectorD dirToFirstP
= _FirstPos
-pos();
6714 dirToFirstP
.z
= 0.0;
6715 if(dirToFirstP
!= CVectorD::Null
)
6717 front(dirToFirstP
.normed(), false, false);
6718 _TargetAngle
= atan2(front().y
, front().x
);
6722 // Mode Combat Float :
6723 if(!_IsThereAMode
&& (_Mode
== MBEHAV::COMBAT_FLOAT
))
6725 // Update the position in combat float.
6726 updatePosCombatFloat(frameTimeRemaining
, target
);
6728 // Compute the average speed to the destination.
6733 bool stageReach
= false;
6734 bool allToFirstPos
= false;
6735 // Compute time to Stage or full Time Step if Stage too far.
6736 double loopTimeStep
= frameTimeRemaining
;
6737 double buLoopTimeStep
= 0.0;
6738 double checkLoopTimeStep
= loopTimeStep
;
6739 // Update the animation used according to the speed/end anim/etc..
6740 updateAnimationState();
6741 // Backup the old time offset.
6742 double oldMovingTimeOffset
= animOffset(MOVE
);
6743 double oldMovingTimeOffsetRun
= animOffset(MOVE_BLEND_OUT
);
6744 // WARNING -> Unknown Animation Selected.
6745 // Play the time step for the loop and truncate to End Anim if Time Step too big.
6746 if((_CurrentState
!= 0) && (animIndex(MOVE
) != CAnimation::UnknownAnim
))
6747 playToEndAnim(oldMovingTimeOffset
, loopTimeStep
);
6750 if(loopTimeStep
> checkLoopTimeStep
)
6752 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > checkLoopTimeStep(%f).", _Slot
, loopTimeStep
, checkLoopTimeStep
);
6755 loopTimeStep
= checkLoopTimeStep
;
6757 checkLoopTimeStep
= loopTimeStep
;
6758 // -- END CHECK -- //
6759 /////////////////////
6760 // (DEBUG) : Backup the Animation Time Offset after the adjustment with end anim to make some checks.
6761 double backupAnimTimeOff
= animOffset(MOVE
);
6763 bool posInStage
= false;
6764 double stageTime
= -1.0;
6765 if(!_Stages
._StageSet
.empty())
6767 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Move
);
6768 // Get the reference on the current stage.
6769 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
6770 CStage
&stage
= (*it
).second
;
6771 if(_Mode
== MBEHAV::COMBAT_FLOAT
&& !_IsThereAMode
)
6774 posInStage
= stage
.isPresent(CLFECOMMON::PROPERTY_POSITION
);
6775 stageTime
= stage
.time();
6777 // dist2FirstPos() should not be Null if the destination is not Null (because of the code in updateStage).
6778 if(dist2FirstPos() > 0.0)
6780 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_dist2FirstPos_gt_0
);
6782 // Get the covered distance from the animation.
6783 double move
= getTheMove(loopTimeStep
, oldMovingTimeOffset
, oldMovingTimeOffsetRun
);
6784 // The move is big enough to reach the first step with motion.
6785 if(move
>= dist2FirstPos()) // dist2FirstPos() > 0 -> move > 0.
6787 double percent
= dist2FirstPos() / move
;
6788 // Adjust loopTimeStep
6789 loopTimeStep
*= percent
;
6790 if(loopTimeStep
> checkLoopTimeStep
) // prevent bugs because of the double's precision
6791 loopTimeStep
= checkLoopTimeStep
;
6792 if(loopTimeStep
< 0.0)
6794 // Update Animation Time Offset (move greater than the dist to next stage; update animation time to get them equal).
6795 animOffset(MOVE
, oldMovingTimeOffset
+ (animOffset(MOVE
) -oldMovingTimeOffset
)*percent
);
6796 animOffset(MOVE_BLEND_OUT
, oldMovingTimeOffsetRun
+ (animOffset(MOVE_BLEND_OUT
)-oldMovingTimeOffsetRun
)*percent
);
6797 // \todo GUIGUI : check if the following line is necessary
6798 buLoopTimeStep
= loopTimeStep
;
6799 // First Position Reached
6801 dist2FirstPos(0.0); // Current entity position is now the same as the First position so dis is Null.
6802 // Complete the Stage.
6803 if(_Mode
!= MBEHAV::COMBAT_FLOAT
|| _IsThereAMode
)
6808 allToFirstPos
= true;
6811 // Even if the movement is not enough to reach the first position, move the entity to this position.
6814 // Compute the vector to the first stage with a position.
6815 CVectorD vectToFirstPos
= _FirstPos
- pos();
6816 vectToFirstPos
.z
= 0.0f
;
6817 // Update entity position.
6818 if(vectToFirstPos
!= CVectorD::Null
)
6819 pos(pos() + vectToFirstPos
*(move
/dist2FirstPos()));
6821 // Else : There is no move.
6825 CHECK(posInStage
==false && dist2Dest()<=0.0);
6828 // If there is no position in the next stage and the stage should be done already.
6829 if(!_Stages
._StageSet
.empty() && !posInStage
&& !stageReach
&& !allToFirstPos
&& ((_LastFrameTime
+loopTimeStep
) >= stageTime
))
6831 // Backup 'loopTimeStep' just in case of the stage could not be done.
6832 buLoopTimeStep
= loopTimeStep
;
6833 // Adjust loopTimeStep
6834 loopTimeStep
= stageTime
- _LastFrameTime
;
6835 if(loopTimeStep
> checkLoopTimeStep
) // prevent bugs because of the double's precision
6836 loopTimeStep
= checkLoopTimeStep
;
6837 if(loopTimeStep
< 0.0)
6840 // \todo GUIGUI : adjust timeOffset, because we stopped the loop before
6847 // Check the Animation Time Offset is not became greater than the old.
6848 if(animOffset(MOVE
) > backupAnimTimeOff
)
6850 nlwarning("CH:updtPos:%d: backupAnimTimeOff(%f) < AnimationsTimeOffset(%f) animLen(%f) -> animOffset(MOVE) = backupAnimTimeOff",
6851 _Slot
, backupAnimTimeOff
, animOffset(MOVE
), EAM
->getAnimationLength(animId(MOVE
)));
6854 animOffset(MOVE
, backupAnimTimeOff
);
6856 // Check loopTimeStep is not < 0;
6857 if(loopTimeStep
< 0.0)
6859 nlwarning("CH:updtPos:%d: loopTimeStep(%f) < 0 -> loopTimeStep=0.0.", _Slot
, loopTimeStep
);
6864 // time spent could not be bigger than the time remaining
6865 if(loopTimeStep
> frameTimeRemaining
)
6867 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > frameTimeRemaining(%f) -> loopTimeStep=frameTimeRemaining.", _Slot
, loopTimeStep
, frameTimeRemaining
);
6870 loopTimeStep
= frameTimeRemaining
;
6872 // -- END CHECK -- //
6873 /////////////////////
6874 // Manage Events that could be created by the animation (like sound).
6876 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Anim_Event
)
6877 animEventsProcessing(oldMovingTimeOffset
, animOffset(MOVE
));
6879 // Apply all stages until the first stage with a pos.
6882 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_All_Stage_To_First_Pos
);
6883 applyAllStagesToFirstPos();
6885 // Stage is complete, apply modifications.
6888 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_Current_Stage
);
6889 if(!applyCurrentStage())
6890 loopTimeStep
= buLoopTimeStep
;
6892 // Compute the remaining Time Step.
6893 frameTimeRemaining
-= loopTimeStep
;
6894 // Update the last Time.
6895 _LastFrameTime
+= loopTimeStep
;
6896 }// while(frameTimeRemaining > 0) //
6898 ////////////////////////////////
6899 // UPDATE THE ENTITY POSITION //
6900 // Set the new position into PACS.
6902 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Pacs
);
6907 // Check frameTimeRemaining is perfectly equal to 0.
6908 if(frameTimeRemaining
< 0.0)
6909 nlwarning("CCharacterCL::updatePos : frameTimeRemaining(%f) < 0 ! This should never happen.", frameTimeRemaining
);
6912 // Update the children display.
6914 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Children
);
6915 std::list
<CEntityCL
*>::iterator itTmp
, it
= _Children
.begin();
6916 while(it
!= _Children
.end())
6919 // Next Child (done before just in case child is detached during his processFrame).
6924 CCharacterCL
*child
= dynamic_cast<CCharacterCL
*>(*itTmp
);
6927 if ( ! ClientCfg
.Light
)
6929 // Update the animation offset for the child.
6930 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
6931 if(animLength
> 0.0)
6933 double factor
= animOffset(MOVE
) / animLength
;
6936 double childTimeOffset
= factor
*EAM
->getAnimationLength(child
->animId(MOVE
));
6937 child
->animOffset(MOVE
, childTimeOffset
);
6940 child
->animOffset(MOVE
, 0.0);
6941 child
->processFrame(currentTimeInMs
);
6943 child
->pacsMove(pos()); // Move the child at the same position than the parent.
6946 nlwarning("Character '%d': Child is not a 'CCharacterCL'.", _Slot
);
6949 nlwarning("Character '%d': Child not allocated.", _Slot
);
6954 //-----------------------------------------------
6955 // updateVisiblePostRender :
6956 // Update the entity after the render like for the head offset.
6957 //-----------------------------------------------
6958 void CCharacterCL::updateVisiblePostRender() // virtual
6960 // Compute the headoffset
6961 if(_HeadBoneId
!= -1 && !_Skeleton
.empty())
6963 if(_Skeleton
.getLastClippedState() && _Skeleton
.isBoneComputed(_HeadBoneId
))
6965 UBone headBone
=_Skeleton
.getBone(_HeadBoneId
);
6966 const CMatrix
&headMat
= headBone
.getLastWorldMatrixComputed();
6967 _HeadOffset
= headMat
.getPos()-pos();
6968 _HeadOffsetComputed
= true;
6971 }// updateVisiblePostRender //
6973 //-----------------------------------------------
6975 void CCharacterCL::updateAllPostRender()
6979 //-----------------------------------------------
6981 //-----------------------------------------------
6982 void CCharacterCL::processFrame(const TTime
¤tTimeInMs
)
6984 // Prepare stages and update information from them.
6987 // Compute the time remaining until frame completely processed.
6988 double timeRemaining
= computeTimeStep(((double)currentTimeInMs
)*0.001);
6990 // Compute the time spent between 2 frames.
6991 while(timeRemaining
> 0.0)
6993 // Time already processed until now.
6994 double timeProcessed
= timeRemaining
;
6997 if(!_Stages
._StageSet
.empty())
6999 // Get the reference on the current stage.
7000 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
7001 CStage
&stage
= (*it
).second
;
7003 // Check if the stage should be done already.
7004 if((_LastFrameTime
+timeProcessed
) >= stage
.time())
7006 // Stage Done during the Frame.
7007 if(stage
.time() > _LastFrameTime
)
7008 timeProcessed
= stage
.time() - _LastFrameTime
;
7009 // This Stage should have been done already before last frame
7011 timeProcessed
= 0.0;
7013 // Process the stage.
7014 processStage(stage
);
7017 _Stages
._StageSet
.erase(it
);
7021 // Compute the remaining Time Step.
7022 timeRemaining
-= timeProcessed
;
7023 // Update the last Time.
7024 _LastFrameTime
+= timeProcessed
;
7027 // Just to be sure, Last frame time = current time once all is done.
7028 _LastFrameTime
= ((double)currentTimeInMs
)*0.001;
7031 //-----------------------------------------------
7033 // Process the stage.
7034 //-----------------------------------------------
7035 void CCharacterCL::processStage(CStage
&stage
)
7037 // Apply Mode (if there is a mode, there is a position too).
7038 pair
<bool, sint64
> resultMode
= stage
.property(PROPERTY_MODE
);
7039 if(resultMode
.first
)
7041 uint8 mo
= *(uint8
*)(&resultMode
.second
);
7042 MBEHAV::EMode mode
= (MBEHAV::EMode
)mo
;
7045 // release the old mode.
7046 if ( (_Mode
== MBEHAV::MOUNT_NORMAL
|| _Mode
== MBEHAV::MOUNT_SWIM
)
7047 && (mode
!= MBEHAV::MOUNT_NORMAL
&& mode
!= MBEHAV::MOUNT_SWIM
)
7050 // Unlink the mount and the rider.
7051 parent(CLFECOMMON::INVALID_SLOT
);
7052 _Mount
= CLFECOMMON::INVALID_SLOT
;
7053 _Rider
= CLFECOMMON::INVALID_SLOT
;
7055 // Restore collisions.
7058 // \todo GUIGUI : do that without dynamic cast
7059 if(dynamic_cast<CPlayerCL
*>(this))
7060 _Primitive
->setOcclusionMask(MaskColPlayer
);
7062 _Primitive
->setOcclusionMask(MaskColNpc
);
7065 // Get stage position.
7066 if(stage
.getPos(_OldPos
))
7069 _OldPosTime
= stage
.time();
7070 // Unseat the entity at the position given in the stage.
7074 nlwarning("CH:processStage:%d: The stage should have a position with the mode.", _Slot
);
7077 // Set the new mode.
7080 // Compute the automaton
7083 setAnim(CAnimationStateSheet::Idle
);
7086 // Not a mode -> so search for a position.
7090 //-----------------------------------------------
7092 // Update the player blink state
7093 //-----------------------------------------------
7094 void CCharacterCL::updateBlink(const TTime
¤tTimeInMs
)
7097 GSGENDER::EGender gender
= getGender();
7098 if ((gender
== GSGENDER::male
) || (gender
== GSGENDER::female
))
7103 static const double blinkTime
= 100.f
;
7104 static const double minBlinkLength
= 500.f
;
7105 static const double maxBlinkLength
= 5000.f
;
7107 // Next blink time is valid ?
7108 bool validTime
= (_NextBlinkTime
+ blinkTime
>= currentTimeInMs
) && (_NextBlinkTime
<= (currentTimeInMs
+ maxBlinkLength
));
7111 bool blinkEnd
= (currentTimeInMs
>= _NextBlinkTime
+ blinkTime
);
7113 // Blink is finished or next blink time is invalid ?
7114 if ( blinkEnd
|| !validTime
)
7118 // Compute next time
7119 _NextBlinkTime
= (TTime
)(((double)rand () / (double)RAND_MAX
) * (maxBlinkLength
- minBlinkLength
) + minBlinkLength
+ (double)currentTimeInMs
);
7124 if (currentTimeInMs
>= _NextBlinkTime
)
7136 SInstanceCL
*face
= getFace ();
7138 // Set the blend shape
7139 if(face
&& !face
->Current
.empty())
7140 face
->Current
.setBlendShapeFactor ("visage_100", blend
, true);
7145 //-----------------------------------------------
7147 // Update eyes blink. For the moment, called by updatePos.
7148 //-----------------------------------------------
7149 CEntityCL::SInstanceCL
*CCharacterCL::getFace ()
7151 // Implemented in CPlayerCL
7152 return idx2Inst(_FaceIdx
);
7156 //---------------------------------------------------
7158 // Get the entity position and set all visual stuff with it.
7159 // \todo GUIGUI : put this method 'virtual' to have a different code for the user (no playlist).
7160 // \todo GUIGUI : manage the parent better.
7161 //---------------------------------------------------
7162 ADD_METHOD(void CCharacterCL::updateDisplay(CEntityCL
*parent
))
7166 // Reverse the animation if needed.
7167 const double animOffsetMOV
= _AnimReversed
[MOVE
] ? EAM
->getAnimationLength(animId(MOVE
)) - animOffset(MOVE
) : animOffset(MOVE
);
7168 const double animOffsetACT
= _AnimReversed
[ACTION
] ? EAM
->getAnimationLength(animId(ACTION
)) - animOffset(ACTION
) : animOffset(ACTION
);
7169 const double animOffsetBLE
= _AnimReversed
[MOVE_BLEND_OUT
] ? EAM
->getAnimationLength(animId(MOVE_BLEND_OUT
)) - animOffset(MOVE_BLEND_OUT
): animOffset(MOVE_BLEND_OUT
);
7170 // Update Speed Factor
7171 _PlayList
->setTimeOrigin(MOVE
, TimeInSec
-animOffsetMOV
);
7172 _PlayList
->setTimeOrigin(ACTION
, TimeInSec
-animOffsetACT
);
7173 _PlayList
->setTimeOrigin(MOVE_BLEND_OUT
, TimeInSec
-animOffsetBLE
);
7176 weight
= _PlayList
->getLocalWeight(MOVE
, TimeInSec
);
7179 _PlayList
->setWeight(MOVE
, weight
*(float)(1.0-runFactor()));
7180 _PlayList
->setWeight(MOVE_BLEND_OUT
, weight
*(float)runFactor());
7181 // If the animation exist update the display.
7182 if(animIndex(MOVE
) != CAnimation::UnknownAnim
)
7185 // Get the 3D position for the current time in the animation (Vector Null if animation has no move).
7186 CVector currentAnimPos
;
7187 if(!EAM
->interpolate(animId(MOVE
), animOffsetMOV
, currentAnimPos
))
7188 currentAnimPos
= CVector::Null
;
7191 // If the current animation state is a move, do not take the Y move in the animation because it's the code that compute the move.
7192 if(_CurrentState
&& _CurrentState
->Move
)
7194 CVector currentAnimPosStart
;
7195 if(!EAM
->interpolate(animId(MOVE
), 0.0, currentAnimPosStart
))
7196 currentAnimPosStart
= CVector::Null
;
7197 if(_CurrentState
->XFactor
) currentAnimPos
.x
= currentAnimPosStart
.x
;
7198 if(_CurrentState
->YFactor
) currentAnimPos
.y
= currentAnimPosStart
.y
;
7199 if(_CurrentState
->ZFactor
) currentAnimPos
.z
= currentAnimPosStart
.z
;
7201 // Scale the animation with the Character Scale Pos if the animation need it.
7203 bool applyCharacterScalePosFactor
= true;
7204 // \todo GUIGUI : faire cette histoire de emote beaucoup mieux, C NULL.
7205 const CAnimationState
*animStatePtr
;
7206 // If the current animation is an emote, get the right animation state.
7207 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
7208 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
7210 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
7213 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
7215 applyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
7218 if(applyCharacterScalePosFactor
)
7219 currentAnimPos
*= _CharacterScalePos
;
7221 // Scale according to the gabarit (choose at the character creation).
7222 currentAnimPos
*= _CustomScalePos
;
7225 if(runFactor() > 0.0)
7227 CVector currentAnimPosRun
;
7228 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), animOffsetBLE
, currentAnimPosRun
))
7229 currentAnimPosRun
= CVector::Null
;
7232 // If the current animation state is a move, do not take the Y move in the animation because it's the code that compute the move.
7233 if(_CurrentState
&& _CurrentState
->Move
)
7235 CVector currentAnimPosStart
;
7236 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), 0.0, currentAnimPosStart
))
7237 currentAnimPosStart
= CVector::Null
;
7238 if(_CurrentState
->XFactor
) currentAnimPosRun
.x
= currentAnimPosStart
.x
;
7239 if(_CurrentState
->YFactor
) currentAnimPosRun
.y
= currentAnimPosStart
.y
;
7240 if(_CurrentState
->ZFactor
) currentAnimPosRun
.z
= currentAnimPosStart
.z
;
7242 // Scale it by the CharacterScalePos, if needed, according to the animation.
7243 bool applyCharacterScalePosFactor
= true;
7244 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE_BLEND_OUT
]->getAnimationState(animState(MOVE_BLEND_OUT
));
7247 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE_BLEND_OUT
));
7249 applyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
7252 if(applyCharacterScalePosFactor
)
7253 currentAnimPosRun
*= _CharacterScalePos
;
7254 // Scale according to the gabarit.
7255 currentAnimPosRun
*= _CustomScalePos
;
7258 currentAnimPos
= currentAnimPos
*(float)(1.0-runFactor()) + currentAnimPosRun
*(float)runFactor();
7261 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7262 CQuat currentAnimRot
;
7263 if(!EAM
->interpolate(animId(MOVE
), animOffsetMOV
, currentAnimRot
))
7264 currentAnimRot
= CQuat::Identity
;
7267 // If the animation is a rotation -> Do just a part of the animation.
7268 if(parent
==0 && _CurrentState
&& _CurrentState
->Rotation
&& _RotationFactor
!=-1.0)
7270 // Get the Rotation at the beginning of the animation.
7271 CQuat currentAnimRotStart
;
7272 if(!EAM
->interpolate(_PlayList
->getAnimation(MOVE
), 0.0, currentAnimRotStart
))
7273 currentAnimRotStart
= CQuat::Identity
;
7275 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
7277 // Get the Rotation at the beginning of the animation.
7278 CQuat currentAnimRotEnd
;
7279 if(!EAM
->interpolate(_PlayList
->getAnimation(MOVE
), animLength
, currentAnimRotEnd
))
7280 currentAnimRotEnd
= CQuat::Identity
;
7282 // Get the angle done by the animation from the beginning
7283 CQuat rotStartTmp
= currentAnimRotStart
;
7284 rotStartTmp
.invert();
7285 CQuat rotTmp
= rotStartTmp
* currentAnimRot
;
7286 float ang
= rotTmp
.getAngle();
7288 currentAnimRot
= applyRotationFactor(currentAnimRot
, (float)_RotationFactor
, currentAnimRotStart
, currentAnimRotEnd
, (float)(animOffsetMOV
/animLength
));
7290 // Get the angle done once scaled.
7291 rotTmp
= rotStartTmp
* currentAnimRot
;
7294 rotMat
.rotateZ(_CurrentState
->RotFactor
*(ang
-rotTmp
.getAngle()));
7296 // Apply the scaled rotation to the position.
7297 currentAnimPos
= rotMat
*currentAnimPos
;
7300 // Get the Rotation at the beginning of the animation.
7301 CQuat currentAnimRotStart;
7302 if(!EAM->interpolate(_PlayList->getAnimation(MOVE), 0.0, currentAnimRotStart))
7303 currentAnimRotStart = CQuat::Identity;
7305 // Find the closest quat.
7306 // currentAnimRotStart.makeClosest(currentAnimRot);
7308 // Get the angle done by the animation from the beginning
7309 CQuat rotStartTmp = currentAnimRotStart;
7310 rotStartTmp.invert();
7311 CQuat rotTmp = rotStartTmp * currentAnimRot;
7312 float ang = rotTmp.getAngle();
7314 // Get the Rotation scaled.
7315 currentAnimRot = CQuat::slerp(currentAnimRotStart, currentAnimRot, (float)_RotationFactor);
7317 // Get the angle done once scaled.
7318 rotTmp = rotStartTmp * currentAnimRot;
7321 rotMat.rotateZ(_CurrentState->RotFactor*(ang-rotTmp.getAngle()));
7323 // Apply the scaled rotation to the position.
7324 currentAnimPos = rotMat*currentAnimPos;
7330 if(runFactor() > 0.0)
7332 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7333 CQuat currentAnimRotRun
;
7334 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), animOffsetBLE
, currentAnimRotRun
))
7335 currentAnimRotRun
= CQuat::Identity
;
7336 currentAnimRotRun
.makeClosest(currentAnimRot
);
7337 currentAnimRot
= CQuat::slerp(currentAnimRot
, currentAnimRotRun
, (float)runFactor());
7341 CMatrix AnimMatrixRot
;
7342 AnimMatrixRot
.identity();
7343 AnimMatrixRot
.setRot(currentAnimRot
);
7345 // Rotation 180 degrees Matrix
7349 rot180
.rotateZ((float)Pi
);
7351 // Logical entity Matrix.
7354 current
= _DirMatrix
;
7358 // Convert the anim position in a world position.
7359 currentAnimPos
= (current
*rot180
)*currentAnimPos
;
7361 rot180
*= AnimMatrixRot
;
7365 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7366 if(ClientCfg
.BlendFrameNumber
&& _BlendRemaining
> 0)
7369 _OldRotQuat
.makeClosest(current
.getRot());
7370 tmpQuat
= CQuat::slerp(current
.getRot(), _OldRotQuat
, ((float)_BlendRemaining
/(float)(ClientCfg
.BlendFrameNumber
+1)));
7371 current
.setRot(tmpQuat
);
7372 // 1 more frame played.
7377 // Compute the position for the instance.
7382 tmpPos
+= currentAnimPos
;
7384 // If the entity is on a mount, just adjust the position with the animation.
7386 tmpPos
= currentAnimPos
;
7387 // Set the skeleton position and rotation.
7388 if(!_Skeleton
.empty())
7390 _Skeleton
.setRotQuat(current
.getRot());
7391 _Skeleton
.setPos(tmpPos
);
7393 // Only Instances with no skeleton (objects).
7394 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7396 _Instances
[0].Current
.setRotQuat(current
.getRot());
7397 _Instances
[0].Current
.setPos(tmpPos
);
7399 // Set the instance position and rotation.
7400 else if(!_Instance
.empty())
7402 _Instance
.setRotQuat(current
.getRot());
7403 _Instance
.setPos(tmpPos
);
7407 static bool once
= false;
7411 nlwarning("CH::updtDisp:%d: no instance nor skeleton. Sheet Id '%d(%s)'.", _Slot
, _SheetId
.asInt(), _SheetId
.toString().c_str());
7416 // Else Keep the lastest correct display.
7419 H_AUTO ( RZ_Client_Entity_CL_Update_Display_Unknown_Anim
)
7421 // Rotation 90 degrees Matrix
7425 rot90
.rotateZ((float)(Pi
/2.0));
7427 // Logical entity Matrix.
7430 current
= _DirMatrix
;
7432 // current.identity();
7436 // Changes the skeleton position.
7437 if(!_Skeleton
.empty())
7439 _Skeleton
.setRotQuat(current
.getRot());
7441 _Skeleton
.setPos(pos());
7443 // _Skeleton.setPos(currentAnimPos);
7445 // Only Instances with no skeleton (objects).
7446 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7448 _Instances
[0].Current
.setRotQuat(current
.getRot());
7450 _Instances
[0].Current
.setPos(pos());
7456 // Changes the skeleton position.
7457 if(!_Skeleton
.empty())
7459 _Skeleton
.setPos(pos());
7461 // Only Instances with no skeleton (objects).
7462 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7464 // Logical entity Matrix.
7467 current
= _DirMatrix
;
7469 _Instances
[0].Current
.setRotQuat(current
.getRot());
7470 _Instances
[0].Current
.setPos(pos());
7472 // Changes the instance position.
7473 else if(!_Instance
.empty())
7475 _Instance
.setPos(pos());
7479 if(!ClientCfg
.Light
)
7481 // update texture Async Loading
7482 updateAsyncTexture();
7483 // update lod Texture
7488 // Update the children display.
7489 std::list
<CEntityCL
*>::iterator it
= _Children
.begin();
7490 while(it
!= _Children
.end())
7492 // Update the display for the child
7493 (*it
)->updateDisplay(this);
7497 }// updateDisplay //
7500 //---------------------------------------------------
7502 // Method to get the position of the head (in the world).
7503 // \param headPos: will be set with the head position if succeed.
7504 // \return 'true' if the param has been updated.
7505 // \warning this method do NOT check if there is a skeleton.
7506 //---------------------------------------------------
7507 bool CCharacterCL::getHeadPos(NLMISC::CVector
&headPos
)
7509 // if never computed (eg: clipped or lod)
7510 if(!_HeadOffsetComputed
)
7512 // force compute the bone
7513 if(_HeadBoneId
!= -1 && !_Skeleton
.empty())
7515 _Skeleton
.forceComputeBone(_HeadBoneId
);
7516 UBone headBone
=_Skeleton
.getBone(_HeadBoneId
);
7517 const CMatrix
&headMat
= headBone
.getLastWorldMatrixComputed();
7518 _HeadOffset
= headMat
.getPos()-pos();
7520 _HeadOffsetComputed
= true;
7523 // return the pos with the last head offset computed
7524 headPos
= pos()+_HeadOffset
;
7529 //---------------------------------------------------
7530 // updateHeadDirection :
7531 // Update the head Direction.
7532 //---------------------------------------------------
7533 void CCharacterCL::updateHeadDirection(CEntityCL
*target
)
7535 // Does the entity got a target to track with the head ?
7536 // No head targeting if the target slot is the same as the entity slot
7537 if(_TargetSlot
!=CLFECOMMON::INVALID_SLOT
&& _TargetSlot
!=_Slot
)
7539 // Is the target allocated.
7542 // Do not orientate the head to the target if too far.
7543 CVectorD vectDist
= target
->pos() - pos();
7544 if((fabs(vectDist
.x
) + fabs(vectDist
.y
)) <= ClientCfg
.MaxHeadTargetDist
&& vectDist
!= CVectorD::Null
)
7546 // Do not orientate the head to the target if behind.
7547 vectDist
.normalize();
7548 if(fabs(angleBetween2Vect(dir(), vectDist
)) < Pi
/3.0)
7550 CVector targetheadPos
;
7551 if(target
->getHeadPos(targetheadPos
))
7553 _TargetAnimCtrl
.Mode
= CTargetAnimCtrl::TargetMode
;
7554 _TargetAnimCtrl
.WorldTarget
= targetheadPos
;
7562 _TargetAnimCtrl
.Mode
= CTargetAnimCtrl::DirectionMode
;
7564 frontMat
.setRot(CVector::I
, front(), CVector::K
, true);
7565 frontMat
.normalize(CMatrix::YZX
);
7566 _TargetAnimCtrl
.CurrentWorldDirection
= frontMat
.getRot();
7567 }// updateHeadDirection //
7570 //---------------------------------------------------
7572 // Display the entity name.
7573 //---------------------------------------------------
7574 void CCharacterCL::displayName()
7576 // There is no Context -> Cannot display a name.
7577 if(TextContext
== 0)
7580 NLMISC::CVector namePos
;
7581 // There is a skeleton.
7582 if(!_Skeleton
.empty() && !ClientCfg
.Light
)
7584 // Only if the skeleton is visible.
7587 // Do not display the name in LoD.
7588 if(_Skeleton
.isDisplayedAsLodCharacter())
7590 // If the entity was not displayed last frame (clipped) -> do not display the name.
7591 if(!_Skeleton
.getLastClippedState())
7594 // Is there a Bone for the Name ?
7595 if(_NameBoneId
!= -1)
7596 namePos
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos();
7597 // No Bone for the name -> Draw it at a default position.
7599 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7601 // If there is no skeleton -> compute a position for the name.
7604 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7607 // Create the matrix and set the orientation according to the camera.
7610 matrix
.setRot(MainCam
.getRotQuat());
7611 matrix
.setPos(namePos
);
7613 CVector distPos
= MainCam
.getPos()-pos();
7614 float scale
= distPos
.norm();
7616 // don't display too far names
7617 if (ClientCfg
.Light
&& scale
> 20)
7622 else if(scale
> ClientCfg
.ConstNameSizeDist
)
7623 scale
= ClientCfg
.ConstNameSizeDist
;
7624 // Too Far to display a name.
7625 else if(scale
> ClientCfg
.MaxNameDist
)
7627 // Compute the final scale.
7628 matrix
.scale(ClientCfg
.NameScale
*scale
);
7634 //---------------------------------------------------
7637 //---------------------------------------------------
7638 void CCharacterCL::drawName(const NLMISC::CMatrix
&mat
) // virtual
7640 const string
&ucname
= getEntityName();
7641 if(!getEntityName().empty())
7643 // If there is no extended name, just display the name
7645 TextContext
->render3D(mat
, ucname
);
7646 // If there is an extended name, display the extended name at the name place and the name above.
7649 // Display the Extended Name at right place.
7650 TextContext
->render3D(mat
, _NameEx
);
7651 // Compute the position for the Name.
7654 mat2
.setRot(MainCam
.getRotQuat());
7655 CVector v
= mat
.getPos()+mat
.getK().normed()*ClientCfg
.NamePos
*mat
.getScaleUniform();
7657 mat2
.scale(mat
.getScaleUniform());
7658 // Diaplay the name.
7659 TextContext
->render3D(mat2
, ucname
);
7667 const char *name
= STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet
->Id
);
7668 if (!FINAL_VERSION
|| !NLMISC::startsWith(name
, "<NotExist:"))
7669 TextContext
->render3D(mat
, name
);
7675 //---------------------------------------------------
7676 // displayModifiers :
7677 // Display the Hp Bar
7678 //---------------------------------------------------
7679 void CCharacterCL::displayModifiers() // virtual
7682 if( _HPDisplayed
.empty())
7685 // **** get the name pos
7686 NLMISC::CVectorD namePos
;
7687 if(!getNamePos(namePos
))
7688 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7689 // remove but keep the Z to the ground
7690 float currentDeltaZ
= float(namePos
.z
- pos().z
);
7691 CVector groundPos
= namePos
;
7692 groundPos
.z
-= currentDeltaZ
;
7695 // **** compute the scale
7696 float dist
= (MainCam
.getPos()-pos()).norm();
7698 if(dist
> ClientCfg
.MaxNameDist
)
7700 if ( dist
< ClientCfg
.ConstNameSizeDist
)
7703 scale
= ClientCfg
.ConstNameSizeDist
/ dist
;
7706 // **** Display HP modifiers.
7707 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
7708 std::list
<HPMD
>::iterator itTmp
;
7709 std::list
<HPMD
>::iterator it
= _HPDisplayed
.begin();
7710 while(it
!= _HPDisplayed
.end())
7714 const float totalDuration
= 3.f
;
7715 const float noFadeDuration
= 1.f
;
7716 const float fadeDuration
= totalDuration
-noFadeDuration
;
7717 if(TimeInSec
> (mod
.Time
+totalDuration
))
7721 _HPDisplayed
.erase(itTmp
);
7723 else if (TimeInSec
>= mod
.Time
)
7726 if (mod
.Text
.empty())
7727 hpModifier
= toString("%d", mod
.Value
);
7729 hpModifier
= mod
.Text
;
7730 double t
= TimeInSec
-mod
.Time
;
7731 // for character, keep the deltaZ the first time it is displayed, and apply the same each frame
7732 // (avoid Z movement of the flying text because of animation)
7733 if(mod
.DeltaZ
==-FLT_MAX
)
7734 mod
.DeltaZ
= currentDeltaZ
;
7735 // Compute the position for the Modifier.
7736 float dynT
= sqrtf((float)t
/totalDuration
); // a sqrt just so it looks much more "jumpy"
7737 CVector pos
= groundPos
+ CVector(0.0f
, 0.0f
, mod
.DeltaZ
+ dynT
*1.f
);
7739 if(t
<noFadeDuration
)
7742 mod
.Color
.A
= 255-(uint8
)((t
-noFadeDuration
)*255.0/fadeDuration
);
7744 // Display the hp modifier. display with a X offset according if user or not, for more readability
7745 sint deltaX
= -pIM
->FlyingTextManager
.getOffsetXForCharacter();
7746 if(UserEntity
&& UserEntity
->slot()==slot())
7748 pIM
->FlyingTextManager
.addFlyingText(&mod
, hpModifier
, pos
, mod
.Color
, scale
, deltaX
);
7759 }// displayModifiers //
7762 //---------------------------------------------------
7765 //---------------------------------------------------
7766 void CCharacterCL::drawPath() // virtual
7770 CVector pl0
= pos();
7771 CVector pl1
= pos()+CVector(0.f
, 0.f
, 2.f
);
7772 line
= CLine(pl0
, pl1
);
7773 line
.Color0
= CRGBA(150,0,255);
7774 line
.Color1
= CRGBA(150,0,255);
7775 Driver
->drawLine(line
, GenericMat
);
7777 line
= CLine(_PositionLimiter
, _PositionLimiter
+CVector(0.f
, 0.f
, 2.f
));
7778 line
.Color0
= CRGBA(255,64,128);
7779 line
.Color1
= CRGBA(255,64,128);
7780 Driver
->drawLine(line
, GenericMat
);
7786 line
= CLine(p0
, p0
+front());
7787 line
.Color0
= CRGBA(0,255,0);
7788 line
.Color1
= CRGBA(0,255,0);
7789 Driver
->drawLine(line
, GenericMat
);
7792 line
= CLine(p0
, p0
+dir());
7793 line
.Color0
= CRGBA(255,255,0);
7794 line
.Color1
= CRGBA(255,255,0);
7795 Driver
->drawLine(line
, GenericMat
);
7797 // Go to the First Stage.
7798 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
7799 // Compute the distance over all Stages.
7800 while(it
!= _Stages
._StageSet
.end())
7802 // Compute Distance.
7804 if((*it
).second
.getPos(stagePos
))
7806 CVector p1
= stagePos
;
7808 p2
.z
= (float)stagePos
.z
;
7809 p2
= p2
+ (stagePos
-p2
).normed();
7811 getCollisionEntity()->snapToGround(p1
);
7813 getCollisionEntity()->snapToGround(p2
);
7816 line
= CLine(p0
, p2
);
7817 line
.Color0
= CRGBA(0,0,255);
7818 line
.Color1
= CRGBA(0,0,255);
7819 Driver
->drawLine(line
, GenericMat
);
7822 line
= CLine(p0
, p1
);
7823 line
.Color0
= CRGBA(255,0,0);
7824 line
.Color1
= CRGBA(255,0,0);
7825 Driver
->drawLine(line
, GenericMat
);
7836 //---------------------------------------------------
7838 // Draw the selection Box.
7839 //---------------------------------------------------
7840 void CCharacterCL::drawBox() // virtual
7842 if(!ClientCfg
.Light
)
7843 ::drawBox(_Aabbox
.getMin(), _Aabbox
.getMax(), CRGBA(0,250,0));
7845 // Draw the PACS box (adjust the color according to the PACS valid or not).
7846 NLMISC::CAABBox PACSBox
= _Aabbox
;
7847 CVector halfSize
= PACSBox
.getHalfSize();
7848 halfSize
.x
= 0; halfSize
.y
= 0;
7849 PACSBox
.setCenter(_FinalPacsPos
+halfSize
);
7850 UGlobalPosition gPos
;
7852 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
7853 ::drawBox(PACSBox
.getMin(), PACSBox
.getMax(), ((gPos
.InstanceId
== -1) && (T1
%1000)>500)?CRGBA(255,0,0):CRGBA(0,250,250));
7855 if(!ClientCfg
.Light
)
7857 ::drawBox(selectBox().getMin(), selectBox().getMax(), CRGBA(250,250,0));
7858 // Draw the clip Sphere
7859 CVector clipPos
= _Aabbox
.getCenter();
7860 clipPos
.z
+= _ClipDeltaZ
- _Aabbox
.getHalfSize().z
; // _ClipDeltaZ is relative to pos on ground
7861 ::drawSphere(clipPos
, _ClipRadius
, CRGBA(0,0,250));
7865 //---------------------------------------------------
7867 // Return the selection box.
7868 //---------------------------------------------------
7869 const NLMISC::CAABBox
&CCharacterCL::selectBox() // virtual
7871 // recompute the selection box?
7872 if(_LastSelectBoxComputeTime
<T1
)
7874 _LastSelectBoxComputeTime
=T1
;
7877 // if skeleton, compute aabox from precise skeleton method
7878 if(!_Skeleton
.empty())
7880 // Don't compute if in LOD form (else flick because sometimes valid because of shadow animation)
7881 if(!_Skeleton
.isDisplayedAsLodCharacter() &&
7882 _Skeleton
.computeRenderedBBoxWithBoneSphere(_SelectBox
))
7885 // else compute from static bot object
7889 // try with _Instances array first
7890 if(!_Instances
.empty())
7891 inst
= _Instances
[0].Current
;
7892 // Fallback to _Instance (??)
7896 // if static instance found
7900 inst
.getShapeAABBox(bbox
);
7901 // if supported (ie bbox not null)
7902 if(bbox
.getHalfSize()!=CVector::Null
)
7904 // Transform bbox to world
7905 _SelectBox
= CAABBox::transformAABBox(inst
.getLastWorldMatrixComputed(), bbox
);
7911 // if not found, fallback to default bbox
7913 _SelectBox
= _Aabbox
;
7916 // Return the selection box.
7921 //---------------------------------------------------
7922 // updateAttachedFXListForSlotRemoved :
7923 //---------------------------------------------------
7924 void CCharacterCL::updateAttachedFXListForSlotRemoved(std::list
<CAttachedFX::TSmartPtr
> &fxList
, const CLFECOMMON::TCLEntityId
&slotRemoved
)
7926 std::list
<CAttachedFX::TSmartPtr
>::iterator it
= fxList
.begin();
7927 while (it
!= fxList
.end())
7929 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpIt
= it
;
7931 if ((*tmpIt
)->StickMode
&&
7932 ((*tmpIt
)->StickMode
== CFXStickMode::OrientedTowardTargeter
||
7933 (*tmpIt
)->StickMode
== CFXStickMode::UserBoneOrientedTowardTargeter
||
7934 (*tmpIt
)->StickMode
== CFXStickMode::UserBoneRay
7938 if ((*tmpIt
)->TargeterInfo
.Slot
== slotRemoved
)
7940 fxList
.erase(tmpIt
);
7946 //---------------------------------------------------
7948 // To Inform about an entity removed (to remove from selection for example).
7949 // This will remove the entity from the target.
7950 // \param slot : Slot of the entity that will be removed.
7951 //---------------------------------------------------
7952 void CCharacterCL::slotRemoved(const CLFECOMMON::TCLEntityId
&slotRemoved
)
7954 // If the target is the entity that will be removed -> no target
7955 if(_TargetSlot
== slotRemoved
)
7956 _TargetSlot
= CLFECOMMON::INVALID_SLOT
;
7957 // invalidate targeter slots in anim fxs
7958 updateAttachedFXListForSlotRemoved(_AttachedFXListForCurrentAnim
, slotRemoved
);
7959 updateAttachedFXListForSlotRemoved(_AttachedFXListToRemove
, slotRemoved
);
7962 //---------------------------------------------------
7964 // Return number of stage remaining.
7965 //---------------------------------------------------
7966 uint
CCharacterCL::nbStage()
7968 return (uint
)_Stages
._StageSet
.size();
7972 //---------------------------------------------------
7974 // Method to return the attack radius of an entity (take the scale into account).
7975 //---------------------------------------------------
7976 double CCharacterCL::attackRadius() const
7978 return _Sheet
->DistToFront
* getScale();
7981 //---------------------------------------------------
7983 // Return the position the attacker should have to combat according to the attack angle.
7984 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
7985 // \todo : GUIGUI precalculate entity matrix
7986 //---------------------------------------------------
7987 CVectorD
CCharacterCL::getAttackerPos(double ang
, double dist
) const
7989 // Compute the local angle
7990 ang
= computeShortestAngle(atan2(front().y
, front().x
), ang
);
7995 // Compute the local position.
7997 float distToSide
, distToFront
, distToBack
;
7998 bool useComplexShape
= false;
7999 if (useComplexShape
) // Keep this code for when AIS become complex shape aware
8001 distToSide
= _Sheet
->DistToSide
;
8002 distToFront
= _Sheet
->DistToFront
;
8003 distToBack
= _Sheet
->DistToBack
;
8005 else // use round shape here
8007 distToSide
= _Sheet
->ColRadius
;
8008 distToFront
= _Sheet
->ColRadius
;
8009 distToBack
= _Sheet
->ColRadius
;
8011 p
.x
= getScale()*distToSide
*sin(-ang
) + dist
*sin(-ang
); // or: pos.x = _Sheet->DistToSide*cos(ang) + dist*cos(ang); but 0 should be right side.
8012 p
.y
= dist
*cos(ang
);
8013 if(fabs(ang
) <= Pi
/2.0)
8014 p
.y
+= getScale()*distToFront
* cos(ang
);
8016 p
.y
+= getScale()*distToBack
* cos(ang
);
8020 // Compute the world position.
8021 // Create the target matrix.
8022 CVector vj
= front();
8027 bodyBase
.setRot(vi
,vj
,vk
,true);
8028 bodyBase
.setPos(pos());
8030 // Get the destination in the world.
8031 return bodyBase
* p
;
8032 }// getAttackerPos //
8034 //---------------------------------------------------
8035 // isPlacedToFight :
8036 // Return true if the opponent is well placed.
8037 //---------------------------------------------------
8038 bool CCharacterCL::isPlacedToFight(const NLMISC::CVectorD
&posAtk
, const NLMISC::CVector
&dirAtk
, double attackerRadius
) const // virtual
8040 NLMISC::CVectorD vDist
= pos()-posAtk
;
8041 if(vDist
!= NLMISC::CVectorD::Null
)
8043 // Attacker Distance
8044 const double distToAttacker
= vDist
.norm();
8045 // Get the Ideal Position
8047 CVectorD rightPos
= getAttackerPos(atan2(vDist
.y
, vDist
.x
), attackerRadius
);
8048 // Vector from the Ideal Position
8049 NLMISC::CVectorD vDist2
= pos()-rightPos
;
8050 // Check the Distance.
8051 if(distToAttacker
<= vDist2
.norm()+ClientCfg
.FightAreaSize
)
8053 // Check the orientation.
8054 NLMISC::CVector vAng
= dirAtk
;
8057 return (fabs(angleBetween2Vect(vAng
, vDist
)) <= NLMISC::Pi
/3.0);
8060 // User is on the target, do not check dist or angle
8065 // NLMISC::CVectorD vDist = pos()-posAtk;
8066 // const double dist = vDist.norm();
8068 // // Get current entity radius
8070 // radius = _Primitive->getRadius();
8073 // // Attack is possible if not too close or too far.
8074 // if(dist>=radius && dist<=(radius+ClientCfg.FightAreaSize))
8077 // NLMISC::CVector vAng = dirAtk;
8080 // return (fabs(angleBetween2Vect(vAng, vDist)) <= NLMISC::Pi/3.0);
8083 }// isPlacedToFight //
8086 //---------------------------------------------------
8087 // \param pos : result given in this variable. Only valid if return 'true'.
8088 // \return bool : 'true' if the 'pos' has been filled.
8089 //---------------------------------------------------
8090 bool CCharacterCL::getNamePos(CVectorD
&pos
) // virtual
8092 // If there is no skeleton -> cannot display the name.
8093 if(_Skeleton
.empty())
8096 // If the entity was not displayed last frame (clipped) -> do not display the name.
8097 if(!_Skeleton
.getLastClippedState())
8100 if(_NameBoneId
== -1)
8103 // Take x and y in pos() else we a have a frame late.
8104 pos
.x
= this->pos().x
;
8105 pos
.y
= this->pos().y
;
8108 if (ClientCfg
.StaticNameHeight
)
8109 namePosZ
= getNamePosZ();
8113 // use bone position if no name position is given
8114 if (namePosZ
== 0.f
)
8116 // if displayed as lod, the NameId bone may not be computed
8117 float skeletonZ
= _Skeleton
.getLastWorldMatrixComputed().getPos().z
;
8118 if(_Skeleton
.isDisplayedAsLodCharacter())
8120 // if never computed
8121 if(_NameCLodDeltaZ
==NameCLodDeltaZNotComputed
)
8123 _Skeleton
.forceComputeBone(_NameBoneId
);
8124 float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8125 _NameCLodDeltaZ
= boneZ
- skeletonZ
;
8127 pos
.z
= skeletonZ
+ _NameCLodDeltaZ
;
8131 float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8133 // update delta Z, for when enter in CLod form
8134 _NameCLodDeltaZ
= boneZ
- skeletonZ
;
8137 // reset name pos history
8138 if (_NamePosHistory
.isInitialized())
8140 _NamePosHistory
.LastNamePosZ
= 0.f
;
8141 _NamePosHistory
.LastBonePosZ
= 0.f
;
8147 const float baseZ
= float( this->pos().z
);
8149 // if displayed as lod, skip smooth transition stuff
8150 if (_Skeleton
.isDisplayedAsLodCharacter())
8152 pos
.z
= baseZ
+ namePosZ
;
8154 // reset name pos history
8155 if (_NamePosHistory
.isInitialized())
8157 _NamePosHistory
.LastNamePosZ
= 0.f
;
8158 _NamePosHistory
.LastBonePosZ
= 0.f
;
8164 const float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8166 float deltaNamePosZ
;
8167 float deltaBonePosZ
;
8168 if (_NamePosHistory
.isInitialized())
8170 deltaNamePosZ
= namePosZ
- _NamePosHistory
.LastNamePosZ
;
8171 deltaBonePosZ
= (boneZ
- baseZ
) - _NamePosHistory
.LastBonePosZ
;
8175 deltaNamePosZ
= 0.f
;
8176 deltaBonePosZ
= 0.f
;
8179 if (deltaNamePosZ
!= 0.f
)
8181 // generate a smooth transition following the bone movement
8182 if (deltaBonePosZ
!= 0.f
&& (deltaBonePosZ
> 0.f
) == (deltaNamePosZ
> 0.f
))
8184 namePosZ
= _NamePosHistory
.LastNamePosZ
+ deltaBonePosZ
;
8188 const float defaultSpeed
= 1.f
; // in meters per sec.
8189 float deltaZ
= defaultSpeed
* DT
;
8190 if (deltaNamePosZ
< 0.f
)
8193 if ( fabs(deltaZ
) < fabs(deltaNamePosZ
) )
8194 namePosZ
= _NamePosHistory
.LastNamePosZ
+ deltaZ
;
8198 pos
.z
= baseZ
+ namePosZ
;
8201 _NamePosHistory
.LastNamePosZ
= namePosZ
;
8202 _NamePosHistory
.LastBonePosZ
= boneZ
- baseZ
;
8207 //---------------------------------------------------
8208 // Return name position on Z axis defined in sheet
8209 //---------------------------------------------------
8210 float CCharacterCL::getNamePosZ() const
8216 switch (_ModeWanted
)
8221 namePosZ
= _Sheet
->NamePosZLow
;
8224 case MBEHAV::MOUNT_NORMAL
:
8225 case MBEHAV::MOUNT_SWIM
:
8226 namePosZ
= _Sheet
->NamePosZHigh
;
8230 namePosZ
= _Sheet
->NamePosZNormal
;
8234 if (namePosZ
== 0.f
)
8235 namePosZ
= _Sheet
->NamePosZNormal
;
8237 return namePosZ
* getScale();
8240 //---------------------------------------------------
8241 // \param pos : result given in this variable. Only valid if return 'true'.
8242 // \return bool : 'true' if the 'pos' has been filled.
8243 //---------------------------------------------------
8244 bool CCharacterCL::getChestPos(CVectorD
&pos
) const // virtual
8246 // If there is no skeleton -> cannot display the Chest.
8247 if(_Skeleton
.empty())
8250 // If the entity was not displayed last frame (clipped) -> do not display the Chest.
8251 if(!_Skeleton
.getLastClippedState())
8254 if(_ChestBoneId
== -1)
8257 // Take x and y in pos() else we a have a frame late.
8258 pos
.x
= this->pos().x
;
8259 pos
.y
= this->pos().y
;
8260 pos
.z
= _Skeleton
.getBone(_ChestBoneId
).getLastWorldMatrixComputed().getPos().z
;
8265 //---------------------------------------------------
8267 // Return the entity sheet scale. (return 1.0 if there is any problem).
8268 //---------------------------------------------------
8269 float CCharacterCL::getSheetScale() const // virtual
8274 return _Sheet
->Scale
;
8276 } // getSheetScale //
8279 //---------------------------------------------------
8281 // Return the entity collision radius. (return 0.5 if there is any problem).
8282 //---------------------------------------------------
8283 float CCharacterCL::getSheetColRadius() const
8288 return _Sheet
->ColRadius
;
8292 //---------------------------------------------------
8294 // Return the entity scale. (return 1.0 if there is any problem).
8295 //---------------------------------------------------
8296 float CCharacterCL::getScale() const // virtual
8298 switch( _OwnerPeople
)
8300 case MOUNT_PEOPLE::Fyros
: return getSheetScale() * ClientCfg
.FyrosScale
* _CustomScale
;
8301 case MOUNT_PEOPLE::Matis
: return getSheetScale() * ClientCfg
.MatisScale
* _CustomScale
;
8302 case MOUNT_PEOPLE::Tryker
: return getSheetScale() * ClientCfg
.TrykerScale
* _CustomScale
;
8303 case MOUNT_PEOPLE::Zorai
: return getSheetScale() * ClientCfg
.ZoraiScale
* _CustomScale
;
8305 return getSheetScale() * _CustomScale
;
8316 //---------------------------------------------------
8317 // currentAnimationName :
8318 // Return the current animation name.
8319 //---------------------------------------------------
8320 const std::string
&CCharacterCL::currentAnimationName() const
8324 uint idCurrentAnimation
= _PlayList
->getAnimation(MOVE
);
8325 if(idCurrentAnimation
!= UPlayList::empty
)
8326 if(EAM
&& EAM
->getAnimationSet())
8327 return EAM
->getAnimationSet()->getAnimationName(idCurrentAnimation
);
8330 // No animation yet.
8331 return CCharacterCL::_EmptyString
;
8332 }// currentAnimationName //
8334 //---------------------------------------------------
8335 // currentAnimationSetName :
8336 // Return the current animation set name.
8337 //---------------------------------------------------
8338 std::string
CCharacterCL::currentAnimationSetName(TAnimationType animType
) const
8340 if( animType
< animTypeCount
)
8342 if( uint(animType
) < _CurrentAnimSet
.size() )
8344 if( _CurrentAnimSet
[animType
] )
8346 return _CurrentAnimSet
[animType
]->getSheetName();
8350 return CCharacterCL::_EmptyString
;
8351 }// currentAnimationSetName //
8355 //---------------------------------------------------
8357 // Return the shape pointer from tha item and according to the character gender.
8358 // \param itemSheet : reference on the item sheet.
8359 // \return string & : reference on the shape name.
8360 //---------------------------------------------------
8361 std::string
CCharacterCL::shapeFromItem(const CItemSheet
&itemSheet
) const
8365 if(_Gender
== GSGENDER::male
)
8368 switch(_Sheet
->Race
)
8370 case EGSPD::CPeople::Fyros
:
8371 sheet
= itemSheet
.getShapeFyros();
8373 case EGSPD::CPeople::Matis
:
8374 sheet
= itemSheet
.getShapeMatis();
8376 case EGSPD::CPeople::Tryker
:
8377 sheet
= itemSheet
.getShapeTryker();
8379 case EGSPD::CPeople::Zorai
:
8380 sheet
= itemSheet
.getShapeZorai();
8387 switch(_Sheet
->Race
)
8389 case EGSPD::CPeople::Fyros
:
8390 sheet
= itemSheet
.getShapeFyrosFemale();
8392 case EGSPD::CPeople::Matis
:
8393 sheet
= itemSheet
.getShapeMatisFemale();
8395 case EGSPD::CPeople::Tryker
:
8396 sheet
= itemSheet
.getShapeTrykerFemale();
8398 case EGSPD::CPeople::Zorai
:
8399 sheet
= itemSheet
.getShapeZoraiFemale();
8403 sheet
= itemSheet
.getShapeFemale();
8406 sheet
= itemSheet
.getShape();
8410 }// shapeFromItem //
8413 //---------------------------------------------------
8414 // createItemInstance :
8415 // Create the instance from an item
8416 //---------------------------------------------------
8417 uint32
CCharacterCL::createItemInstance(const CItemSheet
&itemSheet
, uint32 instIdx
, SLOTTYPE::EVisualSlot visualSlot
, const string
&bindBone
, sint8 texture
, sint color
)
8419 uint32 idx
= CEntityCL::BadIndex
;
8420 // Get the right shape according to the gender of the character.
8421 const string
&shape
= shapeFromItem(itemSheet
);
8426 // Check the item need a shape.
8427 if(shape
!= "none.shape")
8431 idx
= addColoredInstance(shape
, bindBone
, texture
, instIdx
, color
);
8432 SInstanceCL
*pInst
= idx2Inst(idx
);
8433 nlassert( (pInst
== NULL
) || (pInst
!= NULL
&& !pInst
->Loading
.empty()) );
8435 instance
= pInst
->Loading
;
8436 // Check the shape creation has been is well done.
8437 if(!instance
.empty())
8439 // Create the FX associated to the item in a given visual slot.
8440 _Items
[visualSlot
].initFXs(visualSlot
, instance
);
8443 nlwarning("CH:createItemInstance: cannot create the instance for the shape '%s'.", shape
.c_str());
8447 nlwarning("CH:createItemInstance: the item has no shape.");
8450 }// createItemInstance //
8453 //-----------------------------------------------
8455 // Method to Flag the character as alive and do everything needed.
8456 //-----------------------------------------------
8457 void CCharacterCL::setAlive() // virtual
8463 //---------------------------------------------------
8465 // Display Debug Information.
8466 //---------------------------------------------------
8467 ADD_METHOD(void CCharacterCL::displayDebug(float x
, float &y
, float lineStep
)) // virtual
8468 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
8470 CEntityCL::displayDebug(x
, y
, lineStep
);
8472 // Display the Target Mode.
8473 TextContext
->printfAt(x
, y
, "Mode Wanted: %d(%s)", (sint
)_ModeWanted
, MBEHAV::modeToString(_ModeWanted
).c_str());
8476 TextContext
->printfAt(x
, y
, "Stages remaining: %d", _Stages
._StageSet
.size());
8478 // Current Automaton
8479 TextContext
->printfAt(x
, y
, "Automaton: %s", _CurrentAutomaton
.c_str());
8482 TextContext
->printfAt(x
, y
, "Speed: %f (_DestTime(%f) - _LastFrameTime(%f)) = %f", speed(), _DestTime
, _LastFrameTime
, _DestTime
-_LastFrameTime
);
8484 // Display the Run Factor.
8485 TextContext
->printfAt(x
, y
, "(Walk)Run Factor: %f", runFactor());
8487 // Display the current animation name(id)(offset)(nbloop) for channel MOVE.
8488 TextContext
->printfAt(x
, y
, "Current Animation: %s(%u)(%lf)(%u loops)", animId(MOVE
)==std::numeric_limits
<uint
>::max()?"[NONE]":currentAnimationName().c_str(), animId(MOVE
), animOffset(MOVE
), _NbLoopAnim
);
8492 TextContext
->printfAt(x
, y
, "No Position Received", _First_Pos
);
8494 TextContext
->printfAt(x
, y
, "At least 1 Position Received", _First_Pos
);
8497 TextContext
->printfAt(x
, y
, "Prim Ptr: %p", _Primitive
);
8499 // Primitive Position
8502 CVectorD primFinalPos
= _Primitive
->getFinalPosition(dynamicWI
);
8503 TextContext
->printfAt(x
, y
, "Prim Pos: %f %f %f", primFinalPos
.x
, primFinalPos
.y
, primFinalPos
.z
);
8506 // Skeleton Ptr, Animset Ptr and Current State Ptr
8507 TextContext
->printfAt(x
, y
, "Skel Ptr: %p - AnimSet Ptr: %p - State Ptr: %p", &_Skeleton
, _CurrentAnimSet
[MOVE
], _CurrentState
);
8509 // Display the target mount and rider.
8510 TextContext
->printfAt(x
, y
, "Mount: %3u(Theoretical: %3u) Rider: %3u(Theoretical: %3u)", mount(), _TheoreticalMount
, rider(), _TheoreticalRider
);
8513 sint64 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA
))->getValue64();
8514 if(isPlayer() || isUser())
8516 SPropVisualA visualA
= *(SPropVisualA
*)(&prop
);
8517 TextContext
->printfAt(x
, y
, "VPA: %" NL_I64
"X : Chest(%d,%d) Legs(%d,%d) Arms(%d,%d) Hat(%d,%d) RH(%d) LH(%d)", prop
,
8518 (uint
)visualA
.PropertySubData
.JacketModel
, (uint
)visualA
.PropertySubData
.JacketColor
,
8519 (uint
)visualA
.PropertySubData
.TrouserModel
, (uint
)visualA
.PropertySubData
.TrouserColor
,
8520 (uint
)visualA
.PropertySubData
.ArmModel
, (uint
)visualA
.PropertySubData
.ArmColor
,
8521 (uint
)visualA
.PropertySubData
.HatModel
, (uint
)visualA
.PropertySubData
.HatColor
,
8522 (uint
)visualA
.PropertySubData
.WeaponRightHand
,
8523 (uint
)visualA
.PropertySubData
.WeaponLeftHand
);
8526 TextContext
->printfAt(x
, y
, "VPA: %" NL_I64
"X", prop
);
8529 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB
))->getValue64();
8530 if(isPlayer() || isUser())
8532 SPropVisualB visualB
= *(SPropVisualB
*)(&prop
);
8533 TextContext
->printfAt(x
, y
, "VPB: %" NL_I64
"X : Hands(%d,%d) Feet(%d,%d).", prop
,
8534 (uint
)visualB
.PropertySubData
.HandsModel
, (uint
)visualB
.PropertySubData
.HandsColor
,
8535 (uint
)visualB
.PropertySubData
.FeetModel
, (uint
)visualB
.PropertySubData
.FeetColor
);
8538 TextContext
->printfAt(x
, y
, "VPB: %" NL_I64
"X", prop
);
8541 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPC
))->getValue64();
8542 if(isPlayer() || isUser())
8544 SPropVisualC visualC
= *(SPropVisualC
*)(&prop
);
8545 TextContext
->printfAt(x
, y
, "VPC: %" NL_I64
"X : EyesColor(%d) Tattoo(%d).", prop
, visualC
.PropertySubData
.EyesColor
, visualC
.PropertySubData
.Tattoo
);
8548 TextContext
->printfAt(x
, y
, "VPC: %" NL_I64
"X", prop
);
8554 //-----------------------------------------------
8555 // displayDebugPropertyStages
8556 //-----------------------------------------------
8557 void CCharacterCL::displayDebugPropertyStages(float x
, float &y
, float lineStep
)
8559 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
8560 for(;it
!=_Stages
._StageSet
.end();it
++)
8562 CStage
&stage
= it
->second
;
8563 uint32 gc
= it
->first
% 100;
8564 // build the string of props present in this stage
8566 for(uint i
=0;i
<CLFECOMMON::NB_VISUAL_PROPERTIES
;i
++)
8568 if(i
!=CLFECOMMON::PROPERTY_POSY
&& i
!=CLFECOMMON::PROPERTY_POSZ
&& stage
.isPresent(i
))
8569 strProps
+= string(CLFECOMMON::getPropShortText(i
)) + " ";
8571 TextContext
->printfAt(x
, y
, "%02d %s", gc
, strProps
.c_str());
8577 //---------------------------------------------------
8579 // Read/Write Variables from/to the stream.
8580 //---------------------------------------------------
8581 void CCharacterCL::readWrite(NLMISC::IStream
&f
)
8583 CEntityCL::readWrite(f
);
8589 // std::vector<CAnimation::TAnimId> _Animations;
8590 // std::vector<NLSOUND::TSoundAnimId> _SoundId;
8591 // NLSOUND::CSoundContext _SoundContext;
8592 // std::vector<double> _AnimationsTimeOffset;
8593 // std::vector<CAnimationState::TAnimStateId> _AnimationsStateKey;
8594 // const CAnimationSet *_CurrentAnimSet;
8595 f
.serial(_LastFrameTime
);
8596 f
.serial(_LodCharacterAnimEnabled
);
8597 f
.serial(_LodCharacterAnimTimeOffset
);
8598 // uint _LodCharacterMasterAnimSlot;
8599 f
.serial(_CharacterScalePos
);
8600 f
.serial(_FirstPos
);
8601 f
.serial(_FirstTime
);
8602 f
.serial(_DistToFirst
);
8604 f
.serial(_DestTime
);
8605 f
.serial(_DistToDest
);
8607 f
.serial(_OldPosTime
);
8608 // GSGENDER::EGender _Gender;
8609 // sint _NameBoneId;
8610 // NL3D::UTransform _NameTransform;
8611 // std::vector<CItemSheet *> _Items;
8614 // sint _HeadBoneId;
8615 f
.serial(_RotationFactor
);
8616 f
.serial(_DirEndAnim
);
8617 f
.serial(_RotAngle
);
8618 f
.serial(_CurrentAutomaton
);
8619 // const CAutomatonStateSheet *_CurrentState;
8620 // MBEHAV::EMode _ModeWanted;
8621 // sint _BlendRemaining;
8622 f
.serial(_OldAutomaton
);
8623 f
.serial(_OldRotQuat
);
8624 f
.serial(_CustomScalePos
);
8625 // TTime _NextBlinkTime;
8626 f
.serial(_NbLoopAnim
);
8627 // std::vector<CFXStruct *> _FXs;
8628 // std::list<UParticleSystemInstance> _CurrentAnimFXList;
8629 // std::list<UParticleSystemInstance> _RemoveAnimFXList;
8630 // NL3D::UParticleSystemInstance _CurrentAnimFX;
8631 f
.serial(_RightFXActivated
);
8632 f
.serial(_LeftFXActivated
);
8633 // sint _IndexRightFX;
8634 // sint _IndexLeftFX;
8637 f
.serial(_IsThereAMode
);
8638 f
.serial(_HairColor
);
8639 f
.serial(_EyesColor
);
8640 f
.serial(_HairIndex
);
8643 f
.serial(_RunFactor
);
8646 // uint32 _RHandInstIdx;
8647 // uint32 _LHandInstIdx;
8650 //---------------------------------------------------
8652 // To call after a read from a stream to re-initialize the entity.
8653 //---------------------------------------------------
8654 void CCharacterCL::load() // virtual
8656 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
8658 // If the entity should be in the world already
8659 if(_First_Pos
== false)
8661 // Insert the primitive into the world.
8663 _Primitive
->insertInWorldImage(dynamicWI
);
8664 // Insert the entity into PACS
8671 // Visual properties A
8672 _HeadIdx
= CEntityCL::BadIndex
;
8673 sint64 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA
))->getValue64();
8674 updateVisualPropertyVpa(0, prop
);
8678 //---------------------------------------------------
8680 //---------------------------------------------------
8681 void CCharacterCL::buildPlaylist()
8684 // Release the old animation playlist.
8687 EAM
->deletePlayList(_PlayList
);
8690 // Create the new animation playlist.
8691 _PlayList
= EAM
->createPlayList();
8694 nlwarning("Cannot create a playlist for the entity.");
8697 // Register the skeleton to the playlist.
8698 _PlayList
->registerTransform(_Skeleton
);
8699 // Animation should not move alone.
8700 uint posChannel
= EAM
->getAnimationSet()->getChannelIdByName("pos");
8701 if(posChannel
!= NL3D::UAnimationSet::NotFound
)
8702 _PlayList
->enableChannel(posChannel
, false);
8704 nlwarning("Channel 'pos' not found.");
8705 // Animation should not rotate alone.
8706 uint rotquatChannel
= EAM
->getAnimationSet()->getChannelIdByName("rotquat");
8707 if(rotquatChannel
!= NL3D::UAnimationSet::NotFound
)
8708 _PlayList
->enableChannel(rotquatChannel
, false);
8710 nlwarning("Channel 'rotquat' not found.");
8711 // Initialize the new playlist.
8713 _PlayList
->setSpeedFactor (MOVE
, 1.f
);
8714 _PlayList
->setWrapMode (MOVE
, NL3D::UPlayList::Clamp
);
8716 _PlayList
->setAnimation (ACTION
, NL3D::UPlayList::empty
);
8717 _PlayList
->setSpeedFactor (ACTION
, 1.f
);
8718 _PlayList
->setWrapMode (ACTION
, NL3D::UPlayList::Clamp
);
8719 // Compute the current animation state.
8720 _CurrentState
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
8721 if(_CurrentState
== 0)
8723 _PlayList
->setAnimation(MOVE
, NL3D::UPlayList::empty
);
8726 // Get the right animation state and choose an animation.
8728 // Get the animation state
8729 const CAnimationState
*animationState
= 0;
8730 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
8731 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
8733 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
8736 // Choose the animation
8737 animIndex(MOVE
, animationState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), 0.0));
8738 // Should the objects in hands be displayed ?
8739 _ObjectsVisible
= animationState
->areObjectsVisible();
8743 _PlayList
->setAnimation(MOVE
, animId(MOVE
));
8744 }// buildPlaylist //
8749 //-----------------------------------------------
8751 // Return the entity speed
8752 //-----------------------------------------------
8753 double CCharacterCL::getSpeed() const // virtual
8758 //-----------------------------------------------
8760 // Set the Distance from the current entity position to the First Position.
8761 //-----------------------------------------------
8762 void CCharacterCL::dist2FirstPos(double d2FP
)
8764 CHECK((d2FP
== INVALID_DIST
) || (d2FP
== 0.0) || (d2FP
>= ClientCfg
.DestThreshold
));
8765 _DistToFirst
= d2FP
;
8766 }// dist2FirstPos //
8767 //-----------------------------------------------
8769 // Return the Distance from the current entity position to the First Position.
8770 //-----------------------------------------------
8771 double CCharacterCL::dist2FirstPos() const
8773 CHECK((_DistToFirst
== INVALID_DIST
) || (_DistToFirst
== 0.0) || (_DistToFirst
>= ClientCfg
.DestThreshold
));
8774 return _DistToFirst
;
8775 }// dist2FirstPos //
8777 //-----------------------------------------------
8779 // Set the Distance from the current entity position to the Destination.
8780 //-----------------------------------------------
8781 void CCharacterCL::dist2Dest(double d2D
)
8783 CHECK((d2D
== INVALID_DIST
) || (d2D
== 0.0) || (d2D
>= ClientCfg
.DestThreshold
));
8787 //-----------------------------------------------
8789 // Return the Distance from the current entity position to the Destination.
8790 //-----------------------------------------------
8791 double CCharacterCL::dist2Dest() const
8793 CHECK((_DistToDest
== INVALID_DIST
) || (_DistToDest
== 0.0) || (_DistToDest
>= ClientCfg
.DestThreshold
));
8797 //-----------------------------------------------
8799 // Set the Entity Current Speed.
8800 //-----------------------------------------------
8801 void CCharacterCL::speed(double s
)
8803 CHECK((s
== -1.0) || (s
== 0.0) || ((s
>= 0.001) && (s
<= 1000.0)));
8807 //-----------------------------------------------
8809 // Return the Entity Current Speed.
8810 //-----------------------------------------------
8811 double CCharacterCL::speed() const // virtual
8813 CHECK((_Speed
== -1.0) || (_Speed
== 0.0) || ((_Speed
>= 0.001) && (_Speed
<= 1000.0)));
8817 //-----------------------------------------------
8818 // Build the in scene interface
8819 //-----------------------------------------------
8820 void CCharacterCL::buildInSceneInterface ()
8822 // Delete previous interface
8823 releaseInSceneInterfaces();
8825 _InSceneUserInterface
= CGroupInSceneUserInfo::build (this);
8828 CEntityCL::buildInSceneInterface();
8831 //-----------------------------------------------
8833 void CCharacterCL::setBubble (CGroupInSceneBubble
*bubble
)
8835 if (_CurrentBubble
!= NULL
)
8837 CGroupInSceneBubble
*old
= _CurrentBubble
;
8838 _CurrentBubble
= NULL
;
8841 nlassert (_CurrentBubble
== NULL
);
8842 _CurrentBubble
= bubble
;
8845 //-----------------------------------------------
8847 //-----------------------------------------------
8849 // Set the Factor between Walk & Run
8850 //-----------------------------------------------
8851 void CCharacterCL::runFactor(double factor
)
8853 CHECK((factor
>= 0.0) && (factor
<= 1.0));
8854 CHECK((factor
== 0.0) || (animState(MOVE
) == CAnimationStateSheet::Walk
));
8855 _RunFactor
= factor
;
8858 //-----------------------------------------------
8860 // Get the Factor between Walk & Run
8861 //-----------------------------------------------
8862 double CCharacterCL::runFactor() const
8864 CHECK((_RunFactor
>= 0.0) && (_RunFactor
<= 1.0));
8865 CHECK((_RunFactor
== 0.0) || (animState(MOVE
) == CAnimationStateSheet::Walk
));
8869 //-----------------------------------------------
8871 // Set the animation time offset for an animation channel.
8872 //-----------------------------------------------
8873 void CCharacterCL::animOffset(TAnimationType channel
, double timeOffset
)
8877 // Check the channel
8878 CHECK((uint
)channel
< _AnimOffset
.size());
8879 // Check Animation Time Offset is greater or equal to 0.
8880 CHECK(timeOffset
>= 0.0);
8881 // Check the animation time offset is not greater to the animation length.
8882 CHECK(timeOffset
<= EAM
->getAnimationLength(animId(channel
)));
8884 _AnimOffset
[channel
] = timeOffset
;
8887 //-----------------------------------------------
8889 // Return the animation time offset for an animation channel.
8890 //-----------------------------------------------
8891 double CCharacterCL::animOffset(TAnimationType channel
) const
8895 // Check the channel
8896 CHECK((uint
)channel
< _AnimOffset
.size());
8897 // Check Animation Time Offset is greater or equal to 0.
8898 CHECK(_AnimOffset
[channel
] >= 0.0);
8899 // Check the animation time offset is not greater to the animation length.
8900 CHECK(_AnimOffset
[channel
] <= EAM
->getAnimationLength(animId(channel
)));
8902 return _AnimOffset
[channel
];
8905 //---------------------------------------------------
8906 // animationStateKey :
8907 // Set the animation state key.
8908 // \todo GUIGUI : a remplacer par animState directement.
8909 //---------------------------------------------------
8910 bool CCharacterCL::animationStateKey(TAnimationType channel
, TAnimStateId value
)
8912 // Is the new key valid ?
8913 if(value
== CAnimationStateSheet::UnknownState
)
8915 nlwarning("CH::animationStateKey: Char '%d': new state key is Null.", _Slot
);
8919 animState(channel
, value
);
8920 // Debug Animation for the selection
8921 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
8922 nlinfo("CH:animationStateKey:%d: state '%s'.", _Slot
, CAnimationState::getAnimationStateName(value
).c_str());
8925 }// animationStateKey //
8927 //-----------------------------------------------
8929 // Set the Animation 'State' for an animation channel.
8930 //-----------------------------------------------
8931 void CCharacterCL::animState(TAnimationType channel
, TAnimStateId state
)
8933 // Check the channel
8934 CHECK((uint
)channel
< _AnimState
.size());
8935 // Set the new State
8936 _AnimState
[channel
] = state
;
8937 // Reset the Run Factor when the state change.
8941 //-----------------------------------------------
8943 // Get the Animation 'State' for an animation channel.
8944 //-----------------------------------------------
8945 TAnimStateId
CCharacterCL::animState(TAnimationType channel
) const
8947 // Check the channel
8948 CHECK((uint
)channel
< _AnimState
.size());
8949 // Return the Animation State
8950 return _AnimState
[channel
];
8953 //-----------------------------------------------
8955 // Set the Animation 'Index' in the 'State' for an animation channel.
8956 //-----------------------------------------------
8957 void CCharacterCL::animIndex(TAnimationType channel
, CAnimation::TAnimId index
)
8959 // Check the channel
8960 CHECK((uint
)channel
< _AnimState
.size());
8961 // Set the animation index in the state
8962 _AnimIndex
[channel
] = index
;
8963 // Set the animation Id
8964 // If the current animation Index is not a valid one, return empty
8965 if(_AnimIndex
[channel
] == CAnimation::UnknownAnim
)
8966 animId(channel
, NL3D::UPlayList::empty
);
8969 // Check the AnimSet needed to get the animation Id.
8970 CHECK(_CurrentAnimSet
[channel
] != NULL
);
8971 // Get the Pointer on the animation state, if Null, return empty
8972 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[channel
]->getAnimationState( (animState(channel
)==CAnimationStateSheet::Emote
)?_SubStateKey
:animState(channel
));
8973 if(animStatePtr
== 0)
8974 animId(channel
, NL3D::UPlayList::empty
);
8977 // Get the Animation Pointer, if Null, return Empty
8978 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(channel
));
8980 animId(channel
, NL3D::UPlayList::empty
);
8981 // Return The Animation ID
8983 animId(channel
, anim
->id());
8988 //-----------------------------------------------
8990 // Get the Animation 'Index' in the 'State' for an animation channel.
8991 //-----------------------------------------------
8992 CAnimation::TAnimId
CCharacterCL::animIndex(TAnimationType channel
) const
8994 // Check the channel
8995 CHECK((uint
)channel
< _AnimState
.size());
8996 // Return the Animation Index in the State
8997 return _AnimIndex
[channel
];
9000 //-----------------------------------------------
9002 // Set the Animation 'Id' among all the animations for an animation channel.
9003 //-----------------------------------------------
9004 void CCharacterCL::animId(TAnimationType channel
, uint id
)
9006 // Check the channel
9007 CHECK((uint
)channel
< _AnimId
.size());
9009 _AnimId
[channel
] = id
;
9012 //-----------------------------------------------
9014 // Get the Animation 'Id' among all the animations for an animation channel.
9015 //-----------------------------------------------
9016 uint
CCharacterCL::animId(TAnimationType channel
) const
9018 // Check the channel
9019 CHECK((uint
)channel
< _AnimId
.size());
9021 return _AnimId
[channel
];
9026 //-----------------------------------------------
9027 // Align the given FX so that it is oriented like that entity
9028 //-----------------------------------------------
9029 void CCharacterCL::alignFX(UParticleSystemInstance instance
, float scale
/* = 1.f */, const NLMISC::CVector
&localOffset
/*= NLMISC::CVector::Null*/) const
9031 // copy matrix from parent
9033 fxMatrix
.identity();
9034 buildAlignMatrix(fxMatrix
);
9035 alignFX(instance
, fxMatrix
, scale
, localOffset
);
9039 void CCharacterCL::alignFX(UParticleSystemInstance instance
, const CMatrix
&matrix
, float scale
/*=1.f*/, const NLMISC::CVector
&localOffset
/*=NLMISC::CVector::Null*/) const
9041 if(instance
.empty())
9043 CMatrix fxMatrix
= matrix
;
9044 if (scale
!= 1.f
) fxMatrix
.scale(scale
);
9045 fxMatrix
.setPos(fxMatrix
.getPos() + fxMatrix
.mulVector(localOffset
));
9046 instance
.setTransformMode(NL3D::UTransform::DirectMatrix
);
9047 instance
.setMatrix(fxMatrix
);
9048 if(!_Skeleton
.empty())
9050 instance
.setClusterSystem(_Skeleton
.getClusterSystem());
9055 //------------------------------------------------------------
9056 // build a matrix aligned on that entity (includes dir & pos)
9057 //------------------------------------------------------------
9058 void CCharacterCL::buildAlignMatrix(NLMISC::CMatrix
&dest
) const
9060 // if not in clod, pelvis bone has been computed -> use it to get the current orientation
9061 // use the dir matrix otherwise
9063 if (pelvisBone() != -1 && _Skeleton)
9065 if (_Skeleton.isBoneComputed(pelvisBone()))
9067 // the direction is given by the y axis
9068 NL3D::UBone pelvisBone = _Skeleton.getBone(pelvisBone());
9069 forward = pelvisBone->getMatrix().getJ();
9070 forward.z = 0.f; // project onto XY plane
9071 forward.normalize();
9075 // must be in clod -> use the direction
9081 // bone not found, use the direction
9084 dest.setRot(- forward, forward ^ CVector::K, CVector::K);
9086 dest
.setRot(CVector::K
^ _Front
, - _Front
, CVector::K
);
9091 //-----------------------------------------------
9093 //-----------------------------------------------
9094 void CCharacterCL::attachFXInternal(const CAttachedFX::TSmartPtr fx
, TAttachedFXList targetList
)
9099 case FXListToRemove
:
9100 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9101 _AttachedFXListToRemove
.push_front(fx
);
9103 case FXListCurrentAnim
:
9104 _AttachedFXListForCurrentAnim
.push_front(fx
);
9108 if (fx
->AniFX
->Sheet
)
9110 switch(fx
->AniFX
->Sheet
->RepeatMode
)
9112 case CAnimationFXSheet::Respawn
:
9113 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9114 _AttachedFXListToRemove
.push_front(fx
);
9117 _AttachedFXListForCurrentAnim
.push_front(fx
);
9123 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9124 _AttachedFXListToRemove
.push_front(fx
);
9134 //-----------------------------------------------
9136 //-----------------------------------------------
9137 void CCharacterCL::attachFX(const CAttachedFX::TSmartPtr fx
)
9139 attachFXInternal(fx
, FXListToRemove
);
9146 // ***********************************************************************************************************************
9147 void CCharacterCL::setAuraFX(uint index
, const CAnimationFX
*sheet
)
9149 nlassert(index
< MaxNumAura
);
9150 // no-op if same aura
9151 if (_AuraFX
[index
] && _AuraFX
[index
]->AniFX
== sheet
) return;
9155 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
9156 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
9158 if ((*itAttachedFxToStart
).MaxNumAnimCount
== index
)
9159 itAttachedFxToStart
= _AttachedFXListToStart
.erase(itAttachedFxToStart
);
9161 ++itAttachedFxToStart
;
9163 // if there's already an aura attached, and if it is not already shutting down
9164 if (_AuraFX
[index
] && _AuraFX
[index
]->TimeOutDate
== 0.f
)
9166 _AuraFX
[index
]->TimeOutDate
= TimeInSec
+ AURA_SHUTDOWN_TIME
;
9171 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
9172 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
9174 if ((*itAttachedFxToStart
).MaxNumAnimCount
== index
)
9177 // remove previous aura
9178 _AuraFX
[index
] = NULL
;
9179 CAttachedFX::CBuildInfo bi
;
9183 if (sheet
->Sheet
->PSName
== "misc_caravan_teleportout.ps")
9185 bi
.MaxNumAnimCount
= index
;
9186 bi
.StartTime
= TimeInSec
;
9187 bi
.DelayBeforeStart
= 12.5f
;
9188 _AttachedFXListToStart
.push_front(bi
);
9190 else if (sheet
->Sheet
->PSName
== "misc_kami_teleportout.ps")
9192 bi
.MaxNumAnimCount
= index
;
9193 bi
.StartTime
= TimeInSec
;
9194 bi
.DelayBeforeStart
= 11.5f
;
9195 _AttachedFXListToStart
.push_front(bi
);
9199 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
9200 fx
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9201 if (!fx
->FX
.empty())
9203 _AuraFX
[index
] = fx
;
9209 // ***********************************************************************************************************************
9210 void CCharacterCL::setLinkFX(const CAnimationFX
*fx
, const CAnimationFX
*dispell
)
9212 // no-op if same link
9213 if (_LinkFX
&& _LinkFX
->AniFX
== fx
) return;
9218 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
9219 CAttachedFX::CBuildInfo bi
;
9221 fx
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9227 CAttachedFX::TSmartPtr linkFX
= new CAttachedFX
;
9228 CAttachedFX::CBuildInfo bi
;
9230 linkFX
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9231 if (!linkFX
->FX
.empty())
9239 // ***********************************************************************************************************************
9240 void CCharacterCL::startItemAttackFXs(bool activateTrails
, uint intensity
)
9242 uint numItems
= (uint
)_Items
.size();
9243 forceEvalAnim(); // force to eval bones at least once when fx are created
9244 for(uint k
= 0; k
< numItems
; ++k
)
9246 _Items
[k
].startAttackFX(_Skeleton
, intensity
, (SLOTTYPE::EVisualSlot
) k
, activateTrails
);
9250 // ***********************************************************************************************************************
9251 void CCharacterCL::stopItemAttackFXs()
9253 uint numItems
= (uint
)_Items
.size();
9254 for(uint k
= 0; k
< numItems
; ++k
)
9256 if (_Items
[k
].Sheet
)
9258 _Items
[k
].stopAttackFX();
9264 //-----------------------------------------------
9266 //-----------------------------------------------
9267 bool CCharacterCL::isNeutral() const
9272 //-----------------------------------------------
9274 //-----------------------------------------------
9275 bool CCharacterCL::isFriend () const
9280 //-----------------------------------------------
9282 //-----------------------------------------------
9283 bool CCharacterCL::isEnemy () const
9285 // Suppose enemy if attackable
9286 if( properties().attackable() )
9291 return isAnOutpostEnemy();
9295 //-----------------------------------------------
9297 //-----------------------------------------------
9298 bool CCharacterCL::isAlly () const
9300 return this->isAnOutpostAlly();
9304 //-----------------------------------------------
9306 //-----------------------------------------------
9307 void CCharacterCL::applyVisualFX(sint64 prop
)
9311 const CAnimationFX
*auraFX
= NULL
;
9314 auraFX
= CAttackListManager::getInstance().getAuras().getFX(vfx
.Aura
);
9316 setAuraFX(0, auraFX
);
9317 const CAnimationFX
*auraReceiptFX
= NULL
;
9318 if (vfx
.AuraReceipt
)
9320 auraReceiptFX
= CAttackListManager::getInstance().getAuras().getFX(0);
9322 setAuraFX(1, auraReceiptFX
);
9323 const CAnimationFX
*linkFX
= NULL
;
9326 linkFX
= CAttackListManager::getInstance().getLinks().getFX(vfx
.Link
);
9328 const CAnimationFX
*dispellFX
= NULL
;
9329 dispellFX
= CAttackListManager::getInstance().getLinks().getFX(0);
9330 setLinkFX(linkFX
, dispellFX
);
9333 // *********************************************************************************************
9334 const char *CCharacterCL::getBoneNameFromBodyPart(BODY::TBodyPart part
, BODY::TSide side
) const
9336 if (!_Sheet
) return CEntityCL::getBoneNameFromBodyPart(part
, side
);
9337 return _Sheet
->BodyToBone
.getBoneName(part
, side
);
9341 // *********************************************************************************************
9342 const CItemSheet
*CCharacterCL::getRightHandItemSheet() const
9344 if (_RHandInstIdx
== CEntityCL::BadIndex
) return NULL
;
9345 return _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
;
9348 // *********************************************************************************************
9349 const CItemSheet
*CCharacterCL::getLeftHandItemSheet() const
9351 if (_LHandInstIdx
== CEntityCL::BadIndex
) return NULL
;
9352 return _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
;
9355 // ***************************************************************************
9356 void CCharacterCL::resetAllSoundAnimId()
9358 for(uint i
=0;i
<_SoundId
.size();i
++)
9360 _SoundId
[i
]= CSoundAnimationNoId
;
9365 /////////////////////////////
9366 // CCharacterCL::CWornItem //
9367 /////////////////////////////
9370 // ***********************************************************************************************************************
9371 void CCharacterCL::CWornItem::startAttackFX(NL3D::USkeleton skeleton
, uint intensity
, SLOTTYPE::EVisualSlot visualSlot
, bool activateTrail
)
9373 if (intensity
< 1 || intensity
> 5) return;
9375 if (activateTrail
&& !Trail
.empty())
9379 // create the attack fx
9382 const CItemFXSheet
&fxSheet
= Sheet
->FX
;
9383 std::string shapeName
= fxSheet
.getAttackFX();
9384 if (!shapeName
.empty())
9386 const char *stickPoint
= NULL
;
9387 if(!skeleton
.empty())
9391 case SLOTTYPE::RIGHT_HAND_SLOT
:
9392 if( Sheet
->ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
9393 stickPoint
= "box_arme";
9395 case SLOTTYPE::LEFT_HAND_SLOT
:
9396 if(Sheet
&& Sheet
->getAnimSet()=="s")
9397 stickPoint
= "Box_bouclier";
9399 stickPoint
= "box_arme_gauche";
9407 sint boneId
= skeleton
.getBoneIdByName(std::string(stickPoint
));
9410 NL3D::UInstance instance
= Scene
->createInstance(shapeName
);
9411 if (!instance
.empty())
9414 if (skeleton
.getVisibility() == UTransform::Hide
)
9416 // force to compute the bone at least once
9417 skeleton
.forceComputeBone(boneId
);
9419 UParticleSystemInstance atk
;
9420 atk
.cast (instance
);
9423 // set the user params
9424 static const float up
[5][4] =
9426 { 0.f
, 0.f
, 0.f
, 0.f
},
9427 { 1.f
, 0.f
, 0.f
, 0.f
},
9428 { 1.f
, 1.f
, 0.f
, 0.f
},
9429 { 1.f
, 1.f
, 1.f
, 0.f
},
9430 { 1.f
, 1.f
, 1.f
, 1.f
}
9432 for(uint k
= 0; k
< 4; ++k
)
9434 atk
.setUserParam(k
, up
[intensity
- 1][k
]);
9436 atk
.setTransformMode(UTransform::RotEuler
);
9437 atk
.setPos(fxSheet
.AttackFXOffset
);
9438 const float degreeToRad
= (float) Pi
/ 180.f
;
9439 atk
.setRotEuler(fxSheet
.AttackFXRot
.x
* degreeToRad
, fxSheet
.AttackFXRot
.y
* degreeToRad
, fxSheet
.AttackFXRot
.z
* degreeToRad
);
9440 skeleton
.stickObject(atk
, boneId
);
9441 // delegate mng of this object lifetime to the fx manager
9442 FXMngr
.fx2remove(atk
);
9446 Scene
->deleteInstance(atk
);
9455 // ***********************************************************************************************************************
9456 void CCharacterCL::CWornItem::stopAttackFX()
9458 if (!Trail
.empty()) Trail
.stop();
9461 // ***********************************************************************************************************************
9462 void CCharacterCL::CWornItem::initFXs(SLOTTYPE::EVisualSlot
/* visualSlot */, NL3D::UInstance parent
)
9467 const CItemFXSheet
&sheet
= Sheet
->FX
;
9468 std::string shapeName
= sheet
.getTrail();
9469 if (!shapeName
.empty())
9471 Trail
= Scene
->createInstance(shapeName
);
9474 nlwarning("Cannot create instance %s", shapeName
.c_str());
9477 // Initialize the remanence. Must substract the object matrix since parent(parent)
9478 CMatrix mat
= parent
.getMatrix();
9480 mat
*= Trail
.getMatrix();
9481 Trail
.setTransformMode(UTransformable::DirectMatrix
);
9482 Trail
.setMatrix(mat
);
9483 // Initialize instance.
9484 Trail
.parent(parent
);
9488 // ***********************************************************************************************************************
9489 void CCharacterCL::CWornItem::enableAdvantageFX(NL3D::UInstance parent
)
9493 bool enabled
= Sheet
->HasFx
;
9495 if ((enabled
&& !AdvantageFX
.empty()) || (!enabled
&& AdvantageFX
.empty()))
9496 return; // state did not change
9499 // well, it is unlikely that player will loses its ability to master an item after he gained it, but manage the case anyway.
9500 if (!AdvantageFX
.removeByID(NELID("STOP")) && !AdvantageFX
.removeByID(NELID("main")))
9502 AdvantageFX
.activateEmitters(false);
9504 FXMngr
.fx2remove(AdvantageFX
);
9509 std::string shapeName
= Sheet
->FX
.getAdvantageFX();
9510 if (!shapeName
.empty())
9512 NL3D::UInstance fx
= Scene
->createInstance(shapeName
);
9513 if (fx
.empty()) return;
9514 AdvantageFX
.cast (fx
);
9515 if (AdvantageFX
.empty())
9517 Scene
->deleteInstance(fx
);
9520 CMatrix mat
= parent
.getMatrix();
9522 mat
*= AdvantageFX
.getMatrix();
9523 AdvantageFX
.setTransformMode(UTransformable::DirectMatrix
);
9524 AdvantageFX
.setMatrix(mat
);
9525 AdvantageFX
.parent(parent
);
9530 // ***********************************************************************************************************************
9531 void CCharacterCL::CWornItem::releaseFXs()
9536 Scene
->deleteInstance(Trail
);
9537 if (!AdvantageFX
.empty())
9538 Scene
->deleteInstance(AdvantageFX
);
9542 // ***********************************************************************************************************************
9543 void CCharacterCL::CWornItem::setTrailSize(uint size
)
9545 if (Trail
.empty()) return;
9547 clamp(size
, (uint
) 0, (uint
) 15);
9550 Trail
.setSliceTime(0.f
);
9554 float ratio
= (size
- 1) / 15.f
;
9555 Trail
.setSliceTime(ratio
* Sheet
->FX
.TrailMaxSliceTime
+ (1.f
- ratio
) * Sheet
->FX
.TrailMinSliceTime
);
9560 // ***********************************************************************************************************************
9561 const CAttack
*CCharacterCL::getAttack(const CAttackIDSheet
&id
) const
9563 if (!_Sheet
) return NULL
;
9564 return getAttack(id
, _Sheet
->AttackLists
);
9567 // ***********************************************************************************************************************
9568 const CAttack
*CCharacterCL::getAttack(const CAttackIDSheet
&id
, const std::vector
<NLMISC::TSStringId
> &attackList
) const
9570 for(std::vector
<NLMISC::TSStringId
>::const_reverse_iterator it
= attackList
.rbegin(); it
!= attackList
.rend(); ++it
)
9572 const CAttackList
*al
= CAttackListManager::getInstance().getAttackList(ClientSheetsStrings
.get(*it
));
9575 const CAttack
*attk
= al
->getAttackFromID(id
);
9576 if (attk
) return attk
;
9583 // ***********************************************************************************************************************
9584 void CCharacterCL::initStaticFX()
9587 if (ClientCfg
.Light
) return;
9588 std::string staticFX
= _Sheet
->getStaticFX();
9589 if (!staticFX
.empty())
9591 CEntitySheet
*sheet
= SheetMngr
.get(NLMISC::CSheetId(staticFX
));
9594 if (sheet
->Type
== CEntitySheet::ANIMATION_FX
)
9596 CAnimationFXSheet
*afs
= NLMISC::safe_cast
<CAnimationFXSheet
*>(sheet
);
9597 _StaticFX
= new CStaticFX
;
9598 _StaticFX
->AF
.init(afs
, EAM
? EAM
->getAnimationSet() : NULL
);
9599 _StaticFX
->FX
= new CAttachedFX
;
9600 CAttachedFX::CBuildInfo bi
;
9601 bi
.Sheet
= &_StaticFX
->AF
;
9602 _StaticFX
->FX
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9603 if (_StaticFX
->FX
->FX
.empty())
9613 nlwarning("Static fx %s not found", staticFX
.c_str());
9618 // ***************************************************************************
9619 EGSPD::CPeople::TPeople
CCharacterCL::people() const
9622 return _Sheet
->Race
;
9624 return EGSPD::CPeople::Unknown
;
9627 // ***************************************************************************
9628 void CCharacterCL::setPeople(EGSPD::CPeople::TPeople
/* people */)
9632 // ***************************************************************************
9636 // temp : begin cast of projectile (start begin anim and loop anim)
9637 NLMISC_COMMAND(beginCast
, "Start spell cast", "<spell_id> <strenght> [<mode> <caster slot> <behaviour>]")
9639 if (args
.size() < 2) return false;
9640 if (args
.size() > 5) return false;
9641 uint casterSlot
= 0;
9642 if (args
.size() >= 4)
9644 fromString(args
[3], casterSlot
);
9646 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9647 if (!ch
) return false;
9648 CBehaviourContext bc
;
9649 // TODO : the type of missile is contained in the spell id
9650 if (args
.size() >= 5)
9653 fromString(args
[4], missileType
);
9654 bc
.Behav
.Behaviour
= (MBEHAV::EBehaviour
) (MBEHAV::CAST_OFF
+ missileType
);
9658 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF
;
9662 fromString(args
[0], spellId
); // spell id is unused
9663 bc
.Behav
.Spell
.SpellId
= spellId
;
9664 //bc.Behav.Spell.Resist = false;
9665 uint16 spellIntensity
;
9666 fromString(args
[1], spellIntensity
);
9667 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9668 if (args
.size() == 3)
9671 fromString(args
[2], spellMode
);
9672 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9676 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9678 ch
->applyBehaviour(bc
);
9682 // temp : test fail of a cast
9683 NLMISC_COMMAND(failCast
, "Simulate failure of a spell cast", "<spell_id> <strenght> [<mode>]")
9685 if (args
.size() < 2) return false;
9686 if (args
.size() > 3) return false;
9687 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(0));
9688 if (!ch
) return false;
9689 CBehaviourContext bc
;
9690 // TODO : the type of missile is contained in the spell id
9691 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_FAIL
;
9694 fromString(args
[0], spellId
); // spell id is unused
9695 bc
.Behav
.Spell
.SpellId
= spellId
;
9696 //bc.Behav.Spell.Resist = false;
9697 uint16 spellIntensity
;
9698 fromString(args
[1], spellIntensity
);
9699 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9700 if (args
.size() == 3)
9703 fromString(args
[2], spellMode
);
9704 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9708 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9710 ch
->applyBehaviour(bc
);
9714 // temp to test cast of a projectile on another entity
9715 NLMISC_COMMAND(projectile
, "Cast a projectile on another entity", "<spellID> <strenght> [<mode> <target entity> <resist> <source entity>]" )
9717 if (args
.size() < 2) return false;
9718 if (args
.size() > 6) return false;
9719 uint8 casterSlot
= 0;
9720 if (args
.size() > 5)
9722 fromString(args
[5], casterSlot
);
9724 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9725 if (!ch
) return false;
9726 // create a new behaviour to apply to the user
9727 CBehaviourContext bc
;
9728 uint16 spellId
, spellIntensity
;
9729 fromString(args
[0], spellId
);
9730 bc
.Behav
.Spell
.SpellId
= spellId
;
9731 fromString(args
[1], spellIntensity
);
9732 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9733 // TODO : the type of missile is contained in the spell id
9734 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_SUCCESS
;
9736 if (args
.size() > 2)
9739 fromString(args
[2], spellMode
);
9740 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9744 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9746 if (args
.size() > 3)
9749 fromString(args
[3], targetSlot
);
9750 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9751 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9752 if (!target
) return false;
9753 double dist
= (target
->pos() - ch
->pos()).norm();
9754 bool resist
= false;
9755 if (args
.size() > 4) fromString(args
[4], resist
);
9756 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, resist
, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), -11));
9758 bc
.BehavTime
= TimeInSec
;
9759 ch
->applyBehaviour(bc
);
9763 NLMISC_COMMAND(mtProjectile
, "Cast a projectile on one or several entities", "<caster> <spellID> <strenght> <mode> <target0> [<target n>]*" )
9765 if (args
.size() < 5) return false;
9767 fromString(args
[0], casterSlot
);
9768 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9769 if (!ch
) return false;
9770 // create a new behaviour to apply to the user
9771 CBehaviourContext bc
;
9772 uint16 spellId
, spellIntensity
, spellMode
;
9774 fromString(args
[1], spellId
);
9775 bc
.Behav
.Spell
.SpellId
= spellId
;
9777 fromString(args
[2], spellIntensity
);
9778 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9780 fromString(args
[3], spellMode
);
9781 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9782 // get targets and their dist depending on the mode
9783 switch(bc
.Behav
.Spell
.SpellMode
)
9787 uint mainTargetSlot
;
9788 fromString(args
[4], mainTargetSlot
);
9789 if (mainTargetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9790 CEntityCL
*mainTarget
= EntitiesMngr
.entity(mainTargetSlot
);
9793 double dist
= (mainTarget
->pos() - ch
->pos()).norm();
9794 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(mainTargetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), 1));
9795 for(sint k
= 1; k
< (sint
) (args
.size() - 4); ++k
)
9797 uint secondaryTargetSlot
;
9798 fromString(args
[4 + k
], secondaryTargetSlot
);
9799 if (secondaryTargetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9800 CEntityCL
*secondaryTarget
= EntitiesMngr
.entity(secondaryTargetSlot
);
9801 if (secondaryTarget
)
9803 dist
= (secondaryTarget
->pos() - mainTarget
->pos()).norm();
9804 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(secondaryTargetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), k
+1));
9810 case MAGICFX::Spray
:
9812 for(sint k
= 0; k
< (sint
) (args
.size() - 4); ++k
)
9815 fromString(args
[4 + k
], targetSlot
);
9816 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9817 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9820 double dist
= (target
->pos() - ch
->pos()).norm();
9821 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), k
+1));
9826 case MAGICFX::Chain
:
9828 CEntityCL
*startSlot
= ch
;
9829 for(sint k
= 0; k
< (sint
) (args
.size() - 4); ++k
)
9832 fromString(args
[4 + k
], targetSlot
);
9833 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9834 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9837 double dist
= (target
->pos() - startSlot
->pos()).norm();
9838 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), k
+1));
9845 bc
.BehavTime
= TimeInSec
;
9846 // TODO : the type of missile is contained in the spell id
9847 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_SUCCESS
;
9849 ch
->applyBehaviour(bc
);
9853 // temp to test cast of multitarget projectile on another entity
9854 NLMISC_COMMAND(aura
, "enable / disable aura on an entity", "<slot> <aura>")
9856 if (args
.size() != 2) return false;
9858 fromString(args
[0], slot
);
9859 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9860 if (!ch
) return false;
9861 const CAnimationFX
*fx
= NULL
;
9863 fromString(args
[1], auraIndex
);
9864 if (auraIndex
!= -1)
9866 fx
= CAttackListManager::getInstance().getAuras().getFX(auraIndex
);
9867 if (!fx
) return false;
9869 ch
->setAuraFX(0, fx
);
9873 NLMISC_COMMAND(link
, "enable / disable link on an entity", "<slot> <link>")
9875 if (args
.size() != 2) return false;
9877 fromString(args
[0], slot
);
9878 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9879 if (!ch
) return false;
9880 const CAnimationFX
*link
= NULL
;
9882 fromString(args
[1], linkIndex
);
9883 if (linkIndex
!= -1)
9885 link
= CAttackListManager::getInstance().getLinks().getFX(linkIndex
+ 1);
9886 if (!link
) return false;
9888 const CAnimationFX
*linkBreak
= CAttackListManager::getInstance().getLinks().getFX(0);
9889 if (!linkBreak
) return false;
9890 ch
->setLinkFX(link
, linkBreak
);
9894 NLMISC_COMMAND(auraReceipt
, "enable / disable aura receipt on an entity", "<slot> <aura> <0=on/1=off>")
9896 if (args
.size() != 2) return false;
9898 fromString(args
[0], slot
);
9899 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9900 if (!ch
) return false;
9901 const CAnimationFX
*fx
= NULL
;
9903 fromString(args
[1], enableAura
);
9906 fx
= CAttackListManager::getInstance().getAuras().getFX(0); // 0 is special for aura receipt
9907 if (!fx
) return false;
9909 ch
->setAuraFX(1, fx
);
9913 ////////////////////////////
9914 // test for melee weapons //
9915 ////////////////////////////
9917 // these are helpers (the same can be done with /vp, or altLook)
9918 NLMISC_COMMAND(weapon
, "change the weapon in hand", "<slot> <hand> <weapon>")
9920 if (args
.size() != 3) return false;
9921 CInterfaceManager
*im
= CInterfaceManager::getInstance();
9923 fromString(args
[0], slot
);
9924 CCDBNodeLeaf
*propA
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPA
), false);
9925 if (!propA
) return false;
9926 sint64 valueA
= propA
->getValue64();
9928 CCDBNodeLeaf
*propB
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPB
), false);
9929 if (!propB
) return false;
9930 sint64 valueB
= propB
->getValue64();
9933 fromString(args
[1], hand
);
9935 // the VP is dependent of Entity actual type
9936 if(dynamic_cast<CPlayerCL
*>(EntitiesMngr
.entity(slot
)))
9938 SPropVisualA vpa
= (SPropVisualA
&) valueA
;
9939 SPropVisualB vpb
= (SPropVisualB
&) valueB
;
9942 uint16 weaponRightHand
;
9943 fromString(args
[2], weaponRightHand
);
9944 vpa
.PropertySubData
.WeaponRightHand
= weaponRightHand
;
9945 vpb
.PropertySubData
.RTrail
= 1;
9949 uint16 weaponLeftHand
;
9950 fromString(args
[2], weaponLeftHand
);
9951 vpa
.PropertySubData
.WeaponLeftHand
= weaponLeftHand
;
9952 vpb
.PropertySubData
.LTrail
= 1;
9954 propA
->setValue64((sint64
) vpa
.PropertyA
);
9955 propB
->setValue64((sint64
) vpb
.PropertyB
);
9957 // CharacterCL: use a SAltLook
9960 SAltLookProp vpalt
= (SAltLookProp
&) valueA
;
9963 uint16 weaponRightHand
;
9964 fromString(args
[2], weaponRightHand
);
9965 vpalt
.Element
.WeaponRightHand
= weaponRightHand
;
9966 vpalt
.Element
.RTrail
= 1;
9970 uint16 weaponLeftHand
;
9971 fromString(args
[2], weaponLeftHand
);
9972 vpalt
.Element
.WeaponLeftHand
= weaponLeftHand
;
9973 vpalt
.Element
.LTrail
= 1;
9975 propA
->setValue64((sint64
) vpalt
.Summary
);
9978 // Force to update property
9979 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
9980 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPB
);
9982 // display name of weapon sheet
9987 NLMISC_COMMAND(advantageFX
, "turn on / off the advantage fx for an item in hand", "<slot> <hand> <on = 1/ off = 0>")
9989 if (args
.size() != 3) return false;
9990 CInterfaceManager
*im
= CInterfaceManager::getInstance();
9992 fromString(args
[0], slot
);
9995 CCDBNodeLeaf *prop = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPA), false);
9996 if (!prop) return false;
9997 sint64 value = prop->getValue64();
9999 fromString(args[1], hand);
10000 // the VP is dependent of Entity actual type
10001 if(dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(slot)))
10003 SPropVisualA vpa = (SPropVisualA &) value;
10006 fromString(args[2], vpa.PropertySubData.RWeaponFX);
10010 fromString(args[2], vpa.PropertySubData.LWeaponFX);
10012 prop->setValue64((sint64) vpa.PropertyA);
10014 // CharacterCL: use a SAltLook
10017 SAltLookProp vpa = (SAltLookProp&) value;
10020 fromString(args[2], vpa.Element.RWeaponFX);
10024 fromString(args[2], vpa.Element.LWeaponFX);
10026 prop->setValue64((sint64) vpa.Summary);
10030 // Force to update property
10031 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
10036 NLMISC_COMMAND(trailLength
, "set length of trail for one weapon in hand", "<slot> <hand> <power = 0..15>")
10038 if (args
.size() != 3) return false;
10039 CInterfaceManager
*im
= CInterfaceManager::getInstance();
10041 fromString(args
[0], slot
);
10043 CCDBNodeLeaf
*propA
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPA
), false);
10044 if (!propA
) return false;
10045 sint64 valueA
= propA
->getValue64();
10047 CCDBNodeLeaf
*propB
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPB
), false);
10048 if (!propB
) return false;
10049 sint64 valueB
= propB
->getValue64();
10052 fromString(args
[1], hand
);
10054 // the VP is dependent of Entity actual type
10055 if(dynamic_cast<CPlayerCL
*>(EntitiesMngr
.entity(slot
)))
10057 SPropVisualA vpa
= (SPropVisualA
&) valueA
;
10058 SPropVisualB vpb
= (SPropVisualB
&) valueB
;
10062 fromString(args
[2], rTrail
);
10063 vpb
.PropertySubData
.RTrail
= rTrail
;
10064 propB
->setValue64((sint64
) vpb
.PropertyB
);
10069 fromString(args
[2], lTrail
);
10070 vpb
.PropertySubData
.LTrail
= lTrail
/ 2;
10071 propB
->setValue64((sint64
) vpb
.PropertyB
);
10076 SAltLookProp vpalt
= (SAltLookProp
&) valueA
;
10080 fromString(args
[2], rTrail
);
10081 vpalt
.Element
.RTrail
= rTrail
;
10086 fromString(args
[2], lTrail
);
10087 vpalt
.Element
.LTrail
= lTrail
/ 2;
10089 propA
->setValue64((sint64
) vpalt
.Summary
);
10092 // Force to update property
10093 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
10094 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPB
);
10100 // simulate an attack behaviour for the given slot
10101 NLMISC_COMMAND(attack
, "simulate an attack", "<slot> <intensity> <hit_type> <localisation> <dmg_type> <damage_shield_power> <damage_shield_id>")
10103 if (args
.size() < 2) return false;
10104 if (args
.size() > 7) return false;
10105 CBehaviourContext bc
;
10106 bc
.Behav
.Behaviour
= MBEHAV::DEFAULT_ATTACK
;
10107 bc
.Behav
.Combat
.ActionDuration
= 0;
10108 uint16 impactIntensity
;
10109 fromString(args
[1], impactIntensity
);
10110 bc
.Behav
.Combat
.ImpactIntensity
= impactIntensity
;
10111 bc
.Behav
.Combat
.HitType
= HITTYPE::Hit
;
10112 bc
.Behav
.Combat
.Localisation
= BODY::HHead
;
10113 bc
.Behav
.Combat2
.DamageType
= 0;
10114 if (args
.size() > 2)
10117 fromString(args
[2], hitType
);
10118 bc
.Behav
.Combat
.HitType
= hitType
+ 1;
10120 if (args
.size() > 3)
10122 uint16 localisation
;
10123 fromString(args
[3], localisation
);
10124 bc
.Behav
.Combat
.Localisation
= localisation
;
10126 if (args
.size() > 4)
10129 fromString(args
[4], damageType
);
10130 bc
.Behav
.Combat2
.DamageType
= damageType
;
10134 if (args
.size() > 5)
10136 fromString(args
[5], dsPower
);
10138 if (args
.size() > 6)
10140 fromString(args
[6], dsType
);
10144 fromString(args
[0], slot
);
10145 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
10146 if (!entity
) return false;
10147 // push current slection as main target
10148 CMultiTarget::CTarget target
;
10149 target
.TargetSlot
= UserEntity
->selection();
10150 target
.Info
= dsPower
| (dsType
<< 3);
10151 target
.DeltaHP
= -20;
10152 bc
.Targets
.Targets
.push_back(target
);
10153 bc
.BehavTime
= TimeInSec
;
10154 bc
.Behav
.DeltaHP
= -20;
10155 entity
->applyBehaviour(bc
);
10159 // simulate a range attack from the current slor to the current selection
10160 NLMISC_COMMAND(rangeAttack
, "simulate a range attack", "<slot> [intensity] [localisation] [range_weapon_type_if_unequipped]")
10162 if (args
.size() < 1) return false;
10163 if (args
.size() > 4) return false;
10165 fromString(args
[0], slot
);
10166 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
10167 if (!entity
) return false;
10168 const CItemSheet
*weaponSheet
= entity
->getRightHandItemSheet();
10169 CBehaviourContext bc
;
10170 if (!weaponSheet
|| weaponSheet
->Family
!= ITEMFAMILY::RANGE_WEAPON
)
10173 uint16 weaponType
= 0;
10174 if (args
.size() > 3)
10176 fromString(args
[3], weaponType
);
10178 bc
.Behav
.Range
.WeaponType
= weaponType
;
10182 bc
.Behav
.Range
.WeaponType
= weaponSheet
->RangeWeapon
.RangeWeaponType
;
10184 bc
.Behav
.Behaviour
= MBEHAV::RANGE_ATTACK
;
10185 bc
.Behav
.Range
.ImpactIntensity
= 1;
10186 bc
.Behav
.Range
.Localisation
= BODY::HHead
;
10187 bc
.BehavTime
= TimeInSec
;
10188 if (args
.size() > 1)
10190 uint16 impactIntensity
;
10191 fromString(args
[1], impactIntensity
);
10192 bc
.Behav
.Range
.ImpactIntensity
= impactIntensity
;
10194 if (args
.size() > 2)
10196 uint16 localisation
;
10197 fromString(args
[2], localisation
);
10198 bc
.Behav
.Range
.Localisation
= localisation
;
10200 // if not a generic range weapon, add a single target (this is the current selection)
10201 uint8 targetSlot
= UserEntity
->targetSlot();
10202 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
10203 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
10204 if (!target
) return false;
10205 double dist
= (target
->pos() - entity
->pos()).norm();
10206 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), -10));
10207 bc
.Behav
.DeltaHP
= -10;
10208 entity
->applyBehaviour(bc
);
10213 // simulate a creature attack
10214 NLMISC_COMMAND(creatureAttack
, "simulate a creature attack (2 attaques per creature)", "<casterSlot> <targetSlot> [attk=0/1] [magicIntensity] [physicalIntensity] [localisation] [damageType] [hitType] [resist=1/0]")
10216 if (args
.size() < 2) return false;
10217 if (args
.size() > 9) return false;
10218 CBehaviourContext bc
;
10219 bc
.Behav
.Behaviour
= MBEHAV::CREATURE_ATTACK_0
;
10220 if (args
.size() > 2)
10223 fromString(args
[2], attk
);
10224 bc
.Behav
.Behaviour
= attk
== 0 ? MBEHAV::CREATURE_ATTACK_0
: MBEHAV::CREATURE_ATTACK_1
;
10227 fromString(args
[0], casterSlot
);
10228 CCharacterCL
*caster
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
10229 if (!caster
) return false;
10231 fromString(args
[1], targetSlot
);
10232 CCharacterCL
*target
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(targetSlot
));
10233 if (!target
) return false;
10234 double dist
= (target
->pos() - caster
->pos()).norm();
10235 bool resist
= false;
10236 if (args
.size() > 8)
10238 fromString(args
[8], resist
);
10240 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
)), -15));
10241 bc
.Behav
.CreatureAttack
.ActionDuration
= 0;
10242 uint magicImpactIntensity
= 1;
10243 if (args
.size() > 3)
10245 fromString(args
[3], magicImpactIntensity
);
10247 bc
.Behav
.CreatureAttack
.MagicImpactIntensity
= magicImpactIntensity
;
10248 uint physicalImpactIntensity
= 0;
10249 if (args
.size() > 4)
10251 fromString(args
[4], physicalImpactIntensity
);
10253 bc
.Behav
.CreatureAttack
.ImpactIntensity
= physicalImpactIntensity
;
10254 BODY::TBodyPart localisation
= BODY::HHead
;
10255 if (args
.size() > 5)
10258 fromString(args
[5], tmp
);
10259 localisation
= (BODY::TBodyPart
) tmp
;
10261 bc
.Behav
.CreatureAttack
.Localisation
= localisation
;
10262 DMGTYPE::EDamageType dmgType
= DMGTYPE::BLUNT
;
10263 if (args
.size() > 6)
10266 fromString(args
[6], tmp
);
10267 dmgType
= (DMGTYPE::EDamageType
) tmp
;
10269 bc
.Behav
.CreatureAttack2
.DamageType
= dmgType
;
10270 HITTYPE::THitType hitType
= HITTYPE::Hit
;
10271 if (args
.size() > 7)
10274 fromString(args
[7], tmp
);
10275 hitType
= (HITTYPE::THitType
) tmp
;
10277 bc
.Behav
.CreatureAttack2
.HitType
= hitType
;
10278 bc
.BehavTime
= TimeInSec
;
10279 bc
.Behav
.DeltaHP
= -15;
10280 caster
->applyBehaviour(bc
);
10284 NLMISC_COMMAND(setNamePosZ
, "", "<low/high/normal> <value>")
10286 if (args
.size() != 2) return false;
10288 CEntityCL
*target
= EntitiesMngr
.entity(UserEntity
->targetSlot());
10292 float *namePosZ
= NULL
;
10293 string sheetName
, skelName
;
10294 if (target
->Type
== CEntityCL::Player
)
10296 CPlayerCL
*playerTarget
= dynamic_cast<CPlayerCL
*>(target
);
10299 CRaceStatsSheet
*sheet
= const_cast<CRaceStatsSheet
*>(playerTarget
->playerSheet());
10302 if (toLowerAscii(args
[0]) == "low")
10303 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZLow
;
10304 else if (toLowerAscii(args
[0]) == "normal")
10305 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZNormal
;
10306 else if (toLowerAscii(args
[0]) == "high")
10307 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZHigh
;
10309 sheetName
= sheet
->Id
.toString();
10310 skelName
= sheet
->GenderInfos
[playerTarget
->getGender()].Skelfilename
;
10316 CCharacterCL
*creatureTarget
= dynamic_cast<CCharacterCL
*>(target
);
10317 if (creatureTarget
)
10319 CCharacterSheet
*sheet
= const_cast<CCharacterSheet
*>(creatureTarget
->getSheet());
10322 if (toLowerAscii(args
[0]) == "low")
10323 namePosZ
= &sheet
->NamePosZLow
;
10324 else if (toLowerAscii(args
[0]) == "normal")
10325 namePosZ
= &sheet
->NamePosZNormal
;
10326 else if (toLowerAscii(args
[0]) == "high")
10327 namePosZ
= &sheet
->NamePosZHigh
;
10329 sheetName
= sheet
->Id
.toString();
10330 skelName
= sheet
->getSkelFilename();
10337 fromString(args
[1], *namePosZ
);
10338 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName
.c_str(), skelName
.c_str(), args
[0].c_str(), *namePosZ
);
10344 NLMISC_COMMAND(setMyNamePosZ
, "", "<low/high/normal> <value>")
10346 if (args
.size() != 2) return false;
10348 float *namePosZ
= NULL
;
10349 string sheetName
, skelName
;
10350 CRaceStatsSheet
*sheet
= const_cast<CRaceStatsSheet
*>(UserEntity
->playerSheet());
10353 if (toLowerAscii(args
[0]) == "low")
10354 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZLow
;
10355 else if (toLowerAscii(args
[0]) == "normal")
10356 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZNormal
;
10357 else if (toLowerAscii(args
[0]) == "high")
10358 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZHigh
;
10360 sheetName
= sheet
->Id
.toString();
10361 skelName
= sheet
->GenderInfos
[UserEntity
->getGender()].Skelfilename
;
10366 fromString(args
[1], *namePosZ
);
10367 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName
.c_str(), skelName
.c_str(), args
[0].c_str(), *namePosZ
);
10374 NLMISC_COMMAND(pvpMode
, "modify pvp mode", "[<pvp mode> <state>]")
10376 if (args
.size() != 0 && args
.size() != 2) return false;
10378 CInterfaceManager
*IM
= CInterfaceManager::getInstance();
10380 CEntityCL
*target
= EntitiesMngr
.entity(UserEntity
->targetSlot());
10383 IM
->displaySystemInfo(toString("<pvpMode> no target"));
10386 if (target
->Type
!= CEntityCL::Player
&& target
->Type
!= CEntityCL::User
)
10388 IM
->displaySystemInfo(toString("<pvpMode> target is not a player"));
10391 CPlayerCL
*playerTarget
= dynamic_cast<CPlayerCL
*>(target
);
10397 uint16 pvpMode
= playerTarget
->getPvpMode();
10399 if( pvpMode
&PVP_MODE::PvpDuel
)
10401 if( pvpMode
&PVP_MODE::PvpChallenge
)
10403 if( pvpMode
&PVP_MODE::PvpZoneFree
)
10405 if( pvpMode
&PVP_MODE::PvpZoneFaction
)
10406 str
+="zone_faction ";
10407 if( pvpMode
&PVP_MODE::PvpZoneGuild
)
10408 str
+="zone_guild ";
10409 if( pvpMode
&PVP_MODE::PvpZoneOutpost
)
10411 if( pvpMode
&PVP_MODE::PvpFaction
)
10413 if( pvpMode
&PVP_MODE::PvpFactionFlagged
)
10414 str
+="faction_flagged ";
10415 if( pvpMode
&PVP_MODE::PvpZoneSafe
)
10416 str
+="in_safe_zone ";
10417 if( pvpMode
&PVP_MODE::PvpSafe
)
10419 IM
->displaySystemInfo(str
);
10420 nlinfo("<pvpMode> %s",str
.c_str());
10424 PVP_MODE::TPVPMode pvpMode
= PVP_MODE::fromString(args
[0]);
10426 fromString(args
[1], state
);
10429 uint16 currentPVPMode
= playerTarget
->getPvpMode();
10430 currentPVPMode
|= pvpMode
;
10431 playerTarget
->setPvpMode(currentPVPMode
);
10432 IM
->displaySystemInfo(toString("<pvpMode> adding pvp mode %s",args
[0].c_str()));
10436 uint16 currentPVPMode
= playerTarget
->getPvpMode();
10437 currentPVPMode
&= ~pvpMode
;
10438 playerTarget
->setPvpMode(currentPVPMode
);
10439 IM
->displaySystemInfo(toString("<pvpMode> removing pvp mode %s",args
[0].c_str()));
10442 playerTarget
->buildInSceneInterface();
10447 NLMISC_COMMAND(pvpClan, "modify pvp clan", "<pvp clan>")
10449 if (args.size() != 1) return false;
10451 CInterfaceManager *IM = CInterfaceManager::getInstance();
10453 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
10456 IM->displaySystemInfo(toString("<pvpClan> no target"));
10459 if (target->Type != CEntityCL::Player && target->Type != CEntityCL::User)
10461 IM->displaySystemInfo(toString("<pvpMode> target is not a player"));
10464 CPlayerCL *playerTarget = dynamic_cast<CPlayerCL*>(target);
10468 // PVP_CLAN::TPVPClan clan = PVP_CLAN::fromString(args[0]);
10469 // playerTarget->setPvpClan(clan);
10470 playerTarget->buildInSceneInterface();
10475 #endif // !FINAL_VERSION
10477 #include "r2/editor.h"
10479 //---------------------------------------------------
10481 // Method to Flag the character as dead and do everything needed.
10482 //---------------------------------------------------
10483 void CCharacterCL::setDead() // virtual
10485 // If the entity dead is the user -> switch to dead mode.
10486 if(_Slot
== UserEntity
->slot())
10487 UserControls
.mode(CUserControls::DeathMode
);
10489 // If the entity killed was the current user target, we update context cursor
10490 if(_Slot
== UserEntity
->selection())
10492 bool nextContextSelected
= false;
10494 if (!R2::getEditor().isDMing())
10496 if(_Properties
.harvestable())
10497 nextContextSelected
= ContextCur
.context("QUARTER");
10499 else if(_Properties
.lootable())
10500 nextContextSelected
= ContextCur
.context("LOOT");
10502 else if(_Properties
.liftable())
10503 nextContextSelected
= ContextCur
.context("PICKUP");
10504 if( !nextContextSelected
)
10505 ContextCur
.context("STAND BY");
10509 // The character now won't be an obstacle anymore
10510 _Primitive
->setOcclusionMask(MaskColNone
);