1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 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;
382 //-----------------------------------------------
384 // Default Destructor
385 // \warning : Do not remove sheets before the entity.
386 //-----------------------------------------------
387 CCharacterCL::~CCharacterCL()
389 // Delete the UTransform used to compute the Bone for the Name.
390 if(!_NameTransform
.empty())
394 if (!_Skeleton
.empty())
395 _Skeleton
.detachSkeletonSon(_NameTransform
);
396 Scene
->deleteTransform(_NameTransform
);
401 // No more sheet pointed.
404 // Release items (but not their mesh, because they are managed by _Instances)
405 for(uint k
= 0; k
< _Items
.size(); ++k
)
410 // Delete previous interface
411 releaseInSceneInterfaces();
415 _CurrentBubble
->unlink();
420 //-----------------------------------------------
421 // computePrimitive :
422 // Create (or re-create) a primitive.
423 //-----------------------------------------------
424 void CCharacterCL::computePrimitive()
426 // Initialize the primitive.
429 initPrimitive(_Sheet
->ColRadius
*getScale(), _Sheet
->ColHeight
*getScale(), _Sheet
->ColLength
, _Sheet
->ColWidth
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
, _Sheet
->ClipRadius
, _Sheet
->ClipHeight
);
433 initPrimitive(0.5f
, 2.0f
, 0.0f
, 0.0f
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
);
436 _Primitive
->insertInWorldImage(dynamicWI
);
439 }// computePrimitive //
442 //-----------------------------------------------
443 void CCharacterCL::removeAllAttachedFX()
445 _AttachedFXListForCurrentAnim
.clear();
446 _AttachedFXListToRemove
.clear();
448 for(uint k
= 0; k
< MaxNumAura
; ++k
)
456 //-----------------------------------------------
458 void CCharacterCL::releaseInSceneInterfaces()
460 if (_InSceneUserInterface
)
462 CWidgetManager::getInstance()->unMakeWindow(_InSceneUserInterface
);
463 if (_InSceneUserInterface
->getParent())
465 _InSceneUserInterface
->getParent()->delGroup(_InSceneUserInterface
);
469 delete _InSceneUserInterface
;
472 _InSceneUserInterface
= NULL
;
476 //-----------------------------------------------
477 // stopAttachedFXForCurrrentAnim
478 // Stop all attached fxs linked to current animation
479 //-----------------------------------------------
480 void CCharacterCL::stopAttachedFXForCurrrentAnim(bool stopLoopingFX
)
482 // shutdown fxs for current anim
483 std::list
<CAttachedFX::TSmartPtr
>::iterator itAttachedFx
= _AttachedFXListForCurrentAnim
.begin();
484 while(itAttachedFx
!= _AttachedFXListForCurrentAnim
.end())
486 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpItAttached
= itAttachedFx
;
488 if (!(!stopLoopingFX
&& (*tmpItAttached
)->AniFX
&& (*tmpItAttached
)->AniFX
->Sheet
->RepeatMode
== CAnimationFXSheet::Loop
)) // dont remove looping fx if it is requested
490 // test if emitters should be shutdown at the end of the anim
491 if ((*tmpItAttached
)->AniFX
&&
492 (*tmpItAttached
)->AniFX
->Sheet
->RepeatMode
!= CAnimationFXSheet::Respawn
&&
493 (*tmpItAttached
)->StickMode
!= CFXStickMode::SpawnPermanent
496 if(!(*tmpItAttached
)->FX
.empty())
498 if (!(*tmpItAttached
)->FX
.removeByID(NELID("STOP")) && !(*tmpItAttached
)->FX
.removeByID(NELID("main")))
500 (*tmpItAttached
)->FX
.activateEmitters(false);
504 _AttachedFXListToRemove
.splice(_AttachedFXListToRemove
.begin(), _AttachedFXListForCurrentAnim
, tmpItAttached
);
505 _AttachedFXListToRemove
.front()->TimeOutDate
+= TimeInSec
; // compute absolute timeout date
508 /** Removes fx that lives on more that a given count of animation
509 * Useful if framerate is low to avoid that fx overlap
511 itAttachedFx
= _AttachedFXListToRemove
.begin();
512 while(itAttachedFx
!= _AttachedFXListToRemove
.end())
514 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpItAttachedFX
= itAttachedFx
;
516 if ((*tmpItAttachedFX
)->AniFX
)
518 if ((*tmpItAttachedFX
)->MaxAnimCount
!= 0) // if there a limit to the number of animation during which the fx can live ?
520 (*tmpItAttachedFX
)->MaxAnimCount
-= 1;
521 if ((*tmpItAttachedFX
)->MaxAnimCount
== 0)
524 _AttachedFXListToRemove
.erase(tmpItAttachedFX
);
532 //-----------------------------------------------
534 //-----------------------------------------------
535 void CCharacterCL::applyColorSlot(SInstanceCL
&instance
, sint skin
, sint userColor
, sint hair
, sint eyes
)
537 CColorSlotManager::TIntCouple array
[4];
540 array
[0].first
= (uint
)0; array
[0].second
= (uint
)skin
;
543 array
[1].first
= (uint
)1; array
[1].second
= (uint
)userColor
;
546 array
[2].first
= (uint
)2; array
[2].second
= (uint
)hair
;
549 array
[3].first
= (uint
)3; array
[3].second
= (uint
)eyes
;
552 UInstance inst
= instance
.createLoadingFromCurrent();
555 instance
.setColors(skin
, userColor
, hair
, eyes
);
556 ColorSlotManager
.setInstanceSlot(inst
, array
, 4);
558 }// applyColorSlot //
561 //-----------------------------------------------
563 //-----------------------------------------------
564 void CCharacterCL::changeColors(sint userColor
, sint hair
, sint eyes
, sint part
) // virtual
566 // Color Just a part of the entity.
569 if((uint
)part
< _Instances
.size())
570 applyColorSlot(_Instances
[part
], skin(), userColor
, hair
, eyes
);
572 // Color the whole entity.
575 for(uint i
=0; i
<_Instances
.size(); i
++)
576 applyColorSlot(_Instances
[i
], skin(), userColor
, hair
, eyes
);
580 //-----------------------------------------------
581 // addColoredInstance :
582 //-----------------------------------------------
583 uint32
CCharacterCL::addColoredInstance(const std::string
&shapeName
, const std::string
&stickPoint
, sint texture
, uint32 instIdx
, sint color
)
586 uint32 idx
= addInstance(shapeName
, stickPoint
, texture
, instIdx
);
587 SInstanceCL
*instance
= idx2Inst(idx
);
589 applyColorSlot(*instance
, skin(), color
, _HairColor
, _EyesColor
);
591 nlwarning("CH::addColoredInstance: cannot create the instance for the shape '%s'.", shapeName
.c_str());
594 }// addColoredInstance //
597 //-----------------------------------------------
599 // \param slot: structure of the equipement.
600 // \param visualSlot: visual slot used by this item.
601 // \return uint32 : index of the instance or 0xFFFFFFFF.
602 // \todo GUIGUI : find a better choice to avoid all visualSlot checks
603 //-----------------------------------------------
604 uint32
CCharacterCL::buildEquipment(const CCharacterSheet::CEquipment
&slot
, SLOTTYPE::EVisualSlot visualSlot
, sint color
, uint32 instIdx
)
606 uint32 idx
= CEntityCL::BadIndex
;
608 // Do something only if the slot is not empty.
609 if(slot
.getItem().empty())
612 sint slotColor
= slot
.Color
;
614 // This is a reference on an item (the file is store with an UPPER case so check in UPPER CASE).
615 string ext
= CFile::getExtension(slot
.getItem());
616 if((ext
== "item") || (ext
== "sitem"))
618 // IS the item a valid one ?
620 if(itemId
.buildSheetId(NLMISC::toLowerAscii(slot
.getItem())))
622 // Is it stored in the database ?
623 CEntitySheet
*entitySheet
= SheetMngr
.get(itemId
);
626 _Items
[visualSlot
].Sheet
= dynamic_cast<CItemSheet
*>(entitySheet
);
627 if(_Items
[visualSlot
].Sheet
)
629 const CItemSheet
&itemSheet
= *(_Items
[visualSlot
].Sheet
);
631 // Compute the bind point
636 case SLOTTYPE::RIGHT_HAND_SLOT
:
637 if( itemSheet
.ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
638 bindBone
= "box_arme";
641 case SLOTTYPE::LEFT_HAND_SLOT
:
642 // Shields are not stick to the same point.
643 if(itemSheet
.getAnimSet() == "s")
644 bindBone
= "Box_bouclier";
646 bindBone
= "box_arme_gauche";
649 bindBone
= slot
.getBindPoint();
653 // Choose the right colour.
659 // Get the item color
660 slotColor
= itemSheet
.Color
;
661 // Bad item color -> set to 0
669 idx
= createItemInstance(itemSheet
, instIdx
, visualSlot
, bindBone
, slot
.Texture
, slotColor
);
672 nlwarning("CH::buildEquipment: the sheet '%s' is not an item one.", slot
.getItem().c_str());
675 nlwarning("CH::buildEquipment: the sheet '%s' is not stored in the database.", slot
.getItem().c_str());
678 nlwarning("CH::buildEquipment: item '%s' is not in the Sheet Id list.", slot
.getItem().c_str());
692 idx
= addColoredInstance(slot
.getItem(), slot
.getBindPoint(), slot
.Texture
, instIdx
, slotColor
);
697 }// buildEquipment //
699 //-----------------------------------------------
700 // computeSomeBoneId :
701 // Compute the bone for the name, head...
702 // \warning This method do not check the bone is valid, nor there is a Scene.
703 //-----------------------------------------------
704 void CCharacterCL::computeSomeBoneId()
706 // **** Get the Bone for the name.
707 _NameBoneId
= _Skeleton
.getBoneIdByName("name");
708 // Dummy found -> return the name position.
709 if(_NameBoneId
!= -1)
711 // Just to force the bone to be compute (else not computed if not used).
712 _NameTransform
= Scene
->createTransform();
713 if(!_NameTransform
.empty())
714 _Skeleton
.stickObject(_NameTransform
, _NameBoneId
);
716 // No Bone for the Name.
720 pushDebugStr("The Bone for the name is missing.");
721 #endif // FINAL_VERSION
724 // **** Get the head bone.
725 _HeadBoneId
= _Skeleton
.getBoneIdByName("Bip01 Head");
727 if(_HeadBoneId
== -1)
730 pushDebugStr("The Bone for the Head is missing.");
731 #endif // FINAL_VERSION
735 _TargetAnimCtrl
.EyePos
= CVector::Null
;
736 _Skeleton
.setBoneAnimCtrl(_HeadBoneId
, &_TargetAnimCtrl
);
739 // **** Get the "chest" bone. take spine1.
740 _ChestBoneId
= _Skeleton
.getBoneIdByName("Bip01 Spine1");
741 if(_ChestBoneId
== -1)
744 pushDebugStr("The Bone for the Chest 'Bip01 Spine1' is missing.");
745 #endif // FINAL_VERSION
747 }// computeSomeBoneId //
750 //-----------------------------------------------
752 // Create the play list for this entity.
753 //-----------------------------------------------
754 void CCharacterCL::createPlayList()
756 // Release the old animation playlist.
759 EAM
->deletePlayList(_PlayList
);
763 // Create the new animation playlist.
764 _PlayList
= EAM
->createPlayList();
767 pushDebugStr("Cannot create a playlist for the entity.");
771 // Initialize the new playlist.
773 _PlayList
->setSpeedFactor (MOVE
, 1.f
);
774 _PlayList
->setWrapMode (MOVE
, UPlayList::Clamp
);
776 _PlayList
->setSpeedFactor (ACTION
, 1.f
);
777 _PlayList
->setWrapMode (ACTION
, UPlayList::Clamp
);
778 }// createPlayList //
782 //-----------------------------------------------
784 // retrieve ground fxs for that entity
785 //-----------------------------------------------
786 const std::vector
<CGroundFXSheet
> *CCharacterCL::getGroundFX() const
788 return &(_Sheet
->GroundFX
);
791 //-----------------------------------------------
793 // Build the entity from a sheet.
794 //-----------------------------------------------
795 bool CCharacterCL::build(const CEntitySheet
*sheet
) // virtual
797 // Cast the sheet in the right type.
798 _Sheet
= dynamic_cast<const CCharacterSheet
*>(sheet
);
801 pushDebugStr("This is not a character sheet -> entity not initialized.");
806 Type
= (_Sheet
->Race
>= EGSPD::CPeople::Creature
) ? Fauna
: NPC
;
811 // Get the fauna name in the sheet
812 const char *creatureName
= STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet
->Id
);
813 if (!FINAL_VERSION
|| !NLMISC::startsWith(creatureName
, "<NotExist:"))
814 _EntityName
= creatureName
;
818 // Name and title will be send by the server
822 if(IngameDbMngr
.getNodePtr())
824 CCDBNodeBranch
*nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(IngameDbMngr
.getNodePtr()->getNode(0));
827 _DBEntry
= dynamic_cast<CCDBNodeBranch
*>(nodeRoot
->getNode(_Slot
));
829 pushDebugStr("Cannot get a pointer on the DB entry.");
833 if (!ClientCfg
.Light
&& !_Sheet
->getSkelFilename().empty())
835 // Create the Playlist for the entity.
838 // Compute the first automaton.
839 _CurrentAutomaton
= automatonType() + "_normal.automaton";
841 // Get the Character gender.
842 _Gender
= (GSGENDER::EGender
)_Sheet
->Gender
;
844 // Initialize the internal time.
845 _LastFrameTime
= ((double)T1
) * 0.001;
848 if(!ClientCfg
.Light
&& !_Sheet
->getSkelFilename().empty() && skeleton(_Sheet
->getSkelFilename()))
850 // Set the skeleton scale.
851 skeleton()->setScale(getScale(), getScale(), getScale());
853 // Can create all characters except NPC.
857 if(_Sheet
->EyesColor
>= SheetMngr
.nbEyesColor())
859 if (SheetMngr
.nbEyesColor() == 0)
862 _EyesColor
= rand()%SheetMngr
.nbEyesColor();
866 _EyesColor
= _Sheet
->EyesColor
;
870 if(_Sheet
->HairColor
>= SheetMngr
.nbHairColor())
872 if (SheetMngr
.nbHairColor() == 0)
875 _HairColor
= rand()%SheetMngr
.nbHairColor();
879 _HairColor
= _Sheet
->HairColor
;
882 // -- Dress the character --
886 buildEquipment(_Sheet
->Body
, SLOTTYPE::CHEST_SLOT
);
887 buildEquipment(_Sheet
->Arms
, SLOTTYPE::ARMS_SLOT
);
888 buildEquipment(_Sheet
->Hands
, SLOTTYPE::HANDS_SLOT
);
890 buildEquipment(_Sheet
->Legs
, SLOTTYPE::LEGS_SLOT
);
891 buildEquipment(_Sheet
->Feet
, SLOTTYPE::FEET_SLOT
);
893 _FaceIdx
= buildEquipment(_Sheet
->Face
, SLOTTYPE::FACE_SLOT
);
895 // -- Manage the Head --
897 if(!_Sheet
->Head
.getItem().empty())
900 _HeadIdx
= buildEquipment(_Sheet
->Head
, SLOTTYPE::HEAD_SLOT
, -1, _HeadIdx
);
902 SInstanceCL
*pInstFace
= getFace();
905 if(!pInstFace
->Current
.empty())
906 pInstFace
->Current
.hide();
908 pInstFace
->KeepHiddenWhenLoaded
= true;
915 if(_HairIndex
!= _BadHairIndex
)
916 _HeadIdx
= buildEquipment(_Sheet
->HairItemList
[_HairIndex
], SLOTTYPE::HEAD_SLOT
, -1, _HeadIdx
);
918 SInstanceCL
*pInstFace
= getFace();
920 if(!pInstFace
->Current
.empty())
921 pInstFace
->Current
.show();
925 _RHandInstIdx
= buildEquipment(_Sheet
->ObjectInRightHand
, SLOTTYPE::RIGHT_HAND_SLOT
, -1, _RHandInstIdx
); // In The Right Hand
926 _LHandInstIdx
= buildEquipment(_Sheet
->ObjectInLeftHand
, SLOTTYPE::LEFT_HAND_SLOT
, -1, _LHandInstIdx
); // In The Left Hand
928 // Look is now ready.
931 // Cannot build as long as the alternative look property not received, so hide the entity.
935 // Compute the animation set (after weapons are set to choose the right animation set).
937 // Check the animation set is correct.
938 if(_CurrentAnimSet
[MOVE
] == 0)
939 pushDebugStr("Bad animation set");
941 // Set the animation to idle.
942 setAnim(CAnimationStateSheet::Idle
);
944 // Compute the bone for the name.
947 // Compute pelvis bone
948 //_PelvisBoneId = _Skeleton.getBoneIdByName("Bip01 Pelvis");
950 // Setup Lod Character skeleton and shapes colors, if skeleton exist
951 // Get Lod Character Id from the sheet.
952 sint clodId
= getLodCharacterId(*Scene
, _Sheet
->getLodCharacterName());
955 // Setup Lod Character shapes, if enabled.
956 skeleton()->setLodCharacterShape(clodId
);
957 skeleton()->setLodCharacterDistance(_Sheet
->LodCharacterDistance
);
963 uint32 idx
= buildEquipment(_Sheet
->Body
, SLOTTYPE::CHEST_SLOT
);
964 // must set the scale for the BotObject too
965 if(idx
<_Instances
.size())
968 _Instances
[idx
].setScale(CVector(s
,s
,s
));
972 // Setup _CharacterScalePos
973 _CharacterScalePos
= _Sheet
->CharacterScalePos
;
975 // Adjust the custom scale position according to the entity scale.
976 _CustomScalePos
*= getScale();
978 // Create PACS Primitive.
979 initPrimitive(_Sheet
->ColRadius
*getScale(), _Sheet
->ColHeight
*getScale(), _Sheet
->ColLength
, _Sheet
->ColWidth
, UMovePrimitive::DoNothing
, UMovePrimitive::NotATrigger
, MaskColNpc
, MaskColNone
, _Sheet
->ClipRadius
, _Sheet
->ClipHeight
);
981 // Compute the element to be able to snap the entity to the ground.
982 computeCollisionEntity();
984 // Initialize properties of the entity (selectable/attackable/etc.).
987 // copy some properties (special bot objects). before buildInSceneInterface
988 _DisplayInRadar
= _Sheet
->DisplayInRadar
;
989 _DisplayOSDName
= _Sheet
->DisplayOSDName
;
990 _DisplayOSDBars
= _Sheet
->DisplayOSDBars
;
991 _DisplayOSDForceOver
= _Sheet
->DisplayOSDForceOver
;
992 _Traversable
= _Sheet
->Traversable
;
993 _CanTurn
= _Sheet
->Turn
;
995 _SelectableBySpace
= _Sheet
->SelectableBySpace
;
998 buildInSceneInterface ();
1009 //-----------------------------------------------
1011 //-----------------------------------------------
1012 bool CCharacterCL::isKami() const
1016 return (_Sheet
->Race
== EGSPD::CPeople::Kami
);
1019 //-----------------------------------------------
1021 //-----------------------------------------------
1022 bool CCharacterCL::isUnknownRace() const
1026 return (_Sheet
->Race
== EGSPD::CPeople::Unknown
);
1029 //-----------------------------------------------
1030 // getAttackHeight :
1031 // Return the atk height.
1032 // \todo GUIGUI : height relative to attacker instead of global height
1033 //-----------------------------------------------
1034 CCharacterCL::TAtkHeight
CCharacterCL::getAttackHeight(CEntityCL
*target
, BODY::TBodyPart localisation
, BODY::TSide side
) const
1036 // Check there is a target.
1038 return CCharacterCL::AtkMiddle
;
1039 // Get the position for a bone.
1041 if(target
->getBoneHeight(localisation
, side
, height
))
1045 return CCharacterCL::AtkLow
;
1047 else if(height
> 2.0f
)
1048 return CCharacterCL::AtkHigh
;
1050 // Default is Middle atk.
1051 return CCharacterCL::AtkMiddle
;
1052 }// getAttackHeight //
1054 //-----------------------------------------------
1056 //-----------------------------------------------
1057 bool CCharacterCL::getBoneHeight(BODY::TBodyPart localisation
, BODY::TSide side
, float &height
) const // virtual
1059 // If there is no skeleton return false
1060 if(_Skeleton
.empty())
1062 // Get the Bone Name
1063 const char *boneName
= getBoneNameFromBodyPart(localisation
, side
);
1067 sint boneId
= _Skeleton
.getBoneIdByName(std::string(boneName
));
1070 if(_Skeleton
.isBoneComputed(boneId
) == false)
1072 NL3D::UBone bone
= _Skeleton
.getBone(boneId
);
1073 CMatrix BoneMat
= bone
.getLastWorldMatrixComputed();
1074 height
= (float)(BoneMat
.getPos().z
-pos().z
);
1077 else if(height
> 10.0f
)
1080 }// getBoneHeight //
1083 //-----------------------------------------------
1084 // lookAtItemsInHands :
1085 // Look at items in hands to change the animation set.
1086 // \return true if the mode is a mode where items in hands should be hidden
1087 //-----------------------------------------------
1088 bool CCharacterCL::modeWithHiddenItems() const
1090 return ((ClientCfg
.PutBackItems
&& !isFighting()) || isSit() || _Mode
==MBEHAV::SWIM
|| isRiding() || _Mode
==MBEHAV::SWIM_DEATH
|| _Mode
==MBEHAV::REST
);
1091 }// lookAtItemsInHands //
1094 //-----------------------------------------------
1096 //-----------------------------------------------
1097 string
CCharacterCL::automatonType() const // virtual
1099 return _Sheet
->getAutomaton();
1100 }// automatonType //
1102 //-----------------------------------------------
1103 // computeAutomaton :
1104 // Compute the current automaton for the entity.
1105 //-----------------------------------------------
1106 void CCharacterCL::computeAutomaton()
1108 _CurrentAutomaton
= automatonType() + "_" + NLMISC::toLowerAscii(MBEHAV::modeToString(_Mode
)) + ".automaton";
1109 }// computeAutomaton //
1112 //-----------------------------------------------
1114 // Compute the animation set to use according to weapons, mode and race.
1115 //-----------------------------------------------
1116 void CCharacterCL::computeAnimSet()
1120 // Use the generic method to compute the animation set.
1121 if(!::computeAnimSet(_CurrentAnimSet
[MOVE
], _Mode
, _Sheet
->getAnimSetBaseName(), _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
, _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
, !modeWithHiddenItems()))
1123 nlwarning("CH:computeAnimSet:%d: pb when trying to compute the animset. Sheet Id '%u(%s)'.", _Slot
, _SheetId
.asInt(), _SheetId
.toString().c_str());
1126 }// computeAnimSet //
1128 //-----------------------------------------------
1130 // Adjust the Predicted Interval to fix some errors according to the distance.
1131 //-----------------------------------------------
1132 NLMISC::TGameCycle
CCharacterCL::adjustPI(float x
, float y
, float /* z */, const NLMISC::TGameCycle
&pI
)
1134 NLMISC::TGameCycle adjustedPI
= pI
;
1135 if(ClientCfg
.RestrainPI
&& adjustedPI
> 0)
1137 double dist
= (x
-UserEntity
->pos().x
)*(x
-UserEntity
->pos().x
) + (y
-UserEntity
->pos().y
)*(y
-UserEntity
->pos().y
);
1138 // If under 50m check Predicted Interval
1141 NLMISC::TGameCycle maxPi
= (NLMISC::TGameCycle
)(sqrt(dist
)/5.0)+1;
1142 if(adjustedPI
> maxPi
)
1145 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
1146 nlinfo("CH:updtVPPos:%d: dist'%f' PI'%d' newPI'%d'.", _Slot
, sqrt(dist
), pI
, adjustedPI
);
1153 //-----------------------------------------------
1154 // updateVisualPropertyPos :
1155 // Received a new position for the entity.
1156 // \warning Do not send position for the user
1157 //-----------------------------------------------
1158 void CCharacterCL::updateVisualPropertyPos(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
, const NLMISC::TGameCycle
&pI
)
1160 // Check the DB entry (the warning is already done in the build method).
1163 // Get The property 'Y'.
1164 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1167 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1170 // Get The property 'Z'.
1171 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1174 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1178 // Convert Database into a Position
1179 float x
= (float)(prop
)/1000.0f
;
1180 float y
= (float)(nodeY
->getValue64())/1000.0f
;
1181 float z
= (float)(nodeZ
->getValue64())/1000.0f
;
1183 #ifdef TMP_DEBUG_GUIGUI
1184 // Theoretical Position
1185 _TheoreticalPosition
= CVectorD((double)x
, (double)y
, (double)z
);
1186 #endif // TMP_DEBUG_GUIGUI
1188 // First position Managed -> set the PACS Position
1189 if(_FirstPosManaged
)
1191 pacsPos(CVectorD(x
, y
, z
));
1192 _FirstPosManaged
= false;
1196 // Wait for the entity to be spawned
1200 // Stock the position (except if this is the user mount because it's the user that control him not the server)
1201 if( !isRiding() || _Rider
!= 0)
1203 // Adjust the Predicted Interval to fix some "bug" into the Prediction Algo.
1204 NLMISC::TGameCycle adjustedPI
= adjustPI(x
, y
, z
, pI
);
1206 _Stages
.addStage(gameCycle
, PROPERTY_POSX
, prop
, adjustedPI
);
1207 _Stages
.addStage(gameCycle
, PROPERTY_POSY
, nodeY
->getValue64());
1208 _Stages
.addStage(gameCycle
, PROPERTY_POSZ
, nodeZ
->getValue64());
1210 }// updateVisualPropertyPos //
1212 //-----------------------------------------------
1213 // updateVisualPropertyOrient :
1214 // Received a new orientation.
1215 //-----------------------------------------------
1216 void CCharacterCL::updateVisualPropertyOrient(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1218 #ifdef TMP_DEBUG_GUIGUI
1219 // Backup the last orientation received.
1220 _TheoreticalOrientation
= *(float *)(&prop
);
1221 #endif // TMP_DEBUG_GUIGUI
1223 // New Mode Received.
1226 float ori
= *(float *)(&prop
);
1227 nlinfo("(%05d,%03d) CH::updateVPOri:%d: '%f' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, ori
);
1230 // if no skeleton we set the orientation
1231 if(_Skeleton
.empty())
1233 // server forces the entity orientation even if it cannot turn
1234 front(CVector((float)cos(_TheoreticalOrientation
), (float)sin(_TheoreticalOrientation
), 0.f
), true, true, true);
1235 dir(front(), false, false);
1237 _Primitive
->setOrientation(_TheoreticalOrientation
, dynamicWI
);
1241 if( !isRiding() || _Rider
!= 0 )
1243 // Add in right stage.
1244 _Stages
.addStage(gameCycle
, PROPERTY_ORIENTATION
, prop
);
1247 }// updateVisualPropertyOrient //
1249 //-----------------------------------------------
1250 // updateVisualPropertyMode :
1251 // New mode received.
1252 // \warning For the first mode, we must have received the position and orientation (but this should be the case).
1253 // \warning Read the position or orientation from the database when reading the mode (no more updated in updateVisualPropertyPos and updateVisualPropertyOrient).
1254 //-----------------------------------------------
1255 void CCharacterCL::updateVisualPropertyMode(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1258 nlinfo("(%05d,%03d) CH:updtVPMode:%d: '%s(%d)' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, modeToString((MBEHAV::EMode
)prop
).c_str(), (MBEHAV::EMode
)prop
);
1259 // New Mode Received : Set the Theoretical Current Mode if different.
1260 if(_TheoreticalMode
!= (MBEHAV::EMode
)(prop
& 0xffff))
1261 _TheoreticalMode
= (MBEHAV::EMode
)(prop
& 0xffff);
1264 nlwarning("CH:updtVPMode:%d: The mode '%s(%d)' sent is the same as the current one.", _Slot
, modeToString(_TheoreticalMode
).c_str(), _TheoreticalMode
);
1267 // If it is the first mode, set the mode.
1268 if(_Mode
== MBEHAV::UNKNOWN_MODE
)
1270 // SET THE FIRST POSITION
1271 //-----------------------
1272 // Check the DB entry (the warning is already done in the build method).
1275 // Get The property 'PROPERTY_POSX'.
1276 CCDBNodeLeaf
*nodeX
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSX
));
1279 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSX
);
1282 // Get The property 'PROPERTY_POSY'.
1283 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1286 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1289 // Get The property 'PROPERTY_POSZ'.
1290 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1293 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1296 // Next position will no longer be the first one.
1298 // Insert the primitive into the world.
1300 _Primitive
->insertInWorldImage(dynamicWI
);
1301 // float makes a few cm error
1302 double x
= (double)(nodeX
->getValue64())/1000.0;
1303 double y
= (double)(nodeY
->getValue64())/1000.0;
1304 double z
= (double)(nodeZ
->getValue64())/1000.0;
1305 // Set the primitive position.
1306 pacsPos(CVectorD(x
, y
, z
));
1307 // SET THE FIRST ORIENTATION
1308 //--------------------------
1309 // Get The property 'PROPERTY_ORIENTATION'.
1310 CCDBNodeLeaf
*nodeOri
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_ORIENTATION
));
1313 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot
, CLFECOMMON::PROPERTY_ORIENTATION
);
1317 parts
.i64
[0] = nodeOri
->getValue64();
1318 float angleZ
= parts
.f
[0];
1319 // server forces the entity orientation even if it cannot turn
1320 front(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
), true, true, true);
1321 dir(front(), false, false);
1322 _TargetAngle
= angleZ
;
1324 _Primitive
->setOrientation(angleZ
, dynamicWI
);
1325 // SET THE FIRST MODE
1326 //-------------------
1328 _Mode
= _TheoreticalMode
;
1329 _ModeWanted
= _TheoreticalMode
;
1330 if((_Mode
== MBEHAV::MOUNT_NORMAL
) && (_Rider
== CLFECOMMON::INVALID_SLOT
))
1332 _Mode
= MBEHAV::NORMAL
;
1333 _ModeWanted
= MBEHAV::MOUNT_NORMAL
;
1334 // See also updateVisualPropertyRiderEntity() for the case when _Rider is received after the mode
1337 setAnim(CAnimationStateSheet::Idle
);
1338 // Add the mode to the stage.
1339 _Stages
.addStage(gameCycle
, PROPERTY_MODE
, prop
);
1343 setAnim(CAnimationStateSheet::Idle
);
1345 // Not the first mode -> Add to a stage.
1348 // Add the mode to the stage.
1349 _Stages
.addStage(gameCycle
, PROPERTY_MODE
, prop
);
1350 // Float mode push the orientation
1351 if(_TheoreticalMode
== MBEHAV::COMBAT_FLOAT
)
1353 // Get The property 'PROPERTY_ORIENTATION'.
1354 CCDBNodeLeaf
*nodeOri
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_ORIENTATION
));
1357 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot
, CLFECOMMON::PROPERTY_ORIENTATION
);
1360 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_ORIENTATION
, nodeOri
->getValue64());
1362 // Any other mode push the position
1365 if(_TheoreticalMode
!= MBEHAV::MOUNT_NORMAL
)
1367 // Check the DB entry (the warning is already done in the build method).
1370 // Get The property 'PROPERTY_POSX'.
1371 CCDBNodeLeaf
*nodeX
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSX
));
1374 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSX
);
1377 // Get The property 'PROPERTY_POSY'.
1378 CCDBNodeLeaf
*nodeY
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSY
));
1381 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSY
);
1384 // Get The property 'PROPERTY_POSZ'.
1385 CCDBNodeLeaf
*nodeZ
= dynamic_cast<CCDBNodeLeaf
*>(_DBEntry
->getNode(CLFECOMMON::PROPERTY_POSZ
));
1388 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot
, CLFECOMMON::PROPERTY_POSZ
);
1392 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSX
, nodeX
->getValue64());
1393 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSY
, nodeY
->getValue64());
1394 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_POSZ
, nodeZ
->getValue64());
1398 }// updateVisualPropertyMode //
1400 //-----------------------------------------------
1401 // updateVisualPropertyBehaviour :
1402 // New Behaviour received.
1403 //-----------------------------------------------
1404 void CCharacterCL::updateVisualPropertyBehaviour(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1406 // New Behaviour Received.
1407 CBehaviour
beh(prop
);
1409 nlinfo("(%05d,%03d) CH::updateVPBeha:%d: '%s(%d)' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, behaviourToString((EBehaviour
)beh
.Behaviour
).c_str(), (sint
)beh
.Behaviour
);
1411 // Add in right stage.
1412 _Stages
.addStage(gameCycle
, PROPERTY_BEHAVIOUR
, prop
);
1413 }// updateVisualPropertyBehaviour //
1415 void CCharacterCL::updateVisualPropertyTargetList(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
, uint listIndex
)
1417 // Add in right stage.
1418 _Stages
.addStage(gameCycle
, PROPERTY_TARGET_LIST_0
+ listIndex
, prop
);
1421 void CCharacterCL::updateVisualPropertyVisualFX(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1423 _Stages
.addStage(gameCycle
, PROPERTY_VISUAL_FX
, prop
);
1425 //-----------------------------------------------
1426 // updateVisualPropertyName :
1427 // Received the name Id.
1428 //-----------------------------------------------
1429 void CCharacterCL::updateVisualPropertyName(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1431 // Update the entity name (do not need to be managed with LCT).
1432 uint32 nameId
= *(uint32
*)(&prop
);
1434 // Store the name Id
1437 // STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId, this, &_Name);
1438 STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId
, this);
1440 //if(!getEntityName().empty())
1441 // nlwarning("CH::updateVPName:%d: name Id '%d' received but no name allocated.", _Slot, nameId);
1442 //else if(verboseVP(this))
1443 // nlinfo("(%05d,%03d) CH::updateVPName:%d: name '%s(%d)' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, getEntityName().toString().c_str(), nameId);
1445 updateMissionTarget();
1446 }// updateVisualPropertyName //
1448 //-----------------------------------------------
1449 // updateVisualPropertyTarget :
1450 // Received the new target for the entity
1451 // \todo GUIGUI : should be added in a stage.
1452 //-----------------------------------------------
1453 void CCharacterCL::updateVisualPropertyTarget(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1455 // New target Received.
1456 sint targ
= (sint
)prop
;
1459 nlinfo("(%05d,%03d) CH::updateVPTarget:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, targ
);
1461 // New entity target
1462 _TargetSlotNoLag
= (CLFECOMMON::TCLEntityId
)targ
;
1464 // Add in right stage.
1465 _Stages
.addStage(gameCycle
, PROPERTY_TARGET_ID
, prop
);
1466 }// updateVisualPropertyTarget //
1468 //-----------------------------------------------
1469 // updateVisualPropertyVpa :
1470 // Received the new target for the entity
1471 // \todo GUIGUI : should be added in a stage.
1472 //-----------------------------------------------
1473 void CCharacterCL::updateVisualPropertyVpa(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1475 // VPA only useful for NPC
1478 //nlwarning("CH:updtVPVpa:%d: VPA received but NOT an NPC. Sheet Id '%u(%s)'.", _Slot, _SheetId.asInt(), _SheetId.toString().c_str());
1482 // NO SKELETON -> NO VPA
1483 if(_Skeleton
.empty())
1486 // Get the alternative look property.
1487 SAltLookProp altLookProp
= *(SAltLookProp
*)(&prop
);
1488 // Display debug infos
1491 nlinfo("(%05d,%03d) CH:updtVPVpa:%d: TopColor(%d) BotColor(%d) RH(%d) LH(%d) Hat(%d) Seed(%d) HairColor(%d)",
1492 sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
,
1493 (uint
)altLookProp
.Element
.ColorTop
, (uint
)altLookProp
.Element
.ColorBot
,
1494 (uint
)altLookProp
.Element
.WeaponRightHand
, (uint
)altLookProp
.Element
.WeaponLeftHand
,
1495 (uint
)altLookProp
.Element
.Hat
, (uint
)altLookProp
.Element
.Seed
,
1496 (uint
)altLookProp
.Element
.ColorHair
);
1499 // Dress the character.
1502 // The entity is now visually ready.
1505 // Generate a new random.
1506 NLMISC::CRandom rnd
;
1507 rnd
.srand((sint
)altLookProp
.Element
.Seed
);
1509 // Retrieve the right sheet for clothes.
1510 _ClothesSheet
= _Sheet
;
1511 if(!_Sheet
->IdAlternativeClothes
.empty())
1513 sint32 num
= rnd
.rand()%(_Sheet
->IdAlternativeClothes
.size()+1);
1516 CSheetId
altClothesId(_Sheet
->getAlternativeClothes(num
-1));
1517 const CEntitySheet
*sheetAlt
= SheetMngr
.get(altClothesId
);
1518 if(dynamic_cast<const CCharacterSheet
*>(sheetAlt
))
1519 _ClothesSheet
= dynamic_cast<const CCharacterSheet
*>(sheetAlt
);
1524 if(_Sheet
->EyesColor
>= SheetMngr
.nbEyesColor())
1526 if (SheetMngr
.nbEyesColor() == 0)
1529 _EyesColor
= (sint8
)(rnd
.rand()%SheetMngr
.nbEyesColor());
1532 _EyesColor
= _Sheet
->EyesColor
;
1534 if (SheetMngr
.nbHairColor() == 0)
1537 _HairColor
= (sint8
)altLookProp
.Element
.ColorHair
%SheetMngr
.nbHairColor();
1539 if(!_Sheet
->HairItemList
.empty())
1541 sint32 num
= rnd
.rand()%_Sheet
->HairItemList
.size();
1542 if(num
>=0 && num
<_BadHairIndex
)
1543 _HairIndex
= (uint8
)num
;
1545 nlwarning("CH:updtVPVpa:%d: Bad Hair Index '%d'", _Slot
, num
);
1548 // -- Dress the character -- (all parts that should not change)
1549 /** tmp : remove all fx item
1550 * \TODO delete an item only if changed
1552 buildEquipment(_ClothesSheet
->Body
, SLOTTYPE::CHEST_SLOT
, altLookProp
.Element
.ColorTop
); // Chest
1553 buildEquipment(_ClothesSheet
->Arms
, SLOTTYPE::ARMS_SLOT
, altLookProp
.Element
.ColorArm
); // Arms
1554 buildEquipment(_ClothesSheet
->Hands
, SLOTTYPE::HANDS_SLOT
, altLookProp
.Element
.ColorGlove
); // Gloves
1555 buildEquipment(_ClothesSheet
->Legs
, SLOTTYPE::LEGS_SLOT
, altLookProp
.Element
.ColorBot
); // Legs
1556 buildEquipment(_ClothesSheet
->Feet
, SLOTTYPE::FEET_SLOT
, altLookProp
.Element
.ColorBoot
); // Boots
1558 _FaceIdx
= buildEquipment(_Sheet
->Face
, SLOTTYPE::FACE_SLOT
);
1560 // Entity is now dressed
1564 // -- Manage the Head --
1565 // Display the helm.
1566 if(altLookProp
.Element
.Hat
!=0 && !_ClothesSheet
->Head
.getItem().empty())
1569 _HeadIdx
= buildEquipment(_ClothesSheet
->Head
, SLOTTYPE::HEAD_SLOT
, altLookProp
.Element
.ColorHair
, _HeadIdx
);
1571 SInstanceCL
*pInstFace
= getFace();
1574 if(pInstFace
->Current
.empty() == false)
1575 pInstFace
->Current
.hide();
1577 pInstFace
->KeepHiddenWhenLoaded
= true;
1580 // Display the Hair.
1584 if(_HairIndex
!= _BadHairIndex
)
1585 _HeadIdx
= buildEquipment(_Sheet
->HairItemList
[_HairIndex
], SLOTTYPE::HEAD_SLOT
, altLookProp
.Element
.ColorHair
, _HeadIdx
);
1586 // Display the face.
1587 SInstanceCL
*pInstFace
= getFace();
1589 if(!pInstFace
->Current
.empty())
1590 pInstFace
->Current
.show();
1593 // -- Manage weapons -- (weapons can change)
1596 const CItemSheet
*newRightHand
= SheetMngr
.getItem(SLOTTYPE::RIGHT_HAND_SLOT
, (uint
)altLookProp
.Element
.WeaponRightHand
);
1597 if (newRightHand
!= _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
) // item changed ?
1599 // Remove the old Item in the right hand
1600 if(_RHandInstIdx
!= CEntityCL::BadIndex
)
1603 _RHandInstIdx
= addInstance("", "", -1, _RHandInstIdx
);
1605 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].release();
1608 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
= newRightHand
;
1611 if( newRightHand
->ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
1612 _RHandInstIdx
= createItemInstance(*newRightHand
, _RHandInstIdx
, SLOTTYPE::RIGHT_HAND_SLOT
, "box_arme", -1, -1);
1614 _RHandInstIdx
= createItemInstance(*newRightHand
, _RHandInstIdx
, SLOTTYPE::RIGHT_HAND_SLOT
, "", -1, -1);
1617 // update fx for right hand (trail may have been activated, or advantage fx)
1620 SInstanceCL
*instCLRH
= idx2Inst(_RHandInstIdx
);
1623 NL3D::UInstance itemInstance
= (!instCLRH
->Loading
.empty()) ? instCLRH
->Loading
: instCLRH
->Current
;
1624 if (!itemInstance
.empty())
1627 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].enableAdvantageFX(itemInstance
);
1628 if ( _CurrentBehaviour
.Behaviour
!= MBEHAV::EXTRACTING
)
1629 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].setTrailSize(altLookProp
.Element
.RTrail
);
1635 const CItemSheet
*newLeftHand
= SheetMngr
.getItem(SLOTTYPE::LEFT_HAND_SLOT
, (uint
)altLookProp
.Element
.WeaponLeftHand
);
1636 if (newLeftHand
!= _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
) // item changed ?
1638 // Remove the old Item in the left hand
1639 if(_LHandInstIdx
!= CEntityCL::BadIndex
)
1642 _LHandInstIdx
= addInstance("", "", -1, _LHandInstIdx
);
1644 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].release();
1647 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
= newLeftHand
;
1651 if(newLeftHand
->getAnimSet() == "s")
1652 bindBone
= "Box_bouclier";
1654 bindBone
= "box_arme_gauche";
1655 _LHandInstIdx
= createItemInstance(*newLeftHand
, _LHandInstIdx
, SLOTTYPE::LEFT_HAND_SLOT
, bindBone
, -1, -1);
1658 // update fx for left hand (trail may have been activated, or advantage fx)
1661 SInstanceCL
*instCLLH
= idx2Inst(_LHandInstIdx
);
1664 NL3D::UInstance itemInstance
= (!instCLLH
->Loading
.empty()) ? instCLLH
->Loading
: instCLLH
->Current
;
1665 if (!itemInstance
.empty())
1668 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].enableAdvantageFX(itemInstance
);
1669 _Items
[SLOTTYPE::LEFT_HAND_SLOT
].setTrailSize((uint
) (2 * altLookProp
.Element
.LTrail
));
1675 // -- Update Animation -- (after all those changes animation could change).
1677 setAnim(animState(MOVE
));
1678 }// updateVisualPropertyVpb //
1680 //-----------------------------------------------
1681 // updateVisualPropertyVpb :
1682 //-----------------------------------------------
1683 void CCharacterCL::updateVisualPropertyVpb(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1685 // Get the alternative look property.
1686 SAltLookProp2 altLookProp
= *(SAltLookProp2
*)(&prop
);
1687 // Display debug infos
1690 nlinfo("(%05d,%03d) CH:updtVPVpb:%d: Scale(%d)", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
,
1691 (uint
)altLookProp
.PropertySubData
.Scale
);
1694 float oldCustomScale
= _CustomScale
;
1696 if (altLookProp
.PropertySubData
.Scale
==0)
1699 _CustomScale
= (float)altLookProp
.PropertySubData
.Scale
/100.f
;
1700 // Apply modification
1701 _CustomScalePos
/= oldCustomScale
;
1702 _CustomScalePos
*= _CustomScale
;
1703 // change the scale of the skeleton according to the new people
1704 USkeleton
* skel
= skeleton();
1707 skel
->setScale(getScale(), getScale(), getScale());
1709 // modify the stick bone scale to not propagate scale to child
1710 sint boneID
= skel
->getBoneIdByName("stick_1");
1713 UBone bone
= skel
->getBone(boneID
);
1714 CVector newBoneScale
= bone
.getScale() * oldCustomScale
/_CustomScale
;
1715 bone
.setScale( newBoneScale
);
1718 // must set the new scale for the BotObject too
1719 else if(!_Instances
.empty())
1721 float s
= getScale();
1722 _Instances
[0].setScale(CVector(s
,s
,s
));
1729 _Primitive
->getSize(width
, depth
);
1730 UMovePrimitive::TType primtype
= _Primitive
->getPrimitiveType();
1731 _Primitive
->setPrimitiveType(UMovePrimitive::_2DOrientedBox
);
1732 _Primitive
->setSize((width
/ oldCustomScale
) * _CustomScale
, (depth
/ oldCustomScale
) * _CustomScale
);
1733 _Primitive
->setPrimitiveType(primtype
);
1736 }// updateVisualPropertyVpb //
1738 //-----------------------------------------------
1739 // updateVisualPropertyEntityMounted :
1740 // Update Entity Mount
1741 //-----------------------------------------------
1742 void CCharacterCL::updateVisualPropertyEntityMounted(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1744 // New target Received.
1745 sint mountSlot
= (sint
)prop
;
1746 _TheoreticalMount
= (CLFECOMMON::TCLEntityId
)mountSlot
;
1748 nlinfo("(%05d,%03d) CH::updateVPMount:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, mountSlot
);
1750 // Add in right stage.
1751 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
, prop
);
1752 }// updateVisualPropertyEntityMounted //
1754 //-----------------------------------------------
1755 // updateVisualPropertyRiderEntity :
1756 // Update Entity Rider
1757 //-----------------------------------------------
1758 void CCharacterCL::updateVisualPropertyRiderEntity(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1760 // New target Received.
1761 sint riderSlot
= (sint
)prop
;
1762 _TheoreticalRider
= (CLFECOMMON::TCLEntityId
)riderSlot
;
1764 nlinfo("(%05d,%03d) CH::updateVPRider:%d: '%d' received.", sint32(T1
%100000), NetMngr
.getCurrentServerTick(), _Slot
, riderSlot
);
1766 // Add in right stage.
1767 _Stages
.addStage(gameCycle
, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
, prop
);
1768 }// updateVisualPropertyRiderEntity //
1770 //-----------------------------------------------
1771 // updateVisualPropertyBars :
1772 // Update Entity Bars
1773 //-----------------------------------------------
1774 void CCharacterCL::updateVisualPropertyBars(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
) // virtual
1776 CBarManager::CBarInfo barInfo
;
1778 // Encode HP to 7 bits
1779 barInfo
.Score
[SCORES::hit_points
] = (sint8
)((prop
&0x7ff) * 127 / 1023);
1780 // NB: barInfo are sint8, but no problem, since anything following is 7 bits.
1781 barInfo
.Score
[SCORES::stamina
] = (uint8
)((prop
>>11)&0x7f);
1782 barInfo
.Score
[SCORES::sap
] = (uint8
)((prop
>>18)&0x7f);
1783 barInfo
.Score
[SCORES::focus
] = (uint8
)((prop
>>25)&0x7f);
1785 // update The Bar manager
1786 CBarManager
*pBM
= CBarManager::getInstance();
1788 WHY gameCycle+1 ????? (yoyo)
1789 It's because sometimes I have a bug With target DB update and VP update. This is the scenario
1790 where I suppose the problem rises:
1791 tick=320: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1792 tick=321: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=321
1793 EGS::databaseUpdate(), DB updated, with timestamp=321!!!
1795 Thus I receives on client:
1796 first the VP with Hp=10, timestamp=321
1797 second the DB with Hp=49, timestamp=321 too => replaced => BUG
1798 NB: DB is typically sent at low frequency by FrontEnd, thus received later on client.
1800 Since databaseUpdate() is called every 2 ticks, adding +1 to VP timestamps solve easily the problem.
1802 NB: the problem occurs because tickUpdate() and databaseUpdate() are called typically with 1 tick shift (tickUpdate()
1803 on even ticks, and databaseUpdate() on odd ticks for instance).
1805 NB: moreover, tickupdate() is called every 8 (or 16) ticks, and databaseUpdate() every 2 ticks. So there is one more
1807 318: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1808 319: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=319
1809 EGS::databaseUpdate(), BUT decide to send only a small subset of DB (because lot of things to send)
1810 => our TargetHP is not updated
1811 320: nothing. tickupdate() is not called, since every 8 ticks
1812 321: EGS::databaseUpdate(), update TargetHP, with timestamp=321 !!!!! => Bug
1814 (remind that we cannot store a timestamp for each DB property, else would be too big to store and to send...)
1816 BTW, this last bug should be very rare, so don't care.
1818 pBM
->updateBars(dataSetId(), barInfo
, gameCycle
+1,
1819 CBarManager::HpFlag
| CBarManager::StaFlag
| CBarManager::SapFlag
| CBarManager::FocusFlag
);
1821 }// updateVisualPropertyBars //
1823 //-----------------------------------------------
1824 // updateVisualPropertyGuildSymbol :
1826 //-----------------------------------------------
1827 void CCharacterCL::updateVisualPropertyGuildSymbol(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1829 _GuildSymbol
= prop
;
1831 // maybe need to rebuild the in scene interface
1832 if(_InSceneUserInterface
&& _InSceneUserInterface
->needGuildSymbolId())
1833 buildInSceneInterface();
1834 } // updateVisualPropertyGuildSymbol //
1836 //-----------------------------------------------
1837 // updateVisualPropertyGuildNameID :
1839 //-----------------------------------------------
1840 void CCharacterCL::updateVisualPropertyGuildNameID(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1842 _GuildNameId
= uint32(prop
);
1844 // maybe need to rebuild the in scene interface
1845 if(_InSceneUserInterface
&& _InSceneUserInterface
->needGuildNameId())
1846 buildInSceneInterface();
1847 } // updateVisualPropertyGuildNameID //
1849 //-----------------------------------------------
1850 // updateVisualPropertyEventFactionID :
1852 //-----------------------------------------------
1853 void CCharacterCL::updateVisualPropertyEventFactionID(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1855 // ODD Hack by Ulukyn
1856 //_EventFactionId = uint32(prop);
1857 _PvpMode
= uint32(prop
);
1859 buildInSceneInterface();
1864 uint numEntity
= (uint
)EntitiesMngr
.entities().size();
1865 for (i
=0; i
<numEntity
; i
++)
1867 CEntityCL
*entity
= EntitiesMngr
.entity(i
);
1870 CCharacterCL
*character
= dynamic_cast<CCharacterCL
*>(entity
);
1873 if( character
->getPvpMode() != 0 && !character
->isUser())
1874 character
->buildInSceneInterface ();
1879 } // updateVisualPropertyEventFactionID //
1881 //-----------------------------------------------
1882 // updateVisualPropertyPvpMode :
1884 //-----------------------------------------------
1885 void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1887 //_PvpMode = uint32(prop);
1888 //buildInSceneInterface();
1890 } // updateVisualPropertyPvpMode //
1892 //-----------------------------------------------
1893 // updateVisualPropertyPvpClan :
1895 //-----------------------------------------------
1896 void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1898 _LeagueId
= uint32(prop
);
1900 buildInSceneInterface();
1905 uint numEntity
= (uint
)EntitiesMngr
.entities().size();
1906 for (i
=0; i
<numEntity
; i
++)
1908 CEntityCL
*entity
= EntitiesMngr
.entity(i
);
1911 CCharacterCL
*character
= dynamic_cast<CCharacterCL
*>(entity
);
1914 if( character
->getPvpMode() != 0 && !character
->isUser())
1915 character
->buildInSceneInterface ();
1920 } // updateVisualPropertyPvpClan //
1922 //-----------------------------------------------
1923 // updateVisualPropertyStatus :
1924 // Update Entity Status
1925 //-----------------------------------------------
1926 void CCharacterCL::updateVisualPropertyStatus(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&/* prop */) // virtual
1928 nlinfo("CH:updtVPStatus:%d: received.", _Slot
);
1929 }// updateVisualPropertyStatus //
1931 //-----------------------------------------------
1932 // updateVisualPropertyContextual :
1933 // Update Entity Status
1934 //-----------------------------------------------
1935 void CCharacterCL::updateVisualPropertyContextual(const NLMISC::TGameCycle
&gameCycle
, const sint64
&prop
)
1937 bool precAttackable
= _Properties
.attackable();
1940 CEntityCL::updateVisualPropertyContextual(gameCycle
, prop
);
1942 // if attack modified, and npc/fauna, must rebuild the in scene interface,
1943 // cause sheets 'Attackable' property not always correclty filled
1944 if( (isNPC()||isFauna()) && precAttackable
!=_Properties
.attackable())
1945 buildInSceneInterface();
1949 //-----------------------------------------------
1950 // updateVisualPropertyOwnerPeople :
1952 //-----------------------------------------------
1953 void CCharacterCL::updateVisualPropertyOwnerPeople(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
1955 if( _OwnerPeople
!= MOUNT_PEOPLE::TMountPeople(prop
) )
1958 float oldPeopleScaleFactor
;
1959 switch( _OwnerPeople
)
1961 case MOUNT_PEOPLE::Fyros
: oldPeopleScaleFactor
= ClientCfg
.FyrosScale
; break;
1962 case MOUNT_PEOPLE::Matis
: oldPeopleScaleFactor
= ClientCfg
.MatisScale
; break;
1963 case MOUNT_PEOPLE::Tryker
: oldPeopleScaleFactor
= ClientCfg
.TrykerScale
; break;
1964 case MOUNT_PEOPLE::Zorai
: oldPeopleScaleFactor
= ClientCfg
.ZoraiScale
; break;
1966 oldPeopleScaleFactor
= 1.f
;
1968 _CustomScalePos
/= oldPeopleScaleFactor
;
1970 // set the new scale pos
1971 float newPeopleScaleFactor
;
1972 _OwnerPeople
= MOUNT_PEOPLE::TMountPeople(prop
);
1973 switch( _OwnerPeople
)
1975 case MOUNT_PEOPLE::Fyros
: newPeopleScaleFactor
= ClientCfg
.FyrosScale
; break;
1976 case MOUNT_PEOPLE::Matis
: newPeopleScaleFactor
= ClientCfg
.MatisScale
; break;
1977 case MOUNT_PEOPLE::Tryker
: newPeopleScaleFactor
= ClientCfg
.TrykerScale
; break;
1978 case MOUNT_PEOPLE::Zorai
: newPeopleScaleFactor
= ClientCfg
.ZoraiScale
; break;
1980 newPeopleScaleFactor
= 1.f
;
1982 _CustomScalePos
*= newPeopleScaleFactor
;
1984 // change the scale of the skeleton according to the new people
1985 USkeleton
* skel
= skeleton();
1988 skel
->setScale(getScale(), getScale(), getScale());
1990 // modify the stick bone scale to not propagate scale to child
1991 sint boneID
= skel
->getBoneIdByName("stick_1");
1994 UBone bone
= skel
->getBone(boneID
);
1995 CVector newBoneScale
= bone
.getScale() * oldPeopleScaleFactor
/newPeopleScaleFactor
;
1996 bone
.setScale( newBoneScale
);
2001 } // updateVisualPropertyOwnerPeople //
2005 //-----------------------------------------------
2006 // updateVisualPropertyOutpostInfos :
2008 //-----------------------------------------------
2009 void CCharacterCL::updateVisualPropertyOutpostInfos(const NLMISC::TGameCycle
&/* gameCycle */, const sint64
&prop
)
2011 _OutpostId
= ((uint16
)prop
)&0x7FFF;
2012 uint16 side
= (((uint16
)prop
)&0x8000)>>15;
2013 _OutpostSide
= (OUTPOSTENUMS::TPVPSide
)side
;
2015 nldebug("<CCharacterCL::updateVisualPropertyOutpostInfos> prop = %d, id=%d side=%d",(uint16
)prop
,_OutpostId
,_OutpostSide
);
2017 buildInSceneInterface();
2019 } // updateVisualPropertyOutpostInfos //
2022 //-----------------------------------------------
2024 // Get The Entity Skin
2025 //-----------------------------------------------
2026 sint
CCharacterCL::skin() const // virtual
2028 return _Sheet
->Skin
;
2032 //-----------------------------------------------
2034 // Initialize properties of the entity (according to the class).
2035 //-----------------------------------------------
2036 void CCharacterCL::initProperties()
2038 properties().selectable(_Sheet
->Selectable
);
2039 properties().talkableTo(_Sheet
->Talkable
);
2040 properties().attackable(_Sheet
->Attackable
);
2041 properties().givable(_Sheet
->Givable
);
2042 properties().mountable(_Sheet
->Mountable
);
2043 properties().invitable(false); // You cannot group with a bot.
2044 properties().afk(false);
2046 switch(_Sheet
->HLState
)
2048 case LHSTATE::LOOTABLE
:
2049 properties().lootable(true); // You can loot the creature
2050 properties().harvestable(false); // You cannot harvest the creature
2052 case LHSTATE::HARVESTABLE
:
2053 properties().lootable(false); // You cannot loot the creature
2054 properties().harvestable(true); // You can harvest the creature
2056 case LHSTATE::LOOTABLE_HARVESTABLE
:
2057 properties().lootable(true); // You can loot the creature
2058 properties().harvestable(true); // You can harvest the creature
2062 properties().lootable(false); // You cannot loot the creature
2063 properties().harvestable(false); // You cannot harvest the creature
2066 }// initProperties //
2069 //-----------------------------------------------
2070 // computeTimeStep :
2071 // Compute the elapsed time since last call.
2072 // \param currentTime : current time in sec.
2073 // \return double : elapsed time.
2074 //-----------------------------------------------
2075 double CCharacterCL::computeTimeStep(const double ¤tTime
)
2077 // Last Time greater than Current Time.
2078 if(_LastFrameTime
> currentTime
)
2080 nlwarning("CCharacterCL::computeTimeStep : Time since last frame is negative (%f). Set _LastFrameTime with currentTime", _LastFrameTime
- currentTime
);
2081 _LastFrameTime
= currentTime
;
2084 // Time since last Time >= 0
2085 return currentTime
- _LastFrameTime
;
2086 }// computeTimeStep //
2089 //-----------------------------------------------
2091 // \todo GUIGUI : to do an average speed, there is a problem if time become very small because small frame or _LastFrameTime increase when looping
2092 //-----------------------------------------------
2093 double CCharacterCL::computeSpeed()
2096 double t
= _DestTime
- _LastFrameTime
;
2097 if(dist2Dest() <= 0.0) // Already at destination.
2099 else if(_LastFrameTime
== 0.0) // First frame
2101 else if(t
< 0.0001) // We should already be at destination.
2102 spd
= 1000.0;//-1.0;
2105 spd
= dist2Dest()/t
;
2106 if(spd
>0 && spd
<0.001)
2115 //-----------------------------------------------
2116 // computeSpeedFactor :
2117 // Compute and return the speed factor to apply to the animation.
2118 // \param speedToDest : evaluted speed to destination.
2119 // \return double : the speed factor to use for the current animation.
2120 // \todo GUIGUI : review this scale problem and optimize it.
2121 //-----------------------------------------------
2122 double CCharacterCL::computeSpeedFactor(double speedToDest
)
2124 double speedFactor
= 1.0;
2126 // \todo GUIGUI : optimize emotes, currently it's badly designed.
2127 const CAnimationState
*animStatePtr
;
2128 // If the current animation is an emote, get the right animation state.
2129 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
2130 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
2132 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
2135 // Get the animation
2136 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
2139 // If the animation is a move (not idle, turn, etc.).
2142 // Move : adjust speed factor according to the animation.
2143 if(_CurrentState
->Move
)
2145 // Check for oo speed.
2146 if(speedToDest
!= -1)
2148 // Compute the animation average speed according to the scale.
2149 double animSpeed
= EAM
->getAnimationAverageSpeed(anim
->id())*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
2151 speedFactor
= speedToDest
/ animSpeed
;
2153 nlwarning("The animation is a move but animation speed is %f !", animSpeed
);
2155 // \todo GUIGUI : unlimited speed, perhaps return a special value.
2156 // We should be arrived so speed is maximum.
2158 speedFactor
= 1000.0;
2160 // The current animation is a rotation so adjust the rotation speed according to the angle.
2161 if(_CurrentState
->Rotation
&& _RotationFactor
> 0.0)
2163 speedFactor
/= _RotationFactor
;
2164 speedFactor
= std::min(speedFactor
, 1.5);
2165 speedFactor
= std::max(speedFactor
, 0.5);
2167 // This animation must be play in a maximum time when there is someting to do after.
2168 // (must be done after the rotation speed adjustment)
2169 if(_CurrentState
->MaxAnimDuration
> 0.00f
)
2171 if(dist2Dest() >= 0.0)
2173 double animLength
= EAM
->getAnimationLength(anim
->id());
2174 double speedFactorBackup
= speedFactor
;
2175 speedFactor
= animLength
/_CurrentState
->MaxAnimDuration
;
2176 // If the animation speed should have been greater, let it play faster.
2177 if(speedFactor
< speedFactorBackup
)
2178 speedFactor
= speedFactorBackup
;
2181 // Panic mode (too late => accelerate)
2182 if(!_CurrentState
->Move
&& _ImportantStepTime
!=0.0)
2184 const float beginPanic
= 1.5f
; // start panic when too late of 1.5 seconds
2185 const float endPanic
= 5.f
; // max panic factor at 5 seconds of interval
2186 const float maxPanicSpeedFactor
= 10.f
;
2187 float t
= float(TimeInSec
- _ImportantStepTime
);
2190 float lerp
= (t
-beginPanic
)/(endPanic
-beginPanic
);
2191 clamp(lerp
, 0.f
, 1.f
);
2192 float panicSF
= lerp
*maxPanicSpeedFactor
;
2193 if(panicSF
>speedFactor
)
2194 speedFactor
= panicSF
;
2197 // Special panic mode because there is a position just after
2198 // NB: don't accelerate animations that can be breaked because of move
2199 // But still allow acceleration for mode animation transition (_Mode!=_ModeWanted)
2200 if( !_CurrentState
->Move
&& _RunStartTimeNoPop
!=INVALID_TIME
&&
2201 (!_CurrentState
->BreakableOnMove
|| _Mode
!=_ModeWanted
) )
2203 const float maxPanicSpeedFactor
= 1.5f
;
2204 // compare time of rest of animation, to remain time to the first move
2205 float remainAnimTime
= float(EAM
->getAnimationLength(anim
->id()) - animOffset(MOVE
));
2206 remainAnimTime
= max(remainAnimTime
, 0.f
);
2208 // if already too late, then maximize speed
2209 if(TimeInSec
>=_RunStartTimeNoPop
)
2211 panicSF
= maxPanicSpeedFactor
;
2213 // else target the animation speed so it ends at estimated start of move
2216 panicSF
= float(remainAnimTime
/(_RunStartTimeNoPop
-TimeInSec
));
2217 panicSF
= min(panicSF
, maxPanicSpeedFactor
);
2219 // only if greater than prec
2220 if(panicSF
>speedFactor
)
2221 speedFactor
= panicSF
;
2226 if(_Slot==WatchedEntitySlot && EntitiesMngr.isLogingStageChange())
2228 sint64 refLT= EntitiesMngr.getLogStageChangeStartLocalTime();
2229 NLMISC::createDebug();
2230 NLMISC::DebugLog->displayRawNL("** Entity %d: (t=%3d) animState %s: %.2f/%.2f. move: %d. rstnp: %d. sf: %.2f",
2231 (sint32)_Slot, sint32(T1-refLT),
2232 CAnimationState::getAnimationStateName(animStatePtr->state()).c_str(),
2233 float(animOffset(MOVE)), float(EAM->getAnimationLength(anim->id())),
2234 uint(_CurrentState->Move), _RunStartTimeNoPop==INVALID_TIME?-1:sint32(sint64(_RunStartTimeNoPop*1000)-refLT),
2243 // \todo GUIGUI : corriger pour enlever le speed factor min.
2244 // Adjuste speed factor
2245 if(speedFactor
< 0.5)
2248 // Check negative or null speed factor.
2249 if(speedFactor
<= 0.0)
2251 nlwarning("CCharacterCL::computeSpeedFactor: speedFactor = %f and this should never be <= 0.", speedFactor
);
2255 // Return the speed factor.
2257 }// computeSpeedFactor //
2259 //-----------------------------------------------
2260 // endAnimTransition :
2261 // Call it at the end of the current animation to choose the next one.
2262 //-----------------------------------------------
2263 void CCharacterCL::endAnimTransition()
2265 // One more animation played.
2268 // Hide the entity if needed.
2272 // Debug Animation for the selection
2273 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
2274 nlinfo("CH:endAnimTransition:%d: current animation finished.", _Slot
);
2275 // If the animation is a rotation, set the character direction he should have at the end of the animation.
2276 if(_CurrentState
->Rotation
)
2280 nldebug("<CCharacterCL::endAnimTransition> rotation : set dir as end anim dir");
2285 // Fit the current direction to the target when attacking.
2286 if(_CurrentState
->Attack
)
2290 // If user is in range attack and not moving, set dir to target
2293 if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
&& _Mode
==MBEHAV::COMBAT
&& !UserControls
.isMoving())
2295 dir( dirToTarget() );
2297 float frontYawBefore
= frontYaw();
2299 float frontYawAfter
= frontYaw();
2300 float deltaYaw
= frontYawAfter
- frontYawBefore
;
2301 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
2303 if( ClientCfg
.AutomaticCamera
)
2305 UserControls
.resetSmoothCameraDeltaYaw();
2310 // If the next mode in the automaton != Current Mode
2311 if(_CurrentState
->NextMode
!= _Mode
)
2313 // Undo previous behaviour
2314 if (_Mode
== MBEHAV::DEATH
)
2316 // Restore collisions.
2319 // TODO: Without this dynamic cast
2320 if (dynamic_cast<CPlayerCL
*>(this))
2321 _Primitive
->setOcclusionMask(MaskColPlayer
);
2323 _Primitive
->setOcclusionMask(MaskColNpc
);
2327 if (ClientCfg
.UsePACSForAll
&& _Primitive
)
2328 _Primitive
->setCollisionMask(MaskColNone
);
2331 switch(_CurrentState
->NextMode
)
2334 case MBEHAV::COMBAT
:
2335 case MBEHAV::COMBAT_FLOAT
:
2336 if(ClientCfg
.UsePACSForAll
&& _Primitive
)
2337 _Primitive
->setCollisionMask(MaskColPlayer
| MaskColNpc
| MaskColDoor
); // Collision with player and npc.
2340 case MBEHAV::MOUNT_NORMAL
:
2342 // Remove collisions if needed.
2343 if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
2346 _Primitive
->setOcclusionMask(MaskColNone
);
2354 case MBEHAV::UNKNOWN_MODE
:
2357 // NO BREAK is Normal here
2359 case MBEHAV::NORMAL
:
2360 // _Mount = CLFECOMMON::INVALID_SLOT;
2361 // _Rider = CLFECOMMON::INVALID_SLOT;
2362 parent(CLFECOMMON::INVALID_SLOT
);
2365 front(CVector(1.f, 0.f, 0.f));
2372 // Change the current mode.
2373 if ( _ModeWanted
!= MBEHAV::UNKNOWN_MODE
)
2375 _Mode
= _CurrentState
->NextMode
;
2378 // nlinfo( "NO mode change from %u to %u, %s S%hu", _Mode, _CurrentState->NextMode, _SheetId.toString().c_str(), _Slot );
2379 // Change the current automaton.
2381 // Update the animation set according to the new automaton.
2384 // Select the Default Next Animation.
2385 setAnim(_CurrentState
->NextState
);
2386 }// endAnimTransition //
2388 //-----------------------------------------------
2390 //-----------------------------------------------
2391 CCharacterCL::TOnMove
CCharacterCL::onMove(const CAutomatonStateSheet
&curAnimState
)
2393 // Animation is breakable if the distance to destination is long enough (at least > 0).
2394 if(curAnimState
.BreakableOnMove
&& dist2Dest()>0.0)
2396 // \todo GUIGUI : take the next position to current one (it could be possible this position was the same as the first).
2397 CVectorD dirToFirstPos
= _FirstPos
-pos();
2398 dirToFirstPos
.z
= 0.0;
2399 if(dirToFirstPos
!= CVectorD::Null
)
2400 dirToFirstPos
.normalize();
2403 nlwarning("CH:onMove:%d: First pos == pos -> take the dir(%f,%f,%f) instead.", _Slot
, dir().x
, dir().y
, dir().z
);
2404 dirToFirstPos
= dir();
2407 // Compute Angle between the front and the first position.
2408 double angToDest
= computeShortestAngle(atan2(front().y
, front().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2412 if(curAnimState
.OnMoveLeft
!= CAnimationStateSheet::UnknownState
)
2413 if((angToDest
>Pi
/3.0) && (angToDest
<2.0*Pi
/3.0))
2416 if(curAnimState
.OnMoveRight
!= CAnimationStateSheet::UnknownState
)
2417 if((angToDest
<-Pi
/3.0) && (angToDest
>-2.0*Pi
/3.0))
2420 if(curAnimState
.OnMoveBackward
!= CAnimationStateSheet::UnknownState
)
2421 if(fabs(angToDest
)>1.92)
2422 return OnMoveBackward
;
2424 // Forward (default)
2425 if(curAnimState
.OnMoveForward
!= CAnimationStateSheet::UnknownState
)
2427 // if(_MaxLoop || fabs(angToDest)<=1.92)
2428 return OnMoveForward
;
2431 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
);
2437 //-----------------------------------------------
2439 //-----------------------------------------------
2440 CCharacterCL::TOnRot
CCharacterCL::onRotation(const CAutomatonStateSheet
&curAnimState
, CVector
&dirEndA
)
2442 // Turn and About Face
2443 if(curAnimState
.BreakableOnRotation
)
2445 CVector bodyDir
= dir();
2446 CVector destDir
= front();
2448 // when user is attacking, his dest is his target
2449 if( isUser() && _CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
&& _Mode
==MBEHAV::COMBAT
&& !UserControls
.isMoving() )
2451 destDir
= dirToTarget();
2454 // Compute the angle between the current heading and computed heading.
2455 double angToDest
= computeShortestAngle(atan2(bodyDir
.y
, bodyDir
.x
), atan2(destDir
.y
, destDir
.x
));
2456 // Rotation to the left.
2457 if(angToDest
>= (ClientCfg
.AnimatedAngleThreshold
*Pi
/180.0))
2462 // Rotation to the right.
2463 if(angToDest
<= -(ClientCfg
.AnimatedAngleThreshold
*Pi
/180.0))
2473 //-----------------------------------------------
2475 //-----------------------------------------------
2476 CCharacterCL::TOnBigBend
CCharacterCL::onBigBend(const CAutomatonStateSheet
&curAnimState
, CVector
&dirEndA
)
2478 // If the current direction is too different of the direction to the first destination -> play a rotation.
2479 if(curAnimState
.BrkOnBigBend
)
2481 CVector dirToFirstPos
;
2482 if(setVect(dirToFirstPos
, _FirstPos
- pos(), true, false))
2484 dirToFirstPos
= curAnimState
.getMatrix()*dirToFirstPos
;
2485 double angToDest
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2486 // Rotation to the left.
2487 if(angToDest
>= 1.5)
2489 dirEndA
= dirToFirstPos
;
2492 // Rotation to the right.
2493 if(angToDest
<= -1.5)
2495 dirEndA
= dirToFirstPos
;
2499 if(fabs(angToDest
) > 0.1)
2501 double newAngle
= atan2(dir().y
, dir().x
) + angToDest
/2.0;
2502 dir(CVector((float)cos(newAngle
), (float)sin(newAngle
), dir().z
));
2512 //-----------------------------------------------
2514 //-----------------------------------------------
2515 bool CCharacterCL::onBadHeading(const CAutomatonStateSheet
&curAnimState
)
2518 if(curAnimState
.BreakableOnBadHeading
&& !_MaxLoop
)
2520 CVector dirToFirstPos
;
2521 if(setVect(dirToFirstPos
, _FirstPos
- pos(), true, false))
2523 // Compute the angle between the front and the next body direction.
2524 double angToDest
= computeShortestAngle(atan2(front().y
, front().x
), atan2(dirToFirstPos
.y
, dirToFirstPos
.x
));
2525 if(curAnimState
.BadHeadingMin
<= curAnimState
.BadHeadingMax
)
2527 if((angToDest
<curAnimState
.BadHeadingMin
) || (angToDest
>curAnimState
.BadHeadingMax
))
2532 if((angToDest
<curAnimState
.BadHeadingMin
) && (angToDest
>curAnimState
.BadHeadingMax
))
2541 //-----------------------------------------------
2543 // Select a new animation for the entity.
2544 // \todo GUIGUI : better manage when there is no skeleton
2545 // \todo GUIGUI : simplify head control
2546 //-----------------------------------------------
2547 ADD_METHOD(void CCharacterCL::setAnim(TAnimStateKey newKey
, TAnimStateKey subKey
, uint animID
))
2551 // Debug Animation for the target
2552 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
2553 nlinfo("CH:setAnim:%d: state '%s'.", _Slot
, CAnimationState::getAnimationStateName (newKey
).c_str());
2556 // RELEASE THE OLD ANIMATION
2557 TAnimStateId lastAnimStateId
= animState(MOVE
); // Bkup the current anim state Id.
2559 // \todo GUIGUI : Faire une fonction pour savoir si on a changer de type d'animation.
2560 // Reset loop counter
2561 if((newKey
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
))
2564 // RESET THE CURRENT ANIMATION
2566 CAnimation::TAnimId buIndex
= animIndex(MOVE
);
2567 animIndex (MOVE
, CAnimation::UnknownAnim
); // Reset the current animation Index
2568 animState (MOVE
, CAnimationStateSheet::UnknownState
); // Reset the current animation state
2569 animOffset(MOVE
, 0.0); // Reset the current animation offset
2570 animIndex (MOVE_BLEND_OUT
, CAnimation::UnknownAnim
); // Reset the current animation Index
2571 animState (MOVE_BLEND_OUT
, CAnimationStateSheet::UnknownState
); // Reset the current animation state
2572 animOffset(MOVE_BLEND_OUT
, 0.0); // Reset the current animation offset
2573 _RotationFactor
= 1.0; // New animation -> default rotation factor for the time.
2574 _RightFXActivated
= false;
2575 _LeftFXActivated
= false;
2576 _RotationFactor
= 1.0f
; // Rotation factor as the animation for the time.
2577 _SubStateKey
= CAnimationStateSheet::UnknownState
; // No SubStateKey for the time.
2578 dirEndAnim(dir()); // For the time the direction at the end of the animation is the current direction.
2581 // If there is no animation set ->There is nothing we can do properly.
2582 if(_CurrentAnimSet
[MOVE
] == 0)
2587 if(!animationStateKey(MOVE
, newKey
))
2589 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());
2590 if(!animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2591 nlwarning("CH:setAnim:%d: Idle is not warking too", _Slot
);
2595 double speedToDest
= computeSpeed();
2596 // Compute the direction to the first position (direction = front if there is not first pos)
2597 CVector dirToFirstPos
;
2598 if(dist2FirstPos() > 0.0)
2599 dirToFirstPos
= (_FirstPos
- pos()).normed();
2601 dirToFirstPos
= front();
2603 uint antiFreezeCounter
= 0;
2604 // Loop until the process find a steady state.
2606 // Get the state for the current animation.
2607 const CAutomatonStateSheet
*state
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
2610 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot
, CAnimationStateSheet::getAnimationStateName(animState(MOVE
)).c_str(), _CurrentAutomaton
.c_str());
2611 // No animation playing
2613 _PlayList
->setAnimation(MOVE
, UPlayList::empty
);
2616 const CAutomatonStateSheet
&curAnimState
= *state
;
2617 //--------------------//
2618 //--------------------//
2619 // ANTI-FREEZE SYSTEM //
2620 // If too many loop, display some infos
2621 if(antiFreezeCounter
> 10)
2624 nlwarning("CH:setAnim:anitFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
2625 nlwarning("CH:setAnim:anitFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
2626 nlwarning("CH:setAnim:anitFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
2627 nlwarning("CH:setAnim:anitFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
2628 nlwarning("CH:setAnim:anitFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
2629 nlwarning("CH:setAnim:anitFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationStateSheet::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
2631 // Once too many more time reached, leave the method.
2632 if(antiFreezeCounter
> 20)
2635 // Update antiFreezeCounter.
2636 ++antiFreezeCounter
;
2637 // ANTI-FREEZE SYSTEM //
2638 //--------------------//
2639 //--------------------//
2640 // Is there a mode in the queue
2641 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
2643 // Is the current mode not already the mode wanted.
2644 if((_Mode
!= _ModeWanted
) && (dist2Dest()==INVALID_DIST
))
2646 TAnimStateKey transition
;
2647 // Check Default Mode Connection.
2648 if(curAnimState
.getModeConnection(_ModeWanted
, transition
))
2650 // Mode Mount need to be synchro
2651 if(_ModeWanted
== MBEHAV::MOUNT_NORMAL
|| _ModeWanted
== MBEHAV::MOUNT_SWIM
)
2654 if(_Rider
!= CLFECOMMON::INVALID_SLOT
)
2656 if(animationStateKey(MOVE
, transition
))
2659 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());
2662 else if(_Mount
!= CLFECOMMON::INVALID_SLOT
)
2664 CEntityCL
*mountTmp
= EntitiesMngr
.entity(_Mount
);
2665 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(mountTmp
);
2668 if(mountTmp
->mode() == _ModeWanted
)
2670 if(animationStateKey(MOVE
, transition
))
2674 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());
2680 nlwarning("CH:setAnim:%d: Mode Wanted '%s' but the mount does not exist.", _Slot
, MBEHAV::modeToString(_ModeWanted
).c_str());
2684 // Other modes have the same code.
2687 if(animationStateKey(MOVE
, transition
))
2690 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());
2697 // Should we stop the animation once at destination.
2698 if(curAnimState
.BrkAtDest
&& (dist2Dest() <= ClientCfg
.DestThreshold
))
2701 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2704 nlwarning("CH:setAnim:%d: automaton '%s': state '%s': BrkAtDest 'idle' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName (curAnimState
.MoveState
).c_str());
2708 switch(onMove(curAnimState
))
2712 if(animationStateKey(MOVE
, curAnimState
.OnMoveForward
))
2715 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());
2718 case OnMoveBackward
:
2719 if(animationStateKey(MOVE
, curAnimState
.OnMoveBackward
))
2722 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());
2726 if(animationStateKey(MOVE
, curAnimState
.OnMoveLeft
))
2729 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());
2733 if(animationStateKey(MOVE
, curAnimState
.OnMoveRight
))
2736 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());
2742 // On Rotation/About Face
2743 CVector
dirEndA(0.0f
, 0.0f
, 0.0f
);
2744 switch(onRotation(curAnimState
, dirEndA
))
2748 if(animationStateKey(MOVE
, curAnimState
.OnLeftRotation
))
2750 dirEndAnim(dirEndA
);
2754 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());
2758 if(animationStateKey(MOVE
, curAnimState
.OnRightRotation
))
2760 dirEndAnim(dirEndA
);
2764 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());
2771 if(curAnimState
.MaxLoop
&& curAnimState
.MaxLoop
<=_NbLoopAnim
)
2773 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2782 if(onBadHeading(curAnimState
))
2784 if(animationStateKey(MOVE
, CAnimationStateSheet::Idle
))
2787 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': 'Idle' is not valid.", _Slot
, _CurrentAutomaton
.c_str(), CAnimationState::getAnimationStateName(curAnimState
.MoveState
).c_str());
2791 switch(onBigBend(curAnimState
, dirEndA
))
2795 if(animationStateKey(MOVE
, curAnimState
.OnBigBendLeft
))
2797 dirEndAnim(dirEndA
);
2801 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());
2805 if(animationStateKey(MOVE
, curAnimState
.OnBigBendRight
))
2807 dirEndAnim(dirEndA
);
2811 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());
2817 // If the animation change according to a high speed and speed high enough or oo.
2818 if(ClientCfg
.BlendForward
== false)
2820 if(curAnimState
.OnMaxSpeed
.Breakable
)
2822 if(speedToDest
>= _CurrentAnimSet
[MOVE
]->speedToRun() || speedToDest
== -1.0)
2824 if(animationStateKey(MOVE
, curAnimState
.OnMaxSpeed
.NextStateKey
))
2827 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());
2832 // If the animation can change with a low speed.
2833 if(curAnimState
.OnMinSpeed
.Breakable
)
2835 // If the speed is low enough (check this is not -1 (infinite speed)) -> update the animation to play.
2836 if(speedToDest
!= -1.0 && speedToDest
<= _CurrentAnimSet
[MOVE
]->speedToWalk())
2838 if(animationStateKey(MOVE
, curAnimState
.OnMinSpeed
.NextStateKey
))
2841 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());
2847 // \todo GUIGUI : better manage automate change
2848 // Current animation is not of the same kind as the old one.
2849 bool sameAnim
= lastAnimStateId
== animState(MOVE
) && _OldAutomaton
== _CurrentAutomaton
;
2853 stopItemAttackFXs();
2854 stopAttachedFXForCurrrentAnim(true); // stop all anim fx, including looping fxs
2858 stopAttachedFXForCurrrentAnim(false); // stop all anim fxs, but do not stop looping fxs (because the same anim is repeating)
2861 // Compute the current animation state.
2862 _CurrentState
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
2863 // If the state does not exist.
2864 if(_CurrentState
== 0)
2866 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot
, CAnimationStateSheet::getAnimationStateName (animState(MOVE
)).c_str(), _CurrentAutomaton
.c_str());
2868 // No animation playing
2870 _PlayList
->setAnimation(MOVE
, UPlayList::empty
);
2872 // The state is valid.
2875 double angToDest
= 0.0;
2876 // If the new state is a rotation.
2877 if(_CurrentState
->Rotation
)
2878 angToDest
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(dirEndAnim().y
, dirEndAnim().x
));
2879 // Get the right animation state and choose an animation.
2881 // Get the animation state
2882 const CAnimationState
*animationState
= 0;
2883 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
2885 _SubStateKey
= subKey
;
2886 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
2889 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
2892 // Choose the animation
2893 CAnimation::TAnimId index
;
2897 index
= CAnimation::UnknownAnim
;
2898 animIndex(MOVE
, animationState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), angToDest
, index
));
2899 if(animID
!= NL3D::UAnimationSet::NotFound
)
2900 animId(MOVE
, animID
);
2903 // Should the objects in hands be displayed ?
2904 _ObjectsVisible
= animationState
->areObjectsVisible();
2905 showOrHideBodyParts( _ObjectsVisible
);
2907 // in case of user manage the internal view
2910 UserEntity
->updateVisualDisplay();
2914 // Initialize the animation
2915 if(animIndex(MOVE
) != CAnimation::UnknownAnim
)
2917 // If the new state is a rotation.
2918 if(_CurrentState
->Rotation
)
2920 // Get the animation rotation.
2921 double animAngle
= CAnimationMisc::getAnimationRotation(EAM
->getAnimationSet(), animId(MOVE
));
2922 // Compute the rotation factor.
2923 if(animAngle
!= 0.0)
2924 _RotationFactor
= fabs(angToDest
/animAngle
);
2926 _RotationFactor
= -1.0; // \todo GUIGUI : see which value we should use if we have a rot anim without rot and which should rotate character
2929 // If the animation is an atk or forage extraction -> Start all dynamic FXs.
2930 if (_CurrentBehaviour
.Behaviour
== MBEHAV::EXTRACTING
)
2932 // True Extract Animation only for Use AnimationState type
2933 // \todo yoyo: ugly?
2934 if( animState(MOVE
)==CAnimationStateSheet::UseLoop
)
2936 _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].setTrailSize(_CurrentBehaviour
.ForageExtraction
.Level
);
2937 startItemAttackFXs(true, 1);
2940 else if (_CurrentState
->Attack
)
2942 if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
2944 startItemAttackFXs(_CurrentBehaviour
.Range
.ImpactIntensity
!= 0, _CurrentBehaviour
.Range
.ImpactIntensity
);
2948 startItemAttackFXs(_CurrentBehaviour
.Combat
.ImpactIntensity
!= 0 && _CurrentBehaviour
.Combat
.HitType
!= HITTYPE::Failed
, _CurrentBehaviour
.Combat
.ImpactIntensity
);
2953 // Initialize the new animation.
2956 // Blend the last animation and the new one.
2957 if(ClientCfg
.BlendFrameNumber
&& _BlendRemaining
<= 0 // Blend On ?
2958 && animIndex(ACTION
) != CAnimation::UnknownAnim
// Last Animation Valid ?
2959 && ((lastAnimStateId
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
)
2960 || (animState(MOVE
) == CAnimationStateSheet::Emote
)) // Last anim != or automaton != ?
2961 && !isRiding()) // No Blend on a mount. \todo GUIGUI trouver un meilleur moyen.
2963 // Get the rotation of the last animation for the blend.
2964 if(!_Skeleton
.empty())
2965 _OldRotQuat
= _Skeleton
.getRotQuat();
2966 _BlendRemaining
= ClientCfg
.BlendFrameNumber
;
2968 _PlayList
->setAnimation(ACTION
, animId(ACTION
));
2969 // Compute weight step.
2970 float w
= 1.f
/((float)(ClientCfg
.BlendFrameNumber
+1));
2971 // Set Old Anim Weight.
2972 _PlayList
->setWeight(ACTION
, 1.f
-w
);
2973 // Set New Anim Weight.
2974 _PlayList
->setWeight(MOVE
, w
);
2976 _AnimReversed
[ACTION
] = _AnimReversed
[MOVE
];
2979 _PlayList
->setAnimation (MOVE
, animId(MOVE
));
2980 // Blend between Walk and Run.
2981 if(ClientCfg
.BlendForward
&& (animState(MOVE
) == CAnimationStateSheet::Walk
))
2983 _CurrentAnimSet
[MOVE_BLEND_OUT
] = _CurrentAnimSet
[MOVE
];
2984 animState(MOVE_BLEND_OUT
, CAnimationStateSheet::Run
);
2985 const CAnimationState
*animationBlendState
= _CurrentAnimSet
[MOVE_BLEND_OUT
]->getAnimationState(animState(MOVE_BLEND_OUT
));
2986 if(animationBlendState
)
2988 animIndex(MOVE_BLEND_OUT
, animationBlendState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender()));
2989 _PlayList
->setAnimation(MOVE_BLEND_OUT
, animId(MOVE_BLEND_OUT
));
2990 _PlayList
->setWeight(MOVE_BLEND_OUT
, 1.0f
); // \todo GUIGUI : verify what is happening if animId is "empty".
2993 nlwarning("setAnim:%d: animationBlendState is Null.", _Slot
);
2996 // Set children animation.
2997 std::list
<CEntityCL
*>::iterator it
= _Children
.begin();
2998 while(it
!= _Children
.end())
3002 CCharacterCL
*child
= dynamic_cast<CCharacterCL
*>(*it
);
3005 // Set the Child as the parent.
3006 child
->computeAnimSet();
3007 child
->currentAutomaton() = _CurrentAutomaton
; // \todo GUIGUI : CA VA PAS MARCHER A CAUSE DU TYPE !!!
3008 child
->animOffset(MOVE
, animOffset(MOVE
));
3009 child
->animState(MOVE
, animState(MOVE
));
3010 child
->currentState(currentState());
3011 child
->animIndex(MOVE
, CAnimation::UnknownAnim
);
3012 const CAnimationState
*animStatePtr
= child
->currentAnimSet()[MOVE
]->getAnimationState(child
->animState(MOVE
));
3015 child
->animIndex(MOVE
, animStatePtr
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), angToDest
));
3016 child
->playList()->setAnimation(MOVE
, child
->animId(MOVE
));
3018 // TEMP : \todo GUIGUI : Pour le moment on enlever le Blend sur les montures.
3020 child
->playList()->setAnimation(ACTION
, UPlayList::empty
);
3021 child
->currentAnimSet()[ACTION
] = child
->currentAnimSet()[MOVE
];
3022 child
->animState (ACTION
, child
->animState (MOVE
));
3023 child
->animIndex (ACTION
, child
->animIndex (MOVE
));
3024 child
->animOffset(ACTION
, child
->animOffset(MOVE
));
3029 nlwarning("CH:setAnim:%d: Child is not a 'CCharacterCL'.", _Slot
);
3032 nlwarning("CH:setAnim:%d: Child not allocated.", _Slot
);
3039 // Get the animation.
3040 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState((animState(MOVE
) == CAnimationStateSheet::Emote
)?subKey
:animState(MOVE
));
3043 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
3046 _HideSkin
= anim
->hideAtEndAnim();
3048 _AnimReversed
[MOVE
] = anim
->isReverse();
3049 // Is the head controlable.
3050 _TargetAnimCtrl
.Enabled
= anim
->headControlable();
3051 // Select the sound ID
3052 _SoundId
[MOVE
] = anim
->soundId();
3053 // look in behaviour if there's a spell to play
3056 if (_CurrentAttackInfo
.Intensity
>= 1 && _CurrentAttackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
)
3058 MAGICFX::TSpellCastStage attackStage
;
3061 case CAnimationStateSheet::OffensiveCastBegin
:
3062 case CAnimationStateSheet::CurativeCastBegin
:
3063 case CAnimationStateSheet::MixedCastBegin
:
3064 case CAnimationStateSheet::AcidCastInit
:
3065 case CAnimationStateSheet::BlindCastInit
:
3066 case CAnimationStateSheet::ColdCastInit
:
3067 case CAnimationStateSheet::ElecCastInit
:
3068 case CAnimationStateSheet::FearCastInit
:
3069 case CAnimationStateSheet::FireCastInit
:
3070 case CAnimationStateSheet::HealHPCastInit
:
3071 case CAnimationStateSheet::MadCastInit
:
3072 case CAnimationStateSheet::PoisonCastInit
:
3073 case CAnimationStateSheet::RootCastInit
:
3074 case CAnimationStateSheet::RotCastInit
:
3075 case CAnimationStateSheet::ShockCastInit
:
3076 case CAnimationStateSheet::SleepCastInit
:
3077 case CAnimationStateSheet::SlowCastInit
:
3078 case CAnimationStateSheet::StunCastInit
:
3079 attackStage
= MAGICFX::CastBegin
;
3081 case CAnimationStateSheet::OffensiveCastLoop
:
3082 case CAnimationStateSheet::CurativeCastLoop
:
3083 case CAnimationStateSheet::MixedCastLoop
:
3084 case CAnimationStateSheet::AcidCastLoop
:
3085 case CAnimationStateSheet::BlindCastLoop
:
3086 case CAnimationStateSheet::ColdCastLoop
:
3087 case CAnimationStateSheet::ElecCastLoop
:
3088 case CAnimationStateSheet::FearCastLoop
:
3089 case CAnimationStateSheet::FireCastLoop
:
3090 case CAnimationStateSheet::HealHPCastLoop
:
3091 case CAnimationStateSheet::MadCastLoop
:
3092 case CAnimationStateSheet::PoisonCastLoop
:
3093 case CAnimationStateSheet::RootCastLoop
:
3094 case CAnimationStateSheet::RotCastLoop
:
3095 case CAnimationStateSheet::ShockCastLoop
:
3096 case CAnimationStateSheet::SleepCastLoop
:
3097 case CAnimationStateSheet::SlowCastLoop
:
3098 case CAnimationStateSheet::StunCastLoop
:
3099 attackStage
= MAGICFX::CastLoop
;
3101 case CAnimationStateSheet::OffensiveCastSuccess
:
3102 case CAnimationStateSheet::CurativeCastSuccess
:
3103 case CAnimationStateSheet::MixedCastSuccess
:
3104 case CAnimationStateSheet::AcidCastEnd
:
3105 case CAnimationStateSheet::BlindCastEnd
:
3106 case CAnimationStateSheet::ColdCastEnd
:
3107 case CAnimationStateSheet::ElecCastEnd
:
3108 case CAnimationStateSheet::FearCastEnd
:
3109 case CAnimationStateSheet::FireCastEnd
:
3110 case CAnimationStateSheet::HealHPCastEnd
:
3111 case CAnimationStateSheet::MadCastEnd
:
3112 case CAnimationStateSheet::PoisonCastEnd
:
3113 case CAnimationStateSheet::RootCastEnd
:
3114 case CAnimationStateSheet::RotCastEnd
:
3115 case CAnimationStateSheet::ShockCastEnd
:
3116 case CAnimationStateSheet::SleepCastEnd
:
3117 case CAnimationStateSheet::SlowCastEnd
:
3118 case CAnimationStateSheet::StunCastEnd
:
3119 attackStage
= MAGICFX::CastEnd
;
3121 case CAnimationStateSheet::OffensiveCastFail
:
3122 case CAnimationStateSheet::OffensiveCastFumble
:
3123 case CAnimationStateSheet::CurativeCastFail
:
3124 case CAnimationStateSheet::CurativeCastFumble
:
3125 case CAnimationStateSheet::MixedCastFail
:
3126 case CAnimationStateSheet::MixedCastFumble
:
3127 case CAnimationStateSheet::AcidCastFail
:
3128 case CAnimationStateSheet::BlindCastFail
:
3129 case CAnimationStateSheet::ColdCastFail
:
3130 case CAnimationStateSheet::ElecCastFail
:
3131 case CAnimationStateSheet::FearCastFail
:
3132 case CAnimationStateSheet::FireCastFail
:
3133 case CAnimationStateSheet::HealHPCastFail
:
3134 case CAnimationStateSheet::MadCastFail
:
3135 case CAnimationStateSheet::PoisonCastFail
:
3136 case CAnimationStateSheet::RootCastFail
:
3137 case CAnimationStateSheet::RotCastFail
:
3138 case CAnimationStateSheet::ShockCastFail
:
3139 case CAnimationStateSheet::SleepCastFail
:
3140 case CAnimationStateSheet::SlowCastFail
:
3141 case CAnimationStateSheet::StunCastFail
:
3142 attackStage
= MAGICFX::CastFail
;
3145 case CAnimationStateSheet::DefaultAtkLow
:
3146 case CAnimationStateSheet::DefaultAtkMiddle
:
3147 case CAnimationStateSheet::DefaultAtkHigh
:
3148 case CAnimationStateSheet::PowerfulAtkLow
:
3149 case CAnimationStateSheet::PowerfulAtkMiddle
:
3150 case CAnimationStateSheet::PowerfulAtkHigh
:
3151 case CAnimationStateSheet::AreaAtkLow
:
3152 case CAnimationStateSheet::AreaAtkMiddle
:
3153 case CAnimationStateSheet::AreaAtkHigh
:
3154 case CAnimationStateSheet::Attack1
:
3155 case CAnimationStateSheet::Attack2
:
3156 case CAnimationStateSheet::FirstPersonAttack
:
3157 attackStage
= MAGICFX::CastEnd
;
3160 attackStage
= MAGICFX::SpellCastStageCount
;
3163 const CAnimationFXSet
*afs
= NULL
;
3166 case MAGICFX::CastBegin
: afs
= &_CurrentAttack
->AttackBeginFX
; break;
3167 case MAGICFX::CastLoop
: afs
= &_CurrentAttack
->AttackLoopFX
; break;
3168 case MAGICFX::CastEnd
: afs
= &_CurrentAttack
->AttackEndFX
; break;
3169 case MAGICFX::CastFail
: afs
= &_CurrentAttack
->AttackFailFX
; break;
3172 playCastFX(afs
, _CurrentAttackInfo
.Intensity
);
3175 // start forage prospection anim fx(s)
3176 if ( (behaviour() == MBEHAV::PROSPECTING
) || (behaviour() == MBEHAV::PROSPECTING_END
) )
3178 const CAnimationFXSet
& fxSet
= anim
->getFXSet();
3179 std::vector
<UParticleSystemInstance
> fxInstances
;
3180 CAttachedFX::CBuildInfo bi
;
3181 CAttachedFX::CTargeterInfo ti
;
3182 for (uint k
= 0; k
< fxSet
.FX
.size(); ++k
)
3184 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3185 bi
.Sheet
= &(fxSet
.FX
[k
]);
3186 fx
->create(*this, bi
, ti
);
3187 if (!fx
->FX
.empty())
3190 attachFXInternal(fx
,FXListCurrentAnim
);
3193 // Set user param for size of prospection area
3195 fx
->FX
.setUserParam( 0, ((float)(_CurrentBehaviour
.ForageProspection
.Range
)) / 127.0f
);
3197 switch ( _CurrentBehaviour
.ForageProspection
.Angle
)
3199 case 0: up
[2] = up
[1] = up
[0] = 0.0f
; break;
3200 case 1: up
[0]=1.0f
; up
[1]=0.0f
; up
[2]=0.0f
; break;
3201 case 2: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=0.0f
; break;
3202 default: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=1.0f
; break;
3204 fx
->FX
.setUserParam( 1, up
[0] );
3205 fx
->FX
.setUserParam( 2, up
[1] );
3206 fx
->FX
.setUserParam( 3, up
[2] );
3207 //nlinfo( "Prospection %s %u %u", behaviour()==MBEHAV::PROSPECTING?"PROSPTG":(behaviour()==MBEHAV::PROSPECTING_END?"PRO_END":"OTHER"), _CurrentBehaviour.ForageProspection.Range, _CurrentBehaviour.ForageProspection.Angle );
3212 // Set user param for level of prospection
3214 switch ( _CurrentBehaviour
.ForageProspection
.Level
)
3216 case 0: up
[3] = up
[2] = up
[1] = up
[0] = 0.0f
; break;
3217 case 1: up
[0]=0.0f
; up
[1]=1.0f
; up
[2]=0.0f
; up
[3]=0.0f
; break;
3218 case 2: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=0.0f
; up
[3]=0.0f
; break;
3219 case 3: up
[0]=1.0f
; up
[1]=1.0f
; up
[2]=1.0f
; up
[3]=0.0f
; break;
3220 default: up
[3] = up
[2] = up
[1] = up
[0] = 1.0f
; break;
3222 fx
->FX
.setUserParam( 0, up
[0] );
3223 fx
->FX
.setUserParam( 1, up
[1] );
3224 fx
->FX
.setUserParam( 2, up
[2] );
3225 fx
->FX
.setUserParam( 3, up
[3] );
3230 // start standard anim fx
3233 CAttachedFX::CBuildInfo bi
;
3234 CAttachedFX::CTargeterInfo ti
;
3235 bi
.MaxNumAnimCount
= MAX_FX_ANIM_COUNT
;
3236 for (uint k
= 0; k
< anim
->getFXSet().FX
.size(); ++k
)
3238 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
3239 if (anim
->getFXSet().FX
[k
].Sheet
->RepeatMode
== CAnimationFXSheet::Loop
&& sameAnim
) continue;
3240 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3241 bi
.Sheet
= &(anim
->getFXSet().FX
[k
]);
3242 //bi.StickMode = &bi.Sheet->FX[k].StickMode;
3243 if (anim
->getFXSet().FX
[k
].Sheet
)
3245 bi
.StickMode
= &(anim
->getFXSet().FX
[k
].Sheet
->StickMode
);
3247 fx
->create(*this, bi
, ti
);
3248 if (!fx
->FX
.empty())
3250 attachFXInternal(fx
, FXListAuto
);
3257 nlwarning("CH:setAnim:%d: cannot get the pointer on the animation.", _Slot
);
3259 // No animation found -> check if it is just a transition or is there really an animation missing
3262 // INFO : Verbose mode for Animations of the selection.
3263 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
3264 nlinfo("CH:setAnim:%d: Anim Not Found, state '%s'.", _Slot
, CAnimationState::getAnimationStateName(animState(MOVE
)).c_str());
3266 // If the next animation or automaton is not the same -> this is a transition -> no animation needed.
3267 if(_CurrentState
->MoveState
!= _CurrentState
->NextState
|| (_CurrentState
->NextMode
!= _Mode
))
3269 // Choose the next animation.
3270 endAnimTransition();
3272 // Else -> Animation Missing.
3275 // Set the LOD Animation.
3276 setAnimLOD(((lastAnimStateId
!= animState(MOVE
)) || (_OldAutomaton
!= _CurrentAutomaton
)));
3280 //-----------------------------------------------
3282 //-----------------------------------------------
3283 void CCharacterCL::playCastFX(const CAnimationFXSet
*afs
, uint power
)
3286 if (power
<= 0 || power
> 5) return;
3287 static const float castFXUserParams
[MAGICFX::NUM_SPELL_POWER
][4] =
3289 { 0.f
, 0.f
, 0.f
, 0.f
},
3290 { 1.f
, 0.f
, 0.f
, 0.f
},
3291 { 1.f
, 1.f
, 0.f
, 0.f
},
3292 { 1.f
, 1.f
, 1.f
, 0.f
},
3293 { 1.f
, 1.f
, 1.f
, 1.f
}
3295 CAttachedFX::CBuildInfo bi
;
3296 CAttachedFX::CTargeterInfo ti
;
3297 for (uint k
= 0; k
< afs
->FX
.size(); ++k
)
3299 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
3300 bi
.Sheet
= &afs
->FX
[k
];
3301 fx
->create(*this, bi
, ti
);
3302 if (!fx
->FX
.empty())
3304 for(uint l
= 0; l
< 4; ++l
)
3306 fx
->FX
.setUserParam(l
, castFXUserParams
[power
- 1][l
]);
3308 attachFXInternal(fx
, FXListCurrentAnim
);
3314 //-----------------------------------------------
3315 // showOrHideBodyParts
3316 //-----------------------------------------------
3317 void CCharacterCL::showOrHideBodyParts( bool objectsVisible
)
3319 // UnHide all user body parts.
3320 for(uint i
=0; i
<_Instances
.size(); ++i
)
3321 if(!_Instances
[i
].Current
.empty())
3322 _Instances
[i
].Current
.show();
3324 // hide or show the face
3325 if( _Items
[SLOTTYPE::HEAD_SLOT
].Sheet
&& _Items
[SLOTTYPE::HEAD_SLOT
].Sheet
->Family
== ITEMFAMILY::ARMOR
)
3328 SInstanceCL
*face
= getFace ();
3333 if(!face
->Current
.empty())
3334 face
->Current
.hide();
3336 face
->KeepHiddenWhenLoaded
= true;
3342 SInstanceCL
*face
= getFace ();
3347 if(!face
->Current
.empty())
3348 face
->Current
.show();
3350 face
->KeepHiddenWhenLoaded
= false;
3354 // get the instance index for right hand and left hand
3355 uint32 rHandInstIdx
;
3356 uint32 lHandInstIdx
;
3357 if( isPlayer() || isUser() )
3359 rHandInstIdx
= SLOTTYPE::RIGHT_HAND_SLOT
;
3360 lHandInstIdx
= SLOTTYPE::LEFT_HAND_SLOT
;
3362 // hide gloves(armor) if player has magician amplifier
3363 if( _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
&& (_Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
->ItemType
== ITEM_TYPE::MAGICIAN_STAFF
) )
3365 if( !_Instances
[SLOTTYPE::HANDS_SLOT
].Current
.empty() )
3366 _Instances
[SLOTTYPE::HANDS_SLOT
].Current
.hide();
3368 _Instances
[SLOTTYPE::HANDS_SLOT
].KeepHiddenWhenLoaded
= true;
3373 rHandInstIdx
= _RHandInstIdx
;
3374 lHandInstIdx
= _LHandInstIdx
;
3377 // if not already hidden and need to hide :
3378 if( !objectsVisible
)
3381 nlassert(SLOTTYPE::RIGHT_HAND_SLOT
< _Items
.size() && SLOTTYPE::LEFT_HAND_SLOT
< _Items
.size());
3382 if(rHandInstIdx
<_Instances
.size())
3383 if( !(_Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
&& _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
->NeverHideWhenEquipped
) )
3384 if(!_Instances
[rHandInstIdx
].Current
.empty())
3386 _Instances
[rHandInstIdx
].Current
.hide();
3387 _Instances
[rHandInstIdx
].hideStaticFXs();
3390 if(lHandInstIdx
<_Instances
.size())
3391 if( !(_Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
&& _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
->NeverHideWhenEquipped
) )
3392 if(!_Instances
[lHandInstIdx
].Current
.empty())
3394 _Instances
[lHandInstIdx
].Current
.hide();
3395 _Instances
[lHandInstIdx
].hideStaticFXs();
3401 if(rHandInstIdx
<_Instances
.size())
3402 if(!_Instances
[rHandInstIdx
].Current
.empty())
3404 _Instances
[rHandInstIdx
].Current
.show();
3405 _Instances
[rHandInstIdx
].showStaticFXs();
3408 if(lHandInstIdx
<_Instances
.size())
3409 if(!_Instances
[lHandInstIdx
].Current
.empty())
3411 _Instances
[lHandInstIdx
].Current
.show();
3412 _Instances
[lHandInstIdx
].showStaticFXs();
3417 //-----------------------------------------------
3419 // Set the LOD animation.
3420 //-----------------------------------------------
3421 ADD_METHOD(void CCharacterCL::setAnimLOD(bool changed
))
3422 // reset LOD animation.
3423 _LodCharacterAnimEnabled
= false;
3424 // Setup new LOD animation.
3427 // if the entity has a lod Character
3428 sint lodId
= skeleton()->getLodCharacterShape();
3432 _LodCharacterAnimEnabled
= true;
3433 _LodCharacterMasterAnimSlot
= MOVE
;
3434 _LodCharacterAnimTimeOffset
= 0;
3435 // do complex stuff only if the anim state has really changed.
3438 // get the anim state from the set.
3439 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
3440 // if animStatePtr found.
3443 // get Lod Character animation Name from the anim state.
3444 const string
&lodAnimName
= animStatePtr
->getLodCharacterAnimation();
3445 // Find the anim in the UScene LodCharacterManager
3446 sint animId
= Scene
->getCLodAnimIdByName(lodId
, lodAnimName
);
3447 // if Anim not found, get the "idle" anim, with the Id 0.
3450 // setup the skeleton.
3451 skeleton()->setLodCharacterAnimId(animId
);
3459 //-----------------------------------------------
3460 // updateAnimationState :
3461 // \todo GUIGUI : precalculate distance to destination when receiving Stages.
3462 // \todo GUIGUI : improve, we are setting often 'idle' to recompute orientation at the end of animation instead of doing directly the right one.
3463 //-----------------------------------------------
3464 ADD_METHOD(void CCharacterCL::updateAnimationState())
3465 // If the current state is invalid -> return.
3466 if(_CurrentState
== 0)
3469 // Get the Animation Length.
3470 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
3471 // Check Anim length
3472 if(animOffset(MOVE
) >= animLength
)
3474 // Choose the next animation.
3475 endAnimTransition();
3477 // Last animation not finished check for some chances to break current animation.
3481 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
3483 // Is the current mode not already the mode wanted.
3484 if((_Mode
!= _ModeWanted
) && (dist2Dest()==INVALID_DIST
))
3486 if((_ModeWanted
!= MBEHAV::MOUNT_NORMAL
&& _ModeWanted
!= MBEHAV::MOUNT_SWIM
) || (_Rider
!= CLFECOMMON::INVALID_SLOT
))
3488 // Is there a possible connection with the mode wanted.
3489 TAnimStateKey transition
;
3490 if(_CurrentState
->getModeConnection(_ModeWanted
, transition
))
3492 setAnim(transition
);
3498 //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);
3499 // anti-bug : sometimes _rider is not set yet and we stay in an infinite move
3500 _Rider
= _TheoreticalRider
;
3505 // Should we stop the animation once at destination.
3506 if(_CurrentState
->BrkAtDest
&& dist2Dest() <= ClientCfg
.DestThreshold
)
3509 setAnim(CAnimationStateSheet::Idle
);
3514 switch(onMove(*_CurrentState
))
3518 setAnim(_CurrentState
->OnMoveForward
);
3521 case OnMoveBackward
:
3522 setAnim(_CurrentState
->OnMoveBackward
);
3526 setAnim(_CurrentState
->OnMoveLeft
);
3530 setAnim(_CurrentState
->OnMoveRight
);
3538 switch(onRotation(*_CurrentState
, tmp
))
3542 setAnim(CAnimationStateSheet::Idle
);
3546 setAnim(CAnimationStateSheet::Idle
);
3553 if(onBadHeading(*_CurrentState
))
3555 setAnim(CAnimationStateSheet::Idle
);
3560 switch(onBigBend(*_CurrentState
, tmp
))
3562 // Big Bend Left and Right
3565 setAnim(_CurrentState
->MoveState
);
3571 // \todo GUIGUI : changer de place cette partie je pense.
3572 // Adjust the direction to fit the front
3573 if( !(isUser() && ClientCfg
.AutomaticCamera
==false) )
3575 if(_CurrentState
->AdjustOri
)
3577 // Adjust before the half of the attack animation.
3578 const double animL
= animLength
/2.0;
3579 // Half already reatch, dir should be as the front now
3580 if(animOffset(MOVE
) > animL
)
3584 double ang
= computeShortestAngle(atan2(dir().y
, dir().x
), atan2(front().y
, front().x
));
3587 double ang2
= (animOffset(MOVE
)/animL
)*ang
/animL
;
3588 double angleZ
= ang2
+atan2(dir().y
, dir().x
);
3589 dir(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
));
3595 }// updateAnimationState //
3597 //-----------------------------------------------
3599 //-----------------------------------------------
3600 ADD_METHOD(double CCharacterCL::computeMotion(const double &oldMovingTimeOffset
, TAnimationType channel
) const)
3601 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Compute_Motion
)
3603 // Check the state is valid.
3604 if(_CurrentState
== 0)
3607 // Calculate movement for given animation segment.
3608 if(_CurrentState
->Move
)
3610 // Animation is unknown, no move in it.
3611 if(animIndex(channel
) == CAnimation::UnknownAnim
)
3614 CVector oldPos
, newPos
;
3615 uint animID
= animId(channel
);
3616 if(EAM
->interpolate(animID
, oldMovingTimeOffset
, oldPos
))
3618 if(EAM
->interpolate(animID
, animOffset(channel
), newPos
))
3620 CVector mov
= newPos
- oldPos
;
3621 // Scale it by the CharacterScalePos, if needed, according to the animation.
3622 bool mustApplyCharacterScalePosFactor
= true;
3623 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[channel
]->getAnimationState(animState(channel
));
3626 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(channel
));
3628 mustApplyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
3630 // scale it according to the species.
3631 if(mustApplyCharacterScalePosFactor
)
3632 mov
*= _CharacterScalePos
;
3633 // Scale according to the gabarit.
3634 mov
*= _CustomScalePos
;
3635 // Return a significant move.
3636 double distDone
= mov
.norm();
3637 if(distDone
>0.0 && distDone
<ClientCfg
.SignificantDist
)
3638 distDone
= ClientCfg
.SignificantDist
;
3643 // Miss the position's track.
3644 nlwarning("CCharacterCL::computeMotion : Animation should have a track for the position.");
3647 // The animation is not one to move.
3650 }// computeMotion //
3652 //-----------------------------------------------
3654 //-----------------------------------------------
3655 void CCharacterCL::beginCast(const MBEHAV::CBehaviour
&behaviour
)
3657 // if the player has a target, force him to face this target
3658 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot());
3659 if(target
&& !target
->isUser() )
3661 CVectorD dirToTarget
= target
->pos() - pos();
3663 dirToTarget
.normalize();
3664 front( dirToTarget
);
3668 switch(behaviour
.Behaviour
)
3672 // OFFENSIVE CAST BEGIN
3673 case MBEHAV::CAST_OFF
:
3674 setAnim(CAnimationStateSheet::OffensiveCastInit
);
3676 // CURATIVE CAST BEGIN
3677 case MBEHAV::CAST_CUR
:
3678 setAnim(CAnimationStateSheet::CurativeCastInit
);
3681 case MBEHAV::CAST_MIX
:
3682 setAnim(CAnimationStateSheet::MixedCastInit
);
3686 setAnim(CAnimationStateSheet::Idle
);
3689 case MBEHAV::CAST_ACID
:
3690 setAnim(CAnimationStateSheet::AcidCastInit
);
3692 case MBEHAV::CAST_BLIND
:
3693 setAnim(CAnimationStateSheet::BlindCastInit
);
3695 case MBEHAV::CAST_COLD
:
3696 setAnim(CAnimationStateSheet::ColdCastInit
);
3698 case MBEHAV::CAST_ELEC
:
3699 setAnim(CAnimationStateSheet::ElecCastInit
);
3701 case MBEHAV::CAST_FEAR
:
3702 setAnim(CAnimationStateSheet::FearCastInit
);
3704 case MBEHAV::CAST_FIRE
:
3705 setAnim(CAnimationStateSheet::FireCastInit
);
3707 case MBEHAV::CAST_HEALHP
:
3708 setAnim(CAnimationStateSheet::HealHPCastInit
);
3710 case MBEHAV::CAST_MAD
:
3711 setAnim(CAnimationStateSheet::MadCastInit
);
3713 case MBEHAV::CAST_POISON
:
3714 setAnim(CAnimationStateSheet::PoisonCastInit
);
3716 case MBEHAV::CAST_ROOT
:
3717 setAnim(CAnimationStateSheet::RootCastInit
);
3719 case MBEHAV::CAST_ROT
:
3720 setAnim(CAnimationStateSheet::RotCastInit
);
3722 case MBEHAV::CAST_SHOCK
:
3723 setAnim(CAnimationStateSheet::ShockCastInit
);
3725 case MBEHAV::CAST_SLEEP
:
3726 setAnim(CAnimationStateSheet::SleepCastInit
);
3728 case MBEHAV::CAST_SLOW
:
3729 setAnim(CAnimationStateSheet::SlowCastInit
);
3731 case MBEHAV::CAST_STUN
:
3732 setAnim(CAnimationStateSheet::StunCastInit
);
3739 //-----------------------------------------------
3741 //-----------------------------------------------
3742 void CCharacterCL::endCast(const MBEHAV::CBehaviour
&behaviour
, const MBEHAV::CBehaviour
&lastBehaviour
)
3744 if( !(isUser() && isSit()) )
3746 switch(lastBehaviour
.Behaviour
)
3748 case MBEHAV::CAST_ACID
:
3749 setAnim(CAnimationStateSheet::AcidCastEnd
);
3751 case MBEHAV::CAST_BLIND
:
3752 setAnim(CAnimationStateSheet::BlindCastEnd
);
3754 case MBEHAV::CAST_COLD
:
3755 setAnim(CAnimationStateSheet::ColdCastEnd
);
3757 case MBEHAV::CAST_ELEC
:
3758 setAnim(CAnimationStateSheet::ElecCastEnd
);
3760 case MBEHAV::CAST_FEAR
:
3761 setAnim(CAnimationStateSheet::FearCastEnd
);
3763 case MBEHAV::CAST_FIRE
:
3764 setAnim(CAnimationStateSheet::FireCastEnd
);
3766 case MBEHAV::CAST_HEALHP
:
3767 setAnim(CAnimationStateSheet::HealHPCastEnd
);
3769 case MBEHAV::CAST_MAD
:
3770 setAnim(CAnimationStateSheet::MadCastEnd
);
3772 case MBEHAV::CAST_POISON
:
3773 setAnim(CAnimationStateSheet::PoisonCastEnd
);
3775 case MBEHAV::CAST_ROOT
:
3776 setAnim(CAnimationStateSheet::RootCastEnd
);
3778 case MBEHAV::CAST_ROT
:
3779 setAnim(CAnimationStateSheet::RotCastEnd
);
3781 case MBEHAV::CAST_SHOCK
:
3782 setAnim(CAnimationStateSheet::ShockCastEnd
);
3784 case MBEHAV::CAST_SLEEP
:
3785 setAnim(CAnimationStateSheet::SleepCastEnd
);
3787 case MBEHAV::CAST_SLOW
:
3788 setAnim(CAnimationStateSheet::SlowCastEnd
);
3790 case MBEHAV::CAST_STUN
:
3791 setAnim(CAnimationStateSheet::StunCastEnd
);
3794 setAnim(CAnimationStateSheet::Idle
);
3799 switch(behaviour
.Behaviour
)
3803 // OFFENSIVE CAST END
3804 case MBEHAV::CAST_OFF_FAIL
:
3805 setAnim(CAnimationStateSheet::OffensiveCastFail
);
3807 case MBEHAV::CAST_OFF_FUMBLE
:
3808 setAnim(CAnimationStateSheet::OffensiveCastFumble
);
3810 case MBEHAV::CAST_OFF_SUCCESS
:
3811 setAnim(CAnimationStateSheet::OffensiveCastSuccess
);
3813 case MBEHAV::CAST_OFF_LINK
:
3814 setAnim(CAnimationStateSheet::OffensiveCastLink
);
3816 // CURATIVE CAST END
3817 case MBEHAV::CAST_CUR_FAIL
:
3818 setAnim(CAnimationStateSheet::CurativeCastFail
);
3820 case MBEHAV::CAST_CUR_FUMBLE
:
3821 setAnim(CAnimationStateSheet::CurativeCastFumble
);
3823 case MBEHAV::CAST_CUR_SUCCESS
:
3824 setAnim(CAnimationStateSheet::CurativeCastSuccess
);
3826 case MBEHAV::CAST_CUR_LINK
:
3827 setAnim(CAnimationStateSheet::CurativeCastLink
);
3830 case MBEHAV::CAST_MIX_FAIL
:
3831 setAnim(CAnimationStateSheet::MixedCastFail
);
3833 case MBEHAV::CAST_MIX_FUMBLE
:
3834 setAnim(CAnimationStateSheet::MixedCastFumble
);
3836 case MBEHAV::CAST_MIX_SUCCESS
:
3837 setAnim(CAnimationStateSheet::MixedCastSuccess
);
3839 case MBEHAV::CAST_MIX_LINK
:
3840 setAnim(CAnimationStateSheet::MixedCastLink
);
3852 // *************************************************************************************************
3853 void CCharacterCL::updateCurrentAttack()
3855 // This is a behaviour for the magic.
3856 if(_CurrentBehaviour
.isMagic())
3858 _CurrentAttackID
.Type
= CAttackIDSheet::Magic
;
3859 _CurrentAttackID
.SpellInfo
.Mode
= (MAGICFX::TSpellMode
) _CurrentBehaviour
.Spell
.SpellMode
;
3860 _CurrentAttackID
.SpellInfo
.ID
= (MAGICFX::TMagicFx
) _CurrentBehaviour
.Spell
.SpellId
;
3862 // This is a behaviour for the combat.
3863 else if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
3865 _CurrentAttackID
.Type
= CAttackIDSheet::Range
;
3866 _CurrentAttackID
.RangeWeaponType
= (RANGE_WEAPON_TYPE::TRangeWeaponType
) _CurrentBehaviour
.Range
.WeaponType
;
3868 else if (_CurrentBehaviour
.isCombat())
3870 _CurrentAttackID
.Type
= CAttackIDSheet::Melee
;
3872 else if (_CurrentBehaviour
.isCreatureAttack())
3874 _CurrentAttackID
.Type
= CAttackIDSheet::Creature
;
3875 _CurrentAttackID
.CreatureAttackIndex
= _CurrentBehaviour
.Behaviour
== MBEHAV::CREATURE_ATTACK_0
? 0 : 1;
3879 // the behaviour does not generate an attack
3880 _CurrentAttack
= NULL
;
3881 _CurrentAttackID
.Type
= CAttackIDSheet::Unknown
;
3884 _CurrentAttack
= getAttack(_CurrentAttackID
);
3885 // update current attack infos
3886 if(_CurrentBehaviour
.isMagic())
3888 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Spell
.SpellIntensity
;
3889 // physical damge are irrelevant for magic
3890 _CurrentAttackInfo
.DamageType
= DMGTYPE::UNDEFINED
;
3891 _CurrentAttackInfo
.HitType
= HITTYPE::Undefined
;
3892 _CurrentAttackInfo
.Localisation
= BODY::UnknownBodyPart
;
3893 _CurrentAttackInfo
.PhysicalImpactIntensity
= 0;
3895 // This is a behaviour for the combat.
3896 else if(_CurrentBehaviour
.Behaviour
== MBEHAV::RANGE_ATTACK
)
3898 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Range
.ImpactIntensity
;
3899 _CurrentAttackInfo
.DamageType
= DMGTYPE::UNDEFINED
;
3900 _CurrentAttackInfo
.HitType
= HITTYPE::Undefined
;
3901 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.Range
.Localisation
;
3902 _CurrentAttackInfo
.PhysicalImpactIntensity
= 0;
3904 else if (_CurrentBehaviour
.isCombat())
3906 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.Combat
.ImpactIntensity
;
3907 _CurrentAttackInfo
.DamageType
= (DMGTYPE::EDamageType
) _CurrentBehaviour
.Combat2
.DamageType
;
3908 _CurrentAttackInfo
.HitType
= (HITTYPE::THitType
) _CurrentBehaviour
.Combat
.HitType
;
3909 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.Combat
.Localisation
;
3910 _CurrentAttackInfo
.PhysicalImpactIntensity
= _CurrentBehaviour
.Combat
.ImpactIntensity
;
3912 else if (_CurrentBehaviour
.isCreatureAttack())
3914 // creature attack is the most general form
3915 _CurrentAttackInfo
.Intensity
= _CurrentBehaviour
.CreatureAttack
.MagicImpactIntensity
;
3916 _CurrentAttackInfo
.DamageType
= (DMGTYPE::EDamageType
) _CurrentBehaviour
.CreatureAttack2
.DamageType
;
3917 _CurrentAttackInfo
.HitType
= (HITTYPE::THitType
) _CurrentBehaviour
.CreatureAttack2
.HitType
;
3918 _CurrentAttackInfo
.Localisation
= (BODY::TBodyPart
) _CurrentBehaviour
.CreatureAttack
.Localisation
;
3919 _CurrentAttackInfo
.PhysicalImpactIntensity
= _CurrentBehaviour
.CreatureAttack
.ImpactIntensity
;
3921 _CurrentAttackInfo
.Side
= (rand() & 1) ? BODY::Left
: BODY::Right
;
3925 // utility function for performCurrentAttackEnd
3926 inline static void getResistAndDistance(uint8 packedInfo
, bool isDirectAttack
, bool isCombat
, bool &resist
, uint
&distance
)
3928 // get the distance from attacker to defender (consider 0 if a direct attack)
3932 distance
= packedInfo
& 127;
3937 resist
= (packedInfo
& 128) != 0;
3940 // *********************************************************************************************
3941 void CCharacterCL::performCurrentAttackEnd(const CBehaviourContext
&bc
, bool directOffensifSpell
, vector
<double> &targetHitDates
, TAnimStateKey animForCombat
)
3943 if (!_CurrentAttack
) return;
3944 if (!_CurrentAttack
->Sheet
) return;
3945 if (bc
.Targets
.Targets
.empty())
3947 nlwarning ("No target available for current attack.");
3950 nlassert(targetHitDates
.size() == bc
.Targets
.Targets
.size());
3952 CAttackInfo attackInfo
= _CurrentAttackInfo
;
3954 const CAttackSheet
&sheet
= *_CurrentAttack
->Sheet
;
3957 // should cast FX for static Objects like towers be used ?
3958 bool usesStaticCastFX
= _Sheet
&& !_Sheet
->ProjectileCastRay
.empty();
3960 CProjectileBuild pb
;
3963 // ray of cast for static object with several cast points
3964 CVector castWorldOrigin
;
3965 CVector castWorldPos
;
3966 CVector additionnalOffset
= NLMISC::CVector::Null
;
3967 bool castRayValid
= false;
3968 uint mainCastFXIntensity
= attackInfo
.Intensity
;
3971 // *** Compute castStartTime (time at which the projectile is casted)
3973 // *** Compute castStartTime (time at which the projectile is casted)
3975 float delay
= 0.f
; // by default, no delay before impact
3976 double timeFactor
= 1;
3978 // Combat (range or melee) => no resist (yoyo: legacy code. Actually the server should set the resist bit to 0 in Target.Info)
3979 bool isCombat
= _CurrentBehaviour
.isCombat();
3981 // An attack is said 'direct' when no projectile is casted (Generally those are melee attack, but also pistol range attack for instance)
3982 // In this case, all targets are hit at the same time, no matter what date is supplied for them.
3983 bool isDirectAttack
= sheet
.ProjectileFX
.empty();
3990 // compute impact date. It is given by the trigger on right hand in the animation
3991 // Get the current animation id.
3992 if (sheet
.ForceUseProjectileDelay
)
3994 delay
= sheet
.ProjectileDelay
;
3997 if (!directOffensifSpell
)
3999 if ((_CurrentAttackID
.Type
!= CAttackIDSheet::Range
) && (_PlayList
!= NULL
))
4003 // if the animation has been correctly chosen
4004 if(animForCombat
!=CAnimationStateSheet::UnknownState
)
4006 // try to get the MeleeImpactDelay that is stored in the sheet (given by graphists)
4007 const CAnimationSet
*animSet
= currentAnimSet()[MOVE
];
4010 const CAnimationState
*animState
= animSet
->getAnimationState(animForCombat
);
4012 delay
= animState
->getMeleeImpactDelay();
4018 // see if weapon is known, and in this case, delay the impact by the same amount
4019 const CItemSheet
*sheet
= _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
;
4020 if (sheet
) delay
= sheet
->FX
.ImpactFXDelay
;
4023 // all target are reached at the same time
4028 // there's a projectile
4029 if (usesStaticCastFX
)
4031 delay
= sheet
.StaticObjectProjectileDelay
;
4035 if (!directOffensifSpell
)
4037 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4038 // projectile cast from a character
4039 delay
= sheet
.ProjectileDelay
;
4044 // then castStartTime is just start of behav + animation delay
4045 double castStartTime
;
4046 // Special for direct attack. because of lag sometimes behavTime<TimeInSec, which results
4047 // on too early played hits regarding animation played
4050 // start at max(behavTime, TimeInSec), but with a limit of bc.BehavTime+0.5 sec
4051 if(TimeInSec
> bc
.BehavTime
+0.5)
4052 castStartTime
= bc
.BehavTime
+ 0.5;
4053 else if(TimeInSec
> bc
.BehavTime
)
4054 castStartTime
= TimeInSec
;
4056 castStartTime
= bc
.BehavTime
;
4058 castStartTime
+= delay
;
4062 // In case of magic or range (with projectile), this is not required because the projectilemanager should take cares of
4063 // cast start time and current time (and therfore advance the projectile if required)
4064 castStartTime
= bc
.BehavTime
+ delay
;
4067 // *** Casts projectiles and Impacts
4070 // there's a projectile
4071 switch(sheet
.ProjectileMode
)
4073 // TODO: homin code for projectile look quite similar, maybe can merge ?
4076 getResistAndDistance(bc
.Targets
.Targets
[0].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4077 double mainStartDate
= castStartTime
;
4078 double mainEndDate
= mainStartDate
+ timeFactor
* double(distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
);
4079 CCharacterCL
*mainTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[0].TargetSlot
));
4082 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *mainTarget
);
4083 if (usesStaticCastFX
)
4085 // compute the cast pos
4086 computeBestCastRay(*mainTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4087 castRayValid
= true;
4090 // the target hit date
4091 targetHitDates
[0]= mainEndDate
;
4093 // Add the projectile to queue
4094 if (createCurrentAttackEndPart(pb
,
4101 sheet
.PlayImpactAnim
,
4103 sheet
.IsImpactLocalised
,
4108 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4111 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4112 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4113 // all subsequent projectiles are casted from the secondary target, from the first impact point, and have level 1
4114 for(uint k
= 1; k
< bc
.Targets
.Targets
.size(); ++k
)
4116 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4117 double secondaryEndDate
= mainEndDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4118 CCharacterCL
*secondaryTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4119 if (secondaryTarget
)
4121 // the target hit date
4122 targetHitDates
[k
]= secondaryEndDate
;
4124 // Add the projectile to queue
4125 if (mainTarget
->createCurrentAttackEndPart(pb
,
4128 &pb
.ProjectileAimingPoint
,
4131 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4132 sheet
.PlayImpactAnim
,
4134 sheet
.IsImpactLocalised
,
4138 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *secondaryTarget
);
4139 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4146 case MAGICFX::Chain
:
4148 double currDate
= castStartTime
;
4149 CCharacterCL
*currCaster
= this;
4150 const CFXStickMode
*projectileStartPoint
= NULL
; // by default, start at caster hand
4151 CFXStickMode currStickMode
;
4152 for(uint k
= 0; k
< bc
.Targets
.Targets
.size(); ++k
)
4156 // for secondary impacts, intensity is 0 or 1
4157 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4158 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4160 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4161 double nextDate
= currDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4162 CCharacterCL
*currTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4163 if (!currTarget
) break;
4166 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *currTarget
);
4169 if (k
== 0 && usesStaticCastFX
)
4171 // compute the initial cast pos for objects with multiple possible cast positions
4172 computeBestCastRay(*currTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4173 castRayValid
= true;
4176 // the target hit date
4177 targetHitDates
[k
]= nextDate
;
4179 // Add the projectile to queue
4180 if (currCaster
->createCurrentAttackEndPart(pb
,
4183 projectileStartPoint
,
4186 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4187 sheet
.PlayImpactAnim
,
4189 sheet
.IsImpactLocalised
,
4191 k
== 0 ? additionnalOffset
: CVector::Null
4194 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4196 currStickMode
= pb
.ProjectileAimingPoint
;
4197 projectileStartPoint
= &currStickMode
;
4198 currCaster
= currTarget
;
4199 if (!currCaster
) break;
4200 currDate
= nextDate
;
4204 case MAGICFX::Spray
:
4206 for(uint k
= 0; k
< bc
.Targets
.Targets
.size(); ++k
)
4208 getResistAndDistance(bc
.Targets
.Targets
[k
].Info
, isDirectAttack
, isCombat
, resist
, distance
);
4209 double startDate
= castStartTime
;
4210 double endDate
= startDate
+ timeFactor
* ((double) (distance
* MULTI_TARGET_DISTANCE_UNIT
/ MAGICFX::PROJECTILE_SPEED
));
4211 CCharacterCL
*currTarget
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(bc
.Targets
.Targets
[k
].TargetSlot
));
4214 // for secondary impacts, intensity is 0 or 1
4215 attackInfo
.Intensity
= attackInfo
.Intensity
!= 0 ? 1 : 0;
4216 attackInfo
.PhysicalImpactIntensity
= attackInfo
.PhysicalImpactIntensity
!= 0 ? 1 : 0;
4220 computeTargetStickMode(*_CurrentAttack
->Sheet
, attackInfo
, pb
.ProjectileAimingPoint
, *currTarget
);
4221 if (k
== 0 && usesStaticCastFX
)
4223 // compute the initial cast pos for objects with multiple possible cast positions
4224 computeBestCastRay(*currTarget
, pb
.ProjectileAimingPoint
, castWorldOrigin
, castWorldPos
, additionnalOffset
);
4225 castRayValid
= true;
4228 // the target hit date
4229 targetHitDates
[k
]= endDate
;
4231 // nb : only main target display the spell with full power
4232 if (createCurrentAttackEndPart(pb
,
4238 k
!= 0 ? !sheet
.PlayImpactFXOnlyOnMainTarget
: true,
4239 sheet
.PlayImpactAnim
,
4241 sheet
.IsImpactLocalised
,
4243 k
== 0 ? additionnalOffset
: CVector::Null
4246 CProjectileManager::getInstance().addProjectileToQueue(pb
);
4256 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4257 if (usesStaticCastFX
&& castRayValid
)
4259 buildStaticObjectCastFX(castWorldOrigin
, castWorldPos
, *_CurrentAttack
->Sheet
, mainCastFXIntensity
);
4263 // *** Play damage shields when melee attack is done
4265 // TODO: to finalize (server code not done).
4266 if (ClientCfg.DamageShieldEnabled && _CurrentBehaviour.isCombat() && _CurrentBehaviour.todoNotRange())
4268 for(uint k = 0; k < bc.Targets.Targets.size(); ++k)
4270 uint power = bc.Targets.Targets[k].Info & 7;
4273 uint dmType = bc.Targets.Targets[k].Info >> 3;
4275 CAttackIDSheet damageShieldID;
4276 damageShieldID.Type = CAttackIDSheet::DamageShield;
4277 damageShieldID.DamageShieldType = dmType;
4278 CCharacterCL *currTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4279 if (!currTarget) continue;
4280 const CAttack *damageShieldReaction = currTarget->getAttack(damageShieldID);
4281 if (!damageShieldReaction) continue;
4282 CAttackInfo attackInfo;
4283 attackInfo.Intensity = power;
4284 attackInfo.PhysicalImpactIntensity = 0;
4285 attackInfo.Localisation = BODY::UnknownBodyPart;
4287 const CAttackSheet &damageShieldSheet = *damageShieldReaction->Sheet;
4288 computeTargetStickMode(damageShieldSheet, attackInfo, pb.ProjectileAimingPoint, *this);
4289 if (currTarget->createCurrentAttackEndPart(pb,
4290 damageShieldReaction,
4296 damageShieldSheet.PlayImpactAnim,
4301 // play "CastEnd" at the good date (if any ...)
4302 pb.CastAspect = &damageShieldReaction->AttackEndFX;
4303 pb.CastPower = power;
4304 pb.ForcePlayImpact = true;
4305 CProjectileManager::getInstance().addProjectileToQueue(pb);
4313 // *********************************************************************************************
4314 void CCharacterCL::buildStaticObjectCastFX(const NLMISC::CVector
&castWorldOrigin
, NLMISC::CVector
&castWorldPos
, const CAttackSheet
&/* sheet */, uint intensity
)
4316 if (intensity
== 0) return;
4317 const float *userParams
= CProjectileManager::getProjectileFXUserParams(intensity
);
4318 // create additionnal cast fxs on the tower or other static object (if any)
4319 // Build lookat matrix (with respect to axis) looking from castWorldOrigin to castWorldPos
4320 CVector dir
= castWorldPos
- castWorldOrigin
;
4321 CVector I
= dir
.normed();
4322 CVector K
= (CVector::K
- (I
* CVector::K
) * I
).normed();
4324 castMat
.setPos(castWorldPos
);
4325 castMat
.setRot(I
, K
^ I
, K
);
4326 CAttachedFX::CBuildInfo bi
;
4327 CAttachedFX::CTargeterInfo ti
;
4328 bi
.StaticMatrix
= &castMat
;
4329 const CAnimationFXSet
&afs
=_CurrentAttack
->AttackStaticObjectCastFX
;
4330 for (uint k
= 0; k
< afs
.FX
.size(); ++k
)
4332 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
4333 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
4334 bi
.Sheet
= &afs
.FX
[k
];
4335 fx
->create(*this, bi
, ti
);
4336 if (!fx
->FX
.empty())
4340 for(uint l
= 0; l
< 4; ++l
)
4342 fx
->FX
.setUserParam(l
, userParams
[l
]);
4350 // *********************************************************************************************
4351 void CCharacterCL::computeTargetStickMode(const CAttackSheet
&sheet
, const CAttackInfo
&attackInfo
, CFXStickMode
&dest
, CEntityCL
&target
)
4353 bool hasPhysicalImpact
= false;
4354 if (attackInfo
.Localisation
!= BODY::UnknownBodyPart
&&
4355 attackInfo
.PhysicalImpactIntensity
>= 1 &&
4356 attackInfo
.PhysicalImpactIntensity
<= MAGICFX::NUM_SPELL_POWER
&&
4357 attackInfo
.HitType
!= HITTYPE::Failed
&&
4358 attackInfo
.HitType
!= HITTYPE::Undefined
&&
4359 attackInfo
.DamageType
!= DMGTYPE::UNDEFINED
)
4361 hasPhysicalImpact
= true;
4364 if (sheet
.IsImpactLocalised
|| hasPhysicalImpact
)
4366 // get projectile impact point from localisation
4367 // & generate stick mode
4368 const char *targetBoneName
= target
.getBoneNameFromBodyPart(attackInfo
.Localisation
, attackInfo
.Side
);
4371 if (sheet
.DefaultAimingPoint
.Mode
== CFXStickMode::UserBoneOrientedTowardTargeter
)
4373 dest
.Mode
= CFXStickMode::UserBoneOrientedTowardTargeter
;
4377 dest
.Mode
= CFXStickMode::UserBone
;
4379 dest
.UserBoneName
= CStringMapper::map(targetBoneName
);
4384 // use default aiming point given in sheet
4385 dest
= sheet
.DefaultAimingPoint
;
4390 // *********************************************************************************************
4391 bool CCharacterCL::createCurrentAttackEndPart(CProjectileBuild
&destPB
,
4392 const CAttack
*currentAttack
,
4393 const CCharacterCL
&target
,
4394 const CFXStickMode
*sm
,
4398 bool playImpactAnim
,
4400 bool /* mainImpactIsLocalised */,
4401 const CAttackInfo
&attackInfo
,
4402 const NLMISC::CVector
&additionnalOffset
/*= NLMISC::CVector::Null*/
4405 if (!currentAttack
) return false;
4406 if (!currentAttack
->Sheet
) return false;
4409 const CAttackSheet
&sheet
= *currentAttack
->Sheet
;
4412 destPB
.StartDate
= spawnDate
;
4413 destPB
.EndDate
= hitDate
;
4414 // choose fx for projectile
4415 if (attackInfo
.Intensity
>= 1 && attackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
)
4417 destPB
.ProjectileAspect
= ¤tAttack
->ProjectileFX
;
4421 destPB
.ProjectileAspect
= NULL
;
4423 // choose fx for impact
4426 destPB
.ImpactAspect
= NULL
;
4430 if (attackInfo
.Intensity
>= 1 && attackInfo
.Intensity
<= MAGICFX::NUM_SPELL_POWER
) // impact has same intensity than projectile
4432 destPB
.ImpactAspect
= ¤tAttack
->ImpactFX
;
4436 destPB
.ImpactAspect
= NULL
;
4440 // FILL PROJECTILE BUILD
4441 destPB
.AttackInfo
= attackInfo
;
4442 destPB
.TargeterInfo
.Slot
= slot();
4445 destPB
.LocalizedImpact
= sheet
.IsImpactLocalised
;
4446 // If this is a secondary projectile, it may start from another location, which is the impact point of the previous projectile
4447 // (so it doesn't start from the caster hand, or any around settings that is read from the spell sheet)
4448 if (sm
) // start stickmode wanted ?
4450 destPB
.TargeterInfo
.StickMode
= *sm
;
4454 // if no stick mode is not forced, then use the one given in the projectile sheet
4455 if (destPB
.ProjectileAspect
&& !destPB
.ProjectileAspect
->FX
.empty())
4457 destPB
.TargeterInfo
.StickMode
= destPB
.ProjectileAspect
->FX
[0].Sheet
->StickMode
;
4461 // if no projectile is given, then uses the default casting point
4462 destPB
.TargeterInfo
.StickMode
= sheet
.DefaultCastingPoint
;
4465 destPB
.Mode
= sheet
.ProjectileMode
;
4466 destPB
.Target
.Slot
= target
.slot();
4467 destPB
.TargeterInfo
.StickOffset
= CVector::Null
;
4468 destPB
.PlayImpactAnim
= playImpactAnim
;
4469 destPB
.LetProjectileStickedOnTarget
= sheet
.LetProjectileStickedOnTarget
;
4470 destPB
.TargeterInfo
.DefaultPos
= pos().asVector();
4472 destPB
.MagicResist
= magicResist
;
4473 // offset if projectile is launched from a range weapon and projectile is sticked to box_arme
4474 if (destPB
.TargeterInfo
.StickMode
.Mode
== CFXStickMode::UserBone
&& sheet
.ApplyItemOffsetToWeaponBone
)
4476 // should be fired from the 'box_arme' bone, which means it is fired from a weapon
4477 if (CStringMapper::unmap(destPB
.TargeterInfo
.StickMode
.UserBoneName
) == "box_arme")
4479 NLMISC::CVector projectileOffset
= NLMISC::CVector::Null
;
4480 const CItemSheet
*is
= getRightHandItemSheet();
4483 destPB
.TargeterInfo
.StickOffset
= is
->FX
.AttackFXOffset
;
4485 destPB
.TargeterInfo
.StickOffset
+= additionnalOffset
;
4488 destPB
.TargeterInfo
.StickOffset
+= sheet
.AdditionnalStartOffset
;
4493 // *********************************************************************************************
4494 void CCharacterCL::computeBestCastRay(CEntityCL
&targetEntity
,
4495 const CFXStickMode
&targetStickMode
,
4496 NLMISC::CVector
&castWorldOrigin
,
4497 NLMISC::CVector
&castWorldPos
,
4498 NLMISC::CVector
&worldOffsetToCasterPivot
4501 // additionnal offset taken from sheet. Useful for towers that have no bones, but can fire projectiles anyway
4502 nlassert(_Sheet
&& !_Sheet
->ProjectileCastRay
.empty());
4503 // if several offsets are provided, then choose the one that has the smallest angle towards target
4505 CProjectileManager::evalFXPosition(&targetStickMode
, targetEntity
, target
);
4506 float maxDP3
= -FLT_MAX
;
4507 // NB : the offset is relative to object pivot, not to a bone
4508 CMatrix casterMatrix
;
4509 buildAlignMatrix(casterMatrix
);
4510 for(uint k
= 0; k
< _Sheet
->ProjectileCastRay
.size(); ++k
)
4512 CVector currCastWorldPos
= casterMatrix
* _Sheet
->ProjectileCastRay
[k
].Pos
;
4513 CVector currCastWorldOrigin
= casterMatrix
* _Sheet
->ProjectileCastRay
[k
].Origin
;
4514 float dp3
= (target
- currCastWorldPos
).normed() * (currCastWorldPos
- currCastWorldOrigin
).normed();
4518 worldOffsetToCasterPivot
= casterMatrix
.mulVector(_Sheet
->ProjectileCastRay
[k
].Pos
);
4519 castWorldOrigin
= currCastWorldOrigin
;
4520 castWorldPos
= currCastWorldPos
;
4525 // *********************************************************************************************
4526 bool CCharacterCL::isCurrentBehaviourAttackEnd() const
4528 switch(_CurrentBehaviour
.Behaviour
)
4530 case MBEHAV::CAST_OFF_SUCCESS
:
4531 case MBEHAV::CAST_OFF_LINK
:
4532 case MBEHAV::CAST_CUR_SUCCESS
:
4533 case MBEHAV::CAST_CUR_LINK
:
4534 case MBEHAV::CAST_MIX_SUCCESS
:
4535 case MBEHAV::CAST_MIX_LINK
:
4536 case MBEHAV::RANGE_ATTACK
:
4537 case MBEHAV::CREATURE_ATTACK_0
:
4538 case MBEHAV::CREATURE_ATTACK_1
:
4539 case MBEHAV::DEFAULT_ATTACK
:
4540 case MBEHAV::POWERFUL_ATTACK
:
4541 case MBEHAV::AREA_ATTACK
:
4550 // ***************************************************************************
4551 void CCharacterCL::applyBehaviourFlyingHPs(const CBehaviourContext
&bc
, const MBEHAV::CBehaviour
&behaviour
,
4552 const vector
<double> &targetHitDates
)
4554 nlassert(targetHitDates
.size()==bc
.Targets
.Targets
.size());
4556 if(!bc
.Targets
.Targets
.empty())
4558 if(behaviour
.DeltaHP
!= 0)
4560 CRGBA
deltaHPColor(0, 0, 0);
4562 if( behaviour
.DeltaHP
< 0 )
4564 // if the behaviour is casted by the user
4567 deltaHPColor
= ClientCfg
.SystemInfoParams
["dgm"].Color
;
4570 // if the behaviour is casted by an entity that target the user
4571 if( targetSlot() == 0 )
4573 CEntityCL
*actor
= EntitiesMngr
.entity(slot());
4576 // if actor is player : use pvp color
4577 if( actor
->isPlayer() )
4578 deltaHPColor
= ClientCfg
.SystemInfoParams
["dgp"].Color
;
4580 deltaHPColor
= ClientCfg
.SystemInfoParams
["dg"].Color
;
4585 deltaHPColor
= CRGBA(127,127,127);
4590 deltaHPColor
= CRGBA(0,220,0);
4594 for (size_t i
=0; i
<bc
.Targets
.Targets
.size(); ++i
)
4596 CEntityCL
*target2
= EntitiesMngr
.entity(bc
.Targets
.Targets
[i
].TargetSlot
);
4598 target2
->addHPOutput(behaviour
.DeltaHP
, deltaHPColor
, float(targetHitDates
[i
]-TimeInSec
));
4605 //-----------------------------------------------
4606 // Apply the behaviour.
4607 // \param behaviour : the behaviour to apply.
4608 //-----------------------------------------------
4609 void CCharacterCL::applyBehaviour(const CBehaviourContext
&bc
) // virtual
4611 // Backup the current behaviour.
4612 CBehaviour previousBehaviour
= _CurrentBehaviour
;
4613 _CurrentBehaviour
= bc
.Behav
;
4614 const CBehaviour
&behaviour
= bc
.Behav
;
4616 // check if self-target
4617 bool selfSpell
= false;
4618 if (bc
.Targets
.Targets
.size() == 1)
4620 if (bc
.Targets
.Targets
[0].TargetSlot
== 0)
4626 switch(behaviour
.Behaviour
)
4628 case MBEHAV::CAST_OFF
:
4629 case MBEHAV::CAST_OFF_FAIL
:
4630 case MBEHAV::CAST_OFF_SUCCESS
:
4631 case MBEHAV::CAST_OFF_LINK
:
4639 // Get a pointer on the target.
4640 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot());
4643 // ***** Choose Combat Animation to apply
4645 TAnimStateKey combatAnimState
= CAnimationStateSheet::UnknownState
;
4646 bool isMovingCombatAnimState
= false;
4647 // if the behaviour is for combat, compute now the animation to apply
4648 if( !behaviour
.isMagic() && ( behaviour
.isCombat() || behaviour
.isCreatureAttack() ) )
4650 // Atk Animation when moving
4651 if(_CurrentState
&& _CurrentState
->Move
&& (_CurrentState
->OnAtk
!= CAnimationStateSheet::UnknownState
))
4653 combatAnimState
= _CurrentState
->OnAtk
;
4654 isMovingCombatAnimState
= true;
4656 // Atk Animation when NOT moving
4659 // select the combat animation.
4660 switch(behaviour
.Behaviour
)
4662 case CREATURE_ATTACK_0
:
4663 combatAnimState
= CAnimationStateSheet::Attack1
;
4665 case CREATURE_ATTACK_1
:
4666 combatAnimState
= CAnimationStateSheet::Attack2
;
4668 // Default Animation
4669 case DEFAULT_ATTACK
:
4670 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4673 case CCharacterCL::AtkLow
:
4674 combatAnimState
= CAnimationStateSheet::DefaultAtkLow
;
4677 case CCharacterCL::AtkHigh
:
4678 combatAnimState
= CAnimationStateSheet::DefaultAtkHigh
;
4680 // MIDDLE or Default
4681 case CCharacterCL::AtkMiddle
:
4683 combatAnimState
= CAnimationStateSheet::DefaultAtkMiddle
;
4687 // Powerful Animation
4688 case POWERFUL_ATTACK
:
4689 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4692 case CCharacterCL::AtkLow
:
4693 combatAnimState
= CAnimationStateSheet::PowerfulAtkLow
;
4696 case CCharacterCL::AtkHigh
:
4697 combatAnimState
= CAnimationStateSheet::PowerfulAtkHigh
;
4699 // MIDDLE or Default
4700 case CCharacterCL::AtkMiddle
:
4702 combatAnimState
= CAnimationStateSheet::PowerfulAtkMiddle
;
4708 switch(getAttackHeight(target
, (BODY::TBodyPart
)behaviour
.Combat
.Localisation
, BODY::Right
))
4711 case CCharacterCL::AtkLow
:
4712 combatAnimState
= CAnimationStateSheet::AreaAtkLow
;
4715 case CCharacterCL::AtkHigh
:
4716 combatAnimState
= CAnimationStateSheet::AreaAtkHigh
;
4718 // MIDDLE or Default
4719 case CCharacterCL::AtkMiddle
:
4721 combatAnimState
= CAnimationStateSheet::AreaAtkMiddle
;
4727 combatAnimState
= CAnimationStateSheet::Attack1
;
4736 // ***** Compute Impact delays and cast missiles
4738 // Default target hit dates
4739 static vector
<double> targetHitDates
;
4740 targetHitDates
.clear();
4741 targetHitDates
.resize(bc
.Targets
.Targets
.size(), TimeInSec
);
4743 // Update Attack Projectiles and FXs
4744 updateCurrentAttack();
4745 if (isCurrentBehaviourAttackEnd())
4747 // retrieve target hit dates, so flying HPs have the correct ones
4748 performCurrentAttackEnd(bc
, selfSpell
&& isOffensif
, targetHitDates
, combatAnimState
);
4751 // INFO : display some debug information.
4752 if((VerboseAnimUser
&& _Slot
==0) || (VerboseAnimSelection
&& _Slot
== UserEntity
->selection()))
4753 nlinfo("CH:applyBeh:%d: '%d(%s)'", _Slot
, (sint
)behaviour
.Behaviour
, behaviourToString((EBehaviour
)behaviour
.Behaviour
).c_str());
4756 // ***** Apply the behaviour according to type
4758 // This is a behaviour for the magic.
4759 if(behaviour
.isMagic())
4761 // Execute the magic behaviour.
4762 switch(behaviour
.Behaviour
)
4766 case MBEHAV::CAST_OFF
:
4767 case MBEHAV::CAST_CUR
:
4768 case MBEHAV::CAST_MIX
:
4769 case MBEHAV::CAST_ACID
:
4770 case MBEHAV::CAST_BLIND
:
4771 case MBEHAV::CAST_COLD
:
4772 case MBEHAV::CAST_ELEC
:
4773 case MBEHAV::CAST_FEAR
:
4774 case MBEHAV::CAST_FIRE
:
4775 case MBEHAV::CAST_HEALHP
:
4776 case MBEHAV::CAST_MAD
:
4777 case MBEHAV::CAST_POISON
:
4778 case MBEHAV::CAST_ROOT
:
4779 case MBEHAV::CAST_ROT
:
4780 case MBEHAV::CAST_SHOCK
:
4781 case MBEHAV::CAST_SLEEP
:
4782 case MBEHAV::CAST_SLOW
:
4783 case MBEHAV::CAST_STUN
:
4784 beginCast(behaviour
);
4788 case MBEHAV::CAST_OFF_FAIL
:
4789 case MBEHAV::CAST_OFF_FUMBLE
:
4790 if (!selfSpell
) endCast(behaviour
, previousBehaviour
);
4792 case MBEHAV::CAST_OFF_SUCCESS
:
4793 case MBEHAV::CAST_OFF_LINK
:
4794 endCast(behaviour
, previousBehaviour
);
4796 case MBEHAV::CAST_CUR_FAIL
:
4797 case MBEHAV::CAST_CUR_FUMBLE
:
4798 endCast(behaviour
, previousBehaviour
);
4800 case MBEHAV::CAST_CUR_SUCCESS
:
4801 case MBEHAV::CAST_CUR_LINK
:
4802 endCast(behaviour
, previousBehaviour
);
4804 case MBEHAV::CAST_MIX_FAIL
:
4805 case MBEHAV::CAST_MIX_FUMBLE
:
4806 endCast(behaviour
, previousBehaviour
);
4808 case MBEHAV::CAST_MIX_SUCCESS
:
4809 case MBEHAV::CAST_MIX_LINK
:
4810 endCast(behaviour
, previousBehaviour
);
4816 applyBehaviourFlyingHPs(bc
, behaviour
, targetHitDates
);
4818 // This is a behaviour for the combat.
4819 else if(behaviour
.isCombat() || behaviour
.isCreatureAttack())
4821 float frontYawBefore
= 0.f
;
4822 float frontYawAfter
= 0.f
;
4824 // Atk Animation when NOT moving?
4825 if(target
&& !isMovingCombatAnimState
)
4827 // orientate to target
4828 CVectorD dirToTarget
= target
->pos() - pos();
4830 dirToTarget
.normalize();
4831 if( !(isUser() && ClientCfg
.AutomaticCamera
== false) )
4834 frontYawBefore
= frontYaw();
4835 front( dirToTarget
);
4840 // Apply the state animation chosen before
4841 if(combatAnimState
!=CAnimationStateSheet::UnknownState
)
4842 setAnim(combatAnimState
);
4844 // move camera so view doesn't change
4845 if( isUser() && frontYawBefore
!= 0.f
)
4847 frontYawAfter
= frontYaw();
4848 float deltaYaw
= frontYawAfter
- frontYawBefore
;
4851 UserControls
.appendCameraDeltaYaw(-deltaYaw
);
4855 // reset yaw smoothly to center view behind user
4856 if( isUser() && target
&& !target
->isUser() && ClientCfg
.AutomaticCamera
)
4858 UserControls
.resetSmoothCameraDeltaYaw();
4862 applyBehaviourFlyingHPs(bc
, behaviour
, targetHitDates
);
4865 else if(behaviour
.isEmote())
4867 if(ClientCfg
.Light
==false && ClientCfg
.EAMEnabled
)
4873 // old code : fxs attached to emotes
4874 uint emoteIndex = behaviour.Behaviour-EMOTE_BEGIN;
4875 CTextEmotListSheet *pTELS = dynamic_cast<CTextEmotListSheet*>(SheetMngr.get(CSheetId("list.text_emotes")));
4878 if (emoteIndex < pTELS->TextEmotList.size())
4880 const CTextEmotListSheet::STextEmot &emot = pTELS->TextEmotList[emoteIndex];
4881 if (!emot.FXToSpawn.empty())
4883 // Compute the direction Matrix
4885 CVector vi = dir() ^ CVector::K;
4886 CVector vk = vi ^ dir();
4887 fxMatrix.setRot(vi, UserEntity->dir(), vk, true);
4888 fxMatrix.setPos(pos().asVector() + fxMatrix.getJ() * emot.FXSpawnDist);
4889 FXMngr.deferFX(emot.FXToSpawn, fxMatrix, emot.FXSpawnDelay);
4894 if(EAM
->getEmot(behaviour
.Behaviour
-EMOTE_BEGIN
, emot
))
4895 setAnim(CAnimationStateSheet::Emote
, emot
);
4897 nlwarning("CH:applyBeh:%d: Emot '%d' unknown.", _Slot
, behaviour
.Behaviour
-EMOTE_BEGIN
);
4904 switch(behaviour
.Behaviour
)
4907 case MBEHAV::LOOT_INIT
:
4908 setAnim(CAnimationStateSheet::LootInit
);
4911 case MBEHAV::LOOT_END
:
4912 setAnim(CAnimationStateSheet::LootEnd
);
4914 // Prospecting Begin
4915 case MBEHAV::PROSPECTING
:
4916 setAnim(CAnimationStateSheet::ProspectingInit
);
4919 case MBEHAV::PROSPECTING_END
:
4920 setAnim(CAnimationStateSheet::ProspectingEnd
);
4923 case MBEHAV::EXTRACTING
:
4926 if(behaviour
.DeltaHP
!= 0)
4927 target
->addHPOutput(behaviour
.DeltaHP
,CRGBA(0,220,0));
4928 // If receiving a new DeltaHP in the current extraction, don't reset the animation
4929 if ( previousBehaviour
.Behaviour
!= _CurrentBehaviour
.Behaviour
)
4930 setAnim(CAnimationStateSheet::UseInit
);
4933 case MBEHAV::EXTRACTING_END
:
4934 setAnim(CAnimationStateSheet::UseEnd
);
4938 setAnim(CAnimationStateSheet::CareInit
);
4941 case MBEHAV::CARE_END
:
4942 setAnim(CAnimationStateSheet::CareEnd
);
4945 // Begin to use a tool
4946 case MBEHAV::HARVESTING
:
4948 case MBEHAV::REPAIR
:
4949 case MBEHAV::REFINE
:
4950 case MBEHAV::TRAINING
:
4951 setAnim(CAnimationStateSheet::UseInit
);
4954 // End to use a tool
4955 case MBEHAV::HARVESTING_END
:
4956 case MBEHAV::FABER_END
:
4957 case MBEHAV::REPAIR_END
:
4958 case MBEHAV::REFINE_END
:
4959 case MBEHAV::TRAINING_END
:
4960 setAnim(CAnimationStateSheet::UseEnd
);
4964 case MBEHAV::STUNNED
:
4965 setAnim(CAnimationStateSheet::StunBegin
);
4969 case MBEHAV::STUN_END
:
4970 setAnim(CAnimationStateSheet::StunEnd
);
4977 // Unknown behaviour -> idle.
4978 case UNKNOWN_BEHAVIOUR
:
4980 nlwarning("CH::computeBehaviour : Entity in slot %d has an unknown behaviour %d to manage.", _Slot
, (sint
)behaviour
.Behaviour
);
4984 }// computeBehaviour //
4986 //-----------------------------------------------
4988 // Play an impact on the entity
4989 // \param impactType : 0=magic, 1=melee
4990 // \param type : see behaviour for spell
4991 // \param intensity : see behaviour for spell
4992 // \param id : see behaviour for spell
4993 //-----------------------------------------------
4994 void CCharacterCL::impact(uint
/* impactType */, uint type
, uint id
, uint intensity
) // virtual
4996 // Display Magic Debug Infos
4997 if(Verbose
& VerboseMagic
)
4998 nlinfo("CH:impact:%d: type: %d, id: %d, intensity: %d", _Slot
, type
, id
, intensity
);
4999 // No Intensity -> No Impact
5002 // Invalid Intensity -> No Impact
5003 else if(intensity
>5)
5005 nlwarning("CH:impact:%d: invalid intensity %u", _Slot
, intensity
);
5008 // RESIST : temp until resist is in the enum.
5012 NL3D::UInstance resistFX
= Scene
->createInstance("Sp_Resist_Lev5.ps");
5013 if(!resistFX
.empty())
5014 resistFX
.setPos(pos());
5017 // Compute the impact name
5019 if(id
< ClientCfg
.OffImpactFX
.size())
5020 impact
= ClientCfg
.OffImpactFX
[id
];
5025 NL3D::UInstance impactFX
= Scene
->createInstance(impact
);
5026 if(!impactFX
.empty())
5028 impactFX
.setPos(pos());
5029 UParticleSystemInstance instFX
;
5030 instFX
.cast (impactFX
);
5033 // UserParam | Intensity 1 | Intensity 2 | Intensity 3 | Intensity 4 | Intensity 5
5034 // 0 | 0 | 0 | 1 | 1 | 1
5035 // 1 | 0 | 1 | 1 | 1 | 1
5036 // 2 | 0 | 0 | 0 | 1 | 1
5037 // 3 | 0 | 0 | 0 | 0 | 1
5038 float userParam0
= 0.0f
;
5039 float userParam1
= 0.0f
;
5040 float userParam2
= 0.0f
;
5041 float userParam3
= 0.0f
;
5042 // WARNING : there is no break and this is correct.
5054 instFX
.setUserParam(0, userParam0
);
5055 instFX
.setUserParam(1, userParam1
);
5056 instFX
.setUserParam(2, userParam2
);
5057 instFX
.setUserParam(3, userParam3
);
5063 //-----------------------------------------------
5065 //-----------------------------------------------
5066 void CCharacterCL::meleeImpact(const CAttackInfo
&attack
)
5068 if (_Skeleton
.empty()) return;
5069 if (attack
.PhysicalImpactIntensity
< 1 || attack
.PhysicalImpactIntensity
> 5) return;
5070 if (attack
.HitType
== HITTYPE::Failed
) return;
5071 const char *boneName
= getBoneNameFromBodyPart(attack
.Localisation
, attack
.Side
);
5072 if (!boneName
) return;
5073 sint boneId
= _Skeleton
.getBoneIdByName(std::string(boneName
));
5074 if (boneId
== -1) return;
5076 // choose good fx depending on the kind of damage
5077 NL3D::UInstance instance
;
5078 switch(attack
.DamageType
)
5080 case DMGTYPE::BLUNT
: instance
= Scene
->createInstance("mel_impactblunt.ps"); break;
5081 case DMGTYPE::SLASHING
: instance
= Scene
->createInstance("mel_impactslashing.ps"); break;
5082 case DMGTYPE::PIERCING
: instance
= Scene
->createInstance("mel_impactpiercing.ps"); break;
5084 return; // other types not supported
5087 if (instance
.empty()) return;
5088 UParticleSystemInstance impact
;
5089 impact
.cast (instance
);
5092 Scene
->deleteInstance(instance
);
5095 // the 2 first user params of the fx are used to modulate intensity
5096 static const float intensityUP
[5][2] =
5104 impact
.setUserParam(0, intensityUP
[attack
.PhysicalImpactIntensity
- 1][0]);
5105 impact
.setUserParam(1, intensityUP
[attack
.PhysicalImpactIntensity
- 1][1]);
5106 impact
.setUserParam(2, (attack
.HitType
== HITTYPE::CriticalHit
|| attack
.HitType
== HITTYPE::CriticalHitResidual
) ? 1.f
: 0.f
);
5107 impact
.setUserParam(3, (attack
.HitType
== HITTYPE::HitResidual
|| attack
.HitType
== HITTYPE::CriticalHitResidual
) ? 1.f
: 0.f
);
5109 _Skeleton
.stickObject(impact
, boneId
);
5110 // delegate managment of the impact to the fx manager
5111 FXMngr
.fx2remove(impact
);
5115 //-----------------------------------------------
5117 // Play the magic impact on the entity
5118 // \param type : type of the impact (host/good/neutral).
5119 // \param intensity : intensity of the impact.
5120 //-----------------------------------------------
5121 void CCharacterCL::magicImpact(uint type
, uint intensity
) // virtual
5130 impact
= "Sp_Resist_Lev";
5134 impact
= "Sp_Bien_Cure_Lev";
5138 impact
= "Sp_Neutre_Protect_Lev";
5142 impact
= "Sp_Host_Hurt_Lev";
5145 nlwarning("CH:magicImpact:%d: Unknown type '%d'.", _Slot
, type
);
5152 case INTENSITY_TYPE::IMPACT_NONE
:
5155 case INTENSITY_TYPE::IMPACT_INSIGNIFICANT
:
5158 case INTENSITY_TYPE::IMPACT_VERY_WEAK
:
5161 case INTENSITY_TYPE::IMPACT_WEAK
:
5164 case INTENSITY_TYPE::IMPACT_AVERAGE
:
5167 case INTENSITY_TYPE::IMPACT_STRONG
:
5173 nlwarning("CH:magicImpact:%d: Unknown intensity '%d'.", _Slot
, intensity
);
5178 NL3D::UInstance resistFX
= Scene
->createInstance(impact
);
5179 if(!resistFX
.empty())
5180 resistFX
.setPos(pos());
5185 //-----------------------------------------------
5187 // Return the basic max speed for the entity in meter per sec
5188 //-----------------------------------------------
5189 double CCharacterCL::getMaxSpeed() const // virtual
5191 return _Sheet
->MaxSpeed
;
5195 //-----------------------------------------------
5197 // Method called to change the mode (Combat/Mount/etc.).
5198 //-----------------------------------------------
5199 bool CCharacterCL::mode(MBEHAV::EMode m
)
5202 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
5204 nlinfo("CH::mode:%d: m'%s(%d)', _ModeWanted'%s(%d)', _Mode'%s(%d)'", _Slot
,
5205 MBEHAV::modeToString(m
).c_str(), m
,
5206 MBEHAV::modeToString(_ModeWanted
).c_str(), _ModeWanted
,
5207 MBEHAV::modeToString(_Mode
).c_str(), _Mode
);
5209 // Is the mode wanted valid ?
5210 if(m
== MBEHAV::UNKNOWN_MODE
|| m
>= MBEHAV::NUMBER_OF_MODES
)
5212 nlwarning("CH::mode:%d: Invalid Mode Wanted '%s(%d)' -> keep '%s(%d)'.",
5213 _Slot
, MBEHAV::modeToString(m
).c_str(), m
, MBEHAV::modeToString(_Mode
).c_str(), _Mode
);
5216 // Set the mode wanted.
5218 if(_CurrentState
== 0)
5219 _Mode
= _ModeWanted
;
5224 //-----------------------------------------------
5226 // Apply stage modifications.
5227 // \todo GUIGUI ; ameliorer gestion mode.
5228 //-----------------------------------------------
5229 bool CCharacterCL::applyStage(CStage
&stage
)
5231 bool stageDone
= true;
5233 // If the Stage has a position, backup the position and the stage time.
5234 if(stage
.getPos(_OldPos
))
5237 _OldPosTime
= stage
.time();
5238 // Remove the property.
5239 stage
.removeProperty(PROPERTY_POSX
);
5240 stage
.removeProperty(PROPERTY_POSY
);
5241 stage
.removeProperty(PROPERTY_POSZ
);
5244 // Apply orientation.
5245 pair
<bool, sint64
> resultTeta
= stage
.property(PROPERTY_ORIENTATION
);
5246 if(resultTeta
.first
)
5249 parts
.i64
[0] = resultTeta
.second
;
5250 float angleZ
= parts
.f
[0];
5251 // server forces the entity orientation even if it cannot turn
5252 front(CVector((float)cos(angleZ
), (float)sin(angleZ
), 0.f
), true, true, true);
5254 _TargetAngle
= (float)angleZ
;
5255 // Remove the property.
5256 stage
.removeProperty(PROPERTY_ORIENTATION
);
5260 pair
<bool, sint64
> resultMode
= stage
.property(PROPERTY_MODE
);
5261 if(resultMode
.first
)
5263 // Get the mode from stage.
5265 parts
.i64
[0] = resultMode
.second
;
5266 uint8 mo
= parts
.u8
[0];
5267 // If the mode wanted is not the same, change the mode wanted.
5268 if(mo
!= _ModeWanted
)
5270 if(mode((MBEHAV::EMode
)mo
))
5272 //stageDone = false;
5273 if(_Mode
!= _ModeWanted
)
5276 stage
.removeProperty(PROPERTY_MODE
);
5279 stage
.removeProperty(PROPERTY_MODE
);
5281 // If the mode wanted is not the same as the current mode -> Stage not done.
5282 else if(_Mode
!= _ModeWanted
)
5284 // Property applied -> Remove the property form stage.
5286 stage
.removeProperty(PROPERTY_MODE
);
5290 pair
<bool, sint64
> resultBehaviour
= stage
.property(PROPERTY_BEHAVIOUR
);
5291 if(resultBehaviour
.first
)
5293 CBehaviourContext bc
;
5294 bc
.Behav
= CBehaviour(resultBehaviour
.second
);
5295 bc
.BehavTime
= stage
.time();
5296 // See if there's a list of target associated with that behaviour (for multitarget spells)
5297 uint64 spellTarget
[4];
5298 uint numTargets
= 0;
5299 for(uint k
= 0; k
< 4; ++k
)
5301 pair
<bool, sint64
> stProp
= stage
.property(PROPERTY_TARGET_LIST_0
+ k
);
5302 if (!stProp
.first
) break;
5303 spellTarget
[k
] = (uint64
) stProp
.second
;
5305 stage
.removeProperty(PROPERTY_TARGET_LIST_0
+ k
);
5310 // get the list of targets from the visual properties
5311 bc
.Targets
.unpack(spellTarget
, numTargets
);
5313 // Compute the beheviour.
5316 // Remove the property.
5317 stage
.removeProperty(PROPERTY_BEHAVIOUR
);
5320 // Apply the target.
5321 pair
<bool, sint64
> resultTarget
= stage
.property(PROPERTY_TARGET_ID
);
5322 if(resultTarget
.first
)
5324 // Change the entity target.
5325 targetSlot((CLFECOMMON::TCLEntityId
)resultTarget
.second
);
5326 // Remove the property.
5327 stage
.removeProperty(PROPERTY_TARGET_ID
);
5331 pair
<bool, sint64
> resultParent
= stage
.property(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
);
5332 if(resultParent
.first
)
5334 _Mount
= (CLFECOMMON::TCLEntityId
)resultParent
.second
;
5336 // Remove the property.
5337 stage
.removeProperty(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID
);
5341 pair
<bool, sint64
> resultRider
= stage
.property(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
);
5342 if(resultRider
.first
)
5344 _Rider
= (CLFECOMMON::TCLEntityId
)resultRider
.second
;
5346 // Remove the property.
5347 stage
.removeProperty(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID
);
5350 // visual fxs : links and auras
5351 pair
<bool, sint64
> resultVisualFX
= stage
.property(CLFECOMMON::PROPERTY_VISUAL_FX
);
5352 if (resultVisualFX
.first
)
5354 applyVisualFX(resultVisualFX
.second
);
5355 stage
.removeProperty(CLFECOMMON::PROPERTY_VISUAL_FX
);
5362 //-----------------------------------------------
5363 // applyCurrentStage :
5364 // Apply The Current Stage (first stage).
5365 //-----------------------------------------------
5366 bool CCharacterCL::applyCurrentStage()
5370 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5371 if(it
!= _Stages
._StageSet
.end())
5373 // Apply the Stage and remove it if stage done.
5374 if(applyStage((*it
).second
))
5375 _Stages
._StageSet
.erase(it
);
5380 nlwarning("CCharacterCL::applyCurrentStage: there is no stage.");
5382 // Update information from remaining stages.
5386 }// applyCurrentStage //
5389 //-----------------------------------------------
5390 // applyAllStagesToFirstPos :
5391 // Apply all stages to the first stage with a position.
5392 //-----------------------------------------------
5393 void CCharacterCL::applyAllStagesToFirstPos()
5396 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5397 while(it
!= _Stages
._StageSet
.end() && !(*it
).second
.getPos(stagePos
))
5400 if(!applyStage((*it
).second
))
5406 // Backup the iterator to remove
5407 CStageSet::TStageSet::iterator itTmp
= it
;
5410 // Remove the stage done.
5411 _Stages
._StageSet
.erase(itTmp
);
5414 // Apply the stage with the position.
5415 if(it
!= _Stages
._StageSet
.end())
5418 if(applyStage((*it
).second
))
5419 // Remove the stage done.
5420 _Stages
._StageSet
.erase(it
);
5423 nlwarning("CH:applyAllStagesToFirstPos:%d: There is no stage with a position.", _Slot
);
5425 // Upate information from remaining stages.
5427 }// applyAllStagesToFirstPos //
5430 //-----------------------------------------------
5432 // Play the time step for the loop and truncate to End Anim if Time Step too big.
5433 //-----------------------------------------------
5434 ADD_METHOD(void CCharacterCL::playToEndAnim(const double &startTimeOffset
, double &length
))
5436 double speedToDest
= speed();
5438 if(ClientCfg
.BlendForward
&& (animState(MOVE
) == CAnimationStateSheet::Walk
))
5440 uint animWalkId
= animId(MOVE
);
5441 uint animRunId
= animId(MOVE_BLEND_OUT
);
5442 double animWalkSpeed
= EAM
->getAnimationAverageSpeed(animWalkId
)*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
5443 double animRunSpeed
= EAM
->getAnimationAverageSpeed(animRunId
)*getSheetScale()*_CustomScalePos
*_CharacterScalePos
;
5444 if(animWalkSpeed
<=animRunSpeed
)
5446 double startTimeOffRun
= animOffset(MOVE_BLEND_OUT
);
5447 double animWalkLength
= EAM
->getAnimationLength(animWalkId
);
5448 double animRunLength
= EAM
->getAnimationLength(animRunId
);
5449 // Current Speed <= Walk Speed, so use the walk animation only.
5450 if(speed() <= animWalkSpeed
)
5453 double speedFactor
= speed()/animWalkSpeed
;
5454 double animTimeOffWalk
= animOffset(MOVE
) + length
*speedFactor
;
5455 if(animTimeOffWalk
> animWalkLength
)
5457 animOffset(MOVE
, animWalkLength
);
5458 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5459 length
= (animWalkLength
- startTimeOffset
) / speedFactor
;
5461 // Adjust Time Offset for the Run Channel
5464 animOffset(MOVE
, animTimeOffWalk
);
5465 animOffset(MOVE_BLEND_OUT
, animRunLength
*(animTimeOffWalk
/animWalkLength
));
5468 // Current Speed >= Run Speed, so use the run animation only.
5469 else if(speed() >= animRunSpeed
)
5472 double speedFactor
= speed()/animRunSpeed
;
5473 double animTimeOffRun
= animOffset(MOVE_BLEND_OUT
) + length
*speedFactor
;
5474 if(animTimeOffRun
> animRunLength
)
5476 animOffset(MOVE
, animWalkLength
);
5477 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5478 length
= (animRunLength
- startTimeOffRun
) / speedFactor
;
5480 // Adjust Time Offset for the Walk Channel
5483 animOffset(MOVE
, animWalkLength
*(animTimeOffRun
/animRunLength
));
5484 animOffset(MOVE_BLEND_OUT
, animTimeOffRun
);
5487 // Current Speed > Walk Speed & < Run Speed, so mix Walk and Run animation.
5490 double t1
= animRunSpeed
-animWalkSpeed
;
5491 double t2
= speed()-animWalkSpeed
;
5493 double mixLength
= runFactor()*animRunLength
+ (1.0-runFactor())*animWalkLength
;
5494 double animTimeOffWalk
= animOffset(MOVE
) + animWalkLength
/mixLength
*length
;
5495 if(animTimeOffWalk
> animWalkLength
)
5497 animOffset(MOVE
, animWalkLength
);
5498 animOffset(MOVE_BLEND_OUT
, animRunLength
);
5499 length
= (animWalkLength
- startTimeOffset
) / (animWalkLength
/mixLength
);
5503 animOffset(MOVE
, animTimeOffWalk
);
5504 animOffset(MOVE_BLEND_OUT
, animRunLength
*animTimeOffWalk
/animWalkLength
); // Same percentage in the animation than the Walk one.
5510 //nlwarning("playToEndAnim:%d: animWalkSpeed > animRunSpeed", _Slot);
5512 // No Mix between Walk and Run.
5515 double speedFactor
= computeSpeedFactor(speedToDest
);
5516 // Compute the desired new time offset.
5517 double animTimeOffMove
= animOffset(MOVE
) + length
* speedFactor
;
5518 // Truncate animation time offset if it over-runs end of animation and change the loopTimeOffset too.
5519 double animationLength
= EAM
->getAnimationLength(animId(MOVE
));
5520 if(animTimeOffMove
> animationLength
)
5522 animOffset(MOVE
, animationLength
);
5523 length
= (animationLength
- startTimeOffset
) / speedFactor
;
5526 animOffset(MOVE
, animTimeOffMove
);
5527 }// playToEndAnim //
5529 //-----------------------------------------------
5531 // Call this method to give a time for each stage, compute distance to destination and some more information.
5532 // \todo GUIGUI : clean up
5533 //-----------------------------------------------
5534 void CCharacterCL::updateStages()
5536 H_AUTO ( RZ_Client_Entity_CL_updateStages
);
5538 _FirstPos
= INVALID_POS
; // No First Position
5539 _FirstTime
= INVALID_TIME
; //- No First Position
5540 dist2FirstPos(INVALID_DIST
); // No First Position
5541 _DestPos
= INVALID_POS
; // No Destination
5542 _DestTime
= INVALID_TIME
; // No Destination
5543 dist2Dest(INVALID_DIST
); // No Destination
5544 CVectorD posTmp
= pos();
5545 _IsThereAMode
= false;
5546 _ImportantStepTime
= 0.0;
5549 // ***** update predicted interval: if a new pos B is found after a pos A, then the interval B-A is known!
5550 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
5551 CStageSet::TStageSet::iterator itPosPrec
= _Stages
._StageSet
.end();
5552 bool somePosFoundEarly
= false;
5553 while(it
!= _Stages
._StageSet
.end())
5555 // if this stage has a position
5556 if(it
->second
.isPresent(PROPERTY_POSITION
))
5558 somePosFoundEarly
= true;
5559 // then it's cool we can set the new accurate interval to the prec stage which has a pos
5560 if(itPosPrec
!=_Stages
._StageSet
.end())
5562 uint dgc
= it
->first
- itPosPrec
->first
;
5563 itPosPrec
->second
.predictedInterval(dgc
);
5572 // ***** Compute the current LCT Impact for this character
5573 // NB: used only in mode CClientConfig::StageUsePosOnlyLCT
5575 // If disabled, full LCT impact
5576 if(_StartDecreaseLCTImpact
==0)
5580 const sint32 decreaseTick
= 20; // 2 seconds
5581 // blend according to the start of decrease
5582 sint32 dt
= NetMngr
.getCurrentServerTick() - _StartDecreaseLCTImpact
;
5585 else if(dt
>=decreaseTick
)
5588 charLCTI
= ((decreaseTick
-dt
)*256)/decreaseTick
;
5589 // hence, at end of blend, charLCTI is 0
5593 // ***** Compute Stages to give them a time and get some information from those stages.
5594 // yoyo: use any stage with no LCT, until it is to be played AFTER a position
5595 bool stageForceLCTFound
= false;
5596 CStageSet::TStageSet::iterator itTmp
;
5597 it
= _Stages
._StageSet
.begin();
5598 while(it
!= _Stages
._StageSet
.end())
5600 CStage
&stage
= (*it
).second
;
5602 // *** retrieve position in stage if any
5603 CVectorD posInStage
;
5604 bool hasPos
= stage
.getPos(posInStage
);
5605 // check the first pos is correct
5606 if(hasPos
&& dist2Dest()==INVALID_DIST
)
5608 // Compute the distance to the first position
5609 double distToFirst
= (CVectorD(posInStage
.x
, posInStage
.y
, 0.0) - CVectorD( posTmp
.x
, posTmp
.y
, 0.0)).norm();
5610 double distToLimiter
= (CVectorD(posInStage
.x
, posInStage
.y
, 0.0) - CVectorD(_PositionLimiter
.x
, _PositionLimiter
.y
, 0.0)).norm();
5611 // Check if the first pos is Not the same as the current entity pos
5612 if((distToFirst
< ClientCfg
.DestThreshold
)
5613 || (distToLimiter
<= ClientCfg
.PositionLimiterRadius
))
5615 // The FIRST POSITION is the SAME as the CURRENT entity POSITION -> REMOVE POSITION in the stage
5616 stage
.removeProperty(CLFECOMMON::PROPERTY_POSX
);
5617 stage
.removeProperty(CLFECOMMON::PROPERTY_POSY
);
5618 stage
.removeProperty(CLFECOMMON::PROPERTY_POSZ
);
5621 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
5622 nlinfo("CH:updateStages:%d: Bad First, distToFirst(%f), ClientCfg.DestThreshold(%f), distToLimiter(%f), ClientCfg.PositionLimiterRadius(%f)", _Slot
,
5623 distToFirst
, ClientCfg
.DestThreshold
, distToLimiter
, ClientCfg
.PositionLimiterRadius
);
5627 // stage has pos? => force LCT for it and after
5628 stageForceLCTFound
= stageForceLCTFound
|| hasPos
;
5631 // *** Compute the estimated Time for the Stage.
5632 // Compute difference in Game Cycle Between the current Game Cycle and the current Stage.
5634 if(ClientCfg
.StageLCTUsage
==CClientConfig::StageUseAllLCT
)
5635 t
= (*it
).first
- NetMngr
.getCurrentClientTick();
5636 else if(ClientCfg
.StageLCTUsage
==CClientConfig::StageUseNoLCT
)
5637 t
= (*it
).first
- NetMngr
.getCurrentServerTick();
5640 // Update LCTImpact for the stage
5641 if(stageForceLCTFound
)
5643 // Force full impact for stage after or including a POS
5644 stage
.setLCTImpact(256);
5648 /* minimize the LCT impact with the current char one
5649 Hence, if the stage had a lct impact lowered, but LCT decrease was canceled,
5650 the stage will keep its LCT impact
5652 stage
.setLCTImpact(min(stage
.getLCTImpact(), charLCTI
));
5655 sint32 lcti
= stage
.getLCTImpact();
5659 t
= (*it
).first
- NetMngr
.getCurrentClientTick();
5662 t
= (*it
).first
- NetMngr
.getCurrentServerTick();
5666 sint32 twlct
= (*it
).first
- NetMngr
.getCurrentClientTick();
5667 sint32 twolct
= (*it
).first
- NetMngr
.getCurrentServerTick();
5668 t
= (twlct
*lcti
+ twolct
*(256-lcti
))>>8;
5671 // Compute the estimated Time for the Stage.
5672 stage
.time( (double)(NetMngr
.getMachineTimeAtTick() + t
*NetMngr
.getMsPerTick())/1000.0 );
5675 // *** Important step is used for "Panic mode" animation acceleration. skip the first stage
5676 if(_ImportantStepTime
==0.0 && it
!=_Stages
._StageSet
.begin())
5678 // Important steps are ones that takes times (pos, orientation, mode, animation etc....)
5679 if( stage
.isPresent(PROPERTY_POSITION
) ||
5680 stage
.isPresent(PROPERTY_MODE
) ||
5681 stage
.isPresent(PROPERTY_ORIENTATION
) ||
5682 stage
.isPresent(PROPERTY_ENTITY_MOUNTED_ID
) ||
5683 stage
.isPresent(PROPERTY_RIDER_ENTITY_ID
) ||
5684 stage
.isPresent(PROPERTY_BEHAVIOUR
))
5685 _ImportantStepTime
= stage
.time();
5689 // *** Compute dist2dest (until a mode) if has pos
5690 if((_IsThereAMode
==false) && hasPos
)
5692 // Set the destination pos and time.
5693 _DestPos
= posInStage
;
5694 _DestTime
= stage
.time() + (double)(stage
.predictedInterval()*NetMngr
.getMsPerTick())/1000.0;
5695 // Update First Pos.
5696 if(dist2Dest() == INVALID_DIST
)
5698 _FirstPos
= _DestPos
;
5699 _FirstTime
= stage
.time();
5700 // Compute the distance to the first position
5701 double distToFirst
= (CVectorD(_DestPos
.x
, _DestPos
.y
, 0.0) - CVectorD(posTmp
.x
,posTmp
.y
, 0.0)).norm();
5702 // Set the Distance to the Destination as the distance to the first position.
5703 dist2Dest(distToFirst
);
5704 // Set the distance to First Stage.
5705 dist2FirstPos(distToFirst
);
5707 // Increase distance to destination.
5709 dist2Dest(dist2Dest() + (CVectorD(_DestPos
.x
, _DestPos
.y
, 0.0) - CVectorD(posTmp
.x
, posTmp
.y
, 0.0)).norm());
5710 // Backup the last pos.
5713 // Stop if there is a mode in the stage.
5714 if(stage
.isPresent(CLFECOMMON::PROPERTY_MODE
))
5715 _IsThereAMode
= true;
5721 // REMOVE EMPTY STAGE (because only position and the same as the current one).
5723 _Stages
._StageSet
.erase(itTmp
);
5726 // If there is no mode in queue, mode wanted is the current mode
5727 // It must usually be the theorical one except for some mode used only by the client like SWIM
5729 _ModeWanted
= _Mode
;
5731 // ***** update _StartDecreaseLCTImpact
5732 // If a stage that force LCT has been found in the list
5733 if(stageForceLCTFound
)
5734 // Decrease of LCT is disabled
5735 _StartDecreaseLCTImpact
= 0;
5736 else if(_StartDecreaseLCTImpact
==0)
5737 // Start to decrease LCT
5738 _StartDecreaseLCTImpact
= NetMngr
.getCurrentServerTick();
5741 // ***** compute _RunStartTimeNoPop (see _RunStartTimeNoPop)
5742 _RunStartTimeNoPop
= INVALID_TIME
;
5743 // only if have some pos in the queue
5744 if(somePosFoundEarly
)
5747 double fpTime
= INVALID_TIME
;
5748 // if the first pos is computed, use it
5749 if(_FirstTime
!=INVALID_TIME
)
5751 d2fp
= dist2FirstPos();
5754 // else try to compute the first pos, WIHTOUT regarding if there is a mode or not
5755 // (because even mode anim can be accelerated....)
5758 it
= _Stages
._StageSet
.begin();
5759 while(it
!= _Stages
._StageSet
.end())
5761 CStage
&stage
= (*it
).second
;
5763 if(stage
.getPos(firstPos
))
5765 fpTime
= stage
.time() + (double)(stage
.predictedInterval()*NetMngr
.getMsPerTick())/1000.0;
5766 d2fp
= CVectorD(firstPos
.x
-pos().x
, firstPos
.y
-pos().y
, 0.0).norm();
5773 // with d2fp, fpTime, and maxSpeed, we can estimate the moment where the run should start
5774 if(fpTime
!=INVALID_TIME
)
5776 float maxSpeed
= (float)getMaxSpeed();
5779 // compute at which time the first move should begin so it doesn't have to accelerate
5780 _RunStartTimeNoPop
= fpTime
- d2fp
/maxSpeed
;
5787 //-----------------------------------------------
5789 // Return if the impact must be played.
5790 // \param anim : pointer on the current animation (MUST NOT BE NULL).
5791 // \param currentTime : current time in the animation.
5792 // \param triggerName : name of the trigger to check.
5793 // \param isActive : read (and can change) the state.
5794 // \param timeFactor : when to activate the impact if there is no track (value has to be between 0 and 1 to be valid).
5795 // \return bool : true if the trigger is valid.
5796 // \warning This method does not check if the animation is Null.
5797 //-----------------------------------------------
5798 ADD_METHOD(bool CCharacterCL::beginImpact(NL3D::UAnimation
*anim
, NL3D::TAnimationTime currentTime
, const std::string
&triggerName
, bool &isActive
, float timeFactor
))
5799 // Is the impact already activeated.
5803 // Try to find the impact trigger in the animation.
5804 UTrack
*Track
= anim
->getTrackByName(triggerName
.c_str());
5805 // No track -> just check with 2/3 animation
5808 if(Track
->interpolate(currentTime
, isActive
))
5811 nlwarning("CH:beginImpact:%d: Wrong type asked.", _Slot
);
5814 // No Track or pb with it so try with the animation length.
5815 float length
= (float)(anim
->getEndTime()-anim
->getBeginTime());
5816 isActive
= (animOffset(MOVE
) >= length
*timeFactor
);
5820 //-----------------------------------------------
5821 // animEventsProcessing :
5822 // Manage Events that could be created by the animation (like sound).
5823 // \param startTime : time to start processing events from the current animation.
5824 // \param stopTime : time to stop processing events from the current animation.
5825 // \todo GUIGUI : Optimize FXs launch when we would have time
5826 //-----------------------------------------------
5827 void CCharacterCL::animEventsProcessing(double startTime
, double stopTime
)
5829 if (_CurrentState
== 0)
5832 // \todo Vianney : temp le temps de savoir comment on joue les son pour le propre joueur
5833 // No sound for the player.
5834 if(_Slot
!= 0 || _Mode
!= MBEHAV::NORMAL
)
5836 // Retreive the surface material
5837 uint32 matId
= getGroundType();
5838 // Set the material id var
5839 _SoundContext
.Args
[0] = matId
;
5842 // Set the sound family var
5843 _SoundContext
.Args
[2] = _Sheet
->SoundFamily
;
5844 // Set the sound variation var
5845 _SoundContext
.Args
[3] = _Sheet
->SoundVariation
;
5849 // Set the sound family var
5850 _SoundContext
.Args
[2] = 0;
5851 // Set the sound variation var
5852 _SoundContext
.Args
[3] = 0;
5855 CSoundAnimManager
* sndMngr
= CSoundAnimManager::instance();
5856 if(sndMngr
&& (_SoundId
[MOVE
] != CSoundAnimationNoId
))
5858 _SoundContext
.Position
= pos();
5859 // Look for the cluster(s) containing this character...
5860 std::vector
<NL3D::CCluster
*> clusters
;
5861 if (!_Instance
.empty())
5864 _Instance
.getLastParentClusters(clusters
);
5866 else if (!_Skeleton
.empty())
5869 _Skeleton
.getLastParentClusters(clusters
);
5871 CCluster
*pcluster
= 0;
5872 // use the first cluster if at leat one available
5873 if (!clusters
.empty())
5874 pcluster
= clusters
.front();
5875 sndMngr
->playAnimation(_SoundId
[MOVE
], (float) startTime
, (float) stopTime
, pcluster
, _SoundContext
);
5879 }// animEventsProcessing //
5881 //-----------------------------------------------
5882 // updatePreCollision :
5883 // Method called each frame to manage the entity.
5884 // \param time : current time of the frame.
5885 // \parem target : pointer on the current entity target.
5886 //-----------------------------------------------
5887 void CCharacterCL::updatePreCollision(const TTime
¤tTimeInMs
, CEntityCL
*target
) // virtual
5889 H_AUTO ( RZ_Client_Entity_CL_Update_Pre_Collision
);
5890 // Set the Last frame PACS Pos.
5892 _Primitive
->getGlobalPosition(_LastFramePACSPos
, dynamicWI
);
5893 // Set the previous position before changing the current one.
5894 _LastFramePos
= _Position
;
5896 // Turn towards the target when in COMBAT_FLOAT mode.
5897 if(_Mode
== MBEHAV::COMBAT_FLOAT
)
5899 H_AUTO ( RZ_Client_Entity_CL_Update_Combat
)
5901 // Check there is a valid target and it's not the entity itself.
5902 if(targetSlot() != CLFECOMMON::INVALID_SLOT
5903 && targetSlot() != slot()
5905 // Set the new entity direction
5906 front(target
->pos() - pos(), true, false);
5909 // Update Position if not a child & displayable.
5910 if(parent() == CLFECOMMON::INVALID_SLOT
&& _Displayable
)
5912 H_AUTO ( RZ_Client_Entity_CL_Update_Pos
)
5913 updatePos(currentTimeInMs
, target
);
5915 }// updatePreCollision //
5917 //-----------------------------------------------
5919 // Apply track on fxs
5920 // Check if some FX should be removed.
5921 //-----------------------------------------------
5922 inline void CCharacterCL::updateFX()
5928 //-----------------------------------------------
5929 // updateAttachedFX :
5930 // Apply track on animated fxs
5931 // Remove those that should be removed
5932 //-----------------------------------------------
5933 void CCharacterCL::updateAttachedFX()
5935 // build align matrix
5936 CMatrix alignMatrix
;
5937 buildAlignMatrix(alignMatrix
);
5939 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
5940 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
5942 if ((*itAttachedFxToStart
).DelayBeforeStart
< (float)(TimeInSec
- (*itAttachedFxToStart
).StartTime
))
5944 uint index
= (*itAttachedFxToStart
).MaxNumAnimCount
;
5945 (*itAttachedFxToStart
).MaxNumAnimCount
= 0;
5946 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
5947 fx
->create(*this, (*itAttachedFxToStart
), CAttachedFX::CTargeterInfo());
5948 if (!fx
->FX
.empty())
5950 _AuraFX
[index
] = fx
;
5952 itAttachedFxToStart
= _AttachedFXListToStart
.erase(itAttachedFxToStart
);
5956 ++itAttachedFxToStart
;
5960 // update tracks & pos for anim attachedfxs
5961 std::list
<CAttachedFX::TSmartPtr
>::iterator itAttachedFx
= _AttachedFXListForCurrentAnim
.begin();
5962 while(itAttachedFx
!= _AttachedFXListForCurrentAnim
.end())
5964 nlassert(*itAttachedFx
);
5965 CAttachedFX
&attachedFX
= **itAttachedFx
;
5966 attachedFX
.update(*this, alignMatrix
);
5967 if (!attachedFX
.FX
.empty()) attachedFX
.FX
.setUserMatrix(alignMatrix
);
5971 // Try to remove animation FXs still not removed.
5972 itAttachedFx
= _AttachedFXListToRemove
.begin();
5973 while(itAttachedFx
!= _AttachedFXListToRemove
.end())
5975 // If the FX is not present or valid -> remove the FX.
5976 bool mustDelete
= false;
5977 CAttachedFX
&attachedFX
= **itAttachedFx
;
5978 if (attachedFX
.SpawnTime
!= TimeInSec
)
5980 if(attachedFX
.FX
.empty() || !attachedFX
.FX
.isSystemPresent() || !attachedFX
.FX
.isValid())
5985 if (attachedFX
.TimeOutDate
!= 0)
5987 if (TimeInSec
>= attachedFX
.TimeOutDate
)
5994 // Remove from the list.
5995 itAttachedFx
= _AttachedFXListToRemove
.erase(itAttachedFx
);
5999 attachedFX
.update(*this, alignMatrix
);
6000 if (!attachedFX
.FX
.empty()) attachedFX
.FX
.setUserMatrix(alignMatrix
);
6005 // update the aura fx
6006 for(uint k
= 0; k
< MaxNumAura
; ++k
)
6010 if (_AuraFX
[k
]->TimeOutDate
!= 0.f
) // we use that flag to mark the aura as 'shutting down'
6012 if (TimeInSec
>= _AuraFX
[k
]->TimeOutDate
)
6018 float lifeRatio
= (float) ((_AuraFX
[k
]->TimeOutDate
- TimeInSec
) / AURA_SHUTDOWN_TIME
);
6019 if (!_AuraFX
[k
]->FX
.empty()) _AuraFX
[k
]->FX
.setUserParam(0, 1.f
- lifeRatio
);
6022 if (_AuraFX
[k
]) // not deleted yet ?
6024 // update position & orientation
6025 _AuraFX
[k
]->update(*this, alignMatrix
);
6030 // update the link fx
6033 _LinkFX
->update(*this, alignMatrix
);
6037 _StaticFX
->FX
->update(*this, alignMatrix
);
6044 //-----------------------------------------------
6046 //-----------------------------------------------
6047 void CCharacterCL::updateVisible (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6049 // Changes the skeleton state
6050 if(!_Skeleton
.empty())
6054 // Changes the instance position.
6055 else if(!_Instance
.empty())
6060 // Snap the entity to the ground.
6062 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground
)
6066 // Apply the new entity position to the visual of the entity (apply x and z movement due to animation).
6067 if(parent() == CLFECOMMON::INVALID_SLOT
)
6069 H_AUTO ( RZ_Client_Entity_CL_Update_Display
)
6073 // Change the cluster of the entity.
6075 H_AUTO ( RZ_Client_Entity_CL_Update_Cluster
)
6079 // Update the LodCharacter Animation.
6080 if(_LodCharacterAnimEnabled
)
6082 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation
)
6084 // set this value to the skeleton
6086 skeleton()->setLodCharacterAnimTime(_LodCharacterAnimTimeOffset
);
6091 H_AUTO ( RZ_Client_Entity_CL_Update_FX
)
6096 if(!_HPModifiers
.empty())
6099 mod
.CHPModifier::operator= (*_HPModifiers
.begin());
6100 mod
.Time
= TimeInSec
+ mod
.DeltaT
;
6101 _HPDisplayed
.push_back(mod
);
6102 _HPModifiers
.erase(_HPModifiers
.begin());
6106 CEntityCL::updateVisible(currentTimeInMs
, target
);
6107 }// updateVisible //
6109 //-----------------------------------------------
6110 // updateSomeClipped :
6111 //-----------------------------------------------
6112 void CCharacterCL::updateSomeClipped (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6114 // Snap the entity to the ground.
6116 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground
)
6120 // Changes the skeleton position.
6121 if(!_Skeleton
.empty())
6123 _Skeleton
.setPos(pos());
6126 // Changes the instance position.
6127 else if(!_Instance
.empty())
6129 _Instance
.setPos(pos());
6133 if(!ClientCfg
.Light
)
6135 // Update texture Async Loading
6136 updateAsyncTexture();
6137 // Update lod Texture
6142 H_AUTO ( RZ_Client_Entity_CL_Update_FX
)
6147 // Remove Modifiers.
6148 _HPModifiers
.clear();
6149 _HPDisplayed
.clear();
6152 CEntityCL::updateSomeClipped(currentTimeInMs
, target
);
6153 }// updateSomeClipped //
6155 //-----------------------------------------------
6157 //-----------------------------------------------
6158 void CCharacterCL::updateClipped (const TTime
¤tTimeInMs
, CEntityCL
*target
)
6160 // hide the scene interface
6161 if (_InSceneUserInterface
)
6163 if (_InSceneUserInterface
->getActive())
6164 _InSceneUserInterface
->setActive (false);
6168 if (_CurrentBubble
->getActive())
6169 _CurrentBubble
->setActive (false);
6173 CEntityCL::updateClipped(currentTimeInMs
, target
);
6174 }// updateClipped //
6176 //-----------------------------------------------
6177 // updateVisiblePostPos :
6178 // Update the entity after all positions done.
6179 //-----------------------------------------------
6180 void CCharacterCL::updateVisiblePostPos(const NLMISC::TTime
¤tTimeInMs
, CEntityCL
*target
) // virtual
6182 // Stuff to do only when alive.
6185 // Update the head direction.
6187 H_AUTO ( RZ_Client_Entity_CL_Update_Head_Direction
)
6188 updateHeadDirection(target
);
6192 H_AUTO ( RZ_Client_Entity_CL_Update_Blink
)
6193 updateBlink(currentTimeInMs
);
6197 // Update in scene interface
6198 if(_InSceneUserInterface
|| _CurrentBubble
)
6200 // Draw the entity Name if asked or under the cursor.
6201 bool showIS
= mustShowInsceneInterface( (!_Sheet
) || (_Sheet
->DisplayOSD
) );
6202 bool showBubble
= true;
6204 // Don't show bubble if lod
6205 if (!_Skeleton
.empty() && _Skeleton
.isDisplayedAsLodCharacter())
6210 // If the name of the character is unknown, no user info
6211 if (_EntityName
.empty() && _Title
.empty())
6214 // if mounted : don't display name
6215 if( _Rider
!= CLFECOMMON::INVALID_SLOT
)
6222 if (_InSceneUserInterface
)
6225 _InSceneUserInterface
->setActive (showIS
);
6229 // Update dynamic data
6230 _InSceneUserInterface
->updateDynamicData ();
6232 NLMISC::CVectorD pos
;
6233 if (getNamePos(pos
))
6235 // Check the pos validity
6236 if((isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
)) == false)
6238 nlwarning("CH:updateVisiblePostPos:%d: invalid pos %f %f %f", _Slot
, pos
.x
, pos
.y
, pos
.z
);
6241 _InSceneUserInterface
->Position
= pos
;
6245 pos
= (box().getMin() + box().getMax())/2;
6246 pos
.z
= box().getMax().z
;
6247 nlassert(isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
));
6248 _InSceneUserInterface
->Position
= pos
;
6256 showBubble
&= _CurrentBubble
->canBeShown();
6259 if (_CurrentBubble
->getActive() != showBubble
)
6260 _CurrentBubble
->setActive (showBubble
);
6266 if (_InSceneUserInterface
)
6267 offsetX
= - 10 - (_InSceneUserInterface
->getWReal() / 2);
6268 _CurrentBubble
->setOffsetX (offsetX
);
6270 NLMISC::CVectorD pos
;
6271 if (!getNamePos(pos
))
6273 pos
= (box().getMin() + box().getMax())/2;
6274 pos
.z
= box().getMax().z
;
6276 nlassert(isValidDouble(pos
.x
) && isValidDouble(pos
.y
) && isValidDouble(pos
.z
));
6277 _CurrentBubble
->Position
= pos
;
6283 CEntityCL::updateVisiblePostPos(currentTimeInMs
, target
);
6284 }// updateVisiblePostPos //
6287 //-----------------------------------------------
6288 // updatePostCollision :
6289 // Method called each frame to manage the entity.
6290 // \param time : current time of the frame.
6291 // \parem target : pointer on the current entity target.
6292 //-----------------------------------------------
6293 void CCharacterCL::updatePostCollision(const TTime
&/* currentTimeInMs */, CEntityCL
* /* target */) // virtual
6295 H_AUTO ( RZ_Client_Entity_CL_Update_Post_Collision
)
6297 // Finalize PACS position
6299 H_AUTO ( RZ_Client_Entity_CL_Update_Finalize_Move
)
6301 // \todo GUIGUI : fait rapidement pour voir les autres se baigner pour la video, faire mieux.
6303 // changed : Malkav , also do this for mektoub (as they can swim)
6304 if(PACS
&& _Primitive
6305 && (isPlayer() || isNPC()
6306 || (_Sheet
&& (_Sheet
->Race
== EGSPD::CPeople::MektoubMount
|| _Sheet
->Race
== EGSPD::CPeople::MektoubPacker
))
6313 UGlobalPosition gPos
;
6314 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
6316 if(GR
->isWaterPosition(gPos
, waterHeight
))
6318 if(isSwimming()==false)
6322 _Mode
= MBEHAV::SWIM_DEATH
;
6324 else if (isRiding())
6326 _Mode
= MBEHAV::MOUNT_SWIM
;
6328 // also change mounted entity mode
6329 if (_Mount
!= CLFECOMMON::INVALID_SLOT
)
6331 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
6335 mount
->setMode(MBEHAV::MOUNT_SWIM
);
6336 mount
->computeAutomaton();
6337 mount
->computeAnimSet();
6338 mount
->setAnim(CAnimationStateSheet::Idle
);
6344 _Mode
= MBEHAV::SWIM
;
6347 // Compute the current automaton
6349 // Update the animation set according to the mode.
6351 // Animset changed -> update current animation
6352 setAnim(CAnimationStateSheet::Idle
);
6361 _Mode
= MBEHAV::DEATH
;
6363 else if (isRiding())
6365 _Mode
= MBEHAV::MOUNT_NORMAL
;
6366 // also change mounted entity mode
6367 if (_Mount
!= CLFECOMMON::INVALID_SLOT
)
6369 CCharacterCL
*mount
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(_Mount
));
6373 mount
->setMode(MBEHAV::MOUNT_NORMAL
);
6374 mount
->computeAutomaton();
6375 mount
->computeAnimSet();
6376 mount
->setAnim(CAnimationStateSheet::Idle
);
6382 _Mode
= MBEHAV::NORMAL
;
6385 // Compute the current automaton
6387 // Update the animation set according to the mode.
6389 // Animset changed -> update current animation
6390 setAnim(CAnimationStateSheet::Idle
);
6396 }// updatePostCollision //
6399 //-----------------------------------------------
6401 //-----------------------------------------------
6402 double CCharacterCL::getTheMove(double loopTimeStep
, double oldMovingTimeOffset
, double oldMovingTimeOffsetRun
) const
6407 // A Real Move State
6408 if(_CurrentState
->Move
)
6410 // Get the covered distance from the animation.
6411 move
= getTheMove(loopTimeStep
, oldMovingTimeOffset
, MOVE
);
6412 if(runFactor() > 0.0)
6414 // Blend the 2 move (Walk & Run).
6415 move
= move
*(1.0-runFactor()) + getTheMove(loopTimeStep
, oldMovingTimeOffsetRun
, MOVE_BLEND_OUT
)*runFactor();
6416 // The move must be significant.
6417 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6418 move
= ClientCfg
.SignificantDist
;
6422 else if(_CurrentState
->Slide
)
6424 move
= speed() * loopTimeStep
;
6425 // The move must be significant.
6426 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6427 move
= ClientCfg
.SignificantDist
;
6434 move
= speed() * loopTimeStep
;
6435 // The move must be significant.
6436 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6437 move
= ClientCfg
.SignificantDist
;
6439 // Check the move is significant.
6440 CHECK(!((move
>0.0) && (move
<ClientCfg
.SignificantDist
)));
6441 // Return the move done by the entity since last time.
6444 //-----------------------------------------------
6446 //-----------------------------------------------
6447 double CCharacterCL::getTheMove(double loopTimeStep
, double oldMovingTimeOffset
, TAnimationType channel
) const
6450 // Compute a linear motion when the animation is missing.
6451 if(animIndex(channel
) == CAnimation::UnknownAnim
)
6453 double offsetT
= _DestTime
- _LastFrameTime
;
6456 // \todo GUIGUI : in this case, 'loopTimeStep' should not decrease so FIX IT.
6461 move
= dist2Dest() * (loopTimeStep
/ offsetT
);
6462 // The move must be significant.
6463 if((move
>0.0) && (move
<ClientCfg
.SignificantDist
))
6464 move
= ClientCfg
.SignificantDist
;
6467 // Get the motion done by the animation.
6469 move
= computeMotion(oldMovingTimeOffset
, channel
);
6471 CHECK(!(move
>0.0 && move
<ClientCfg
.SignificantDist
));
6475 //-----------------------------------------------
6476 // updatePosCombatFloat :
6477 //-----------------------------------------------
6478 void CCharacterCL::updatePosCombatFloat(double /* frameTimeRemaining */, CEntityCL
*target
) // virtual
6480 H_AUTO_USE ( RZ_Client_Character_CL_Update_Pos_Combat_Float
)
6482 // The target is valid
6485 // Get the position where the attacker should go to attack his target according to the attack angle.
6486 CVectorD dirToTarget
= target
->pos() - pos();
6487 dirToTarget
.z
= 0.0;
6489 || ((dirToTarget
!= CVectorD::Null
)
6490 && fabs(target
->pos().x
-target
->lastFramePos().x
)>0.01
6491 && fabs(target
->pos().y
-target
->lastFramePos().y
)>0.01))
6493 double angToTarget
= atan2(dirToTarget
.y
, dirToTarget
.x
);
6494 _DestPos
= target
->getAttackerPos(angToTarget
, attackRadius() + ClientCfg
.AttackDist
);
6497 _DestPos
= target
->getAttackerPos(_TargetAngle
, attackRadius() + ClientCfg
.AttackDist
);
6498 // Compute the distance to destination.
6499 CVectorD vectToDest
= _DestPos
- pos();
6501 // Distance to destination is big enough.
6502 if(vectToDest
.norm() > ClientCfg
.DestThreshold
)
6504 dist2Dest(vectToDest
.norm());
6505 // Compute the time to reach the destination at the max speed.
6506 double lengthOfTimeToDest
= 0.0; // 0 = No Speed Limit
6507 _FirstPos
= _DestPos
;
6508 _DestTime
= _LastFrameTime
+ lengthOfTimeToDest
+ ClientCfg
.ChaseReactionTime
;
6509 _FirstTime
= _DestTime
;
6511 // The time remaining will be enough to reach the destination
6512 if(frameTimeRemaining >= lengthOfTimeToDest)
6514 _FirstPos = _DestPos;
6515 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6516 _FirstTime = _DestTime;
6518 // The time remaining is not enough to reach the destination at max speed -> compute a first pos possible to reach.
6521 _FirstPos = pos() + vectToDest*frameTimeRemaining/lengthOfTimeToDest;
6522 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6523 _FirstTime = _LastFrameTime + frameTimeRemaining + ClientCfg.ChaseReactionTime;
6526 // Compute the distance to the first position.
6527 CVectorD tmp2computeDist2FirstPos
= _FirstPos
-pos();
6528 tmp2computeDist2FirstPos
.z
= 0.0;
6529 dist2FirstPos(tmp2computeDist2FirstPos
.norm());
6531 updatePosCombatFloatChanged(target
);
6533 // Destination is too close (will consider to be at destination.
6536 _FirstPos
= _DestPos
= pos();
6539 _FirstTime
= _DestTime
= _LastFrameTime
;
6542 // The target is not allocated.
6545 _FirstPos
= _DestPos
= pos();
6548 _FirstTime
= _DestTime
= _LastFrameTime
;
6550 }// updatePosCombatFloat //
6552 //-----------------------------------------------
6554 // Upadte the player position
6555 // \param time : Time for the position of the entity after the motion.
6556 // \param target : pointer on the current target.
6557 // \todo GUIGUI : compute it when receiving a new stage instead of every frame (should be faster).
6558 // \todo GUIGUI: recompute distance to destination even if the Stage not reached.
6559 //-----------------------------------------------
6560 ADD_METHOD(void CCharacterCL::updatePos(const TTime
¤tTimeInMs
, CEntityCL
*target
))
6561 _OldAutomaton
= _CurrentAutomaton
;
6562 // Compute the Time Step.
6563 double frameTimeRemaining
= computeTimeStep(((double)currentTimeInMs
)*0.001);
6564 // Update the LodCharacter Animation.
6565 if(_LodCharacterAnimEnabled
)
6567 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation
);
6568 // \todo GUIGUI : replace 'getSpeedFactor' by the correct speed factor !!
6569 // update lod anim time. multiply by speed factor of the most important slot.
6570 _LodCharacterAnimTimeOffset
+= DT
* _PlayList
->getSpeedFactor(_LodCharacterMasterAnimSlot
);
6575 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Set_Play_List
);
6576 // \todo GUIGUI : do something better for the blend (mounts there).
6577 if(isRiding() || ClientCfg
.BlendFrameNumber
== 0 || _BlendRemaining
<= 0)
6579 _BlendRemaining
= 0;
6580 _PlayList
->setAnimation(ACTION
, UPlayList::empty
);
6581 _PlayList
->setWeight(ACTION
, 0.0f
);
6582 if(runFactor() < 0.5 || (_CurrentAnimSet
[MOVE_BLEND_OUT
]==0))
6584 if(_CurrentAnimSet
[MOVE
])
6586 _CurrentAnimSet
[ACTION
] = _CurrentAnimSet
[MOVE
];
6587 animState (ACTION
, animState (MOVE
));
6588 animIndex (ACTION
, animIndex (MOVE
)); // This also call "animId" and set it.
6589 animOffset(ACTION
, animOffset(MOVE
));
6593 _CurrentAnimSet
[ACTION
] = 0;
6594 animState (ACTION
, CAnimationStateSheet::UnknownState
);
6595 animIndex (ACTION
, CAnimation::UnknownAnim
); // This also call "animId" and set it.
6596 animOffset(ACTION
, 0.0);
6601 _CurrentAnimSet
[ACTION
] = _CurrentAnimSet
[MOVE_BLEND_OUT
];
6602 animState (ACTION
, animState (MOVE_BLEND_OUT
));
6603 animIndex (ACTION
, animIndex (MOVE_BLEND_OUT
)); // This also call "animId" and set it.
6604 animOffset(ACTION
, animOffset(MOVE_BLEND_OUT
));
6606 _AnimReversed
[ACTION
] = false;
6610 double animLength
= EAM
->getAnimationLength(animId(ACTION
));
6611 // Check Anim length
6612 if(animOffset(ACTION
)+frameTimeRemaining
> animLength
)
6613 animOffset(ACTION
, animLength
);
6615 animOffset(ACTION
, animOffset(ACTION
)+frameTimeRemaining
);
6616 // Compute weight step.
6617 float w
= (float)_BlendRemaining
/(float)(ClientCfg
.BlendFrameNumber
+1);
6618 // Set Old Anim Weight.
6619 _PlayList
->setWeight(ACTION
, w
);
6620 // Set New Anim Weight.
6621 _PlayList
->setWeight(MOVE
, 1.f
-w
);
6624 uint antiFreezeCounter
= 0;
6625 // While the time Step is not Null.
6626 while(frameTimeRemaining
> 0)
6628 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_WhileStep
);
6630 //--------------------//
6631 //--------------------//
6632 // ANTI-FREEZE SYSTEM //
6633 // If too many loop, display some infos
6634 if(antiFreezeCounter
> 50)
6637 nlwarning("CH:updatePos:antiFreeze:%d: frameTimeRemaining '%f'", _Slot, frameTimeRemaining);
6638 nlwarning("CH:updatePos:antiFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
6639 nlwarning("CH:updatePos:antiFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
6640 nlwarning("CH:updatePos:antiFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
6641 nlwarning("CH:updatePos:antiFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
6642 nlwarning("CH:updatePos:antiFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
6643 nlwarning("CH:updatePos:antiFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationState::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
6645 // Once too many more time reached, leave the method.
6646 if(antiFreezeCounter
> 60)
6649 // Update antiFreezeCounter.
6650 ++antiFreezeCounter
;
6651 // ANTI-FREEZE SYSTEM //
6652 //--------------------//
6653 //--------------------//
6654 // \todo GUIGUI : improve dist2first and dist2dest
6657 // \todo GUIGUI : Bug with _TargetAngle in fight float, we overwrite here angle sent by the server ?
6658 // If the entity is too far (orientation not received yet), set the front vector as the moving direction.
6659 CVectorD distToUser
= pos()-UserEntity
->pos();
6661 if(distToUser
.norm()*1000.0 > CLFECOMMON::THRESHOLD_ORIENTATION
*0.9)
6663 if(_FirstPos
!= INVALID_POS
)
6665 CVectorD dirToFirstP
= _FirstPos
-pos();
6666 dirToFirstP
.z
= 0.0;
6667 if(dirToFirstP
!= CVectorD::Null
)
6669 front(dirToFirstP
.normed(), false, false);
6670 _TargetAngle
= atan2(front().y
, front().x
);
6674 // Mode Combat Float :
6675 if(!_IsThereAMode
&& (_Mode
== MBEHAV::COMBAT_FLOAT
))
6677 // Update the position in combat float.
6678 updatePosCombatFloat(frameTimeRemaining
, target
);
6680 // Compute the average speed to the destination.
6685 bool stageReach
= false;
6686 bool allToFirstPos
= false;
6687 // Compute time to Stage or full Time Step if Stage too far.
6688 double loopTimeStep
= frameTimeRemaining
;
6689 double buLoopTimeStep
= 0.0;
6690 double checkLoopTimeStep
= loopTimeStep
;
6691 // Update the animation used according to the speed/end anim/etc..
6692 updateAnimationState();
6693 // Backup the old time offset.
6694 double oldMovingTimeOffset
= animOffset(MOVE
);
6695 double oldMovingTimeOffsetRun
= animOffset(MOVE_BLEND_OUT
);
6696 // WARNING -> Unknown Animation Selected.
6697 // Play the time step for the loop and truncate to End Anim if Time Step too big.
6698 if((_CurrentState
!= 0) && (animIndex(MOVE
) != CAnimation::UnknownAnim
))
6699 playToEndAnim(oldMovingTimeOffset
, loopTimeStep
);
6702 if(loopTimeStep
> checkLoopTimeStep
)
6704 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > checkLoopTimeStep(%f).", _Slot
, loopTimeStep
, checkLoopTimeStep
);
6707 loopTimeStep
= checkLoopTimeStep
;
6709 checkLoopTimeStep
= loopTimeStep
;
6710 // -- END CHECK -- //
6711 /////////////////////
6712 // (DEBUG) : Backup the Animation Time Offset after the adjustment with end anim to make some checks.
6713 double backupAnimTimeOff
= animOffset(MOVE
);
6715 bool posInStage
= false;
6716 double stageTime
= -1.0;
6717 if(!_Stages
._StageSet
.empty())
6719 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Move
);
6720 // Get the reference on the current stage.
6721 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
6722 CStage
&stage
= (*it
).second
;
6723 if(_Mode
== MBEHAV::COMBAT_FLOAT
&& !_IsThereAMode
)
6726 posInStage
= stage
.isPresent(CLFECOMMON::PROPERTY_POSITION
);
6727 stageTime
= stage
.time();
6729 // dist2FirstPos() should not be Null if the destination is not Null (because of the code in updateStage).
6730 if(dist2FirstPos() > 0.0)
6732 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_dist2FirstPos_gt_0
);
6734 // Get the covered distance from the animation.
6735 double move
= getTheMove(loopTimeStep
, oldMovingTimeOffset
, oldMovingTimeOffsetRun
);
6736 // The move is big enough to reach the first step with motion.
6737 if(move
>= dist2FirstPos()) // dist2FirstPos() > 0 -> move > 0.
6739 double percent
= dist2FirstPos() / move
;
6740 // Adjust loopTimeStep
6741 loopTimeStep
*= percent
;
6742 if(loopTimeStep
> checkLoopTimeStep
) // prevent bugs because of the double's precision
6743 loopTimeStep
= checkLoopTimeStep
;
6744 if(loopTimeStep
< 0.0)
6746 // Update Animation Time Offset (move greater than the dist to next stage; update animation time to get them equal).
6747 animOffset(MOVE
, oldMovingTimeOffset
+ (animOffset(MOVE
) -oldMovingTimeOffset
)*percent
);
6748 animOffset(MOVE_BLEND_OUT
, oldMovingTimeOffsetRun
+ (animOffset(MOVE_BLEND_OUT
)-oldMovingTimeOffsetRun
)*percent
);
6749 // \todo GUIGUI : check if the following line is necessary
6750 buLoopTimeStep
= loopTimeStep
;
6751 // First Position Reached
6753 dist2FirstPos(0.0); // Current entity position is now the same as the First position so dis is Null.
6754 // Complete the Stage.
6755 if(_Mode
!= MBEHAV::COMBAT_FLOAT
|| _IsThereAMode
)
6760 allToFirstPos
= true;
6763 // Even if the movement is not enough to reach the first position, move the entity to this position.
6766 // Compute the vector to the first stage with a position.
6767 CVectorD vectToFirstPos
= _FirstPos
- pos();
6768 vectToFirstPos
.z
= 0.0f
;
6769 // Update entity position.
6770 if(vectToFirstPos
!= CVectorD::Null
)
6771 pos(pos() + vectToFirstPos
*(move
/dist2FirstPos()));
6773 // Else : There is no move.
6777 CHECK(posInStage
==false && dist2Dest()<=0.0);
6780 // If there is no position in the next stage and the stage should be done already.
6781 if(!_Stages
._StageSet
.empty() && !posInStage
&& !stageReach
&& !allToFirstPos
&& ((_LastFrameTime
+loopTimeStep
) >= stageTime
))
6783 // Backup 'loopTimeStep' just in case of the stage could not be done.
6784 buLoopTimeStep
= loopTimeStep
;
6785 // Adjust loopTimeStep
6786 loopTimeStep
= stageTime
- _LastFrameTime
;
6787 if(loopTimeStep
> checkLoopTimeStep
) // prevent bugs because of the double's precision
6788 loopTimeStep
= checkLoopTimeStep
;
6789 if(loopTimeStep
< 0.0)
6792 // \todo GUIGUI : adjust timeOffset, because we stopped the loop before
6799 // Check the Animation Time Offset is not became greater than the old.
6800 if(animOffset(MOVE
) > backupAnimTimeOff
)
6802 nlwarning("CH:updtPos:%d: backupAnimTimeOff(%f) < AnimationsTimeOffset(%f) animLen(%f) -> animOffset(MOVE) = backupAnimTimeOff",
6803 _Slot
, backupAnimTimeOff
, animOffset(MOVE
), EAM
->getAnimationLength(animId(MOVE
)));
6806 animOffset(MOVE
, backupAnimTimeOff
);
6808 // Check loopTimeStep is not < 0;
6809 if(loopTimeStep
< 0.0)
6811 nlwarning("CH:updtPos:%d: loopTimeStep(%f) < 0 -> loopTimeStep=0.0.", _Slot
, loopTimeStep
);
6816 // time spent could not be bigger than the time remaining
6817 if(loopTimeStep
> frameTimeRemaining
)
6819 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > frameTimeRemaining(%f) -> loopTimeStep=frameTimeRemaining.", _Slot
, loopTimeStep
, frameTimeRemaining
);
6822 loopTimeStep
= frameTimeRemaining
;
6824 // -- END CHECK -- //
6825 /////////////////////
6826 // Manage Events that could be created by the animation (like sound).
6828 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Anim_Event
)
6829 animEventsProcessing(oldMovingTimeOffset
, animOffset(MOVE
));
6831 // Apply all stages until the first stage with a pos.
6834 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_All_Stage_To_First_Pos
);
6835 applyAllStagesToFirstPos();
6837 // Stage is complete, apply modifications.
6840 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_Current_Stage
);
6841 if(!applyCurrentStage())
6842 loopTimeStep
= buLoopTimeStep
;
6844 // Compute the remaining Time Step.
6845 frameTimeRemaining
-= loopTimeStep
;
6846 // Update the last Time.
6847 _LastFrameTime
+= loopTimeStep
;
6848 }// while(frameTimeRemaining > 0) //
6850 ////////////////////////////////
6851 // UPDATE THE ENTITY POSITION //
6852 // Set the new position into PACS.
6854 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Pacs
);
6859 // Check frameTimeRemaining is perfectly equal to 0.
6860 if(frameTimeRemaining
< 0.0)
6861 nlwarning("CCharacterCL::updatePos : frameTimeRemaining(%f) < 0 ! This should never happen.", frameTimeRemaining
);
6864 // Update the children display.
6866 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Children
);
6867 std::list
<CEntityCL
*>::iterator itTmp
, it
= _Children
.begin();
6868 while(it
!= _Children
.end())
6871 // Next Child (done before just in case child is detached during his processFrame).
6876 CCharacterCL
*child
= dynamic_cast<CCharacterCL
*>(*itTmp
);
6879 if ( ! ClientCfg
.Light
)
6881 // Update the animation offset for the child.
6882 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
6883 if(animLength
> 0.0)
6885 double factor
= animOffset(MOVE
) / animLength
;
6888 double childTimeOffset
= factor
*EAM
->getAnimationLength(child
->animId(MOVE
));
6889 child
->animOffset(MOVE
, childTimeOffset
);
6892 child
->animOffset(MOVE
, 0.0);
6893 child
->processFrame(currentTimeInMs
);
6895 child
->pacsMove(pos()); // Move the child at the same position than the parent.
6898 nlwarning("Character '%d': Child is not a 'CCharacterCL'.", _Slot
);
6901 nlwarning("Character '%d': Child not allocated.", _Slot
);
6906 //-----------------------------------------------
6907 // updateVisiblePostRender :
6908 // Update the entity after the render like for the head offset.
6909 //-----------------------------------------------
6910 void CCharacterCL::updateVisiblePostRender() // virtual
6912 // Compute the headoffset
6913 if(_HeadBoneId
!= -1 && !_Skeleton
.empty())
6915 if(_Skeleton
.getLastClippedState() && _Skeleton
.isBoneComputed(_HeadBoneId
))
6917 UBone headBone
=_Skeleton
.getBone(_HeadBoneId
);
6918 const CMatrix
&headMat
= headBone
.getLastWorldMatrixComputed();
6919 _HeadOffset
= headMat
.getPos()-pos();
6920 _HeadOffsetComputed
= true;
6923 }// updateVisiblePostRender //
6925 //-----------------------------------------------
6927 void CCharacterCL::updateAllPostRender()
6931 //-----------------------------------------------
6933 //-----------------------------------------------
6934 void CCharacterCL::processFrame(const TTime
¤tTimeInMs
)
6936 // Prepare stages and update information from them.
6939 // Compute the time remaining until frame completely processed.
6940 double timeRemaining
= computeTimeStep(((double)currentTimeInMs
)*0.001);
6942 // Compute the time spent between 2 frames.
6943 while(timeRemaining
> 0.0)
6945 // Time already processed until now.
6946 double timeProcessed
= timeRemaining
;
6949 if(!_Stages
._StageSet
.empty())
6951 // Get the reference on the current stage.
6952 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
6953 CStage
&stage
= (*it
).second
;
6955 // Check if the stage should be done already.
6956 if((_LastFrameTime
+timeProcessed
) >= stage
.time())
6958 // Stage Done during the Frame.
6959 if(stage
.time() > _LastFrameTime
)
6960 timeProcessed
= stage
.time() - _LastFrameTime
;
6961 // This Stage should have been done already before last frame
6963 timeProcessed
= 0.0;
6965 // Process the stage.
6966 processStage(stage
);
6969 _Stages
._StageSet
.erase(it
);
6973 // Compute the remaining Time Step.
6974 timeRemaining
-= timeProcessed
;
6975 // Update the last Time.
6976 _LastFrameTime
+= timeProcessed
;
6979 // Just to be sure, Last frame time = current time once all is done.
6980 _LastFrameTime
= ((double)currentTimeInMs
)*0.001;
6983 //-----------------------------------------------
6985 // Process the stage.
6986 //-----------------------------------------------
6987 void CCharacterCL::processStage(CStage
&stage
)
6989 // Apply Mode (if there is a mode, there is a position too).
6990 pair
<bool, sint64
> resultMode
= stage
.property(PROPERTY_MODE
);
6991 if(resultMode
.first
)
6993 uint8 mo
= *(uint8
*)(&resultMode
.second
);
6994 MBEHAV::EMode mode
= (MBEHAV::EMode
)mo
;
6997 // release the old mode.
6998 if ( (_Mode
== MBEHAV::MOUNT_NORMAL
|| _Mode
== MBEHAV::MOUNT_SWIM
)
6999 && (mode
!= MBEHAV::MOUNT_NORMAL
&& mode
!= MBEHAV::MOUNT_SWIM
)
7002 // Unlink the mount and the rider.
7003 parent(CLFECOMMON::INVALID_SLOT
);
7004 _Mount
= CLFECOMMON::INVALID_SLOT
;
7005 _Rider
= CLFECOMMON::INVALID_SLOT
;
7007 // Restore collisions.
7010 // \todo GUIGUI : do that without dynamic cast
7011 if(dynamic_cast<CPlayerCL
*>(this))
7012 _Primitive
->setOcclusionMask(MaskColPlayer
);
7014 _Primitive
->setOcclusionMask(MaskColNpc
);
7017 // Get stage position.
7018 if(stage
.getPos(_OldPos
))
7021 _OldPosTime
= stage
.time();
7022 // Unseat the entity at the position given in the stage.
7026 nlwarning("CH:processStage:%d: The stage should have a position with the mode.", _Slot
);
7029 // Set the new mode.
7032 // Compute the automaton
7035 setAnim(CAnimationStateSheet::Idle
);
7038 // Not a mode -> so search for a position.
7042 //-----------------------------------------------
7044 // Update the player blink state
7045 //-----------------------------------------------
7046 void CCharacterCL::updateBlink(const TTime
¤tTimeInMs
)
7049 GSGENDER::EGender gender
= getGender();
7050 if ((gender
== GSGENDER::male
) || (gender
== GSGENDER::female
))
7055 static const double blinkTime
= 100.f
;
7056 static const double minBlinkLength
= 500.f
;
7057 static const double maxBlinkLength
= 5000.f
;
7059 // Next blink time is valid ?
7060 bool validTime
= (_NextBlinkTime
+ blinkTime
>= currentTimeInMs
) && (_NextBlinkTime
<= (currentTimeInMs
+ maxBlinkLength
));
7063 bool blinkEnd
= (currentTimeInMs
>= _NextBlinkTime
+ blinkTime
);
7065 // Blink is finished or next blink time is invalid ?
7066 if ( blinkEnd
|| !validTime
)
7070 // Compute next time
7071 _NextBlinkTime
= (TTime
)(((double)rand () / (double)RAND_MAX
) * (maxBlinkLength
- minBlinkLength
) + minBlinkLength
+ (double)currentTimeInMs
);
7076 if (currentTimeInMs
>= _NextBlinkTime
)
7088 SInstanceCL
*face
= getFace ();
7090 // Set the blend shape
7091 if(face
&& !face
->Current
.empty())
7092 face
->Current
.setBlendShapeFactor ("visage_100", blend
, true);
7097 //-----------------------------------------------
7099 // Update eyes blink. For the moment, called by updatePos.
7100 //-----------------------------------------------
7101 CEntityCL::SInstanceCL
*CCharacterCL::getFace ()
7103 // Implemented in CPlayerCL
7104 return idx2Inst(_FaceIdx
);
7108 //---------------------------------------------------
7110 // Get the entity position and set all visual stuff with it.
7111 // \todo GUIGUI : put this method 'virtual' to have a different code for the user (no playlist).
7112 // \todo GUIGUI : manage the parent better.
7113 //---------------------------------------------------
7114 ADD_METHOD(void CCharacterCL::updateDisplay(CEntityCL
*parent
))
7118 // Reverse the animation if needed.
7119 const double animOffsetMOV
= _AnimReversed
[MOVE
] ? EAM
->getAnimationLength(animId(MOVE
)) - animOffset(MOVE
) : animOffset(MOVE
);
7120 const double animOffsetACT
= _AnimReversed
[ACTION
] ? EAM
->getAnimationLength(animId(ACTION
)) - animOffset(ACTION
) : animOffset(ACTION
);
7121 const double animOffsetBLE
= _AnimReversed
[MOVE_BLEND_OUT
] ? EAM
->getAnimationLength(animId(MOVE_BLEND_OUT
)) - animOffset(MOVE_BLEND_OUT
): animOffset(MOVE_BLEND_OUT
);
7122 // Update Speed Factor
7123 _PlayList
->setTimeOrigin(MOVE
, TimeInSec
-animOffsetMOV
);
7124 _PlayList
->setTimeOrigin(ACTION
, TimeInSec
-animOffsetACT
);
7125 _PlayList
->setTimeOrigin(MOVE_BLEND_OUT
, TimeInSec
-animOffsetBLE
);
7128 weight
= _PlayList
->getLocalWeight(MOVE
, TimeInSec
);
7131 _PlayList
->setWeight(MOVE
, weight
*(float)(1.0-runFactor()));
7132 _PlayList
->setWeight(MOVE_BLEND_OUT
, weight
*(float)runFactor());
7133 // If the animation exist update the display.
7134 if(animIndex(MOVE
) != CAnimation::UnknownAnim
)
7137 // Get the 3D position for the current time in the animation (Vector Null if animation has no move).
7138 CVector currentAnimPos
;
7139 if(!EAM
->interpolate(animId(MOVE
), animOffsetMOV
, currentAnimPos
))
7140 currentAnimPos
= CVector::Null
;
7143 // 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.
7144 if(_CurrentState
&& _CurrentState
->Move
)
7146 CVector currentAnimPosStart
;
7147 if(!EAM
->interpolate(animId(MOVE
), 0.0, currentAnimPosStart
))
7148 currentAnimPosStart
= CVector::Null
;
7149 if(_CurrentState
->XFactor
) currentAnimPos
.x
= currentAnimPosStart
.x
;
7150 if(_CurrentState
->YFactor
) currentAnimPos
.y
= currentAnimPosStart
.y
;
7151 if(_CurrentState
->ZFactor
) currentAnimPos
.z
= currentAnimPosStart
.z
;
7153 // Scale the animation with the Character Scale Pos if the animation need it.
7155 bool applyCharacterScalePosFactor
= true;
7156 // \todo GUIGUI : faire cette histoire de emote beaucoup mieux, C NULL.
7157 const CAnimationState
*animStatePtr
;
7158 // If the current animation is an emote, get the right animation state.
7159 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
7160 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
7162 animStatePtr
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
7165 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE
));
7167 applyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
7170 if(applyCharacterScalePosFactor
)
7171 currentAnimPos
*= _CharacterScalePos
;
7173 // Scale according to the gabarit (choose at the character creation).
7174 currentAnimPos
*= _CustomScalePos
;
7177 if(runFactor() > 0.0)
7179 CVector currentAnimPosRun
;
7180 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), animOffsetBLE
, currentAnimPosRun
))
7181 currentAnimPosRun
= CVector::Null
;
7184 // 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.
7185 if(_CurrentState
&& _CurrentState
->Move
)
7187 CVector currentAnimPosStart
;
7188 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), 0.0, currentAnimPosStart
))
7189 currentAnimPosStart
= CVector::Null
;
7190 if(_CurrentState
->XFactor
) currentAnimPosRun
.x
= currentAnimPosStart
.x
;
7191 if(_CurrentState
->YFactor
) currentAnimPosRun
.y
= currentAnimPosStart
.y
;
7192 if(_CurrentState
->ZFactor
) currentAnimPosRun
.z
= currentAnimPosStart
.z
;
7194 // Scale it by the CharacterScalePos, if needed, according to the animation.
7195 bool applyCharacterScalePosFactor
= true;
7196 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[MOVE_BLEND_OUT
]->getAnimationState(animState(MOVE_BLEND_OUT
));
7199 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(MOVE_BLEND_OUT
));
7201 applyCharacterScalePosFactor
= anim
->applyCharacterScalePosFactor();
7204 if(applyCharacterScalePosFactor
)
7205 currentAnimPosRun
*= _CharacterScalePos
;
7206 // Scale according to the gabarit.
7207 currentAnimPosRun
*= _CustomScalePos
;
7210 currentAnimPos
= currentAnimPos
*(float)(1.0-runFactor()) + currentAnimPosRun
*(float)runFactor();
7213 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7214 CQuat currentAnimRot
;
7215 if(!EAM
->interpolate(animId(MOVE
), animOffsetMOV
, currentAnimRot
))
7216 currentAnimRot
= CQuat::Identity
;
7219 // If the animation is a rotation -> Do just a part of the animation.
7220 if(parent
==0 && _CurrentState
&& _CurrentState
->Rotation
&& _RotationFactor
!=-1.0)
7222 // Get the Rotation at the beginning of the animation.
7223 CQuat currentAnimRotStart
;
7224 if(!EAM
->interpolate(_PlayList
->getAnimation(MOVE
), 0.0, currentAnimRotStart
))
7225 currentAnimRotStart
= CQuat::Identity
;
7227 double animLength
= EAM
->getAnimationLength(animId(MOVE
));
7229 // Get the Rotation at the beginning of the animation.
7230 CQuat currentAnimRotEnd
;
7231 if(!EAM
->interpolate(_PlayList
->getAnimation(MOVE
), animLength
, currentAnimRotEnd
))
7232 currentAnimRotEnd
= CQuat::Identity
;
7234 // Get the angle done by the animation from the beginning
7235 CQuat rotStartTmp
= currentAnimRotStart
;
7236 rotStartTmp
.invert();
7237 CQuat rotTmp
= rotStartTmp
* currentAnimRot
;
7238 float ang
= rotTmp
.getAngle();
7240 currentAnimRot
= applyRotationFactor(currentAnimRot
, (float)_RotationFactor
, currentAnimRotStart
, currentAnimRotEnd
, (float)(animOffsetMOV
/animLength
));
7242 // Get the angle done once scaled.
7243 rotTmp
= rotStartTmp
* currentAnimRot
;
7246 rotMat
.rotateZ(_CurrentState
->RotFactor
*(ang
-rotTmp
.getAngle()));
7248 // Apply the scaled rotation to the position.
7249 currentAnimPos
= rotMat
*currentAnimPos
;
7252 // Get the Rotation at the beginning of the animation.
7253 CQuat currentAnimRotStart;
7254 if(!EAM->interpolate(_PlayList->getAnimation(MOVE), 0.0, currentAnimRotStart))
7255 currentAnimRotStart = CQuat::Identity;
7257 // Find the closest quat.
7258 // currentAnimRotStart.makeClosest(currentAnimRot);
7260 // Get the angle done by the animation from the beginning
7261 CQuat rotStartTmp = currentAnimRotStart;
7262 rotStartTmp.invert();
7263 CQuat rotTmp = rotStartTmp * currentAnimRot;
7264 float ang = rotTmp.getAngle();
7266 // Get the Rotation scaled.
7267 currentAnimRot = CQuat::slerp(currentAnimRotStart, currentAnimRot, (float)_RotationFactor);
7269 // Get the angle done once scaled.
7270 rotTmp = rotStartTmp * currentAnimRot;
7273 rotMat.rotateZ(_CurrentState->RotFactor*(ang-rotTmp.getAngle()));
7275 // Apply the scaled rotation to the position.
7276 currentAnimPos = rotMat*currentAnimPos;
7282 if(runFactor() > 0.0)
7284 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7285 CQuat currentAnimRotRun
;
7286 if(!EAM
->interpolate(animId(MOVE_BLEND_OUT
), animOffsetBLE
, currentAnimRotRun
))
7287 currentAnimRotRun
= CQuat::Identity
;
7288 currentAnimRotRun
.makeClosest(currentAnimRot
);
7289 currentAnimRot
= CQuat::slerp(currentAnimRot
, currentAnimRotRun
, (float)runFactor());
7293 CMatrix AnimMatrixRot
;
7294 AnimMatrixRot
.identity();
7295 AnimMatrixRot
.setRot(currentAnimRot
);
7297 // Rotation 180 degrees Matrix
7301 rot180
.rotateZ((float)Pi
);
7303 // Logical entity Matrix.
7306 current
= _DirMatrix
;
7310 // Convert the anim position in a world position.
7311 currentAnimPos
= (current
*rot180
)*currentAnimPos
;
7313 rot180
*= AnimMatrixRot
;
7317 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7318 if(ClientCfg
.BlendFrameNumber
&& _BlendRemaining
> 0)
7321 _OldRotQuat
.makeClosest(current
.getRot());
7322 tmpQuat
= CQuat::slerp(current
.getRot(), _OldRotQuat
, ((float)_BlendRemaining
/(float)(ClientCfg
.BlendFrameNumber
+1)));
7323 current
.setRot(tmpQuat
);
7324 // 1 more frame played.
7329 // Compute the position for the instance.
7334 tmpPos
+= currentAnimPos
;
7336 // If the entity is on a mount, just adjust the position with the animation.
7338 tmpPos
= currentAnimPos
;
7339 // Set the skeleton position and rotation.
7340 if(!_Skeleton
.empty())
7342 _Skeleton
.setRotQuat(current
.getRot());
7343 _Skeleton
.setPos(tmpPos
);
7345 // Only Instances with no skeleton (objects).
7346 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7348 _Instances
[0].Current
.setRotQuat(current
.getRot());
7349 _Instances
[0].Current
.setPos(tmpPos
);
7351 // Set the instance position and rotation.
7352 else if(!_Instance
.empty())
7354 _Instance
.setRotQuat(current
.getRot());
7355 _Instance
.setPos(tmpPos
);
7359 static bool once
= false;
7363 nlwarning("CH::updtDisp:%d: no instance nor skeleton. Sheet Id '%d(%s)'.", _Slot
, _SheetId
.asInt(), _SheetId
.toString().c_str());
7368 // Else Keep the lastest correct display.
7371 H_AUTO ( RZ_Client_Entity_CL_Update_Display_Unknown_Anim
)
7373 // Rotation 90 degrees Matrix
7377 rot90
.rotateZ((float)(Pi
/2.0));
7379 // Logical entity Matrix.
7382 current
= _DirMatrix
;
7384 // current.identity();
7388 // Changes the skeleton position.
7389 if(!_Skeleton
.empty())
7391 _Skeleton
.setRotQuat(current
.getRot());
7393 _Skeleton
.setPos(pos());
7395 // _Skeleton.setPos(currentAnimPos);
7397 // Only Instances with no skeleton (objects).
7398 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7400 _Instances
[0].Current
.setRotQuat(current
.getRot());
7402 _Instances
[0].Current
.setPos(pos());
7408 // Changes the skeleton position.
7409 if(!_Skeleton
.empty())
7411 _Skeleton
.setPos(pos());
7413 // Only Instances with no skeleton (objects).
7414 else if(!_Instances
.empty() && !_Instances
[0].Current
.empty())
7416 // Logical entity Matrix.
7419 current
= _DirMatrix
;
7421 _Instances
[0].Current
.setRotQuat(current
.getRot());
7422 _Instances
[0].Current
.setPos(pos());
7424 // Changes the instance position.
7425 else if(!_Instance
.empty())
7427 _Instance
.setPos(pos());
7431 if(!ClientCfg
.Light
)
7433 // update texture Async Loading
7434 updateAsyncTexture();
7435 // update lod Texture
7440 // Update the children display.
7441 std::list
<CEntityCL
*>::iterator it
= _Children
.begin();
7442 while(it
!= _Children
.end())
7444 // Update the display for the child
7445 (*it
)->updateDisplay(this);
7449 }// updateDisplay //
7452 //---------------------------------------------------
7454 // Method to get the position of the head (in the world).
7455 // \param headPos: will be set with the head position if succeed.
7456 // \return 'true' if the param has been updated.
7457 // \warning this method do NOT check if there is a skeleton.
7458 //---------------------------------------------------
7459 bool CCharacterCL::getHeadPos(NLMISC::CVector
&headPos
)
7461 // if never computed (eg: clipped or lod)
7462 if(!_HeadOffsetComputed
)
7464 // force compute the bone
7465 if(_HeadBoneId
!= -1 && !_Skeleton
.empty())
7467 _Skeleton
.forceComputeBone(_HeadBoneId
);
7468 UBone headBone
=_Skeleton
.getBone(_HeadBoneId
);
7469 const CMatrix
&headMat
= headBone
.getLastWorldMatrixComputed();
7470 _HeadOffset
= headMat
.getPos()-pos();
7472 _HeadOffsetComputed
= true;
7475 // return the pos with the last head offset computed
7476 headPos
= pos()+_HeadOffset
;
7481 //---------------------------------------------------
7482 // updateHeadDirection :
7483 // Update the head Direction.
7484 //---------------------------------------------------
7485 void CCharacterCL::updateHeadDirection(CEntityCL
*target
)
7487 // Does the entity got a target to track with the head ?
7488 // No head targeting if the target slot is the same as the entity slot
7489 if(_TargetSlot
!=CLFECOMMON::INVALID_SLOT
&& _TargetSlot
!=_Slot
)
7491 // Is the target allocated.
7494 // Do not orientate the head to the target if too far.
7495 CVectorD vectDist
= target
->pos() - pos();
7496 if((fabs(vectDist
.x
) + fabs(vectDist
.y
)) <= ClientCfg
.MaxHeadTargetDist
&& vectDist
!= CVectorD::Null
)
7498 // Do not orientate the head to the target if behind.
7499 vectDist
.normalize();
7500 if(fabs(angleBetween2Vect(dir(), vectDist
)) < Pi
/3.0)
7502 CVector targetheadPos
;
7503 if(target
->getHeadPos(targetheadPos
))
7505 _TargetAnimCtrl
.Mode
= CTargetAnimCtrl::TargetMode
;
7506 _TargetAnimCtrl
.WorldTarget
= targetheadPos
;
7514 _TargetAnimCtrl
.Mode
= CTargetAnimCtrl::DirectionMode
;
7516 frontMat
.setRot(CVector::I
, front(), CVector::K
, true);
7517 frontMat
.normalize(CMatrix::YZX
);
7518 _TargetAnimCtrl
.CurrentWorldDirection
= frontMat
.getRot();
7519 }// updateHeadDirection //
7522 //---------------------------------------------------
7524 // Display the entity name.
7525 //---------------------------------------------------
7526 void CCharacterCL::displayName()
7528 // There is no Context -> Cannot display a name.
7529 if(TextContext
== 0)
7532 NLMISC::CVector namePos
;
7533 // There is a skeleton.
7534 if(!_Skeleton
.empty() && !ClientCfg
.Light
)
7536 // Only if the skeleton is visible.
7539 // Do not display the name in LoD.
7540 if(_Skeleton
.isDisplayedAsLodCharacter())
7542 // If the entity was not displayed last frame (clipped) -> do not display the name.
7543 if(!_Skeleton
.getLastClippedState())
7546 // Is there a Bone for the Name ?
7547 if(_NameBoneId
!= -1)
7548 namePos
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos();
7549 // No Bone for the name -> Draw it at a default position.
7551 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7553 // If there is no skeleton -> compute a position for the name.
7556 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7559 // Create the matrix and set the orientation according to the camera.
7562 matrix
.setRot(MainCam
.getRotQuat());
7563 matrix
.setPos(namePos
);
7565 CVector distPos
= MainCam
.getPos()-pos();
7566 float scale
= distPos
.norm();
7568 // don't display too far names
7569 if (ClientCfg
.Light
&& scale
> 20)
7574 else if(scale
> ClientCfg
.ConstNameSizeDist
)
7575 scale
= ClientCfg
.ConstNameSizeDist
;
7576 // Too Far to display a name.
7577 else if(scale
> ClientCfg
.MaxNameDist
)
7579 // Compute the final scale.
7580 matrix
.scale(ClientCfg
.NameScale
*scale
);
7586 //---------------------------------------------------
7589 //---------------------------------------------------
7590 void CCharacterCL::drawName(const NLMISC::CMatrix
&mat
) // virtual
7592 const string
&ucname
= getEntityName();
7593 if(!getEntityName().empty())
7595 // If there is no extended name, just display the name
7597 TextContext
->render3D(mat
, ucname
);
7598 // If there is an extended name, display the extended name at the name place and the name above.
7601 // Display the Extended Name at right place.
7602 TextContext
->render3D(mat
, _NameEx
);
7603 // Compute the position for the Name.
7606 mat2
.setRot(MainCam
.getRotQuat());
7607 CVector v
= mat
.getPos()+mat
.getK().normed()*ClientCfg
.NamePos
*mat
.getScaleUniform();
7609 mat2
.scale(mat
.getScaleUniform());
7610 // Diaplay the name.
7611 TextContext
->render3D(mat2
, ucname
);
7619 const char *name
= STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet
->Id
);
7620 if (!FINAL_VERSION
|| !NLMISC::startsWith(name
, "<NotExist:"))
7621 TextContext
->render3D(mat
, name
);
7627 //---------------------------------------------------
7628 // displayModifiers :
7629 // Display the Hp Bar
7630 //---------------------------------------------------
7631 void CCharacterCL::displayModifiers() // virtual
7634 if( _HPDisplayed
.empty())
7637 // **** get the name pos
7638 NLMISC::CVectorD namePos
;
7639 if(!getNamePos(namePos
))
7640 namePos
= pos() + CVector(0.f
, 0.f
, 2.0f
);
7641 // remove but keep the Z to the ground
7642 float currentDeltaZ
= float(namePos
.z
- pos().z
);
7643 CVector groundPos
= namePos
;
7644 groundPos
.z
-= currentDeltaZ
;
7647 // **** compute the scale
7648 float dist
= (MainCam
.getPos()-pos()).norm();
7650 if(dist
> ClientCfg
.MaxNameDist
)
7652 if ( dist
< ClientCfg
.ConstNameSizeDist
)
7655 scale
= ClientCfg
.ConstNameSizeDist
/ dist
;
7658 // **** Display HP modifiers.
7659 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
7660 std::list
<HPMD
>::iterator itTmp
;
7661 std::list
<HPMD
>::iterator it
= _HPDisplayed
.begin();
7662 while(it
!= _HPDisplayed
.end())
7666 const float totalDuration
= 3.f
;
7667 const float noFadeDuration
= 1.f
;
7668 const float fadeDuration
= totalDuration
-noFadeDuration
;
7669 if(TimeInSec
> (mod
.Time
+totalDuration
))
7673 _HPDisplayed
.erase(itTmp
);
7675 else if (TimeInSec
>= mod
.Time
)
7678 if (mod
.Text
.empty())
7679 hpModifier
= toString("%d", mod
.Value
);
7681 hpModifier
= mod
.Text
;
7682 double t
= TimeInSec
-mod
.Time
;
7683 // for character, keep the deltaZ the first time it is displayed, and apply the same each frame
7684 // (avoid Z movement of the flying text because of animation)
7685 if(mod
.DeltaZ
==-FLT_MAX
)
7686 mod
.DeltaZ
= currentDeltaZ
;
7687 // Compute the position for the Modifier.
7688 float dynT
= sqrtf((float)t
/totalDuration
); // a sqrt just so it looks much more "jumpy"
7689 CVector pos
= groundPos
+ CVector(0.0f
, 0.0f
, mod
.DeltaZ
+ dynT
*1.f
);
7691 if(t
<noFadeDuration
)
7694 mod
.Color
.A
= 255-(uint8
)((t
-noFadeDuration
)*255.0/fadeDuration
);
7696 // Display the hp modifier. display with a X offset according if user or not, for more readability
7697 sint deltaX
= -pIM
->FlyingTextManager
.getOffsetXForCharacter();
7698 if(UserEntity
&& UserEntity
->slot()==slot())
7700 pIM
->FlyingTextManager
.addFlyingText(&mod
, hpModifier
, pos
, mod
.Color
, scale
, deltaX
);
7711 }// displayModifiers //
7714 //---------------------------------------------------
7717 //---------------------------------------------------
7718 void CCharacterCL::drawPath() // virtual
7722 CVector pl0
= pos();
7723 CVector pl1
= pos()+CVector(0.f
, 0.f
, 2.f
);
7724 line
= CLine(pl0
, pl1
);
7725 line
.Color0
= CRGBA(150,0,255);
7726 line
.Color1
= CRGBA(150,0,255);
7727 Driver
->drawLine(line
, GenericMat
);
7729 line
= CLine(_PositionLimiter
, _PositionLimiter
+CVector(0.f
, 0.f
, 2.f
));
7730 line
.Color0
= CRGBA(255,64,128);
7731 line
.Color1
= CRGBA(255,64,128);
7732 Driver
->drawLine(line
, GenericMat
);
7738 line
= CLine(p0
, p0
+front());
7739 line
.Color0
= CRGBA(0,255,0);
7740 line
.Color1
= CRGBA(0,255,0);
7741 Driver
->drawLine(line
, GenericMat
);
7744 line
= CLine(p0
, p0
+dir());
7745 line
.Color0
= CRGBA(255,255,0);
7746 line
.Color1
= CRGBA(255,255,0);
7747 Driver
->drawLine(line
, GenericMat
);
7749 // Go to the First Stage.
7750 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
7751 // Compute the distance over all Stages.
7752 while(it
!= _Stages
._StageSet
.end())
7754 // Compute Distance.
7756 if((*it
).second
.getPos(stagePos
))
7758 CVector p1
= stagePos
;
7760 p2
.z
= (float)stagePos
.z
;
7761 p2
= p2
+ (stagePos
-p2
).normed();
7763 getCollisionEntity()->snapToGround(p1
);
7765 getCollisionEntity()->snapToGround(p2
);
7768 line
= CLine(p0
, p2
);
7769 line
.Color0
= CRGBA(0,0,255);
7770 line
.Color1
= CRGBA(0,0,255);
7771 Driver
->drawLine(line
, GenericMat
);
7774 line
= CLine(p0
, p1
);
7775 line
.Color0
= CRGBA(255,0,0);
7776 line
.Color1
= CRGBA(255,0,0);
7777 Driver
->drawLine(line
, GenericMat
);
7788 //---------------------------------------------------
7790 // Draw the selection Box.
7791 //---------------------------------------------------
7792 void CCharacterCL::drawBox() // virtual
7794 if(!ClientCfg
.Light
)
7795 ::drawBox(_Aabbox
.getMin(), _Aabbox
.getMax(), CRGBA(0,250,0));
7797 // Draw the PACS box (adjust the color according to the PACS valid or not).
7798 NLMISC::CAABBox PACSBox
= _Aabbox
;
7799 CVector halfSize
= PACSBox
.getHalfSize();
7800 halfSize
.x
= 0; halfSize
.y
= 0;
7801 PACSBox
.setCenter(_FinalPacsPos
+halfSize
);
7802 UGlobalPosition gPos
;
7804 _Primitive
->getGlobalPosition(gPos
, dynamicWI
);
7805 ::drawBox(PACSBox
.getMin(), PACSBox
.getMax(), ((gPos
.InstanceId
== -1) && (T1
%1000)>500)?CRGBA(255,0,0):CRGBA(0,250,250));
7807 if(!ClientCfg
.Light
)
7809 ::drawBox(selectBox().getMin(), selectBox().getMax(), CRGBA(250,250,0));
7810 // Draw the clip Sphere
7811 CVector clipPos
= _Aabbox
.getCenter();
7812 clipPos
.z
+= _ClipDeltaZ
- _Aabbox
.getHalfSize().z
; // _ClipDeltaZ is relative to pos on ground
7813 ::drawSphere(clipPos
, _ClipRadius
, CRGBA(0,0,250));
7817 //---------------------------------------------------
7819 // Return the selection box.
7820 //---------------------------------------------------
7821 const NLMISC::CAABBox
&CCharacterCL::selectBox() // virtual
7823 // recompute the selection box?
7824 if(_LastSelectBoxComputeTime
<T1
)
7826 _LastSelectBoxComputeTime
=T1
;
7829 // if skeleton, compute aabox from precise skeleton method
7830 if(!_Skeleton
.empty())
7832 // Don't compute if in LOD form (else flick because sometimes valid because of shadow animation)
7833 if(!_Skeleton
.isDisplayedAsLodCharacter() &&
7834 _Skeleton
.computeRenderedBBoxWithBoneSphere(_SelectBox
))
7837 // else compute from static bot object
7841 // try with _Instances array first
7842 if(!_Instances
.empty())
7843 inst
= _Instances
[0].Current
;
7844 // Fallback to _Instance (??)
7848 // if static instance found
7852 inst
.getShapeAABBox(bbox
);
7853 // if supported (ie bbox not null)
7854 if(bbox
.getHalfSize()!=CVector::Null
)
7856 // Transform bbox to world
7857 _SelectBox
= CAABBox::transformAABBox(inst
.getLastWorldMatrixComputed(), bbox
);
7863 // if not found, fallback to default bbox
7865 _SelectBox
= _Aabbox
;
7868 // Return the selection box.
7873 //---------------------------------------------------
7874 // updateAttachedFXListForSlotRemoved :
7875 //---------------------------------------------------
7876 void CCharacterCL::updateAttachedFXListForSlotRemoved(std::list
<CAttachedFX::TSmartPtr
> &fxList
, const CLFECOMMON::TCLEntityId
&slotRemoved
)
7878 std::list
<CAttachedFX::TSmartPtr
>::iterator it
= fxList
.begin();
7879 while (it
!= fxList
.end())
7881 std::list
<CAttachedFX::TSmartPtr
>::iterator tmpIt
= it
;
7883 if ((*tmpIt
)->StickMode
&&
7884 ((*tmpIt
)->StickMode
== CFXStickMode::OrientedTowardTargeter
||
7885 (*tmpIt
)->StickMode
== CFXStickMode::UserBoneOrientedTowardTargeter
||
7886 (*tmpIt
)->StickMode
== CFXStickMode::UserBoneRay
7890 if ((*tmpIt
)->TargeterInfo
.Slot
== slotRemoved
)
7892 fxList
.erase(tmpIt
);
7898 //---------------------------------------------------
7900 // To Inform about an entity removed (to remove from selection for example).
7901 // This will remove the entity from the target.
7902 // \param slot : Slot of the entity that will be removed.
7903 //---------------------------------------------------
7904 void CCharacterCL::slotRemoved(const CLFECOMMON::TCLEntityId
&slotRemoved
)
7906 // If the target is the entity that will be removed -> no target
7907 if(_TargetSlot
== slotRemoved
)
7908 _TargetSlot
= CLFECOMMON::INVALID_SLOT
;
7909 // invalidate targeter slots in anim fxs
7910 updateAttachedFXListForSlotRemoved(_AttachedFXListForCurrentAnim
, slotRemoved
);
7911 updateAttachedFXListForSlotRemoved(_AttachedFXListToRemove
, slotRemoved
);
7914 //---------------------------------------------------
7916 // Return number of stage remaining.
7917 //---------------------------------------------------
7918 uint
CCharacterCL::nbStage()
7920 return (uint
)_Stages
._StageSet
.size();
7924 //---------------------------------------------------
7926 // Method to return the attack radius of an entity (take the scale into account).
7927 //---------------------------------------------------
7928 double CCharacterCL::attackRadius() const
7930 return _Sheet
->DistToFront
* getScale();
7933 //---------------------------------------------------
7935 // Return the position the attacker should have to combat according to the attack angle.
7936 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
7937 // \todo : GUIGUI precalculate entity matrix
7938 //---------------------------------------------------
7939 CVectorD
CCharacterCL::getAttackerPos(double ang
, double dist
) const
7941 // Compute the local angle
7942 ang
= computeShortestAngle(atan2(front().y
, front().x
), ang
);
7947 // Compute the local position.
7949 float distToSide
, distToFront
, distToBack
;
7950 bool useComplexShape
= false;
7951 if (useComplexShape
) // Keep this code for when AIS become complex shape aware
7953 distToSide
= _Sheet
->DistToSide
;
7954 distToFront
= _Sheet
->DistToFront
;
7955 distToBack
= _Sheet
->DistToBack
;
7957 else // use round shape here
7959 distToSide
= _Sheet
->ColRadius
;
7960 distToFront
= _Sheet
->ColRadius
;
7961 distToBack
= _Sheet
->ColRadius
;
7963 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.
7964 p
.y
= dist
*cos(ang
);
7965 if(fabs(ang
) <= Pi
/2.0)
7966 p
.y
+= getScale()*distToFront
* cos(ang
);
7968 p
.y
+= getScale()*distToBack
* cos(ang
);
7972 // Compute the world position.
7973 // Create the target matrix.
7974 CVector vj
= front();
7979 bodyBase
.setRot(vi
,vj
,vk
,true);
7980 bodyBase
.setPos(pos());
7982 // Get the destination in the world.
7983 return bodyBase
* p
;
7984 }// getAttackerPos //
7986 //---------------------------------------------------
7987 // isPlacedToFight :
7988 // Return true if the opponent is well placed.
7989 //---------------------------------------------------
7990 bool CCharacterCL::isPlacedToFight(const NLMISC::CVectorD
&posAtk
, const NLMISC::CVector
&dirAtk
, double attackerRadius
) const // virtual
7992 NLMISC::CVectorD vDist
= pos()-posAtk
;
7993 if(vDist
!= NLMISC::CVectorD::Null
)
7995 // Attacker Distance
7996 const double distToAttacker
= vDist
.norm();
7997 // Get the Ideal Position
7999 CVectorD rightPos
= getAttackerPos(atan2(vDist
.y
, vDist
.x
), attackerRadius
);
8000 // Vector from the Ideal Position
8001 NLMISC::CVectorD vDist2
= pos()-rightPos
;
8002 // Check the Distance.
8003 if(distToAttacker
<= vDist2
.norm()+ClientCfg
.FightAreaSize
)
8005 // Check the orientation.
8006 NLMISC::CVector vAng
= dirAtk
;
8009 return (fabs(angleBetween2Vect(vAng
, vDist
)) <= NLMISC::Pi
/3.0);
8012 // User is on the target, do not check dist or angle
8017 // NLMISC::CVectorD vDist = pos()-posAtk;
8018 // const double dist = vDist.norm();
8020 // // Get current entity radius
8022 // radius = _Primitive->getRadius();
8025 // // Attack is possible if not too close or too far.
8026 // if(dist>=radius && dist<=(radius+ClientCfg.FightAreaSize))
8029 // NLMISC::CVector vAng = dirAtk;
8032 // return (fabs(angleBetween2Vect(vAng, vDist)) <= NLMISC::Pi/3.0);
8035 }// isPlacedToFight //
8038 //---------------------------------------------------
8039 // \param pos : result given in this variable. Only valid if return 'true'.
8040 // \return bool : 'true' if the 'pos' has been filled.
8041 //---------------------------------------------------
8042 bool CCharacterCL::getNamePos(CVectorD
&pos
) // virtual
8044 // If there is no skeleton -> cannot display the name.
8045 if(_Skeleton
.empty())
8048 // If the entity was not displayed last frame (clipped) -> do not display the name.
8049 if(!_Skeleton
.getLastClippedState())
8052 if(_NameBoneId
== -1)
8055 // Take x and y in pos() else we a have a frame late.
8056 pos
.x
= this->pos().x
;
8057 pos
.y
= this->pos().y
;
8060 if (ClientCfg
.StaticNameHeight
)
8061 namePosZ
= getNamePosZ();
8065 // use bone position if no name position is given
8066 if (namePosZ
== 0.f
)
8068 // if displayed as lod, the NameId bone may not be computed
8069 float skeletonZ
= _Skeleton
.getLastWorldMatrixComputed().getPos().z
;
8070 if(_Skeleton
.isDisplayedAsLodCharacter())
8072 // if never computed
8073 if(_NameCLodDeltaZ
==NameCLodDeltaZNotComputed
)
8075 _Skeleton
.forceComputeBone(_NameBoneId
);
8076 float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8077 _NameCLodDeltaZ
= boneZ
- skeletonZ
;
8079 pos
.z
= skeletonZ
+ _NameCLodDeltaZ
;
8083 float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8085 // update delta Z, for when enter in CLod form
8086 _NameCLodDeltaZ
= boneZ
- skeletonZ
;
8089 // reset name pos history
8090 if (_NamePosHistory
.isInitialized())
8092 _NamePosHistory
.LastNamePosZ
= 0.f
;
8093 _NamePosHistory
.LastBonePosZ
= 0.f
;
8099 const float baseZ
= float( this->pos().z
);
8101 // if displayed as lod, skip smooth transition stuff
8102 if (_Skeleton
.isDisplayedAsLodCharacter())
8104 pos
.z
= baseZ
+ namePosZ
;
8106 // reset name pos history
8107 if (_NamePosHistory
.isInitialized())
8109 _NamePosHistory
.LastNamePosZ
= 0.f
;
8110 _NamePosHistory
.LastBonePosZ
= 0.f
;
8116 const float boneZ
= _Skeleton
.getBone(_NameBoneId
).getLastWorldMatrixComputed().getPos().z
;
8118 float deltaNamePosZ
;
8119 float deltaBonePosZ
;
8120 if (_NamePosHistory
.isInitialized())
8122 deltaNamePosZ
= namePosZ
- _NamePosHistory
.LastNamePosZ
;
8123 deltaBonePosZ
= (boneZ
- baseZ
) - _NamePosHistory
.LastBonePosZ
;
8127 deltaNamePosZ
= 0.f
;
8128 deltaBonePosZ
= 0.f
;
8131 if (deltaNamePosZ
!= 0.f
)
8133 // generate a smooth transition following the bone movement
8134 if (deltaBonePosZ
!= 0.f
&& (deltaBonePosZ
> 0.f
) == (deltaNamePosZ
> 0.f
))
8136 namePosZ
= _NamePosHistory
.LastNamePosZ
+ deltaBonePosZ
;
8140 const float defaultSpeed
= 1.f
; // in meters per sec.
8141 float deltaZ
= defaultSpeed
* DT
;
8142 if (deltaNamePosZ
< 0.f
)
8145 if ( fabs(deltaZ
) < fabs(deltaNamePosZ
) )
8146 namePosZ
= _NamePosHistory
.LastNamePosZ
+ deltaZ
;
8150 pos
.z
= baseZ
+ namePosZ
;
8153 _NamePosHistory
.LastNamePosZ
= namePosZ
;
8154 _NamePosHistory
.LastBonePosZ
= boneZ
- baseZ
;
8159 //---------------------------------------------------
8160 // Return name position on Z axis defined in sheet
8161 //---------------------------------------------------
8162 float CCharacterCL::getNamePosZ() const
8168 switch (_ModeWanted
)
8173 namePosZ
= _Sheet
->NamePosZLow
;
8176 case MBEHAV::MOUNT_NORMAL
:
8177 case MBEHAV::MOUNT_SWIM
:
8178 namePosZ
= _Sheet
->NamePosZHigh
;
8182 namePosZ
= _Sheet
->NamePosZNormal
;
8186 if (namePosZ
== 0.f
)
8187 namePosZ
= _Sheet
->NamePosZNormal
;
8189 return namePosZ
* getScale();
8192 //---------------------------------------------------
8193 // \param pos : result given in this variable. Only valid if return 'true'.
8194 // \return bool : 'true' if the 'pos' has been filled.
8195 //---------------------------------------------------
8196 bool CCharacterCL::getChestPos(CVectorD
&pos
) const // virtual
8198 // If there is no skeleton -> cannot display the Chest.
8199 if(_Skeleton
.empty())
8202 // If the entity was not displayed last frame (clipped) -> do not display the Chest.
8203 if(!_Skeleton
.getLastClippedState())
8206 if(_ChestBoneId
== -1)
8209 // Take x and y in pos() else we a have a frame late.
8210 pos
.x
= this->pos().x
;
8211 pos
.y
= this->pos().y
;
8212 pos
.z
= _Skeleton
.getBone(_ChestBoneId
).getLastWorldMatrixComputed().getPos().z
;
8217 //---------------------------------------------------
8219 // Return the entity sheet scale. (return 1.0 if there is any problem).
8220 //---------------------------------------------------
8221 float CCharacterCL::getSheetScale() const // virtual
8226 return _Sheet
->Scale
;
8228 } // getSheetScale //
8231 //---------------------------------------------------
8233 // Return the entity collision radius. (return 0.5 if there is any problem).
8234 //---------------------------------------------------
8235 float CCharacterCL::getSheetColRadius() const
8240 return _Sheet
->ColRadius
;
8244 //---------------------------------------------------
8246 // Return the entity scale. (return 1.0 if there is any problem).
8247 //---------------------------------------------------
8248 float CCharacterCL::getScale() const // virtual
8250 switch( _OwnerPeople
)
8252 case MOUNT_PEOPLE::Fyros
: return getSheetScale() * ClientCfg
.FyrosScale
* _CustomScale
;
8253 case MOUNT_PEOPLE::Matis
: return getSheetScale() * ClientCfg
.MatisScale
* _CustomScale
;
8254 case MOUNT_PEOPLE::Tryker
: return getSheetScale() * ClientCfg
.TrykerScale
* _CustomScale
;
8255 case MOUNT_PEOPLE::Zorai
: return getSheetScale() * ClientCfg
.ZoraiScale
* _CustomScale
;
8257 return getSheetScale() * _CustomScale
;
8268 //---------------------------------------------------
8269 // currentAnimationName :
8270 // Return the current animation name.
8271 //---------------------------------------------------
8272 const std::string
&CCharacterCL::currentAnimationName() const
8276 uint idCurrentAnimation
= _PlayList
->getAnimation(MOVE
);
8277 if(idCurrentAnimation
!= UPlayList::empty
)
8278 if(EAM
&& EAM
->getAnimationSet())
8279 return EAM
->getAnimationSet()->getAnimationName(idCurrentAnimation
);
8282 // No animation yet.
8283 return CCharacterCL::_EmptyString
;
8284 }// currentAnimationName //
8286 //---------------------------------------------------
8287 // currentAnimationSetName :
8288 // Return the current animation set name.
8289 //---------------------------------------------------
8290 std::string
CCharacterCL::currentAnimationSetName(TAnimationType animType
) const
8292 if( animType
< animTypeCount
)
8294 if( uint(animType
) < _CurrentAnimSet
.size() )
8296 if( _CurrentAnimSet
[animType
] )
8298 return _CurrentAnimSet
[animType
]->getSheetName();
8302 return CCharacterCL::_EmptyString
;
8303 }// currentAnimationSetName //
8307 //---------------------------------------------------
8309 // Return the shape pointer from tha item and according to the character gender.
8310 // \param itemSheet : reference on the item sheet.
8311 // \return string & : reference on the shape name.
8312 //---------------------------------------------------
8313 std::string
CCharacterCL::shapeFromItem(const CItemSheet
&itemSheet
) const
8317 if(_Gender
== GSGENDER::male
)
8320 switch(_Sheet
->Race
)
8322 case EGSPD::CPeople::Fyros
:
8323 sheet
= itemSheet
.getShapeFyros();
8325 case EGSPD::CPeople::Matis
:
8326 sheet
= itemSheet
.getShapeMatis();
8328 case EGSPD::CPeople::Tryker
:
8329 sheet
= itemSheet
.getShapeTryker();
8331 case EGSPD::CPeople::Zorai
:
8332 sheet
= itemSheet
.getShapeZorai();
8339 switch(_Sheet
->Race
)
8341 case EGSPD::CPeople::Fyros
:
8342 sheet
= itemSheet
.getShapeFyrosFemale();
8344 case EGSPD::CPeople::Matis
:
8345 sheet
= itemSheet
.getShapeMatisFemale();
8347 case EGSPD::CPeople::Tryker
:
8348 sheet
= itemSheet
.getShapeTrykerFemale();
8350 case EGSPD::CPeople::Zorai
:
8351 sheet
= itemSheet
.getShapeZoraiFemale();
8355 sheet
= itemSheet
.getShapeFemale();
8358 sheet
= itemSheet
.getShape();
8362 }// shapeFromItem //
8365 //---------------------------------------------------
8366 // createItemInstance :
8367 // Create the instance from an item
8368 //---------------------------------------------------
8369 uint32
CCharacterCL::createItemInstance(const CItemSheet
&itemSheet
, uint32 instIdx
, SLOTTYPE::EVisualSlot visualSlot
, const string
&bindBone
, sint8 texture
, sint color
)
8371 uint32 idx
= CEntityCL::BadIndex
;
8372 // Get the right shape according to the gender of the character.
8373 const string
&shape
= shapeFromItem(itemSheet
);
8378 // Check the item need a shape.
8379 if(shape
!= "none.shape")
8383 idx
= addColoredInstance(shape
, bindBone
, texture
, instIdx
, color
);
8384 SInstanceCL
*pInst
= idx2Inst(idx
);
8385 nlassert( (pInst
== NULL
) || (pInst
!= NULL
&& !pInst
->Loading
.empty()) );
8387 instance
= pInst
->Loading
;
8388 // Check the shape creation has been is well done.
8389 if(!instance
.empty())
8391 // Create the FX associated to the item in a given visual slot.
8392 _Items
[visualSlot
].initFXs(visualSlot
, instance
);
8395 nlwarning("CH:createItemInstance: cannot create the instance for the shape '%s'.", shape
.c_str());
8399 nlwarning("CH:createItemInstance: the item has no shape.");
8402 }// createItemInstance //
8405 //-----------------------------------------------
8407 // Method to Flag the character as alive and do everything needed.
8408 //-----------------------------------------------
8409 void CCharacterCL::setAlive() // virtual
8415 //---------------------------------------------------
8417 // Display Debug Information.
8418 //---------------------------------------------------
8419 ADD_METHOD(void CCharacterCL::displayDebug(float x
, float &y
, float lineStep
)) // virtual
8420 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
8422 CEntityCL::displayDebug(x
, y
, lineStep
);
8424 // Display the Target Mode.
8425 TextContext
->printfAt(x
, y
, "Mode Wanted: %d(%s)", (sint
)_ModeWanted
, MBEHAV::modeToString(_ModeWanted
).c_str());
8428 TextContext
->printfAt(x
, y
, "Stages remaining: %d", _Stages
._StageSet
.size());
8430 // Current Automaton
8431 TextContext
->printfAt(x
, y
, "Automaton: %s", _CurrentAutomaton
.c_str());
8434 TextContext
->printfAt(x
, y
, "Speed: %f (_DestTime(%f) - _LastFrameTime(%f)) = %f", speed(), _DestTime
, _LastFrameTime
, _DestTime
-_LastFrameTime
);
8436 // Display the Run Factor.
8437 TextContext
->printfAt(x
, y
, "(Walk)Run Factor: %f", runFactor());
8439 // Display the current animation name(id)(offset)(nbloop) for channel MOVE.
8440 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
);
8444 TextContext
->printfAt(x
, y
, "No Position Received", _First_Pos
);
8446 TextContext
->printfAt(x
, y
, "At least 1 Position Received", _First_Pos
);
8449 TextContext
->printfAt(x
, y
, "Prim Ptr: %p", _Primitive
);
8451 // Primitive Position
8454 CVectorD primFinalPos
= _Primitive
->getFinalPosition(dynamicWI
);
8455 TextContext
->printfAt(x
, y
, "Prim Pos: %f %f %f", primFinalPos
.x
, primFinalPos
.y
, primFinalPos
.z
);
8458 // Skeleton Ptr, Animset Ptr and Current State Ptr
8459 TextContext
->printfAt(x
, y
, "Skel Ptr: %p - AnimSet Ptr: %p - State Ptr: %p", &_Skeleton
, _CurrentAnimSet
[MOVE
], _CurrentState
);
8461 // Display the target mount and rider.
8462 TextContext
->printfAt(x
, y
, "Mount: %3u(Theoretical: %3u) Rider: %3u(Theoretical: %3u)", mount(), _TheoreticalMount
, rider(), _TheoreticalRider
);
8465 sint64 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA
))->getValue64();
8466 if(isPlayer() || isUser())
8468 SPropVisualA visualA
= *(SPropVisualA
*)(&prop
);
8469 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
,
8470 (uint
)visualA
.PropertySubData
.JacketModel
, (uint
)visualA
.PropertySubData
.JacketColor
,
8471 (uint
)visualA
.PropertySubData
.TrouserModel
, (uint
)visualA
.PropertySubData
.TrouserColor
,
8472 (uint
)visualA
.PropertySubData
.ArmModel
, (uint
)visualA
.PropertySubData
.ArmColor
,
8473 (uint
)visualA
.PropertySubData
.HatModel
, (uint
)visualA
.PropertySubData
.HatColor
,
8474 (uint
)visualA
.PropertySubData
.WeaponRightHand
,
8475 (uint
)visualA
.PropertySubData
.WeaponLeftHand
);
8478 TextContext
->printfAt(x
, y
, "VPA: %" NL_I64
"X", prop
);
8481 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB
))->getValue64();
8482 if(isPlayer() || isUser())
8484 SPropVisualB visualB
= *(SPropVisualB
*)(&prop
);
8485 TextContext
->printfAt(x
, y
, "VPB: %" NL_I64
"X : Hands(%d,%d) Feet(%d,%d).", prop
,
8486 (uint
)visualB
.PropertySubData
.HandsModel
, (uint
)visualB
.PropertySubData
.HandsColor
,
8487 (uint
)visualB
.PropertySubData
.FeetModel
, (uint
)visualB
.PropertySubData
.FeetColor
);
8490 TextContext
->printfAt(x
, y
, "VPB: %" NL_I64
"X", prop
);
8493 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPC
))->getValue64();
8494 if(isPlayer() || isUser())
8496 SPropVisualC visualC
= *(SPropVisualC
*)(&prop
);
8497 TextContext
->printfAt(x
, y
, "VPC: %" NL_I64
"X : EyesColor(%d) Tattoo(%d).", prop
, visualC
.PropertySubData
.EyesColor
, visualC
.PropertySubData
.Tattoo
);
8500 TextContext
->printfAt(x
, y
, "VPC: %" NL_I64
"X", prop
);
8506 //-----------------------------------------------
8507 // displayDebugPropertyStages
8508 //-----------------------------------------------
8509 void CCharacterCL::displayDebugPropertyStages(float x
, float &y
, float lineStep
)
8511 CStageSet::TStageSet::iterator it
= _Stages
._StageSet
.begin();
8512 for(;it
!=_Stages
._StageSet
.end();it
++)
8514 CStage
&stage
= it
->second
;
8515 uint32 gc
= it
->first
% 100;
8516 // build the string of props present in this stage
8518 for(uint i
=0;i
<CLFECOMMON::NB_VISUAL_PROPERTIES
;i
++)
8520 if(i
!=CLFECOMMON::PROPERTY_POSY
&& i
!=CLFECOMMON::PROPERTY_POSZ
&& stage
.isPresent(i
))
8521 strProps
+= string(CLFECOMMON::getPropShortText(i
)) + " ";
8523 TextContext
->printfAt(x
, y
, "%02d %s", gc
, strProps
.c_str());
8529 //---------------------------------------------------
8531 // Read/Write Variables from/to the stream.
8532 //---------------------------------------------------
8533 void CCharacterCL::readWrite(NLMISC::IStream
&f
)
8535 CEntityCL::readWrite(f
);
8541 // std::vector<CAnimation::TAnimId> _Animations;
8542 // std::vector<NLSOUND::TSoundAnimId> _SoundId;
8543 // NLSOUND::CSoundContext _SoundContext;
8544 // std::vector<double> _AnimationsTimeOffset;
8545 // std::vector<CAnimationState::TAnimStateId> _AnimationsStateKey;
8546 // const CAnimationSet *_CurrentAnimSet;
8547 f
.serial(_LastFrameTime
);
8548 f
.serial(_LodCharacterAnimEnabled
);
8549 f
.serial(_LodCharacterAnimTimeOffset
);
8550 // uint _LodCharacterMasterAnimSlot;
8551 f
.serial(_CharacterScalePos
);
8552 f
.serial(_FirstPos
);
8553 f
.serial(_FirstTime
);
8554 f
.serial(_DistToFirst
);
8556 f
.serial(_DestTime
);
8557 f
.serial(_DistToDest
);
8559 f
.serial(_OldPosTime
);
8560 // GSGENDER::EGender _Gender;
8561 // sint _NameBoneId;
8562 // NL3D::UTransform _NameTransform;
8563 // std::vector<CItemSheet *> _Items;
8566 // sint _HeadBoneId;
8567 f
.serial(_RotationFactor
);
8568 f
.serial(_DirEndAnim
);
8569 f
.serial(_RotAngle
);
8570 f
.serial(_CurrentAutomaton
);
8571 // const CAutomatonStateSheet *_CurrentState;
8572 // MBEHAV::EMode _ModeWanted;
8573 // sint _BlendRemaining;
8574 f
.serial(_OldAutomaton
);
8575 f
.serial(_OldRotQuat
);
8576 f
.serial(_CustomScalePos
);
8577 // TTime _NextBlinkTime;
8578 f
.serial(_NbLoopAnim
);
8579 // std::vector<CFXStruct *> _FXs;
8580 // std::list<UParticleSystemInstance> _CurrentAnimFXList;
8581 // std::list<UParticleSystemInstance> _RemoveAnimFXList;
8582 // NL3D::UParticleSystemInstance _CurrentAnimFX;
8583 f
.serial(_RightFXActivated
);
8584 f
.serial(_LeftFXActivated
);
8585 // sint _IndexRightFX;
8586 // sint _IndexLeftFX;
8589 f
.serial(_IsThereAMode
);
8590 f
.serial(_HairColor
);
8591 f
.serial(_EyesColor
);
8592 f
.serial(_HairIndex
);
8595 f
.serial(_RunFactor
);
8598 // uint32 _RHandInstIdx;
8599 // uint32 _LHandInstIdx;
8602 //---------------------------------------------------
8604 // To call after a read from a stream to re-initialize the entity.
8605 //---------------------------------------------------
8606 void CCharacterCL::load() // virtual
8608 CInterfaceManager
*IM
= CInterfaceManager::getInstance ();
8610 // If the entity should be in the world already
8611 if(_First_Pos
== false)
8613 // Insert the primitive into the world.
8615 _Primitive
->insertInWorldImage(dynamicWI
);
8616 // Insert the entity into PACS
8623 // Visual properties A
8624 _HeadIdx
= CEntityCL::BadIndex
;
8625 sint64 prop
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot
)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA
))->getValue64();
8626 updateVisualPropertyVpa(0, prop
);
8630 //---------------------------------------------------
8632 //---------------------------------------------------
8633 void CCharacterCL::buildPlaylist()
8636 // Release the old animation playlist.
8639 EAM
->deletePlayList(_PlayList
);
8642 // Create the new animation playlist.
8643 _PlayList
= EAM
->createPlayList();
8646 nlwarning("Cannot create a playlist for the entity.");
8649 // Register the skeleton to the playlist.
8650 _PlayList
->registerTransform(_Skeleton
);
8651 // Animation should not move alone.
8652 uint posChannel
= EAM
->getAnimationSet()->getChannelIdByName("pos");
8653 if(posChannel
!= NL3D::UAnimationSet::NotFound
)
8654 _PlayList
->enableChannel(posChannel
, false);
8656 nlwarning("Channel 'pos' not found.");
8657 // Animation should not rotate alone.
8658 uint rotquatChannel
= EAM
->getAnimationSet()->getChannelIdByName("rotquat");
8659 if(rotquatChannel
!= NL3D::UAnimationSet::NotFound
)
8660 _PlayList
->enableChannel(rotquatChannel
, false);
8662 nlwarning("Channel 'rotquat' not found.");
8663 // Initialize the new playlist.
8665 _PlayList
->setSpeedFactor (MOVE
, 1.f
);
8666 _PlayList
->setWrapMode (MOVE
, NL3D::UPlayList::Clamp
);
8668 _PlayList
->setAnimation (ACTION
, NL3D::UPlayList::empty
);
8669 _PlayList
->setSpeedFactor (ACTION
, 1.f
);
8670 _PlayList
->setWrapMode (ACTION
, NL3D::UPlayList::Clamp
);
8671 // Compute the current animation state.
8672 _CurrentState
= EAM
->mState(_CurrentAutomaton
, animState(MOVE
));
8673 if(_CurrentState
== 0)
8675 _PlayList
->setAnimation(MOVE
, NL3D::UPlayList::empty
);
8678 // Get the right animation state and choose an animation.
8680 // Get the animation state
8681 const CAnimationState
*animationState
= 0;
8682 if(animState(MOVE
) == CAnimationStateSheet::Emote
)
8683 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(_SubStateKey
);
8685 animationState
= _CurrentAnimSet
[MOVE
]->getAnimationState(animState(MOVE
));
8688 // Choose the animation
8689 animIndex(MOVE
, animationState
->chooseAnim(_AnimJobSpecialisation
, people(), getGender(), 0.0));
8690 // Should the objects in hands be displayed ?
8691 _ObjectsVisible
= animationState
->areObjectsVisible();
8695 _PlayList
->setAnimation(MOVE
, animId(MOVE
));
8696 }// buildPlaylist //
8701 //-----------------------------------------------
8703 // Return the entity speed
8704 //-----------------------------------------------
8705 double CCharacterCL::getSpeed() const // virtual
8710 //-----------------------------------------------
8712 // Set the Distance from the current entity position to the First Position.
8713 //-----------------------------------------------
8714 void CCharacterCL::dist2FirstPos(double d2FP
)
8716 CHECK((d2FP
== INVALID_DIST
) || (d2FP
== 0.0) || (d2FP
>= ClientCfg
.DestThreshold
));
8717 _DistToFirst
= d2FP
;
8718 }// dist2FirstPos //
8719 //-----------------------------------------------
8721 // Return the Distance from the current entity position to the First Position.
8722 //-----------------------------------------------
8723 double CCharacterCL::dist2FirstPos() const
8725 CHECK((_DistToFirst
== INVALID_DIST
) || (_DistToFirst
== 0.0) || (_DistToFirst
>= ClientCfg
.DestThreshold
));
8726 return _DistToFirst
;
8727 }// dist2FirstPos //
8729 //-----------------------------------------------
8731 // Set the Distance from the current entity position to the Destination.
8732 //-----------------------------------------------
8733 void CCharacterCL::dist2Dest(double d2D
)
8735 CHECK((d2D
== INVALID_DIST
) || (d2D
== 0.0) || (d2D
>= ClientCfg
.DestThreshold
));
8739 //-----------------------------------------------
8741 // Return the Distance from the current entity position to the Destination.
8742 //-----------------------------------------------
8743 double CCharacterCL::dist2Dest() const
8745 CHECK((_DistToDest
== INVALID_DIST
) || (_DistToDest
== 0.0) || (_DistToDest
>= ClientCfg
.DestThreshold
));
8749 //-----------------------------------------------
8751 // Set the Entity Current Speed.
8752 //-----------------------------------------------
8753 void CCharacterCL::speed(double s
)
8755 CHECK((s
== -1.0) || (s
== 0.0) || ((s
>= 0.001) && (s
<= 1000.0)));
8759 //-----------------------------------------------
8761 // Return the Entity Current Speed.
8762 //-----------------------------------------------
8763 double CCharacterCL::speed() const // virtual
8765 CHECK((_Speed
== -1.0) || (_Speed
== 0.0) || ((_Speed
>= 0.001) && (_Speed
<= 1000.0)));
8769 //-----------------------------------------------
8770 // Build the in scene interface
8771 //-----------------------------------------------
8772 void CCharacterCL::buildInSceneInterface ()
8774 // Delete previous interface
8775 releaseInSceneInterfaces();
8777 _InSceneUserInterface
= CGroupInSceneUserInfo::build (this);
8780 CEntityCL::buildInSceneInterface();
8783 //-----------------------------------------------
8785 void CCharacterCL::setBubble (CGroupInSceneBubble
*bubble
)
8787 if (_CurrentBubble
!= NULL
)
8789 CGroupInSceneBubble
*old
= _CurrentBubble
;
8790 _CurrentBubble
= NULL
;
8793 nlassert (_CurrentBubble
== NULL
);
8794 _CurrentBubble
= bubble
;
8797 //-----------------------------------------------
8799 //-----------------------------------------------
8801 // Set the Factor between Walk & Run
8802 //-----------------------------------------------
8803 void CCharacterCL::runFactor(double factor
)
8805 CHECK((factor
>= 0.0) && (factor
<= 1.0));
8806 CHECK((factor
== 0.0) || (animState(MOVE
) == CAnimationStateSheet::Walk
));
8807 _RunFactor
= factor
;
8810 //-----------------------------------------------
8812 // Get the Factor between Walk & Run
8813 //-----------------------------------------------
8814 double CCharacterCL::runFactor() const
8816 CHECK((_RunFactor
>= 0.0) && (_RunFactor
<= 1.0));
8817 CHECK((_RunFactor
== 0.0) || (animState(MOVE
) == CAnimationStateSheet::Walk
));
8821 //-----------------------------------------------
8823 // Set the animation time offset for an animation channel.
8824 //-----------------------------------------------
8825 void CCharacterCL::animOffset(TAnimationType channel
, double timeOffset
)
8829 // Check the channel
8830 CHECK((uint
)channel
< _AnimOffset
.size());
8831 // Check Animation Time Offset is greater or equal to 0.
8832 CHECK(timeOffset
>= 0.0);
8833 // Check the animation time offset is not greater to the animation length.
8834 CHECK(timeOffset
<= EAM
->getAnimationLength(animId(channel
)));
8836 _AnimOffset
[channel
] = timeOffset
;
8839 //-----------------------------------------------
8841 // Return the animation time offset for an animation channel.
8842 //-----------------------------------------------
8843 double CCharacterCL::animOffset(TAnimationType channel
) const
8847 // Check the channel
8848 CHECK((uint
)channel
< _AnimOffset
.size());
8849 // Check Animation Time Offset is greater or equal to 0.
8850 CHECK(_AnimOffset
[channel
] >= 0.0);
8851 // Check the animation time offset is not greater to the animation length.
8852 CHECK(_AnimOffset
[channel
] <= EAM
->getAnimationLength(animId(channel
)));
8854 return _AnimOffset
[channel
];
8857 //---------------------------------------------------
8858 // animationStateKey :
8859 // Set the animation state key.
8860 // \todo GUIGUI : a remplacer par animState directement.
8861 //---------------------------------------------------
8862 bool CCharacterCL::animationStateKey(TAnimationType channel
, TAnimStateId value
)
8864 // Is the new key valid ?
8865 if(value
== CAnimationStateSheet::UnknownState
)
8867 nlwarning("CH::animationStateKey: Char '%d': new state key is Null.", _Slot
);
8871 animState(channel
, value
);
8872 // Debug Animation for the selection
8873 if(VerboseAnimSelection
&& _Slot
== UserEntity
->selection())
8874 nlinfo("CH:animationStateKey:%d: state '%s'.", _Slot
, CAnimationState::getAnimationStateName(value
).c_str());
8877 }// animationStateKey //
8879 //-----------------------------------------------
8881 // Set the Animation 'State' for an animation channel.
8882 //-----------------------------------------------
8883 void CCharacterCL::animState(TAnimationType channel
, TAnimStateId state
)
8885 // Check the channel
8886 CHECK((uint
)channel
< _AnimState
.size());
8887 // Set the new State
8888 _AnimState
[channel
] = state
;
8889 // Reset the Run Factor when the state change.
8893 //-----------------------------------------------
8895 // Get the Animation 'State' for an animation channel.
8896 //-----------------------------------------------
8897 TAnimStateId
CCharacterCL::animState(TAnimationType channel
) const
8899 // Check the channel
8900 CHECK((uint
)channel
< _AnimState
.size());
8901 // Return the Animation State
8902 return _AnimState
[channel
];
8905 //-----------------------------------------------
8907 // Set the Animation 'Index' in the 'State' for an animation channel.
8908 //-----------------------------------------------
8909 void CCharacterCL::animIndex(TAnimationType channel
, CAnimation::TAnimId index
)
8911 // Check the channel
8912 CHECK((uint
)channel
< _AnimState
.size());
8913 // Set the animation index in the state
8914 _AnimIndex
[channel
] = index
;
8915 // Set the animation Id
8916 // If the current animation Index is not a valid one, return empty
8917 if(_AnimIndex
[channel
] == CAnimation::UnknownAnim
)
8918 animId(channel
, NL3D::UPlayList::empty
);
8921 // Check the AnimSet needed to get the animation Id.
8922 CHECK(_CurrentAnimSet
[channel
] != NULL
);
8923 // Get the Pointer on the animation state, if Null, return empty
8924 const CAnimationState
*animStatePtr
= _CurrentAnimSet
[channel
]->getAnimationState( (animState(channel
)==CAnimationStateSheet::Emote
)?_SubStateKey
:animState(channel
));
8925 if(animStatePtr
== 0)
8926 animId(channel
, NL3D::UPlayList::empty
);
8929 // Get the Animation Pointer, if Null, return Empty
8930 const CAnimation
*anim
= animStatePtr
->getAnimation(animIndex(channel
));
8932 animId(channel
, NL3D::UPlayList::empty
);
8933 // Return The Animation ID
8935 animId(channel
, anim
->id());
8940 //-----------------------------------------------
8942 // Get the Animation 'Index' in the 'State' for an animation channel.
8943 //-----------------------------------------------
8944 CAnimation::TAnimId
CCharacterCL::animIndex(TAnimationType channel
) const
8946 // Check the channel
8947 CHECK((uint
)channel
< _AnimState
.size());
8948 // Return the Animation Index in the State
8949 return _AnimIndex
[channel
];
8952 //-----------------------------------------------
8954 // Set the Animation 'Id' among all the animations for an animation channel.
8955 //-----------------------------------------------
8956 void CCharacterCL::animId(TAnimationType channel
, uint id
)
8958 // Check the channel
8959 CHECK((uint
)channel
< _AnimId
.size());
8961 _AnimId
[channel
] = id
;
8964 //-----------------------------------------------
8966 // Get the Animation 'Id' among all the animations for an animation channel.
8967 //-----------------------------------------------
8968 uint
CCharacterCL::animId(TAnimationType channel
) const
8970 // Check the channel
8971 CHECK((uint
)channel
< _AnimId
.size());
8973 return _AnimId
[channel
];
8978 //-----------------------------------------------
8979 // Align the given FX so that it is oriented like that entity
8980 //-----------------------------------------------
8981 void CCharacterCL::alignFX(UParticleSystemInstance instance
, float scale
/* = 1.f */, const NLMISC::CVector
&localOffset
/*= NLMISC::CVector::Null*/) const
8983 // copy matrix from parent
8985 fxMatrix
.identity();
8986 buildAlignMatrix(fxMatrix
);
8987 alignFX(instance
, fxMatrix
, scale
, localOffset
);
8991 void CCharacterCL::alignFX(UParticleSystemInstance instance
, const CMatrix
&matrix
, float scale
/*=1.f*/, const NLMISC::CVector
&localOffset
/*=NLMISC::CVector::Null*/) const
8993 if(instance
.empty())
8995 CMatrix fxMatrix
= matrix
;
8996 if (scale
!= 1.f
) fxMatrix
.scale(scale
);
8997 fxMatrix
.setPos(fxMatrix
.getPos() + fxMatrix
.mulVector(localOffset
));
8998 instance
.setTransformMode(NL3D::UTransform::DirectMatrix
);
8999 instance
.setMatrix(fxMatrix
);
9000 if(!_Skeleton
.empty())
9002 instance
.setClusterSystem(_Skeleton
.getClusterSystem());
9007 //------------------------------------------------------------
9008 // build a matrix aligned on that entity (includes dir & pos)
9009 //------------------------------------------------------------
9010 void CCharacterCL::buildAlignMatrix(NLMISC::CMatrix
&dest
) const
9012 // if not in clod, pelvis bone has been computed -> use it to get the current orientation
9013 // use the dir matrix otherwise
9015 if (pelvisBone() != -1 && _Skeleton)
9017 if (_Skeleton.isBoneComputed(pelvisBone()))
9019 // the direction is given by the y axis
9020 NL3D::UBone pelvisBone = _Skeleton.getBone(pelvisBone());
9021 forward = pelvisBone->getMatrix().getJ();
9022 forward.z = 0.f; // project onto XY plane
9023 forward.normalize();
9027 // must be in clod -> use the direction
9033 // bone not found, use the direction
9036 dest.setRot(- forward, forward ^ CVector::K, CVector::K);
9038 dest
.setRot(CVector::K
^ _Front
, - _Front
, CVector::K
);
9043 //-----------------------------------------------
9045 //-----------------------------------------------
9046 void CCharacterCL::attachFXInternal(const CAttachedFX::TSmartPtr fx
, TAttachedFXList targetList
)
9051 case FXListToRemove
:
9052 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9053 _AttachedFXListToRemove
.push_front(fx
);
9055 case FXListCurrentAnim
:
9056 _AttachedFXListForCurrentAnim
.push_front(fx
);
9060 if (fx
->AniFX
->Sheet
)
9062 switch(fx
->AniFX
->Sheet
->RepeatMode
)
9064 case CAnimationFXSheet::Respawn
:
9065 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9066 _AttachedFXListToRemove
.push_front(fx
);
9069 _AttachedFXListForCurrentAnim
.push_front(fx
);
9075 fx
->TimeOutDate
+= TimeInSec
; // in remove list timeout date is absolute
9076 _AttachedFXListToRemove
.push_front(fx
);
9086 //-----------------------------------------------
9088 //-----------------------------------------------
9089 void CCharacterCL::attachFX(const CAttachedFX::TSmartPtr fx
)
9091 attachFXInternal(fx
, FXListToRemove
);
9098 // ***********************************************************************************************************************
9099 void CCharacterCL::setAuraFX(uint index
, const CAnimationFX
*sheet
)
9101 nlassert(index
< MaxNumAura
);
9102 // no-op if same aura
9103 if (_AuraFX
[index
] && _AuraFX
[index
]->AniFX
== sheet
) return;
9107 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
9108 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
9110 if ((*itAttachedFxToStart
).MaxNumAnimCount
== index
)
9111 itAttachedFxToStart
= _AttachedFXListToStart
.erase(itAttachedFxToStart
);
9113 ++itAttachedFxToStart
;
9115 // if there's already an aura attached, and if it is not already shutting down
9116 if (_AuraFX
[index
] && _AuraFX
[index
]->TimeOutDate
== 0.f
)
9118 _AuraFX
[index
]->TimeOutDate
= TimeInSec
+ AURA_SHUTDOWN_TIME
;
9123 std::list
<CAttachedFX::CBuildInfo
>::iterator itAttachedFxToStart
= _AttachedFXListToStart
.begin();
9124 while(itAttachedFxToStart
!= _AttachedFXListToStart
.end())
9126 if ((*itAttachedFxToStart
).MaxNumAnimCount
== index
)
9129 // remove previous aura
9130 _AuraFX
[index
] = NULL
;
9131 CAttachedFX::CBuildInfo bi
;
9135 if (sheet
->Sheet
->PSName
== "misc_caravan_teleportout.ps")
9137 bi
.MaxNumAnimCount
= index
;
9138 bi
.StartTime
= TimeInSec
;
9139 bi
.DelayBeforeStart
= 12.5f
;
9140 _AttachedFXListToStart
.push_front(bi
);
9142 else if (sheet
->Sheet
->PSName
== "misc_kami_teleportout.ps")
9144 bi
.MaxNumAnimCount
= index
;
9145 bi
.StartTime
= TimeInSec
;
9146 bi
.DelayBeforeStart
= 11.5f
;
9147 _AttachedFXListToStart
.push_front(bi
);
9151 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
9152 fx
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9153 if (!fx
->FX
.empty())
9155 _AuraFX
[index
] = fx
;
9161 // ***********************************************************************************************************************
9162 void CCharacterCL::setLinkFX(const CAnimationFX
*fx
, const CAnimationFX
*dispell
)
9164 // no-op if same link
9165 if (_LinkFX
&& _LinkFX
->AniFX
== fx
) return;
9170 CAttachedFX::TSmartPtr fx
= new CAttachedFX
;
9171 CAttachedFX::CBuildInfo bi
;
9173 fx
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9179 CAttachedFX::TSmartPtr linkFX
= new CAttachedFX
;
9180 CAttachedFX::CBuildInfo bi
;
9182 linkFX
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9183 if (!linkFX
->FX
.empty())
9191 // ***********************************************************************************************************************
9192 void CCharacterCL::startItemAttackFXs(bool activateTrails
, uint intensity
)
9194 uint numItems
= (uint
)_Items
.size();
9195 forceEvalAnim(); // force to eval bones at least once when fx are created
9196 for(uint k
= 0; k
< numItems
; ++k
)
9198 _Items
[k
].startAttackFX(_Skeleton
, intensity
, (SLOTTYPE::EVisualSlot
) k
, activateTrails
);
9202 // ***********************************************************************************************************************
9203 void CCharacterCL::stopItemAttackFXs()
9205 uint numItems
= (uint
)_Items
.size();
9206 for(uint k
= 0; k
< numItems
; ++k
)
9208 if (_Items
[k
].Sheet
)
9210 _Items
[k
].stopAttackFX();
9216 //-----------------------------------------------
9218 //-----------------------------------------------
9219 bool CCharacterCL::isNeutral() const
9224 //-----------------------------------------------
9226 //-----------------------------------------------
9227 bool CCharacterCL::isFriend () const
9232 //-----------------------------------------------
9234 //-----------------------------------------------
9235 bool CCharacterCL::isEnemy () const
9237 // Suppose enemy if attackable
9238 if( properties().attackable() )
9243 return isAnOutpostEnemy();
9247 //-----------------------------------------------
9249 //-----------------------------------------------
9250 bool CCharacterCL::isAlly () const
9252 return this->isAnOutpostAlly();
9256 //-----------------------------------------------
9258 //-----------------------------------------------
9259 void CCharacterCL::applyVisualFX(sint64 prop
)
9263 const CAnimationFX
*auraFX
= NULL
;
9266 auraFX
= CAttackListManager::getInstance().getAuras().getFX(vfx
.Aura
);
9268 setAuraFX(0, auraFX
);
9269 const CAnimationFX
*auraReceiptFX
= NULL
;
9270 if (vfx
.AuraReceipt
)
9272 auraReceiptFX
= CAttackListManager::getInstance().getAuras().getFX(0);
9274 setAuraFX(1, auraReceiptFX
);
9275 const CAnimationFX
*linkFX
= NULL
;
9278 linkFX
= CAttackListManager::getInstance().getLinks().getFX(vfx
.Link
);
9280 const CAnimationFX
*dispellFX
= NULL
;
9281 dispellFX
= CAttackListManager::getInstance().getLinks().getFX(0);
9282 setLinkFX(linkFX
, dispellFX
);
9285 // *********************************************************************************************
9286 const char *CCharacterCL::getBoneNameFromBodyPart(BODY::TBodyPart part
, BODY::TSide side
) const
9288 if (!_Sheet
) return CEntityCL::getBoneNameFromBodyPart(part
, side
);
9289 return _Sheet
->BodyToBone
.getBoneName(part
, side
);
9293 // *********************************************************************************************
9294 const CItemSheet
*CCharacterCL::getRightHandItemSheet() const
9296 if (_RHandInstIdx
== CEntityCL::BadIndex
) return NULL
;
9297 return _Items
[SLOTTYPE::RIGHT_HAND_SLOT
].Sheet
;
9300 // *********************************************************************************************
9301 const CItemSheet
*CCharacterCL::getLeftHandItemSheet() const
9303 if (_LHandInstIdx
== CEntityCL::BadIndex
) return NULL
;
9304 return _Items
[SLOTTYPE::LEFT_HAND_SLOT
].Sheet
;
9307 // ***************************************************************************
9308 void CCharacterCL::resetAllSoundAnimId()
9310 for(uint i
=0;i
<_SoundId
.size();i
++)
9312 _SoundId
[i
]= CSoundAnimationNoId
;
9317 /////////////////////////////
9318 // CCharacterCL::CWornItem //
9319 /////////////////////////////
9322 // ***********************************************************************************************************************
9323 void CCharacterCL::CWornItem::startAttackFX(NL3D::USkeleton skeleton
, uint intensity
, SLOTTYPE::EVisualSlot visualSlot
, bool activateTrail
)
9325 if (intensity
< 1 || intensity
> 5) return;
9327 if (activateTrail
&& !Trail
.empty())
9331 // create the attack fx
9334 const CItemFXSheet
&fxSheet
= Sheet
->FX
;
9335 std::string shapeName
= fxSheet
.getAttackFX();
9336 if (!shapeName
.empty())
9338 const char *stickPoint
= NULL
;
9339 if(!skeleton
.empty())
9343 case SLOTTYPE::RIGHT_HAND_SLOT
:
9344 if( Sheet
->ItemType
!= ITEM_TYPE::MAGICIAN_STAFF
)
9345 stickPoint
= "box_arme";
9347 case SLOTTYPE::LEFT_HAND_SLOT
:
9348 if(Sheet
&& Sheet
->getAnimSet()=="s")
9349 stickPoint
= "Box_bouclier";
9351 stickPoint
= "box_arme_gauche";
9359 sint boneId
= skeleton
.getBoneIdByName(std::string(stickPoint
));
9362 NL3D::UInstance instance
= Scene
->createInstance(shapeName
);
9363 if (!instance
.empty())
9366 if (skeleton
.getVisibility() == UTransform::Hide
)
9368 // force to compute the bone at least once
9369 skeleton
.forceComputeBone(boneId
);
9371 UParticleSystemInstance atk
;
9372 atk
.cast (instance
);
9375 // set the user params
9376 static const float up
[5][4] =
9378 { 0.f
, 0.f
, 0.f
, 0.f
},
9379 { 1.f
, 0.f
, 0.f
, 0.f
},
9380 { 1.f
, 1.f
, 0.f
, 0.f
},
9381 { 1.f
, 1.f
, 1.f
, 0.f
},
9382 { 1.f
, 1.f
, 1.f
, 1.f
}
9384 for(uint k
= 0; k
< 4; ++k
)
9386 atk
.setUserParam(k
, up
[intensity
- 1][k
]);
9388 atk
.setTransformMode(UTransform::RotEuler
);
9389 atk
.setPos(fxSheet
.AttackFXOffset
);
9390 const float degreeToRad
= (float) Pi
/ 180.f
;
9391 atk
.setRotEuler(fxSheet
.AttackFXRot
.x
* degreeToRad
, fxSheet
.AttackFXRot
.y
* degreeToRad
, fxSheet
.AttackFXRot
.z
* degreeToRad
);
9392 skeleton
.stickObject(atk
, boneId
);
9393 // delegate mng of this object lifetime to the fx manager
9394 FXMngr
.fx2remove(atk
);
9398 Scene
->deleteInstance(atk
);
9407 // ***********************************************************************************************************************
9408 void CCharacterCL::CWornItem::stopAttackFX()
9410 if (!Trail
.empty()) Trail
.stop();
9413 // ***********************************************************************************************************************
9414 void CCharacterCL::CWornItem::initFXs(SLOTTYPE::EVisualSlot
/* visualSlot */, NL3D::UInstance parent
)
9419 const CItemFXSheet
&sheet
= Sheet
->FX
;
9420 std::string shapeName
= sheet
.getTrail();
9421 if (!shapeName
.empty())
9423 Trail
= Scene
->createInstance(shapeName
);
9426 nlwarning("Cannot create instance %s", shapeName
.c_str());
9429 // Initialize the remanence. Must substract the object matrix since parent(parent)
9430 CMatrix mat
= parent
.getMatrix();
9432 mat
*= Trail
.getMatrix();
9433 Trail
.setTransformMode(UTransformable::DirectMatrix
);
9434 Trail
.setMatrix(mat
);
9435 // Initialize instance.
9436 Trail
.parent(parent
);
9440 // ***********************************************************************************************************************
9441 void CCharacterCL::CWornItem::enableAdvantageFX(NL3D::UInstance parent
)
9445 bool enabled
= Sheet
->HasFx
;
9447 if ((enabled
&& !AdvantageFX
.empty()) || (!enabled
&& AdvantageFX
.empty()))
9448 return; // state did not change
9451 // well, it is unlikely that player will loses its ability to master an item after he gained it, but manage the case anyway.
9452 if (!AdvantageFX
.removeByID(NELID("STOP")) && !AdvantageFX
.removeByID(NELID("main")))
9454 AdvantageFX
.activateEmitters(false);
9456 FXMngr
.fx2remove(AdvantageFX
);
9461 std::string shapeName
= Sheet
->FX
.getAdvantageFX();
9462 if (!shapeName
.empty())
9464 NL3D::UInstance fx
= Scene
->createInstance(shapeName
);
9465 if (fx
.empty()) return;
9466 AdvantageFX
.cast (fx
);
9467 if (AdvantageFX
.empty())
9469 Scene
->deleteInstance(fx
);
9472 CMatrix mat
= parent
.getMatrix();
9474 mat
*= AdvantageFX
.getMatrix();
9475 AdvantageFX
.setTransformMode(UTransformable::DirectMatrix
);
9476 AdvantageFX
.setMatrix(mat
);
9477 AdvantageFX
.parent(parent
);
9482 // ***********************************************************************************************************************
9483 void CCharacterCL::CWornItem::releaseFXs()
9488 Scene
->deleteInstance(Trail
);
9489 if (!AdvantageFX
.empty())
9490 Scene
->deleteInstance(AdvantageFX
);
9494 // ***********************************************************************************************************************
9495 void CCharacterCL::CWornItem::setTrailSize(uint size
)
9497 if (Trail
.empty()) return;
9499 clamp(size
, (uint
) 0, (uint
) 15);
9502 Trail
.setSliceTime(0.f
);
9506 float ratio
= (size
- 1) / 15.f
;
9507 Trail
.setSliceTime(ratio
* Sheet
->FX
.TrailMaxSliceTime
+ (1.f
- ratio
) * Sheet
->FX
.TrailMinSliceTime
);
9512 // ***********************************************************************************************************************
9513 const CAttack
*CCharacterCL::getAttack(const CAttackIDSheet
&id
) const
9515 if (!_Sheet
) return NULL
;
9516 return getAttack(id
, _Sheet
->AttackLists
);
9519 // ***********************************************************************************************************************
9520 const CAttack
*CCharacterCL::getAttack(const CAttackIDSheet
&id
, const std::vector
<NLMISC::TSStringId
> &attackList
) const
9522 for(std::vector
<NLMISC::TSStringId
>::const_reverse_iterator it
= attackList
.rbegin(); it
!= attackList
.rend(); ++it
)
9524 const CAttackList
*al
= CAttackListManager::getInstance().getAttackList(ClientSheetsStrings
.get(*it
));
9527 const CAttack
*attk
= al
->getAttackFromID(id
);
9528 if (attk
) return attk
;
9535 // ***********************************************************************************************************************
9536 void CCharacterCL::initStaticFX()
9539 if (ClientCfg
.Light
) return;
9540 std::string staticFX
= _Sheet
->getStaticFX();
9541 if (!staticFX
.empty())
9543 CEntitySheet
*sheet
= SheetMngr
.get(NLMISC::CSheetId(staticFX
));
9546 if (sheet
->Type
== CEntitySheet::ANIMATION_FX
)
9548 CAnimationFXSheet
*afs
= NLMISC::safe_cast
<CAnimationFXSheet
*>(sheet
);
9549 _StaticFX
= new CStaticFX
;
9550 _StaticFX
->AF
.init(afs
, EAM
? EAM
->getAnimationSet() : NULL
);
9551 _StaticFX
->FX
= new CAttachedFX
;
9552 CAttachedFX::CBuildInfo bi
;
9553 bi
.Sheet
= &_StaticFX
->AF
;
9554 _StaticFX
->FX
->create(*this, bi
, CAttachedFX::CTargeterInfo());
9555 if (_StaticFX
->FX
->FX
.empty())
9565 nlwarning("Static fx %s not found", staticFX
.c_str());
9570 // ***************************************************************************
9571 EGSPD::CPeople::TPeople
CCharacterCL::people() const
9574 return _Sheet
->Race
;
9576 return EGSPD::CPeople::Unknown
;
9579 // ***************************************************************************
9580 void CCharacterCL::setPeople(EGSPD::CPeople::TPeople
/* people */)
9584 // ***************************************************************************
9588 // temp : begin cast of projectile (start begin anim and loop anim)
9589 NLMISC_COMMAND(beginCast
, "Start spell cast", "<spell_id> <strenght> [<mode> <caster slot> <behaviour>]")
9591 if (args
.size() < 2) return false;
9592 if (args
.size() > 5) return false;
9593 uint casterSlot
= 0;
9594 if (args
.size() >= 4)
9596 fromString(args
[3], casterSlot
);
9598 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9599 if (!ch
) return false;
9600 CBehaviourContext bc
;
9601 // TODO : the type of missile is contained in the spell id
9602 if (args
.size() >= 5)
9605 fromString(args
[4], missileType
);
9606 bc
.Behav
.Behaviour
= (MBEHAV::EBehaviour
) (MBEHAV::CAST_OFF
+ missileType
);
9610 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF
;
9614 fromString(args
[0], spellId
); // spell id is unused
9615 bc
.Behav
.Spell
.SpellId
= spellId
;
9616 //bc.Behav.Spell.Resist = false;
9617 uint16 spellIntensity
;
9618 fromString(args
[1], spellIntensity
);
9619 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9620 if (args
.size() == 3)
9623 fromString(args
[2], spellMode
);
9624 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9628 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9630 ch
->applyBehaviour(bc
);
9634 // temp : test fail of a cast
9635 NLMISC_COMMAND(failCast
, "Simulate failure of a spell cast", "<spell_id> <strenght> [<mode>]")
9637 if (args
.size() < 2) return false;
9638 if (args
.size() > 3) return false;
9639 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(0));
9640 if (!ch
) return false;
9641 CBehaviourContext bc
;
9642 // TODO : the type of missile is contained in the spell id
9643 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_FAIL
;
9646 fromString(args
[0], spellId
); // spell id is unused
9647 bc
.Behav
.Spell
.SpellId
= spellId
;
9648 //bc.Behav.Spell.Resist = false;
9649 uint16 spellIntensity
;
9650 fromString(args
[1], spellIntensity
);
9651 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9652 if (args
.size() == 3)
9655 fromString(args
[2], spellMode
);
9656 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9660 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9662 ch
->applyBehaviour(bc
);
9666 // temp to test cast of a projectile on another entity
9667 NLMISC_COMMAND(projectile
, "Cast a projectile on another entity", "<spellID> <strenght> [<mode> <target entity> <resist> <source entity>]" )
9669 if (args
.size() < 2) return false;
9670 if (args
.size() > 6) return false;
9671 uint8 casterSlot
= 0;
9672 if (args
.size() > 5)
9674 fromString(args
[5], casterSlot
);
9676 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9677 if (!ch
) return false;
9678 // create a new behaviour to apply to the user
9679 CBehaviourContext bc
;
9680 uint16 spellId
, spellIntensity
;
9681 fromString(args
[0], spellId
);
9682 bc
.Behav
.Spell
.SpellId
= spellId
;
9683 fromString(args
[1], spellIntensity
);
9684 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9685 // TODO : the type of missile is contained in the spell id
9686 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_SUCCESS
;
9688 if (args
.size() > 2)
9691 fromString(args
[2], spellMode
);
9692 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9696 bc
.Behav
.Spell
.SpellMode
= MAGICFX::Bomb
; // 'bomb' is the default
9698 if (args
.size() > 3)
9701 fromString(args
[3], targetSlot
);
9702 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9703 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9704 if (!target
) return false;
9705 double dist
= (target
->pos() - ch
->pos()).norm();
9706 bool resist
= false;
9707 if (args
.size() > 4) fromString(args
[4], resist
);
9708 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, resist
, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
9710 bc
.BehavTime
= TimeInSec
;
9711 ch
->applyBehaviour(bc
);
9715 NLMISC_COMMAND(mtProjectile
, "Cast a projectile on one or several entities", "<caster> <spellID> <strenght> <mode> <target0> [<target n>]*" )
9717 if (args
.size() < 5) return false;
9719 fromString(args
[0], casterSlot
);
9720 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
9721 if (!ch
) return false;
9722 // create a new behaviour to apply to the user
9723 CBehaviourContext bc
;
9724 uint16 spellId
, spellIntensity
, spellMode
;
9726 fromString(args
[1], spellId
);
9727 bc
.Behav
.Spell
.SpellId
= spellId
;
9729 fromString(args
[2], spellIntensity
);
9730 bc
.Behav
.Spell
.SpellIntensity
= spellIntensity
;
9732 fromString(args
[3], spellMode
);
9733 bc
.Behav
.Spell
.SpellMode
= spellMode
;
9734 // get targets and their dist depending on the mode
9735 switch(bc
.Behav
.Spell
.SpellMode
)
9739 uint mainTargetSlot
;
9740 fromString(args
[4], mainTargetSlot
);
9741 if (mainTargetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9742 CEntityCL
*mainTarget
= EntitiesMngr
.entity(mainTargetSlot
);
9745 double dist
= (mainTarget
->pos() - ch
->pos()).norm();
9746 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(mainTargetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
9747 for(sint k
= 1; k
< (sint
) (args
.size() - 4); ++k
)
9749 uint secondaryTargetSlot
;
9750 fromString(args
[4 + k
], secondaryTargetSlot
);
9751 if (secondaryTargetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9752 CEntityCL
*secondaryTarget
= EntitiesMngr
.entity(secondaryTargetSlot
);
9753 if (secondaryTarget
)
9755 dist
= (secondaryTarget
->pos() - mainTarget
->pos()).norm();
9756 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(secondaryTargetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
9762 case MAGICFX::Spray
:
9764 for(sint k
= 0; k
< (sint
) (args
.size() - 4); ++k
)
9767 fromString(args
[4 + k
], targetSlot
);
9768 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9769 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9772 double dist
= (target
->pos() - ch
->pos()).norm();
9773 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
9778 case MAGICFX::Chain
:
9780 CEntityCL
*startSlot
= ch
;
9781 for(sint k
= 0; k
< (sint
) (args
.size() - 4); ++k
)
9784 fromString(args
[4 + k
], targetSlot
);
9785 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
9786 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
9789 double dist
= (target
->pos() - startSlot
->pos()).norm();
9790 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
9797 bc
.BehavTime
= TimeInSec
;
9798 // TODO : the type of missile is contained in the spell id
9799 bc
.Behav
.Behaviour
= MBEHAV::CAST_OFF_SUCCESS
;
9801 ch
->applyBehaviour(bc
);
9805 // temp to test cast of multitarget projectile on another entity
9806 NLMISC_COMMAND(aura
, "enable / disable aura on an entity", "<slot> <aura>")
9808 if (args
.size() != 2) return false;
9810 fromString(args
[0], slot
);
9811 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9812 if (!ch
) return false;
9813 const CAnimationFX
*fx
= NULL
;
9815 fromString(args
[1], auraIndex
);
9816 if (auraIndex
!= -1)
9818 fx
= CAttackListManager::getInstance().getAuras().getFX(auraIndex
);
9819 if (!fx
) return false;
9821 ch
->setAuraFX(0, fx
);
9825 NLMISC_COMMAND(link
, "enable / disable link on an entity", "<slot> <link>")
9827 if (args
.size() != 2) return false;
9829 fromString(args
[0], slot
);
9830 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9831 if (!ch
) return false;
9832 const CAnimationFX
*link
= NULL
;
9834 fromString(args
[1], linkIndex
);
9835 if (linkIndex
!= -1)
9837 link
= CAttackListManager::getInstance().getLinks().getFX(linkIndex
+ 1);
9838 if (!link
) return false;
9840 const CAnimationFX
*linkBreak
= CAttackListManager::getInstance().getLinks().getFX(0);
9841 if (!linkBreak
) return false;
9842 ch
->setLinkFX(link
, linkBreak
);
9846 NLMISC_COMMAND(auraReceipt
, "enable / disable aura receipt on an entity", "<slot> <aura> <0=on/1=off>")
9848 if (args
.size() != 2) return false;
9850 fromString(args
[0], slot
);
9851 CCharacterCL
*ch
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
9852 if (!ch
) return false;
9853 const CAnimationFX
*fx
= NULL
;
9855 fromString(args
[1], enableAura
);
9858 fx
= CAttackListManager::getInstance().getAuras().getFX(0); // 0 is special for aura receipt
9859 if (!fx
) return false;
9861 ch
->setAuraFX(1, fx
);
9865 ////////////////////////////
9866 // test for melee weapons //
9867 ////////////////////////////
9869 // these are helpers (the same can be done with /vp, or altLook)
9870 NLMISC_COMMAND(weapon
, "change the weapon in hand", "<slot> <hand> <weapon>")
9872 if (args
.size() != 3) return false;
9873 CInterfaceManager
*im
= CInterfaceManager::getInstance();
9875 fromString(args
[0], slot
);
9876 CCDBNodeLeaf
*propA
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPA
), false);
9877 if (!propA
) return false;
9878 sint64 valueA
= propA
->getValue64();
9880 CCDBNodeLeaf
*propB
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPB
), false);
9881 if (!propB
) return false;
9882 sint64 valueB
= propB
->getValue64();
9885 fromString(args
[1], hand
);
9887 // the VP is dependent of Entity actual type
9888 if(dynamic_cast<CPlayerCL
*>(EntitiesMngr
.entity(slot
)))
9890 SPropVisualA vpa
= (SPropVisualA
&) valueA
;
9891 SPropVisualB vpb
= (SPropVisualB
&) valueB
;
9894 uint16 weaponRightHand
;
9895 fromString(args
[2], weaponRightHand
);
9896 vpa
.PropertySubData
.WeaponRightHand
= weaponRightHand
;
9897 vpb
.PropertySubData
.RTrail
= 1;
9901 uint16 weaponLeftHand
;
9902 fromString(args
[2], weaponLeftHand
);
9903 vpa
.PropertySubData
.WeaponLeftHand
= weaponLeftHand
;
9904 vpb
.PropertySubData
.LTrail
= 1;
9906 propA
->setValue64((sint64
) vpa
.PropertyA
);
9907 propB
->setValue64((sint64
) vpb
.PropertyB
);
9909 // CharacterCL: use a SAltLook
9912 SAltLookProp vpalt
= (SAltLookProp
&) valueA
;
9915 uint16 weaponRightHand
;
9916 fromString(args
[2], weaponRightHand
);
9917 vpalt
.Element
.WeaponRightHand
= weaponRightHand
;
9918 vpalt
.Element
.RTrail
= 1;
9922 uint16 weaponLeftHand
;
9923 fromString(args
[2], weaponLeftHand
);
9924 vpalt
.Element
.WeaponLeftHand
= weaponLeftHand
;
9925 vpalt
.Element
.LTrail
= 1;
9927 propA
->setValue64((sint64
) vpalt
.Summary
);
9930 // Force to update property
9931 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
9932 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPB
);
9934 // display name of weapon sheet
9939 NLMISC_COMMAND(advantageFX
, "turn on / off the advantage fx for an item in hand", "<slot> <hand> <on = 1/ off = 0>")
9941 if (args
.size() != 3) return false;
9942 CInterfaceManager
*im
= CInterfaceManager::getInstance();
9944 fromString(args
[0], slot
);
9947 CCDBNodeLeaf *prop = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPA), false);
9948 if (!prop) return false;
9949 sint64 value = prop->getValue64();
9951 fromString(args[1], hand);
9952 // the VP is dependent of Entity actual type
9953 if(dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(slot)))
9955 SPropVisualA vpa = (SPropVisualA &) value;
9958 fromString(args[2], vpa.PropertySubData.RWeaponFX);
9962 fromString(args[2], vpa.PropertySubData.LWeaponFX);
9964 prop->setValue64((sint64) vpa.PropertyA);
9966 // CharacterCL: use a SAltLook
9969 SAltLookProp vpa = (SAltLookProp&) value;
9972 fromString(args[2], vpa.Element.RWeaponFX);
9976 fromString(args[2], vpa.Element.LWeaponFX);
9978 prop->setValue64((sint64) vpa.Summary);
9982 // Force to update property
9983 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
9988 NLMISC_COMMAND(trailLength
, "set length of trail for one weapon in hand", "<slot> <hand> <power = 0..15>")
9990 if (args
.size() != 3) return false;
9991 CInterfaceManager
*im
= CInterfaceManager::getInstance();
9993 fromString(args
[0], slot
);
9995 CCDBNodeLeaf
*propA
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPA
), false);
9996 if (!propA
) return false;
9997 sint64 valueA
= propA
->getValue64();
9999 CCDBNodeLeaf
*propB
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot
, (int) PROPERTY_VPB
), false);
10000 if (!propB
) return false;
10001 sint64 valueB
= propB
->getValue64();
10004 fromString(args
[1], hand
);
10006 // the VP is dependent of Entity actual type
10007 if(dynamic_cast<CPlayerCL
*>(EntitiesMngr
.entity(slot
)))
10009 SPropVisualA vpa
= (SPropVisualA
&) valueA
;
10010 SPropVisualB vpb
= (SPropVisualB
&) valueB
;
10014 fromString(args
[2], rTrail
);
10015 vpb
.PropertySubData
.RTrail
= rTrail
;
10016 propB
->setValue64((sint64
) vpb
.PropertyB
);
10021 fromString(args
[2], lTrail
);
10022 vpb
.PropertySubData
.LTrail
= lTrail
/ 2;
10023 propB
->setValue64((sint64
) vpb
.PropertyB
);
10028 SAltLookProp vpalt
= (SAltLookProp
&) valueA
;
10032 fromString(args
[2], rTrail
);
10033 vpalt
.Element
.RTrail
= rTrail
;
10038 fromString(args
[2], lTrail
);
10039 vpalt
.Element
.LTrail
= lTrail
/ 2;
10041 propA
->setValue64((sint64
) vpalt
.Summary
);
10044 // Force to update property
10045 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
10046 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPB
);
10052 // simulate an attack behaviour for the given slot
10053 NLMISC_COMMAND(attack
, "simulate an attack", "<slot> <intensity> <hit_type> <localisation> <dmg_type> <damage_shield_power> <damage_shield_id>")
10055 if (args
.size() < 2) return false;
10056 if (args
.size() > 7) return false;
10057 CBehaviourContext bc
;
10058 bc
.Behav
.Behaviour
= MBEHAV::DEFAULT_ATTACK
;
10059 bc
.Behav
.Combat
.ActionDuration
= 0;
10060 uint16 impactIntensity
;
10061 fromString(args
[1], impactIntensity
);
10062 bc
.Behav
.Combat
.ImpactIntensity
= impactIntensity
;
10063 bc
.Behav
.Combat
.HitType
= HITTYPE::Hit
;
10064 bc
.Behav
.Combat
.Localisation
= BODY::HHead
;
10065 bc
.Behav
.Combat2
.DamageType
= 0;
10066 if (args
.size() > 2)
10069 fromString(args
[2], hitType
);
10070 bc
.Behav
.Combat
.HitType
= hitType
+ 1;
10072 if (args
.size() > 3)
10074 uint16 localisation
;
10075 fromString(args
[3], localisation
);
10076 bc
.Behav
.Combat
.Localisation
= localisation
;
10078 if (args
.size() > 4)
10081 fromString(args
[4], damageType
);
10082 bc
.Behav
.Combat2
.DamageType
= damageType
;
10086 if (args
.size() > 5)
10088 fromString(args
[5], dsPower
);
10090 if (args
.size() > 6)
10092 fromString(args
[6], dsType
);
10096 fromString(args
[0], slot
);
10097 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
10098 if (!entity
) return false;
10099 // push current slection as main target
10100 CMultiTarget::CTarget target
;
10101 target
.TargetSlot
= UserEntity
->selection();
10102 target
.Info
= dsPower
| (dsType
<< 3);
10103 bc
.Targets
.Targets
.push_back(target
);
10104 bc
.BehavTime
= TimeInSec
;
10105 bc
.Behav
.DeltaHP
= -20;
10106 entity
->applyBehaviour(bc
);
10110 // simulate a range attack from the current slor to the current selection
10111 NLMISC_COMMAND(rangeAttack
, "simulate a range attack", "<slot> [intensity] [localisation] [range_weapon_type_if_unequipped]")
10113 if (args
.size() < 1) return false;
10114 if (args
.size() > 4) return false;
10116 fromString(args
[0], slot
);
10117 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(slot
));
10118 if (!entity
) return false;
10119 const CItemSheet
*weaponSheet
= entity
->getRightHandItemSheet();
10120 CBehaviourContext bc
;
10121 if (!weaponSheet
|| weaponSheet
->Family
!= ITEMFAMILY::RANGE_WEAPON
)
10124 uint16 weaponType
= 0;
10125 if (args
.size() > 3)
10127 fromString(args
[3], weaponType
);
10129 bc
.Behav
.Range
.WeaponType
= weaponType
;
10133 bc
.Behav
.Range
.WeaponType
= weaponSheet
->RangeWeapon
.RangeWeaponType
;
10135 bc
.Behav
.Behaviour
= MBEHAV::RANGE_ATTACK
;
10136 bc
.Behav
.Range
.ImpactIntensity
= 1;
10137 bc
.Behav
.Range
.Localisation
= BODY::HHead
;
10138 bc
.BehavTime
= TimeInSec
;
10139 if (args
.size() > 1)
10141 uint16 impactIntensity
;
10142 fromString(args
[1], impactIntensity
);
10143 bc
.Behav
.Range
.ImpactIntensity
= impactIntensity
;
10145 if (args
.size() > 2)
10147 uint16 localisation
;
10148 fromString(args
[2], localisation
);
10149 bc
.Behav
.Range
.Localisation
= localisation
;
10151 // if not a generic range weapon, add a single target (this is the current selection)
10152 uint8 targetSlot
= UserEntity
->targetSlot();
10153 if (targetSlot
>= CLFECOMMON::INVALID_SLOT
) return false;
10154 CEntityCL
*target
= EntitiesMngr
.entity(targetSlot
);
10155 if (!target
) return false;
10156 double dist
= (target
->pos() - entity
->pos()).norm();
10157 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
10158 bc
.Behav
.DeltaHP
= -10;
10159 entity
->applyBehaviour(bc
);
10164 // simulate a creature attack
10165 NLMISC_COMMAND(creatureAttack
, "simulate a creature attack (2 attaques per creature)", "<casterSlot> <targetSlot> [attk=0/1] [magicIntensity] [physicalIntensity] [localisation] [damageType] [hitType] [resist=1/0]")
10167 if (args
.size() < 2) return false;
10168 if (args
.size() > 9) return false;
10169 CBehaviourContext bc
;
10170 bc
.Behav
.Behaviour
= MBEHAV::CREATURE_ATTACK_0
;
10171 if (args
.size() > 2)
10174 fromString(args
[2], attk
);
10175 bc
.Behav
.Behaviour
= attk
== 0 ? MBEHAV::CREATURE_ATTACK_0
: MBEHAV::CREATURE_ATTACK_1
;
10178 fromString(args
[0], casterSlot
);
10179 CCharacterCL
*caster
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(casterSlot
));
10180 if (!caster
) return false;
10182 fromString(args
[1], targetSlot
);
10183 CCharacterCL
*target
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.entity(targetSlot
));
10184 if (!target
) return false;
10185 double dist
= (target
->pos() - caster
->pos()).norm();
10186 bool resist
= false;
10187 if (args
.size() > 8)
10189 fromString(args
[8], resist
);
10191 bc
.Targets
.Targets
.push_back(CMultiTarget::CTarget(targetSlot
, false, (uint8
) ceilf((float) (dist
/ MULTI_TARGET_DISTANCE_UNIT
))));
10192 bc
.Behav
.CreatureAttack
.ActionDuration
= 0;
10193 uint magicImpactIntensity
= 1;
10194 if (args
.size() > 3)
10196 fromString(args
[3], magicImpactIntensity
);
10198 bc
.Behav
.CreatureAttack
.MagicImpactIntensity
= magicImpactIntensity
;
10199 uint physicalImpactIntensity
= 0;
10200 if (args
.size() > 4)
10202 fromString(args
[4], physicalImpactIntensity
);
10204 bc
.Behav
.CreatureAttack
.ImpactIntensity
= physicalImpactIntensity
;
10205 BODY::TBodyPart localisation
= BODY::HHead
;
10206 if (args
.size() > 5)
10209 fromString(args
[5], tmp
);
10210 localisation
= (BODY::TBodyPart
) tmp
;
10212 bc
.Behav
.CreatureAttack
.Localisation
= localisation
;
10213 DMGTYPE::EDamageType dmgType
= DMGTYPE::BLUNT
;
10214 if (args
.size() > 6)
10217 fromString(args
[6], tmp
);
10218 dmgType
= (DMGTYPE::EDamageType
) tmp
;
10220 bc
.Behav
.CreatureAttack2
.DamageType
= dmgType
;
10221 HITTYPE::THitType hitType
= HITTYPE::Hit
;
10222 if (args
.size() > 7)
10225 fromString(args
[7], tmp
);
10226 hitType
= (HITTYPE::THitType
) tmp
;
10228 bc
.Behav
.CreatureAttack2
.HitType
= hitType
;
10229 bc
.BehavTime
= TimeInSec
;
10230 bc
.Behav
.DeltaHP
= -15;
10231 caster
->applyBehaviour(bc
);
10235 NLMISC_COMMAND(setNamePosZ
, "", "<low/high/normal> <value>")
10237 if (args
.size() != 2) return false;
10239 CEntityCL
*target
= EntitiesMngr
.entity(UserEntity
->targetSlot());
10243 float *namePosZ
= NULL
;
10244 string sheetName
, skelName
;
10245 if (target
->Type
== CEntityCL::Player
)
10247 CPlayerCL
*playerTarget
= dynamic_cast<CPlayerCL
*>(target
);
10250 CRaceStatsSheet
*sheet
= const_cast<CRaceStatsSheet
*>(playerTarget
->playerSheet());
10253 if (toLowerAscii(args
[0]) == "low")
10254 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZLow
;
10255 else if (toLowerAscii(args
[0]) == "normal")
10256 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZNormal
;
10257 else if (toLowerAscii(args
[0]) == "high")
10258 namePosZ
= &sheet
->GenderInfos
[playerTarget
->getGender()].NamePosZHigh
;
10260 sheetName
= sheet
->Id
.toString();
10261 skelName
= sheet
->GenderInfos
[playerTarget
->getGender()].Skelfilename
;
10267 CCharacterCL
*creatureTarget
= dynamic_cast<CCharacterCL
*>(target
);
10268 if (creatureTarget
)
10270 CCharacterSheet
*sheet
= const_cast<CCharacterSheet
*>(creatureTarget
->getSheet());
10273 if (toLowerAscii(args
[0]) == "low")
10274 namePosZ
= &sheet
->NamePosZLow
;
10275 else if (toLowerAscii(args
[0]) == "normal")
10276 namePosZ
= &sheet
->NamePosZNormal
;
10277 else if (toLowerAscii(args
[0]) == "high")
10278 namePosZ
= &sheet
->NamePosZHigh
;
10280 sheetName
= sheet
->Id
.toString();
10281 skelName
= sheet
->getSkelFilename();
10288 fromString(args
[1], *namePosZ
);
10289 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName
.c_str(), skelName
.c_str(), args
[0].c_str(), *namePosZ
);
10295 NLMISC_COMMAND(setMyNamePosZ
, "", "<low/high/normal> <value>")
10297 if (args
.size() != 2) return false;
10299 float *namePosZ
= NULL
;
10300 string sheetName
, skelName
;
10301 CRaceStatsSheet
*sheet
= const_cast<CRaceStatsSheet
*>(UserEntity
->playerSheet());
10304 if (toLowerAscii(args
[0]) == "low")
10305 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZLow
;
10306 else if (toLowerAscii(args
[0]) == "normal")
10307 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZNormal
;
10308 else if (toLowerAscii(args
[0]) == "high")
10309 namePosZ
= &sheet
->GenderInfos
[UserEntity
->getGender()].NamePosZHigh
;
10311 sheetName
= sheet
->Id
.toString();
10312 skelName
= sheet
->GenderInfos
[UserEntity
->getGender()].Skelfilename
;
10317 fromString(args
[1], *namePosZ
);
10318 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName
.c_str(), skelName
.c_str(), args
[0].c_str(), *namePosZ
);
10325 NLMISC_COMMAND(pvpMode
, "modify pvp mode", "[<pvp mode> <state>]")
10327 if (args
.size() != 0 && args
.size() != 2) return false;
10329 CInterfaceManager
*IM
= CInterfaceManager::getInstance();
10331 CEntityCL
*target
= EntitiesMngr
.entity(UserEntity
->targetSlot());
10334 IM
->displaySystemInfo(toString("<pvpMode> no target"));
10337 if (target
->Type
!= CEntityCL::Player
&& target
->Type
!= CEntityCL::User
)
10339 IM
->displaySystemInfo(toString("<pvpMode> target is not a player"));
10342 CPlayerCL
*playerTarget
= dynamic_cast<CPlayerCL
*>(target
);
10348 uint16 pvpMode
= playerTarget
->getPvpMode();
10350 if( pvpMode
&PVP_MODE::PvpDuel
)
10352 if( pvpMode
&PVP_MODE::PvpChallenge
)
10354 if( pvpMode
&PVP_MODE::PvpZoneFree
)
10356 if( pvpMode
&PVP_MODE::PvpZoneFaction
)
10357 str
+="zone_faction ";
10358 if( pvpMode
&PVP_MODE::PvpZoneGuild
)
10359 str
+="zone_guild ";
10360 if( pvpMode
&PVP_MODE::PvpZoneOutpost
)
10362 if( pvpMode
&PVP_MODE::PvpFaction
)
10364 if( pvpMode
&PVP_MODE::PvpFactionFlagged
)
10365 str
+="faction_flagged ";
10366 if( pvpMode
&PVP_MODE::PvpZoneSafe
)
10367 str
+="in_safe_zone ";
10368 if( pvpMode
&PVP_MODE::PvpSafe
)
10370 IM
->displaySystemInfo(str
);
10371 nlinfo("<pvpMode> %s",str
.c_str());
10375 PVP_MODE::TPVPMode pvpMode
= PVP_MODE::fromString(args
[0]);
10377 fromString(args
[1], state
);
10380 uint16 currentPVPMode
= playerTarget
->getPvpMode();
10381 currentPVPMode
|= pvpMode
;
10382 playerTarget
->setPvpMode(currentPVPMode
);
10383 IM
->displaySystemInfo(toString("<pvpMode> adding pvp mode %s",args
[0].c_str()));
10387 uint16 currentPVPMode
= playerTarget
->getPvpMode();
10388 currentPVPMode
&= ~pvpMode
;
10389 playerTarget
->setPvpMode(currentPVPMode
);
10390 IM
->displaySystemInfo(toString("<pvpMode> removing pvp mode %s",args
[0].c_str()));
10393 playerTarget
->buildInSceneInterface();
10398 NLMISC_COMMAND(pvpClan, "modify pvp clan", "<pvp clan>")
10400 if (args.size() != 1) return false;
10402 CInterfaceManager *IM = CInterfaceManager::getInstance();
10404 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
10407 IM->displaySystemInfo(toString("<pvpClan> no target"));
10410 if (target->Type != CEntityCL::Player && target->Type != CEntityCL::User)
10412 IM->displaySystemInfo(toString("<pvpMode> target is not a player"));
10415 CPlayerCL *playerTarget = dynamic_cast<CPlayerCL*>(target);
10419 // PVP_CLAN::TPVPClan clan = PVP_CLAN::fromString(args[0]);
10420 // playerTarget->setPvpClan(clan);
10421 playerTarget->buildInSceneInterface();
10426 #endif // !FINAL_VERSION
10428 #include "r2/editor.h"
10430 //---------------------------------------------------
10432 // Method to Flag the character as dead and do everything needed.
10433 //---------------------------------------------------
10434 void CCharacterCL::setDead() // virtual
10436 // If the entity dead is the user -> switch to dead mode.
10437 if(_Slot
== UserEntity
->slot())
10438 UserControls
.mode(CUserControls::DeathMode
);
10440 // If the entity killed was the current user target, we update context cursor
10441 if(_Slot
== UserEntity
->selection())
10443 bool nextContextSelected
= false;
10445 if (!R2::getEditor().isDMing())
10447 if(_Properties
.harvestable())
10448 nextContextSelected
= ContextCur
.context("QUARTER");
10450 else if(_Properties
.lootable())
10451 nextContextSelected
= ContextCur
.context("LOOT");
10453 else if(_Properties
.liftable())
10454 nextContextSelected
= ContextCur
.context("PICKUP");
10455 if( !nextContextSelected
)
10456 ContextCur
.context("STAND BY");
10460 // The character now won't be an obstacle anymore
10461 _Primitive
->setOcclusionMask(MaskColNone
);