Add infos into target window
[ryzomcore.git] / ryzom / client / src / character_cl.cpp
blob43cf7c1a703d984142b78d8743b4b7c9ef0c1f3f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 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>
8 //
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 :
30 * For each character
31 * updatePreCollision
32 * updatePos
33 * PACS::move
35 * The characters know where they want to be in 2d (pos().x and pos().y valid).
37 * PACS::evalCollision
39 * For each character
40 * updatePostCollision
41 * pacsFinalizepos
42 * PACS::getGlobalPosition
43 * If real position too far from PACS position
44 * PACS::setGlobalPosition
45 * -- Here pos().z is estimated
46 * snapToGround
47 * NL3D::visualCollisionEntities
48 * -- Here pos().z is good
49 * updateDisplay
50 * updateCluster
51 * updateHeadDirection
52 * -- Here the character is setuped in the engine
56 /////////////
57 // INCLUDE //
58 /////////////
59 #include "stdpch.h"
60 // Client.
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.
74 #include "misc.h"
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"
90 // sheets
91 #include "client_sheets/item_fx_sheet.h"
92 #include "client_sheets/attack_id_sheet.h"
93 // Misc
94 #include "nel/misc/geom_ext.h"
95 #include "nel/misc/random.h"
96 // 3D
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"
106 // PACS
107 #include "nel/pacs/u_global_position.h"
108 // SOUND
109 #include "nel/sound/sound_anim_manager.h"
110 // Std.
111 #include <vector>
112 // Game share
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"
119 #ifdef DEBUG_NEW
120 #define new DEBUG_NEW
121 #endif
123 ////////////
124 // DEFINE //
125 ////////////
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
137 ///////////
138 // USING //
139 ///////////
140 using namespace NLMISC;
141 using namespace NL3D;
142 using namespace NLPACS;
143 using namespace NLSOUND;
144 using namespace std;
145 using namespace MBEHAV;
146 using namespace CLFECOMMON;
148 ////////////
149 // EXTERN //
150 ////////////
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;
160 ///////////
161 // MACRO //
162 ///////////
163 #if !FINAL_VERSION
164 std::string LastMethod;
166 #define ADD_METHOD(header) \
167 header \
169 LastMethod = #header;
171 #define CHECK(param) \
172 if((param)==false) \
174 nlwarning("entity:%d: Test '%s'", _Slot, #param); \
175 nlwarning("entity:%d: Last Method called '%s'", _Slot, LastMethod.c_str()); \
176 nlstop; \
178 #define METHOD_NAME(param) LastMethod = (param);
180 #else
181 #define ADD_METHOD(header) \
182 header \
184 #define CHECK(param)
185 #define METHOD_NAME(param)
186 #endif
190 ////////////
191 // STATIC //
192 ////////////
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 )
201 /////////////
202 // METHODS //
203 /////////////
206 //---------------------------------------------------
207 // dirEndAnim :
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);
214 }// dirEndAnim //
217 //-----------------------------------------------
218 // CCharacterCL :
219 // Constructor.
220 //-----------------------------------------------
221 CCharacterCL::CCharacterCL()
222 : CEntityCL()
224 Type = NPC;
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;
258 // Reset Lod.
259 _LodCharacterAnimEnabled= false;
260 _LodCharacterMasterAnimSlot= MOVE;
262 // default POS scale to 1.
263 _CharacterScalePos= 1.f;
265 // No sheet pointed.
266 _Sheet = 0;
268 // Unknown gender at the entity creation.
269 _Gender = GSGENDER::unknown;
271 // The bone for the name is not known for the time
272 _NameBoneId = -1;
273 // No UTransform for the name needed if there is no name so not allocated for the time.
274 _NameTransform = 0;
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;
284 _CurrentState = 0;
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.
299 _BlendRemaining = 0;
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.
314 _HeadBoneId = -1;
316 _IsThereAMode = false;
317 _ImportantStepTime= 0.0;
318 _StartDecreaseLCTImpact= 0;
320 // Entity has no look and so is not displayable for the time.
321 _LookRdy = false;
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;
328 _HairColor = 0;
329 _EyesColor = 0;
330 // No Hair Index at the beginning.
331 _HairIndex = _BadHairIndex;
332 _ClothesSheet = 0;
334 _NbLoopAnim = 0;
335 _MaxLoop = false;
337 setAlive();
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
346 runFactor(0.0);
347 // Initialize the Speed
348 speed(0.0);
352 _CurrentAttack = NULL;
353 _CurrentAttackID.Type = CAttackIDSheet::Unknown;
355 //_PelvisBoneId = -1;
357 _ChestBoneId = -1;
360 _HideSkin = false;
363 _GuildNameId = 0;
364 _GuildSymbol = 0;
366 _EventFactionId = 0;
367 _PvpMode = PVP_MODE::None;
369 _LeagueId = 0;
370 _OutpostId = 0;
371 _OutpostSide = OUTPOSTENUMS::UnknownPVPSide;
373 _SelectableBySpace = true;
375 _LastSelectBoxComputeTime= 0;
379 _ScaleDone = false;
380 _CustomScale = 1.f;
381 _BaseCustomScale = 0.f;
382 _StepCustomScale = 0.f;
383 _StartCustomScale = ryzomGetLocalTime();
384 }// CCharacterCL //
386 //-----------------------------------------------
387 // ~CCharacterCL:
388 // Default Destructor
389 // \warning : Do not remove sheets before the entity.
390 //-----------------------------------------------
391 CCharacterCL::~CCharacterCL()
393 // Delete the UTransform used to compute the Bone for the Name.
394 if(!_NameTransform.empty())
396 if(Scene)
398 if (!_Skeleton.empty())
399 _Skeleton.detachSkeletonSon(_NameTransform);
400 Scene->deleteTransform(_NameTransform);
402 _NameTransform = 0;
405 // No more sheet pointed.
406 _Sheet = NULL;
408 // Release items (but not their mesh, because they are managed by _Instances)
409 for(uint k = 0; k < _Items.size(); ++k)
411 _Items[k].release();
414 // Delete previous interface
415 releaseInSceneInterfaces();
417 if (_CurrentBubble)
419 _CurrentBubble->unlink();
424 //-----------------------------------------------
425 // computePrimitive :
426 // Create (or re-create) a primitive.
427 //-----------------------------------------------
428 void CCharacterCL::computePrimitive()
430 // Initialize the primitive.
431 if (_Sheet)
433 initPrimitive(_Sheet->ColRadius*getScale(), _Sheet->ColHeight*getScale(), _Sheet->ColLength, _Sheet->ColWidth, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColNpc, MaskColNone, _Sheet->ClipRadius, _Sheet->ClipHeight);
435 else
437 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColNpc, MaskColNone);
439 if(_Primitive)
440 _Primitive->insertInWorldImage(dynamicWI);
441 // Set the position.
442 pacsPos(pos());
443 }// computePrimitive //
446 //-----------------------------------------------
447 void CCharacterCL::removeAllAttachedFX()
449 _AttachedFXListForCurrentAnim.clear();
450 _AttachedFXListToRemove.clear();
451 _StaticFX = NULL;
452 for(uint k = 0; k < MaxNumAura; ++k)
454 _AuraFX[k] = NULL;
456 _LinkFX = NULL;
460 //-----------------------------------------------
462 void CCharacterCL::releaseInSceneInterfaces()
464 if (_InSceneUserInterface)
466 CWidgetManager::getInstance()->unMakeWindow(_InSceneUserInterface);
467 if (_InSceneUserInterface->getParent())
469 _InSceneUserInterface->getParent()->delGroup(_InSceneUserInterface);
471 else
473 delete _InSceneUserInterface;
476 _InSceneUserInterface = NULL;
480 //-----------------------------------------------
481 // stopAttachedFXForCurrrentAnim
482 // Stop all attached fxs linked to current animation
483 //-----------------------------------------------
484 void CCharacterCL::stopAttachedFXForCurrrentAnim(bool stopLoopingFX)
486 // shutdown fxs for current anim
487 std::list<CAttachedFX::TSmartPtr>::iterator itAttachedFx = _AttachedFXListForCurrentAnim.begin();
488 while(itAttachedFx != _AttachedFXListForCurrentAnim.end())
490 std::list<CAttachedFX::TSmartPtr>::iterator tmpItAttached = itAttachedFx;
491 ++itAttachedFx;
492 if (!(!stopLoopingFX && (*tmpItAttached)->AniFX && (*tmpItAttached)->AniFX->Sheet->RepeatMode == CAnimationFXSheet::Loop)) // dont remove looping fx if it is requested
494 // test if emitters should be shutdown at the end of the anim
495 if ((*tmpItAttached)->AniFX &&
496 (*tmpItAttached)->AniFX->Sheet->RepeatMode != CAnimationFXSheet::Respawn &&
497 (*tmpItAttached)->StickMode != CFXStickMode::SpawnPermanent
500 if(!(*tmpItAttached)->FX.empty())
502 if (!(*tmpItAttached)->FX.removeByID(NELID("STOP")) && !(*tmpItAttached)->FX.removeByID(NELID("main")))
504 (*tmpItAttached)->FX.activateEmitters(false);
508 _AttachedFXListToRemove.splice(_AttachedFXListToRemove.begin(), _AttachedFXListForCurrentAnim, tmpItAttached);
509 _AttachedFXListToRemove.front()->TimeOutDate += TimeInSec; // compute absolute timeout date
512 /** Removes fx that lives on more that a given count of animation
513 * Useful if framerate is low to avoid that fx overlap
515 itAttachedFx = _AttachedFXListToRemove.begin();
516 while(itAttachedFx != _AttachedFXListToRemove.end())
518 std::list<CAttachedFX::TSmartPtr>::iterator tmpItAttachedFX = itAttachedFx;
519 ++itAttachedFx;
520 if ((*tmpItAttachedFX)->AniFX)
522 if ((*tmpItAttachedFX)->MaxAnimCount != 0) // if there a limit to the number of animation during which the fx can live ?
524 (*tmpItAttachedFX)->MaxAnimCount -= 1;
525 if ((*tmpItAttachedFX)->MaxAnimCount == 0)
527 // remove the fx
528 _AttachedFXListToRemove.erase(tmpItAttachedFX);
536 //-----------------------------------------------
537 // applyColorSlot :
538 //-----------------------------------------------
539 void CCharacterCL::applyColorSlot(SInstanceCL &instance, sint skin, sint userColor, sint hair, sint eyes)
541 CColorSlotManager::TIntCouple array[4];
543 // Skin
544 array[0].first = (uint)0; array[0].second = (uint)skin;
546 // User Color
547 array[1].first = (uint)1; array[1].second = (uint)userColor;
549 // Hair Color
550 array[2].first = (uint)2; array[2].second = (uint)hair;
552 // Eyes Color
553 array[3].first = (uint)3; array[3].second = (uint)eyes;
555 // Set Values.
556 UInstance inst = instance.createLoadingFromCurrent();
557 if (!inst.empty())
559 instance.setColors(skin, userColor, hair, eyes);
560 ColorSlotManager.setInstanceSlot(inst, array, 4);
562 }// applyColorSlot //
565 //-----------------------------------------------
566 // changeColors :
567 //-----------------------------------------------
568 void CCharacterCL::changeColors(sint userColor, sint hair, sint eyes, sint part) // virtual
570 // Color Just a part of the entity.
571 if(part >= 0)
573 if((uint)part < _Instances.size())
574 applyColorSlot(_Instances[part], skin(), userColor, hair, eyes);
576 // Color the whole entity.
577 else
579 for(uint i=0; i<_Instances.size(); i++)
580 applyColorSlot(_Instances[i], skin(), userColor, hair, eyes);
582 }// changeColors //
584 //-----------------------------------------------
585 // addColoredInstance :
586 //-----------------------------------------------
587 uint32 CCharacterCL::addColoredInstance(const std::string &shapeName, const std::string &stickPoint, sint texture, uint32 instIdx, sint color)
589 // Get the instance
590 uint32 idx = addInstance(shapeName, stickPoint, texture, instIdx);
591 SInstanceCL *instance = idx2Inst(idx);
592 if(instance)
593 applyColorSlot(*instance, skin(), color, _HairColor, _EyesColor);
594 else
595 nlwarning("CH::addColoredInstance: cannot create the instance for the shape '%s'.", shapeName.c_str());
597 return idx;
598 }// addColoredInstance //
601 //-----------------------------------------------
602 // buildEquipment :
603 // \param slot: structure of the equipement.
604 // \param visualSlot: visual slot used by this item.
605 // \return uint32 : index of the instance or 0xFFFFFFFF.
606 // \todo GUIGUI : find a better choice to avoid all visualSlot checks
607 //-----------------------------------------------
608 uint32 CCharacterCL::buildEquipment(const CCharacterSheet::CEquipment &slot, SLOTTYPE::EVisualSlot visualSlot, sint color, uint32 instIdx)
610 uint32 idx = CEntityCL::BadIndex;
612 // Do something only if the slot is not empty.
613 if(slot.getItem().empty())
614 return idx;
616 sint slotColor = slot.Color;
618 // This is a reference on an item (the file is store with an UPPER case so check in UPPER CASE).
619 string ext = CFile::getExtension(slot.getItem());
620 if((ext == "item") || (ext == "sitem"))
622 // IS the item a valid one ?
623 CSheetId itemId;
624 if(itemId.buildSheetId(NLMISC::toLowerAscii(slot.getItem())))
626 // Is it stored in the database ?
627 CEntitySheet *entitySheet = SheetMngr.get(itemId);
628 if(entitySheet)
630 _Items[visualSlot].Sheet = dynamic_cast<CItemSheet *>(entitySheet);
631 if(_Items[visualSlot].Sheet)
633 const CItemSheet &itemSheet = *(_Items[visualSlot].Sheet);
635 // Compute the bind point
636 string bindBone;
637 switch(visualSlot)
639 // Right Hand
640 case SLOTTYPE::RIGHT_HAND_SLOT:
641 if( itemSheet.ItemType != ITEM_TYPE::MAGICIAN_STAFF )
642 bindBone = "box_arme";
643 break;
644 // Left Hand
645 case SLOTTYPE::LEFT_HAND_SLOT:
646 // Shields are not stick to the same point.
647 if(itemSheet.getAnimSet() == "s")
648 bindBone = "Box_bouclier";
649 else
650 bindBone = "box_arme_gauche";
651 break;
652 default:
653 bindBone = slot.getBindPoint();
654 break;
657 // Choose the right colour.
658 if(color==-1)
660 // Color in the item
661 if(slotColor < 0)
663 // Get the item color
664 slotColor = itemSheet.Color;
665 // Bad item color -> set to 0
666 if(slotColor < 0)
667 slotColor = 0;
670 else
671 slotColor = color;
673 idx = createItemInstance(itemSheet, instIdx, visualSlot, bindBone, slot.Texture, slotColor);
675 else
676 nlwarning("CH::buildEquipment: the sheet '%s' is not an item one.", slot.getItem().c_str());
678 else
679 nlwarning("CH::buildEquipment: the sheet '%s' is not stored in the database.", slot.getItem().c_str());
681 else
682 nlwarning("CH::buildEquipment: item '%s' is not in the Sheet Id list.", slot.getItem().c_str());
684 // This is a shape.
685 else
687 if(color==-1)
689 if(slotColor < 0)
690 slotColor = 0;
692 else
693 slotColor = color;
695 // Get the instance
696 idx = addColoredInstance(slot.getItem(), slot.getBindPoint(), slot.Texture, instIdx, slotColor);
699 // Return the index.
700 return idx;
701 }// buildEquipment //
703 //-----------------------------------------------
704 // computeSomeBoneId :
705 // Compute the bone for the name, head...
706 // \warning This method do not check the bone is valid, nor there is a Scene.
707 //-----------------------------------------------
708 void CCharacterCL::computeSomeBoneId()
710 // **** Get the Bone for the name.
711 _NameBoneId = _Skeleton.getBoneIdByName("name");
712 // Dummy found -> return the name position.
713 if(_NameBoneId != -1)
715 // Just to force the bone to be compute (else not computed if not used).
716 _NameTransform = Scene->createTransform();
717 if(!_NameTransform.empty())
718 _Skeleton.stickObject(_NameTransform, _NameBoneId);
720 // No Bone for the Name.
721 else
723 #if !FINAL_VERSION
724 pushDebugStr("The Bone for the name is missing.");
725 #endif // FINAL_VERSION
728 // **** Get the head bone.
729 _HeadBoneId = _Skeleton.getBoneIdByName("Bip01 Head");
730 // Bone found
731 if(_HeadBoneId == -1)
733 #if !FINAL_VERSION
734 pushDebugStr("The Bone for the Head is missing.");
735 #endif // FINAL_VERSION
737 else
739 _TargetAnimCtrl.EyePos = CVector::Null;
740 _Skeleton.setBoneAnimCtrl(_HeadBoneId, &_TargetAnimCtrl);
743 // **** Get the "chest" bone. take spine1.
744 _ChestBoneId = _Skeleton.getBoneIdByName("Bip01 Spine1");
745 if(_ChestBoneId == -1)
747 #if !FINAL_VERSION
748 pushDebugStr("The Bone for the Chest 'Bip01 Spine1' is missing.");
749 #endif // FINAL_VERSION
751 }// computeSomeBoneId //
754 //-----------------------------------------------
755 // createPlayList :
756 // Create the play list for this entity.
757 //-----------------------------------------------
758 void CCharacterCL::createPlayList()
760 // Release the old animation playlist.
761 if(_PlayList)
763 EAM->deletePlayList(_PlayList);
764 _PlayList = 0;
767 // Create the new animation playlist.
768 _PlayList = EAM->createPlayList();
769 if(!_PlayList)
771 pushDebugStr("Cannot create a playlist for the entity.");
772 return;
775 // Initialize the new playlist.
776 // MOVE Channel
777 _PlayList->setSpeedFactor (MOVE, 1.f);
778 _PlayList->setWrapMode (MOVE, UPlayList::Clamp);
779 // ACTION Channel
780 _PlayList->setSpeedFactor (ACTION, 1.f);
781 _PlayList->setWrapMode (ACTION, UPlayList::Clamp);
782 }// createPlayList //
786 //-----------------------------------------------
787 // getGroundFX :
788 // retrieve ground fxs for that entity
789 //-----------------------------------------------
790 const std::vector<CGroundFXSheet> *CCharacterCL::getGroundFX() const
792 return &(_Sheet->GroundFX);
795 //-----------------------------------------------
796 // build :
797 // Build the entity from a sheet.
798 //-----------------------------------------------
799 bool CCharacterCL::build(const CEntitySheet *sheet) // virtual
801 // Cast the sheet in the right type.
802 _Sheet = dynamic_cast<const CCharacterSheet *>(sheet);
803 if(!_Sheet)
805 pushDebugStr("This is not a character sheet -> entity not initialized.");
806 return false;
809 // Type
810 Type = (_Sheet->Race >= EGSPD::CPeople::Creature) ? Fauna : NPC;
812 // Names
813 if (Type == Fauna)
815 // Get the fauna name in the sheet
816 const char *creatureName = STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet->Id);
817 if (!FINAL_VERSION || !NLMISC::startsWith(creatureName, "<NotExist:"))
818 _EntityName = creatureName;
820 else
822 // Name and title will be send by the server
825 // Get the DB Entry
826 if(IngameDbMngr.getNodePtr())
828 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
829 if(nodeRoot)
831 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
832 if(_DBEntry == 0)
833 pushDebugStr("Cannot get a pointer on the DB entry.");
837 if (!ClientCfg.Light && !_Sheet->getSkelFilename().empty())
839 // Create the Playlist for the entity.
840 createPlayList();
842 // Compute the first automaton.
843 _CurrentAutomaton = automatonType() + "_normal.automaton";
845 // Get the Character gender.
846 _Gender = (GSGENDER::EGender)_Sheet->Gender;
848 // Initialize the internal time.
849 _LastFrameTime = ((double)T1) * 0.001;
851 // Set the skeleton.
852 if(!ClientCfg.Light && !_Sheet->getSkelFilename().empty() && skeleton(_Sheet->getSkelFilename()))
854 // Set the skeleton scale.
855 skeleton()->setScale(getScale(), getScale(), getScale());
857 // Can create all characters except NPC.
858 if(isNPC()== false)
860 // Eyes Color
861 if(_Sheet->EyesColor >= SheetMngr.nbEyesColor())
863 if (SheetMngr.nbEyesColor() == 0)
864 _EyesColor = 0;
865 else
866 _EyesColor = rand()%SheetMngr.nbEyesColor();
868 else
870 _EyesColor = _Sheet->EyesColor;
873 // Hair Color
874 if(_Sheet->HairColor >= SheetMngr.nbHairColor())
876 if (SheetMngr.nbHairColor() == 0)
877 _HairColor = 0;
878 else
879 _HairColor = rand()%SheetMngr.nbHairColor();
881 else
883 _HairColor = _Sheet->HairColor;
886 // -- Dress the character --
889 // Top Items
890 buildEquipment(_Sheet->Body, SLOTTYPE::CHEST_SLOT);
891 buildEquipment(_Sheet->Arms, SLOTTYPE::ARMS_SLOT);
892 buildEquipment(_Sheet->Hands, SLOTTYPE::HANDS_SLOT);
893 // Bottom Items
894 buildEquipment(_Sheet->Legs, SLOTTYPE::LEGS_SLOT);
895 buildEquipment(_Sheet->Feet, SLOTTYPE::FEET_SLOT);
896 // Face
897 _FaceIdx = buildEquipment(_Sheet->Face, SLOTTYPE::FACE_SLOT);
899 // -- Manage the Head --
900 // Display the helm.
901 if(!_Sheet->Head.getItem().empty())
903 // Create the Helm.
904 _HeadIdx = buildEquipment(_Sheet->Head, SLOTTYPE::HEAD_SLOT, -1, _HeadIdx);
905 // Hide the face
906 SInstanceCL *pInstFace = getFace();
907 if(pInstFace)
909 if(!pInstFace->Current.empty())
910 pInstFace->Current.hide();
911 else
912 pInstFace->KeepHiddenWhenLoaded = true;
915 // Display the Hair.
916 else
918 // Create the Hair.
919 if(_HairIndex != _BadHairIndex)
920 _HeadIdx = buildEquipment(_Sheet->HairItemList[_HairIndex], SLOTTYPE::HEAD_SLOT, -1, _HeadIdx);
921 // Display the face.
922 SInstanceCL *pInstFace = getFace();
923 if(pInstFace)
924 if(!pInstFace->Current.empty())
925 pInstFace->Current.show();
928 // Objects in Hands
929 _RHandInstIdx = buildEquipment(_Sheet->ObjectInRightHand, SLOTTYPE::RIGHT_HAND_SLOT, -1, _RHandInstIdx); // In The Right Hand
930 _LHandInstIdx = buildEquipment(_Sheet->ObjectInLeftHand, SLOTTYPE::LEFT_HAND_SLOT, -1, _LHandInstIdx); // In The Left Hand
932 // Look is now ready.
933 _LookRdy = true;
935 // Cannot build as long as the alternative look property not received, so hide the entity.
936 else
937 skeleton()->hide();
939 // Compute the animation set (after weapons are set to choose the right animation set).
940 computeAnimSet();
941 // Check the animation set is correct.
942 if(_CurrentAnimSet[MOVE] == 0)
943 pushDebugStr("Bad animation set");
945 // Set the animation to idle.
946 setAnim(CAnimationStateSheet::Idle);
948 // Compute the bone for the name.
949 computeSomeBoneId();
951 // Compute pelvis bone
952 //_PelvisBoneId = _Skeleton.getBoneIdByName("Bip01 Pelvis");
954 // Setup Lod Character skeleton and shapes colors, if skeleton exist
955 // Get Lod Character Id from the sheet.
956 sint clodId = getLodCharacterId(*Scene, _Sheet->getLodCharacterName());
957 if(clodId >= 0)
959 // Setup Lod Character shapes, if enabled.
960 skeleton()->setLodCharacterShape(clodId);
961 skeleton()->setLodCharacterDistance(_Sheet->LodCharacterDistance);
964 // Instances
965 else
967 uint32 idx= buildEquipment(_Sheet->Body, SLOTTYPE::CHEST_SLOT);
968 // must set the scale for the BotObject too
969 if(idx<_Instances.size())
971 float s= getScale();
972 _Instances[idx].setScale(CVector(s,s,s));
976 // Setup _CharacterScalePos
977 _CharacterScalePos = _Sheet->CharacterScalePos;
979 // Adjust the custom scale position according to the entity scale.
980 _CustomScalePos *= getScale();
982 // Create PACS Primitive.
983 initPrimitive(_Sheet->ColRadius*getScale(), _Sheet->ColHeight*getScale(), _Sheet->ColLength, _Sheet->ColWidth, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColNpc, MaskColNone, _Sheet->ClipRadius, _Sheet->ClipHeight);
985 // Compute the element to be able to snap the entity to the ground.
986 computeCollisionEntity();
988 // Initialize properties of the entity (selectable/attackable/etc.).
989 initProperties();
991 // copy some properties (special bot objects). before buildInSceneInterface
992 _DisplayInRadar= _Sheet->DisplayInRadar;
993 _DisplayOSDName= _Sheet->DisplayOSDName;
994 _DisplayOSDBars= _Sheet->DisplayOSDBars;
995 _DisplayOSDForceOver= _Sheet->DisplayOSDForceOver;
996 _Traversable= _Sheet->Traversable;
997 _CanTurn = _Sheet->Turn;
999 _SelectableBySpace = _Sheet->SelectableBySpace;
1001 // Rebuild interface
1002 buildInSceneInterface ();
1005 initStaticFX();
1009 // Entity created.
1010 return true;
1011 }// build //
1013 //-----------------------------------------------
1014 // isKami()
1015 //-----------------------------------------------
1016 bool CCharacterCL::isKami() const
1018 if (!_Sheet)
1019 return false;
1020 return (_Sheet->Race == EGSPD::CPeople::Kami);
1023 //-----------------------------------------------
1024 // isUnknownRace()
1025 //-----------------------------------------------
1026 bool CCharacterCL::isUnknownRace() const
1028 if (!_Sheet)
1029 return false;
1030 return (_Sheet->Race == EGSPD::CPeople::Unknown);
1033 //-----------------------------------------------
1034 // getAttackHeight :
1035 // Return the atk height.
1036 // \todo GUIGUI : height relative to attacker instead of global height
1037 //-----------------------------------------------
1038 CCharacterCL::TAtkHeight CCharacterCL::getAttackHeight(CEntityCL *target, BODY::TBodyPart localisation, BODY::TSide side) const
1040 // Check there is a target.
1041 if(target == 0)
1042 return CCharacterCL::AtkMiddle;
1043 // Get the position for a bone.
1044 float height;
1045 if(target->getBoneHeight(localisation, side, height))
1047 // Low
1048 if(height < 1.0f)
1049 return CCharacterCL::AtkLow;
1050 // High
1051 else if(height > 2.0f)
1052 return CCharacterCL::AtkHigh;
1054 // Default is Middle atk.
1055 return CCharacterCL::AtkMiddle;
1056 }// getAttackHeight //
1058 //-----------------------------------------------
1059 // getBoneHeight :
1060 //-----------------------------------------------
1061 bool CCharacterCL::getBoneHeight(BODY::TBodyPart localisation, BODY::TSide side, float &height) const // virtual
1063 // If there is no skeleton return false
1064 if(_Skeleton.empty())
1065 return false;
1066 // Get the Bone Name
1067 const char *boneName = getBoneNameFromBodyPart(localisation, side);
1068 if(boneName == 0)
1069 return false;
1070 // Get the Bone Id
1071 sint boneId = _Skeleton.getBoneIdByName(std::string(boneName));
1072 if (boneId == -1)
1073 return false;
1074 if(_Skeleton.isBoneComputed(boneId) == false)
1075 return false;
1076 NL3D::UBone bone = _Skeleton.getBone(boneId);
1077 CMatrix BoneMat = bone.getLastWorldMatrixComputed();
1078 height = (float)(BoneMat.getPos().z-pos().z);
1079 if(height < 0.0f)
1080 height = 0.0f;
1081 else if(height > 10.0f)
1082 height = 10.0f;
1083 return true;
1084 }// getBoneHeight //
1087 //-----------------------------------------------
1088 // lookAtItemsInHands :
1089 // Look at items in hands to change the animation set.
1090 // \return true if the mode is a mode where items in hands should be hidden
1091 //-----------------------------------------------
1092 bool CCharacterCL::modeWithHiddenItems() const
1094 return ((ClientCfg.PutBackItems && !isFighting()) || isSit() || _Mode==MBEHAV::SWIM || isRiding() || _Mode==MBEHAV::SWIM_DEATH || _Mode==MBEHAV::REST);
1095 }// lookAtItemsInHands //
1098 //-----------------------------------------------
1099 // automatonType :
1100 //-----------------------------------------------
1101 string CCharacterCL::automatonType() const // virtual
1103 return _Sheet->getAutomaton();
1104 }// automatonType //
1106 //-----------------------------------------------
1107 // computeAutomaton :
1108 // Compute the current automaton for the entity.
1109 //-----------------------------------------------
1110 void CCharacterCL::computeAutomaton()
1112 _CurrentAutomaton = automatonType() + "_" + NLMISC::toLowerAscii(MBEHAV::modeToString(_Mode)) + ".automaton";
1113 }// computeAutomaton //
1116 //-----------------------------------------------
1117 // computeAnimSet :
1118 // Compute the animation set to use according to weapons, mode and race.
1119 //-----------------------------------------------
1120 void CCharacterCL::computeAnimSet(sint32 fakeLeftHand, sint32 fakeRightHand)
1122 if(ClientCfg.Light)
1123 return;
1124 // Use the generic method to compute the animation set.
1125 if(!::computeAnimSet(_CurrentAnimSet[MOVE], _Mode, _Sheet->getAnimSetBaseName(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet, !modeWithHiddenItems()))
1127 nlwarning("CH:computeAnimSet:%d: pb when trying to compute the animset. Sheet Id '%u(%s)'.", _Slot, _SheetId.asInt(), _SheetId.toString().c_str());
1130 }// computeAnimSet //
1132 //-----------------------------------------------
1133 // adjustPI :
1134 // Adjust the Predicted Interval to fix some errors according to the distance.
1135 //-----------------------------------------------
1136 NLMISC::TGameCycle CCharacterCL::adjustPI(float x , float y, float /* z */, const NLMISC::TGameCycle &pI)
1138 NLMISC::TGameCycle adjustedPI = pI;
1139 if(ClientCfg.RestrainPI && adjustedPI > 0)
1141 double dist = (x-UserEntity->pos().x)*(x-UserEntity->pos().x) + (y-UserEntity->pos().y)*(y-UserEntity->pos().y);
1142 // If under 50m check Predicted Interval
1143 if(dist < 50*50)
1145 NLMISC::TGameCycle maxPi = (NLMISC::TGameCycle)(sqrt(dist)/5.0)+1;
1146 if(adjustedPI > maxPi)
1148 adjustedPI = maxPi;
1149 if(VerboseAnimSelection && _Slot == UserEntity->selection())
1150 nlinfo("CH:updtVPPos:%d: dist'%f' PI'%d' newPI'%d'.", _Slot, sqrt(dist), pI, adjustedPI);
1154 return adjustedPI;
1155 }// adjustPI //
1157 //-----------------------------------------------
1158 // updateVisualPropertyPos :
1159 // Received a new position for the entity.
1160 // \warning Do not send position for the user
1161 //-----------------------------------------------
1162 void CCharacterCL::updateVisualPropertyPos(const NLMISC::TGameCycle &gameCycle, const sint64 &prop, const NLMISC::TGameCycle &pI)
1164 // Check the DB entry (the warning is already done in the build method).
1165 if(_DBEntry == 0)
1166 return;
1167 // Get The property 'Y'.
1168 CCDBNodeLeaf *nodeY = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSY));
1169 if(nodeY == 0)
1171 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSY);
1172 return;
1174 // Get The property 'Z'.
1175 CCDBNodeLeaf *nodeZ = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSZ));
1176 if(nodeZ == 0)
1178 nlwarning("CH::updtVPPos:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSZ);
1179 return;
1182 // Convert Database into a Position
1183 float x = (float)(prop)/1000.0f;
1184 float y = (float)(nodeY->getValue64())/1000.0f;
1185 float z = (float)(nodeZ->getValue64())/1000.0f;
1187 #ifdef TMP_DEBUG_GUIGUI
1188 // Theoretical Position
1189 _TheoreticalPosition = CVectorD((double)x, (double)y, (double)z);
1190 #endif // TMP_DEBUG_GUIGUI
1192 // First position Managed -> set the PACS Position
1193 if(_FirstPosManaged)
1195 pacsPos(CVectorD(x, y, z));
1196 _FirstPosManaged = false;
1197 return;
1200 // Wait for the entity to be spawned
1201 if(_First_Pos)
1202 return;
1204 // Stock the position (except if this is the user mount because it's the user that control him not the server)
1205 if( !isRiding() || _Rider != 0)
1207 // Adjust the Predicted Interval to fix some "bug" into the Prediction Algo.
1208 NLMISC::TGameCycle adjustedPI = adjustPI(x, y, z, pI);
1209 // Add Stage.
1210 _Stages.addStage(gameCycle, PROPERTY_POSX, prop, adjustedPI);
1211 _Stages.addStage(gameCycle, PROPERTY_POSY, nodeY->getValue64());
1212 _Stages.addStage(gameCycle, PROPERTY_POSZ, nodeZ->getValue64());
1214 }// updateVisualPropertyPos //
1216 //-----------------------------------------------
1217 // updateVisualPropertyOrient :
1218 // Received a new orientation.
1219 //-----------------------------------------------
1220 void CCharacterCL::updateVisualPropertyOrient(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1222 #ifdef TMP_DEBUG_GUIGUI
1223 // Backup the last orientation received.
1224 _TheoreticalOrientation = *(float *)(&prop);
1225 #endif // TMP_DEBUG_GUIGUI
1227 // New Mode Received.
1228 if(verboseVP(this))
1230 float ori = *(float *)(&prop);
1231 nlinfo("(%05d,%03d) CH::updateVPOri:%d: '%f' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, ori);
1234 // if no skeleton we set the orientation
1235 if(_Skeleton.empty())
1237 // server forces the entity orientation even if it cannot turn
1238 front(CVector((float)cos(_TheoreticalOrientation), (float)sin(_TheoreticalOrientation), 0.f), true, true, true);
1239 dir(front(), false, false);
1240 if(_Primitive)
1241 _Primitive->setOrientation(_TheoreticalOrientation, dynamicWI);
1243 else
1245 if( !isRiding() || _Rider != 0 )
1247 // Add in right stage.
1248 _Stages.addStage(gameCycle, PROPERTY_ORIENTATION, prop);
1251 }// updateVisualPropertyOrient //
1253 //-----------------------------------------------
1254 // updateVisualPropertyMode :
1255 // New mode received.
1256 // \warning For the first mode, we must have received the position and orientation (but this should be the case).
1257 // \warning Read the position or orientation from the database when reading the mode (no more updated in updateVisualPropertyPos and updateVisualPropertyOrient).
1258 //-----------------------------------------------
1259 void CCharacterCL::updateVisualPropertyMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1261 if(verboseVP(this))
1262 nlinfo("(%05d,%03d) CH:updtVPMode:%d: '%s(%d)' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, modeToString((MBEHAV::EMode)prop).c_str(), (MBEHAV::EMode)prop);
1263 // New Mode Received : Set the Theoretical Current Mode if different.
1264 if(_TheoreticalMode != (MBEHAV::EMode)(prop & 0xffff))
1265 _TheoreticalMode = (MBEHAV::EMode)(prop & 0xffff);
1266 else
1268 nlwarning("CH:updtVPMode:%d: The mode '%s(%d)' sent is the same as the current one.", _Slot, modeToString(_TheoreticalMode).c_str(), _TheoreticalMode);
1269 return;
1271 // If it is the first mode, set the mode.
1272 if(_Mode == MBEHAV::UNKNOWN_MODE)
1274 // SET THE FIRST POSITION
1275 //-----------------------
1276 // Check the DB entry (the warning is already done in the build method).
1277 if(_DBEntry == 0)
1278 return;
1279 // Get The property 'PROPERTY_POSX'.
1280 CCDBNodeLeaf *nodeX = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSX));
1281 if(nodeX == 0)
1283 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSX);
1284 return;
1286 // Get The property 'PROPERTY_POSY'.
1287 CCDBNodeLeaf *nodeY = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSY));
1288 if(nodeY == 0)
1290 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSY);
1291 return;
1293 // Get The property 'PROPERTY_POSZ'.
1294 CCDBNodeLeaf *nodeZ = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSZ));
1295 if(nodeZ == 0)
1297 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSZ);
1298 return;
1300 // Next position will no longer be the first one.
1301 _First_Pos = false;
1302 // Insert the primitive into the world.
1303 if(_Primitive)
1304 _Primitive->insertInWorldImage(dynamicWI);
1305 // float makes a few cm error
1306 double x = (double)(nodeX->getValue64())/1000.0;
1307 double y = (double)(nodeY->getValue64())/1000.0;
1308 double z = (double)(nodeZ->getValue64())/1000.0;
1309 // Set the primitive position.
1310 pacsPos(CVectorD(x, y, z));
1311 // SET THE FIRST ORIENTATION
1312 //--------------------------
1313 // Get The property 'PROPERTY_ORIENTATION'.
1314 CCDBNodeLeaf *nodeOri = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_ORIENTATION));
1315 if(nodeOri == 0)
1317 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot, CLFECOMMON::PROPERTY_ORIENTATION);
1318 return;
1320 C64BitsParts parts;
1321 parts.i64[0] = nodeOri->getValue64();
1322 float angleZ = parts.f[0];
1323 // server forces the entity orientation even if it cannot turn
1324 front(CVector((float)cos(angleZ), (float)sin(angleZ), 0.f), true, true, true);
1325 dir(front(), false, false);
1326 _TargetAngle = angleZ;
1327 if(_Primitive)
1328 _Primitive->setOrientation(angleZ, dynamicWI);
1329 // SET THE FIRST MODE
1330 //-------------------
1331 // Set the mode Now
1332 _Mode = _TheoreticalMode;
1333 _ModeWanted = _TheoreticalMode;
1334 if((_Mode == MBEHAV::MOUNT_NORMAL) && (_Rider == CLFECOMMON::INVALID_SLOT))
1336 _Mode = MBEHAV::NORMAL;
1337 _ModeWanted = MBEHAV::MOUNT_NORMAL;
1338 // See also updateVisualPropertyRiderEntity() for the case when _Rider is received after the mode
1339 computeAutomaton();
1340 computeAnimSet();
1341 setAnim(CAnimationStateSheet::Idle);
1342 // Add the mode to the stage.
1343 _Stages.addStage(gameCycle, PROPERTY_MODE, prop);
1345 computeAutomaton();
1346 computeAnimSet();
1347 setAnim(CAnimationStateSheet::Idle);
1349 // Not the first mode -> Add to a stage.
1350 else
1352 // Add the mode to the stage.
1353 _Stages.addStage(gameCycle, PROPERTY_MODE, prop);
1354 // Float mode push the orientation
1355 if(_TheoreticalMode == MBEHAV::COMBAT_FLOAT)
1357 // Get The property 'PROPERTY_ORIENTATION'.
1358 CCDBNodeLeaf *nodeOri = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_ORIENTATION));
1359 if(nodeOri == 0)
1361 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_ORIENTATION(%d)'.", _Slot, CLFECOMMON::PROPERTY_ORIENTATION);
1362 return;
1364 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_ORIENTATION, nodeOri->getValue64());
1366 // Any other mode push the position
1367 else
1369 if(_TheoreticalMode != MBEHAV::MOUNT_NORMAL)
1371 // Check the DB entry (the warning is already done in the build method).
1372 if(_DBEntry == 0)
1373 return;
1374 // Get The property 'PROPERTY_POSX'.
1375 CCDBNodeLeaf *nodeX = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSX));
1376 if(nodeX == 0)
1378 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSX(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSX);
1379 return;
1381 // Get The property 'PROPERTY_POSY'.
1382 CCDBNodeLeaf *nodeY = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSY));
1383 if(nodeY == 0)
1385 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSY(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSY);
1386 return;
1388 // Get The property 'PROPERTY_POSZ'.
1389 CCDBNodeLeaf *nodeZ = dynamic_cast<CCDBNodeLeaf *>(_DBEntry->getNode(CLFECOMMON::PROPERTY_POSZ));
1390 if(nodeZ == 0)
1392 nlwarning("CH::updtVPMode:%d: Cannot find the property 'PROPERTY_POSZ(%d)'.", _Slot, CLFECOMMON::PROPERTY_POSZ);
1393 return;
1395 // Add Stage.
1396 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_POSX, nodeX->getValue64());
1397 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_POSY, nodeY->getValue64());
1398 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_POSZ, nodeZ->getValue64());
1402 }// updateVisualPropertyMode //
1404 //-----------------------------------------------
1405 // updateVisualPropertyBehaviour :
1406 // New Behaviour received.
1407 //-----------------------------------------------
1408 void CCharacterCL::updateVisualPropertyBehaviour(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1410 // New Behaviour Received.
1411 CBehaviour beh(prop);
1412 if(verboseVP(this))
1413 nlinfo("(%05d,%03d) CH::updateVPBeha:%d: '%s(%d)' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, behaviourToString((EBehaviour)beh.Behaviour).c_str(), (sint)beh.Behaviour);
1415 // Add in right stage.
1416 _Stages.addStage(gameCycle, PROPERTY_BEHAVIOUR, prop);
1417 }// updateVisualPropertyBehaviour //
1419 void CCharacterCL::updateVisualPropertyTargetList(const NLMISC::TGameCycle &gameCycle, const sint64 &prop, uint listIndex)
1421 // Add in right stage.
1422 _Stages.addStage(gameCycle, PROPERTY_TARGET_LIST_0 + listIndex, prop);
1425 void CCharacterCL::updateVisualPropertyVisualFX(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1427 _Stages.addStage(gameCycle, PROPERTY_VISUAL_FX, prop);
1429 //-----------------------------------------------
1430 // updateVisualPropertyName :
1431 // Received the name Id.
1432 //-----------------------------------------------
1433 void CCharacterCL::updateVisualPropertyName(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1435 // Update the entity name (do not need to be managed with LCT).
1436 uint32 nameId = *(uint32 *)(&prop);
1438 // Store the name Id
1439 _NameId = nameId;
1441 // STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId, this, &_Name);
1442 STRING_MANAGER::CStringManagerClient::instance()->waitString(nameId, this);
1444 //if(!getEntityName().empty())
1445 // nlwarning("CH::updateVPName:%d: name Id '%d' received but no name allocated.", _Slot, nameId);
1446 //else if(verboseVP(this))
1447 // nlinfo("(%05d,%03d) CH::updateVPName:%d: name '%s(%d)' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, getEntityName().toString().c_str(), nameId);
1449 updateMissionTarget();
1450 }// updateVisualPropertyName //
1452 //-----------------------------------------------
1453 // updateVisualPropertyTarget :
1454 // Received the new target for the entity
1455 // \todo GUIGUI : should be added in a stage.
1456 //-----------------------------------------------
1457 void CCharacterCL::updateVisualPropertyTarget(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1459 // New target Received.
1460 sint targ = (sint)prop;
1462 if(verboseVP(this))
1463 nlinfo("(%05d,%03d) CH::updateVPTarget:%d: '%d' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, targ);
1465 // New entity target
1466 _TargetSlotNoLag = (CLFECOMMON::TCLEntityId)targ;
1468 // Add in right stage.
1469 _Stages.addStage(gameCycle, PROPERTY_TARGET_ID, prop);
1470 }// updateVisualPropertyTarget //
1472 //-----------------------------------------------
1473 // updateVisualPropertyVpa :
1474 // Received the new target for the entity
1475 // \todo GUIGUI : should be added in a stage.
1476 //-----------------------------------------------
1477 void CCharacterCL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1479 // VPA only useful for NPC
1480 if(isNPC()==false)
1482 //nlwarning("CH:updtVPVpa:%d: VPA received but NOT an NPC. Sheet Id '%u(%s)'.", _Slot, _SheetId.asInt(), _SheetId.toString().c_str());
1483 return;
1486 // NO SKELETON -> NO VPA
1487 if(_Skeleton.empty())
1488 return;
1490 // Get the alternative look property.
1491 SAltLookProp altLookProp = *(SAltLookProp *)(&prop);
1492 // Display debug infos
1493 if(verboseVP(this))
1495 nlinfo("(%05d,%03d) CH:updtVPVpa:%d: TopColor(%d) BotColor(%d) RH(%d) LH(%d) Hat(%d) Seed(%d) HairColor(%d)",
1496 sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot,
1497 (uint)altLookProp.Element.ColorTop, (uint)altLookProp.Element.ColorBot,
1498 (uint)altLookProp.Element.WeaponRightHand, (uint)altLookProp.Element.WeaponLeftHand,
1499 (uint)altLookProp.Element.Hat, (uint)altLookProp.Element.Seed,
1500 (uint)altLookProp.Element.ColorHair);
1503 // Dress the character.
1504 if(!_LookRdy)
1506 // The entity is now visually ready.
1507 _LookRdy = true;
1509 // Generate a new random.
1510 NLMISC::CRandom rnd;
1511 rnd.srand((sint)altLookProp.Element.Seed);
1513 // Retrieve the right sheet for clothes.
1514 _ClothesSheet = _Sheet;
1515 if(!_Sheet->IdAlternativeClothes.empty())
1517 sint32 num = rnd.rand()%(_Sheet->IdAlternativeClothes.size()+1);
1518 if(num > 0)
1520 CSheetId altClothesId(_Sheet->getAlternativeClothes(num-1));
1521 const CEntitySheet *sheetAlt = SheetMngr.get(altClothesId);
1522 if(dynamic_cast<const CCharacterSheet *>(sheetAlt))
1523 _ClothesSheet = dynamic_cast<const CCharacterSheet *>(sheetAlt);
1527 // Eyes Color
1528 if(_Sheet->EyesColor >= SheetMngr.nbEyesColor())
1530 if (SheetMngr.nbEyesColor() == 0)
1531 _EyesColor = 0;
1532 else
1533 _EyesColor = (sint8)(rnd.rand()%SheetMngr.nbEyesColor());
1535 else
1536 _EyesColor = _Sheet->EyesColor;
1537 // Hair Color
1538 if (SheetMngr.nbHairColor() == 0)
1539 _HairColor = 0;
1540 else
1541 _HairColor = (sint8)altLookProp.Element.ColorHair%SheetMngr.nbHairColor();
1542 // Hair Index
1543 if(!_Sheet->HairItemList.empty())
1545 sint32 num = rnd.rand()%_Sheet->HairItemList.size();
1546 if(num>=0 && num <_BadHairIndex)
1547 _HairIndex = (uint8)num;
1548 else
1549 nlwarning("CH:updtVPVpa:%d: Bad Hair Index '%d'", _Slot, num);
1552 // -- Dress the character -- (all parts that should not change)
1553 /** tmp : remove all fx item
1554 * \TODO delete an item only if changed
1556 buildEquipment(_ClothesSheet->Body, SLOTTYPE::CHEST_SLOT, altLookProp.Element.ColorTop); // Chest
1557 buildEquipment(_ClothesSheet->Arms, SLOTTYPE::ARMS_SLOT, altLookProp.Element.ColorArm); // Arms
1558 buildEquipment(_ClothesSheet->Hands, SLOTTYPE::HANDS_SLOT, altLookProp.Element.ColorGlove); // Gloves
1559 buildEquipment(_ClothesSheet->Legs, SLOTTYPE::LEGS_SLOT, altLookProp.Element.ColorBot); // Legs
1560 buildEquipment(_ClothesSheet->Feet, SLOTTYPE::FEET_SLOT, altLookProp.Element.ColorBoot); // Boots
1561 // Face
1562 _FaceIdx = buildEquipment(_Sheet->Face, SLOTTYPE::FACE_SLOT);
1564 // Entity is now dressed
1565 skeleton()->show();
1568 // -- Manage the Head --
1569 // Display the helm.
1570 if(altLookProp.Element.Hat!=0 && !_ClothesSheet->Head.getItem().empty())
1572 // Create the Helm.
1573 _HeadIdx = buildEquipment(_ClothesSheet->Head, SLOTTYPE::HEAD_SLOT, altLookProp.Element.ColorHair, _HeadIdx);
1574 // Hide the face.
1575 SInstanceCL *pInstFace = getFace();
1576 if(pInstFace)
1578 if(pInstFace->Current.empty() == false)
1579 pInstFace->Current.hide();
1580 else
1581 pInstFace->KeepHiddenWhenLoaded = true;
1584 // Display the Hair.
1585 else
1587 // Create the Hair.
1588 if(_HairIndex != _BadHairIndex)
1589 _HeadIdx = buildEquipment(_Sheet->HairItemList[_HairIndex], SLOTTYPE::HEAD_SLOT, altLookProp.Element.ColorHair, _HeadIdx);
1590 // Display the face.
1591 SInstanceCL *pInstFace = getFace();
1592 if(pInstFace)
1593 if(!pInstFace->Current.empty())
1594 pInstFace->Current.show();
1597 // -- Manage weapons -- (weapons can change)
1599 // Right Hand
1600 const CItemSheet *newRightHand = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, (uint)altLookProp.Element.WeaponRightHand);
1601 if (newRightHand != _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet) // item changed ?
1603 // Remove the old Item in the right hand
1604 if(_RHandInstIdx != CEntityCL::BadIndex)
1606 // remove shape
1607 _RHandInstIdx = addInstance("", "", -1, _RHandInstIdx);
1608 // remove fxs
1609 _Items[SLOTTYPE::RIGHT_HAND_SLOT].release();
1611 // set new one
1612 _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = newRightHand;
1613 if (newRightHand)
1615 if( newRightHand->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
1616 _RHandInstIdx = createItemInstance(*newRightHand, _RHandInstIdx, SLOTTYPE::RIGHT_HAND_SLOT, "box_arme", -1, -1);
1617 else
1618 _RHandInstIdx = createItemInstance(*newRightHand, _RHandInstIdx, SLOTTYPE::RIGHT_HAND_SLOT, "", -1, -1);
1621 // update fx for right hand (trail may have been activated, or advantage fx)
1622 if (newRightHand)
1624 SInstanceCL *instCLRH = idx2Inst(_RHandInstIdx);
1625 if(instCLRH)
1627 NL3D::UInstance itemInstance = (!instCLRH->Loading.empty()) ? instCLRH->Loading : instCLRH->Current;
1628 if (!itemInstance.empty())
1630 // update fxs
1631 _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance);
1632 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
1633 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(altLookProp.Element.RTrail);
1638 // Left Hand
1639 const CItemSheet *newLeftHand = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, (uint)altLookProp.Element.WeaponLeftHand);
1640 if (newLeftHand != _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet) // item changed ?
1642 // Remove the old Item in the left hand
1643 if(_LHandInstIdx != CEntityCL::BadIndex)
1645 // remove shape
1646 _LHandInstIdx = addInstance("", "", -1, _LHandInstIdx);
1647 // remove fxs
1648 _Items[SLOTTYPE::LEFT_HAND_SLOT].release();
1650 // set new one
1651 _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = newLeftHand;
1652 if (newLeftHand)
1654 string bindBone;
1655 if(newLeftHand->getAnimSet() == "s")
1656 bindBone = "Box_bouclier";
1657 else
1658 bindBone = "box_arme_gauche";
1659 _LHandInstIdx = createItemInstance(*newLeftHand, _LHandInstIdx, SLOTTYPE::LEFT_HAND_SLOT, bindBone, -1, -1);
1662 // update fx for left hand (trail may have been activated, or advantage fx)
1663 if (newLeftHand)
1665 SInstanceCL *instCLLH = idx2Inst(_LHandInstIdx);
1666 if(instCLLH)
1668 NL3D::UInstance itemInstance = (!instCLLH->Loading.empty()) ? instCLLH->Loading : instCLLH->Current;
1669 if (!itemInstance.empty())
1671 // update fxs
1672 _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance);
1673 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize((uint) (2 * altLookProp.Element.LTrail));
1679 // -- Update Animation -- (after all those changes animation could change).
1680 computeAnimSet();
1681 setAnim(animState(MOVE));
1682 }// updateVisualPropertyVpb //
1684 //-----------------------------------------------
1685 // updateVisualPropertyVpb :
1686 //-----------------------------------------------
1687 void CCharacterCL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1689 // Get the alternative look property.
1690 SAltLookProp2 altLookProp = *(SAltLookProp2 *)(&prop);
1691 // Display debug infos
1692 if(verboseVP(this))
1694 nlinfo("(%05d,%03d) CH:updtVPVpb:%d: Scale(%d)", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot,
1695 (uint)altLookProp.PropertySubData.Scale);
1698 float customScale;
1699 // Set new scale
1700 if (altLookProp.PropertySubData.Scale==0)
1701 customScale = 1.f;
1702 else
1703 customScale = (float)altLookProp.PropertySubData.Scale/100.f;
1705 if (!_ScaleDone) // first time
1707 _CustomScale = customScale;
1708 applyScale(1.0f);
1709 _ScaleDone = true;
1711 else
1713 _StartCustomScale = ryzomGetLocalTime();
1714 _BaseCustomScale = _CustomScale;
1715 _StepCustomScale = customScale - _BaseCustomScale;
1720 void CCharacterCL::scale()
1722 // Save old scale
1723 float oldCustomScale = _CustomScale;
1725 _CustomScale = _BaseCustomScale + ((float(ryzomGetLocalTime() - _StartCustomScale) * _StepCustomScale) / 1000.f);
1726 if ((_StepCustomScale >= 0.f && _CustomScale > _BaseCustomScale + _StepCustomScale)
1727 || (_StepCustomScale < 0.f && _CustomScale < _BaseCustomScale + _StepCustomScale))
1729 _StepCustomScale = 0.f;
1732 applyScale(oldCustomScale);
1735 void CCharacterCL::applyScale(float oldCustomScale)
1737 // Apply modification
1738 _CustomScalePos /= oldCustomScale;
1739 _CustomScalePos *= _CustomScale;
1740 // change the scale of the skeleton according to the new people
1741 USkeleton * skel = skeleton();
1742 if( skel )
1744 skel->setScale(getScale(), getScale(), getScale());
1746 // modify the stick bone scale to not propagate scale to child
1747 sint boneID = skel->getBoneIdByName("stick_1");
1748 if( boneID != -1 )
1750 UBone bone = skel->getBone(boneID);
1751 CVector newBoneScale = bone.getScale() * oldCustomScale/_CustomScale;
1752 bone.setScale( newBoneScale );
1755 // must set the new scale for the BotObject too
1756 else if(!_Instances.empty())
1758 float s= getScale();
1759 _Instances[0].setScale(CVector(s,s,s));
1763 if (_Primitive)
1765 float width, depth;
1766 _Primitive->getSize(width, depth);
1767 UMovePrimitive::TType primtype = _Primitive->getPrimitiveType();
1768 _Primitive->setPrimitiveType(UMovePrimitive::_2DOrientedBox);
1769 _Primitive->setSize((width / oldCustomScale) * _CustomScale, (depth / oldCustomScale) * _CustomScale);
1770 _Primitive->setPrimitiveType(primtype);
1773 }// updateVisualPropertyVpb //
1775 //-----------------------------------------------
1776 // updateVisualPropertyEntityMounted :
1777 // Update Entity Mount
1778 //-----------------------------------------------
1779 void CCharacterCL::updateVisualPropertyEntityMounted(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1781 // New target Received.
1782 sint mountSlot = (sint)prop;
1783 _TheoreticalMount = (CLFECOMMON::TCLEntityId)mountSlot;
1784 if(verboseVP(this))
1785 nlinfo("(%05d,%03d) CH::updateVPMount:%d: '%d' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, mountSlot);
1787 // Add in right stage.
1788 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID, prop);
1789 }// updateVisualPropertyEntityMounted //
1791 //-----------------------------------------------
1792 // updateVisualPropertyRiderEntity :
1793 // Update Entity Rider
1794 //-----------------------------------------------
1795 void CCharacterCL::updateVisualPropertyRiderEntity(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1797 // New target Received.
1798 sint riderSlot = (sint)prop;
1799 _TheoreticalRider = (CLFECOMMON::TCLEntityId)riderSlot;
1800 if(verboseVP(this))
1801 nlinfo("(%05d,%03d) CH::updateVPRider:%d: '%d' received.", sint32(T1%100000), NetMngr.getCurrentServerTick(), _Slot, riderSlot);
1803 // Add in right stage.
1804 _Stages.addStage(gameCycle, CLFECOMMON::PROPERTY_RIDER_ENTITY_ID, prop);
1805 }// updateVisualPropertyRiderEntity //
1807 //-----------------------------------------------
1808 // updateVisualPropertyBars :
1809 // Update Entity Bars
1810 //-----------------------------------------------
1811 void CCharacterCL::updateVisualPropertyBars(const NLMISC::TGameCycle &gameCycle, const sint64 &prop) // virtual
1813 CBarManager::CBarInfo barInfo;
1815 // Encode HP to 7 bits
1816 barInfo.Score[SCORES::hit_points] = (sint8)((prop&0x7ff) * 127 / 1023);
1817 // NB: barInfo are sint8, but no problem, since anything following is 7 bits.
1818 barInfo.Score[SCORES::stamina] = (uint8)((prop>>11)&0x7f);
1819 barInfo.Score[SCORES::sap] = (uint8)((prop>>18)&0x7f);
1820 barInfo.Score[SCORES::focus] = (uint8)((prop>>25)&0x7f);
1822 // update The Bar manager
1823 CBarManager *pBM= CBarManager::getInstance();
1824 /* ***********
1825 WHY gameCycle+1 ????? (yoyo)
1826 It's because sometimes I have a bug With target DB update and VP update. This is the scenario
1827 where I suppose the problem rises:
1828 tick=320: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1829 tick=321: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=321
1830 EGS::databaseUpdate(), DB updated, with timestamp=321!!!
1832 Thus I receives on client:
1833 first the VP with Hp=10, timestamp=321
1834 second the DB with Hp=49, timestamp=321 too => replaced => BUG
1835 NB: DB is typically sent at low frequency by FrontEnd, thus received later on client.
1837 Since databaseUpdate() is called every 2 ticks, adding +1 to VP timestamps solve easily the problem.
1839 NB: the problem occurs because tickUpdate() and databaseUpdate() are called typically with 1 tick shift (tickUpdate()
1840 on even ticks, and databaseUpdate() on odd ticks for instance).
1842 NB: moreover, tickupdate() is called every 8 (or 16) ticks, and databaseUpdate() every 2 ticks. So there is one more
1843 possible bug:
1844 318: EGS::tickUpdate(): player.DBTargetHP.setProp(49)
1845 319: EGS::combat update, target ennemy receives a Hit, VPHp=10 => transmitted to client with timestamp=319
1846 EGS::databaseUpdate(), BUT decide to send only a small subset of DB (because lot of things to send)
1847 => our TargetHP is not updated
1848 320: nothing. tickupdate() is not called, since every 8 ticks
1849 321: EGS::databaseUpdate(), update TargetHP, with timestamp=321 !!!!! => Bug
1851 (remind that we cannot store a timestamp for each DB property, else would be too big to store and to send...)
1853 BTW, this last bug should be very rare, so don't care.
1854 *********** */
1855 pBM->updateBars(dataSetId(), barInfo, gameCycle+1,
1856 CBarManager::HpFlag | CBarManager::StaFlag | CBarManager::SapFlag | CBarManager::FocusFlag);
1858 }// updateVisualPropertyBars //
1860 //-----------------------------------------------
1861 // updateVisualPropertyGuildSymbol :
1863 //-----------------------------------------------
1864 void CCharacterCL::updateVisualPropertyGuildSymbol(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1866 _GuildSymbol = prop;
1868 // maybe need to rebuild the in scene interface
1869 if(_InSceneUserInterface && _InSceneUserInterface->needGuildSymbolId())
1870 buildInSceneInterface();
1871 } // updateVisualPropertyGuildSymbol //
1873 //-----------------------------------------------
1874 // updateVisualPropertyGuildNameID :
1876 //-----------------------------------------------
1877 void CCharacterCL::updateVisualPropertyGuildNameID(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1879 _GuildNameId = uint32(prop);
1881 // maybe need to rebuild the in scene interface
1882 if(_InSceneUserInterface && _InSceneUserInterface->needGuildNameId())
1883 buildInSceneInterface();
1884 } // updateVisualPropertyGuildNameID //
1886 //-----------------------------------------------
1887 // updateVisualPropertyEventFactionID :
1889 //-----------------------------------------------
1890 void CCharacterCL::updateVisualPropertyEventFactionID(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1892 // ODD Hack by Ulukyn
1893 //_EventFactionId = uint32(prop);
1894 _PvpMode = uint32(prop);
1896 buildInSceneInterface();
1898 if (isUser())
1900 uint i;
1901 uint numEntity = (uint)EntitiesMngr.entities().size();
1902 for (i=0; i<numEntity; i++)
1904 CEntityCL *entity = EntitiesMngr.entity(i);
1905 if (entity)
1907 CCharacterCL *character = dynamic_cast<CCharacterCL*>(entity);
1908 if (character)
1910 if( character->getPvpMode() != 0 && !character->isUser())
1911 character->buildInSceneInterface ();
1916 } // updateVisualPropertyEventFactionID //
1918 //-----------------------------------------------
1919 // updateVisualPropertyPvpMode :
1921 //-----------------------------------------------
1922 void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1924 //_PvpMode = uint32(prop);
1925 //buildInSceneInterface();
1927 } // updateVisualPropertyPvpMode //
1929 //-----------------------------------------------
1930 // updateVisualPropertyPvpClan :
1932 //-----------------------------------------------
1933 void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1935 _LeagueId = uint32(prop);
1937 buildInSceneInterface();
1939 if (isUser())
1941 uint i;
1942 uint numEntity = (uint)EntitiesMngr.entities().size();
1943 for (i=0; i<numEntity; i++)
1945 CEntityCL *entity = EntitiesMngr.entity(i);
1946 if (entity)
1948 CCharacterCL *character = dynamic_cast<CCharacterCL*>(entity);
1949 if (character)
1951 if( character->getPvpMode() != 0 && !character->isUser())
1952 character->buildInSceneInterface ();
1957 } // updateVisualPropertyPvpClan //
1959 //-----------------------------------------------
1960 // updateVisualPropertyStatus :
1961 // Update Entity Status
1962 //-----------------------------------------------
1963 void CCharacterCL::updateVisualPropertyStatus(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &/* prop */) // virtual
1965 nlinfo("CH:updtVPStatus:%d: received.", _Slot);
1966 }// updateVisualPropertyStatus //
1968 //-----------------------------------------------
1969 // updateVisualPropertyContextual :
1970 // Update Entity Status
1971 //-----------------------------------------------
1972 void CCharacterCL::updateVisualPropertyContextual(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1974 bool precAttackable= _Properties.attackable();
1976 // call parent
1977 CEntityCL::updateVisualPropertyContextual(gameCycle, prop);
1979 // if attack modified, and npc/fauna, must rebuild the in scene interface,
1980 // cause sheets 'Attackable' property not always correclty filled
1981 if( (isNPC()||isFauna()) && precAttackable!=_Properties.attackable())
1982 buildInSceneInterface();
1986 //-----------------------------------------------
1987 // updateVisualPropertyOwnerPeople :
1989 //-----------------------------------------------
1990 void CCharacterCL::updateVisualPropertyOwnerPeople(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1992 if( _OwnerPeople != MOUNT_PEOPLE::TMountPeople(prop) )
1994 // reset scale pos
1995 float oldPeopleScaleFactor;
1996 switch( _OwnerPeople )
1998 case MOUNT_PEOPLE::Fyros : oldPeopleScaleFactor = ClientCfg.FyrosScale; break;
1999 case MOUNT_PEOPLE::Matis : oldPeopleScaleFactor = ClientCfg.MatisScale; break;
2000 case MOUNT_PEOPLE::Tryker : oldPeopleScaleFactor = ClientCfg.TrykerScale; break;
2001 case MOUNT_PEOPLE::Zorai : oldPeopleScaleFactor = ClientCfg.ZoraiScale; break;
2002 default:
2003 oldPeopleScaleFactor = 1.f;
2005 _CustomScalePos /= oldPeopleScaleFactor;
2007 // set the new scale pos
2008 float newPeopleScaleFactor;
2009 _OwnerPeople = MOUNT_PEOPLE::TMountPeople(prop);
2010 switch( _OwnerPeople )
2012 case MOUNT_PEOPLE::Fyros : newPeopleScaleFactor = ClientCfg.FyrosScale; break;
2013 case MOUNT_PEOPLE::Matis : newPeopleScaleFactor = ClientCfg.MatisScale; break;
2014 case MOUNT_PEOPLE::Tryker : newPeopleScaleFactor = ClientCfg.TrykerScale; break;
2015 case MOUNT_PEOPLE::Zorai : newPeopleScaleFactor = ClientCfg.ZoraiScale; break;
2016 default:
2017 newPeopleScaleFactor = 1.f;
2019 _CustomScalePos *= newPeopleScaleFactor;
2021 // change the scale of the skeleton according to the new people
2022 USkeleton * skel = skeleton();
2023 if( skel )
2025 skel->setScale(getScale(), getScale(), getScale());
2027 // modify the stick bone scale to not propagate scale to child
2028 sint boneID = skel->getBoneIdByName("stick_1");
2029 if( boneID != -1 )
2031 UBone bone = skel->getBone(boneID);
2032 CVector newBoneScale = bone.getScale() * oldPeopleScaleFactor/newPeopleScaleFactor;
2033 bone.setScale( newBoneScale );
2038 } // updateVisualPropertyOwnerPeople //
2042 //-----------------------------------------------
2043 // updateVisualPropertyOutpostInfos :
2045 //-----------------------------------------------
2046 void CCharacterCL::updateVisualPropertyOutpostInfos(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
2048 _OutpostId = ((uint16)prop)&0x7FFF;
2049 uint16 side = (((uint16)prop)&0x8000)>>15;
2050 _OutpostSide = (OUTPOSTENUMS::TPVPSide)side;
2052 nldebug("<CCharacterCL::updateVisualPropertyOutpostInfos> prop = %d, id=%d side=%d",(uint16)prop,_OutpostId,_OutpostSide);
2054 buildInSceneInterface();
2056 } // updateVisualPropertyOutpostInfos //
2059 //-----------------------------------------------
2060 // skin :
2061 // Get The Entity Skin
2062 //-----------------------------------------------
2063 sint CCharacterCL::skin() const // virtual
2065 return _Sheet->Skin;
2066 }// skin //
2069 //-----------------------------------------------
2070 // initProperties :
2071 // Initialize properties of the entity (according to the class).
2072 //-----------------------------------------------
2073 void CCharacterCL::initProperties()
2075 properties().selectable(_Sheet->Selectable);
2076 properties().talkableTo(_Sheet->Talkable);
2077 properties().attackable(_Sheet->Attackable);
2078 properties().givable(_Sheet->Givable);
2079 properties().mountable(_Sheet->Mountable);
2080 properties().invitable(false); // You cannot group with a bot.
2081 properties().afk(false);
2083 switch(_Sheet->HLState)
2085 case LHSTATE::LOOTABLE:
2086 properties().lootable(true); // You can loot the creature
2087 properties().harvestable(false); // You cannot harvest the creature
2088 break;
2089 case LHSTATE::HARVESTABLE:
2090 properties().lootable(false); // You cannot loot the creature
2091 properties().harvestable(true); // You can harvest the creature
2092 break;
2093 case LHSTATE::LOOTABLE_HARVESTABLE:
2094 properties().lootable(true); // You can loot the creature
2095 properties().harvestable(true); // You can harvest the creature
2096 break;
2098 default:
2099 properties().lootable(false); // You cannot loot the creature
2100 properties().harvestable(false); // You cannot harvest the creature
2101 break;
2103 }// initProperties //
2106 //-----------------------------------------------
2107 // computeTimeStep :
2108 // Compute the elapsed time since last call.
2109 // \param currentTime : current time in sec.
2110 // \return double : elapsed time.
2111 //-----------------------------------------------
2112 double CCharacterCL::computeTimeStep(const double &currentTime)
2114 // Last Time greater than Current Time.
2115 if(_LastFrameTime > currentTime)
2117 nlwarning("CCharacterCL::computeTimeStep : Time since last frame is negative (%f). Set _LastFrameTime with currentTime", _LastFrameTime - currentTime);
2118 _LastFrameTime = currentTime;
2121 // Time since last Time >= 0
2122 return currentTime - _LastFrameTime;
2123 }// computeTimeStep //
2126 //-----------------------------------------------
2127 // computeSpeed :
2128 // \todo GUIGUI : to do an average speed, there is a problem if time become very small because small frame or _LastFrameTime increase when looping
2129 //-----------------------------------------------
2130 double CCharacterCL::computeSpeed()
2132 double spd;
2133 double t = _DestTime - _LastFrameTime;
2134 if(dist2Dest() <= 0.0) // Already at destination.
2135 spd = 0.0;
2136 else if(_LastFrameTime == 0.0) // First frame
2137 spd = 1.0;
2138 else if(t < 0.0001) // We should already be at destination.
2139 spd = 1000.0;//-1.0;
2140 else
2142 spd = dist2Dest()/t;
2143 if(spd>0 && spd<0.001)
2144 spd = 0.001;
2145 if(spd > 1000.0)
2146 spd = 1000.0;
2148 speed(spd);
2149 return spd;
2150 }// computeSpeed //
2152 //-----------------------------------------------
2153 // computeSpeedFactor :
2154 // Compute and return the speed factor to apply to the animation.
2155 // \param speedToDest : evaluted speed to destination.
2156 // \return double : the speed factor to use for the current animation.
2157 // \todo GUIGUI : review this scale problem and optimize it.
2158 //-----------------------------------------------
2159 double CCharacterCL::computeSpeedFactor(double speedToDest)
2162 double speedFactor = 1.0;
2164 // \todo GUIGUI : optimize emotes, currently it's badly designed.
2165 const CAnimationState *animStatePtr;
2166 // If the current animation is an emote, get the right animation state.
2167 if(animState(MOVE) == CAnimationStateSheet::Emote)
2168 animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(_SubStateKey);
2169 else
2170 animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
2171 if(animStatePtr)
2173 // Get the animation
2174 const CAnimation *anim = animStatePtr->getAnimation(animIndex(MOVE));
2175 if(anim)
2177 // If the animation is a move (not idle, turn, etc.).
2178 if(_CurrentState)
2180 // Move : adjust speed factor according to the animation.
2181 if(_CurrentState->Move)
2183 // Check for oo speed.
2184 if(speedToDest != -1)
2186 // Compute the animation average speed according to the scale.
2187 double animSpeed = EAM->getAnimationAverageSpeed(anim->id())*getSheetScale()*_CustomScalePos*_CharacterScalePos;
2188 if(animSpeed > 0.0)
2189 speedFactor = speedToDest / animSpeed;
2190 else
2191 nlwarning("The animation is a move but animation speed is %f !", animSpeed);
2193 // \todo GUIGUI : unlimited speed, perhaps return a special value.
2194 // We should be arrived so speed is maximum.
2195 else
2196 speedFactor = 1000.0;
2198 // The current animation is a rotation so adjust the rotation speed according to the angle.
2199 if(_CurrentState->Rotation && _RotationFactor > 0.0)
2201 speedFactor /= _RotationFactor;
2202 speedFactor = std::min(speedFactor, 1.5);
2203 speedFactor = std::max(speedFactor, 0.5);
2205 // This animation must be play in a maximum time when there is someting to do after.
2206 // (must be done after the rotation speed adjustment)
2207 if(_CurrentState->MaxAnimDuration > 0.00f)
2209 if(dist2Dest() >= 0.0)
2211 double animLength = EAM->getAnimationLength(anim->id());
2212 double speedFactorBackup = speedFactor;
2213 speedFactor = animLength/_CurrentState->MaxAnimDuration;
2214 // If the animation speed should have been greater, let it play faster.
2215 if(speedFactor < speedFactorBackup)
2216 speedFactor = speedFactorBackup;
2219 // Panic mode (too late => accelerate)
2220 if(!_CurrentState->Move && _ImportantStepTime!=0.0)
2222 const float beginPanic= 1.5f; // start panic when too late of 1.5 seconds
2223 const float endPanic= 5.f; // max panic factor at 5 seconds of interval
2224 const float maxPanicSpeedFactor= 10.f;
2225 float t= float(TimeInSec - _ImportantStepTime);
2226 if(t>beginPanic)
2228 float lerp= (t-beginPanic)/(endPanic-beginPanic);
2229 clamp(lerp, 0.f, 1.f);
2230 float panicSF= lerp*maxPanicSpeedFactor;
2231 if(panicSF>speedFactor)
2232 speedFactor= panicSF;
2235 // Special panic mode because there is a position just after
2236 // NB: don't accelerate animations that can be breaked because of move
2237 // But still allow acceleration for mode animation transition (_Mode!=_ModeWanted)
2238 if( !_CurrentState->Move && _RunStartTimeNoPop!=INVALID_TIME &&
2239 (!_CurrentState->BreakableOnMove || _Mode!=_ModeWanted) )
2241 const float maxPanicSpeedFactor= 1.5f;
2242 // compare time of rest of animation, to remain time to the first move
2243 float remainAnimTime= float(EAM->getAnimationLength(anim->id()) - animOffset(MOVE));
2244 remainAnimTime= max(remainAnimTime, 0.f);
2245 float panicSF;
2246 // if already too late, then maximize speed
2247 if(TimeInSec>=_RunStartTimeNoPop)
2249 panicSF= maxPanicSpeedFactor;
2251 // else target the animation speed so it ends at estimated start of move
2252 else
2254 panicSF= float(remainAnimTime/(_RunStartTimeNoPop-TimeInSec));
2255 panicSF= min(panicSF, maxPanicSpeedFactor);
2257 // only if greater than prec
2258 if(panicSF>speedFactor)
2259 speedFactor= panicSF;
2262 // TestYoyo
2264 if(_Slot==WatchedEntitySlot && EntitiesMngr.isLogingStageChange())
2266 sint64 refLT= EntitiesMngr.getLogStageChangeStartLocalTime();
2267 NLMISC::createDebug();
2268 NLMISC::DebugLog->displayRawNL("** Entity %d: (t=%3d) animState %s: %.2f/%.2f. move: %d. rstnp: %d. sf: %.2f",
2269 (sint32)_Slot, sint32(T1-refLT),
2270 CAnimationState::getAnimationStateName(animStatePtr->state()).c_str(),
2271 float(animOffset(MOVE)), float(EAM->getAnimationLength(anim->id())),
2272 uint(_CurrentState->Move), _RunStartTimeNoPop==INVALID_TIME?-1:sint32(sint64(_RunStartTimeNoPop*1000)-refLT),
2273 speedFactor
2281 // \todo GUIGUI : corriger pour enlever le speed factor min.
2282 // Adjuste speed factor
2283 if(speedFactor < 0.5)
2284 speedFactor = 0.5;
2286 // Check negative or null speed factor.
2287 if(speedFactor <= 0.0)
2289 nlwarning("CCharacterCL::computeSpeedFactor: speedFactor = %f and this should never be <= 0.", speedFactor);
2290 speedFactor = 1.0;
2293 // Return the speed factor.
2294 return speedFactor;
2295 }// computeSpeedFactor //
2297 //-----------------------------------------------
2298 // endAnimTransition :
2299 // Call it at the end of the current animation to choose the next one.
2300 //-----------------------------------------------
2301 void CCharacterCL::endAnimTransition()
2303 // One more animation played.
2304 _NbLoopAnim++;
2306 // Hide the entity if needed.
2307 if(_HideSkin)
2308 hideSkin();
2310 // Debug Animation for the selection
2311 if(VerboseAnimSelection && _Slot == UserEntity->selection())
2312 nlinfo("CH:endAnimTransition:%d: current animation finished.", _Slot);
2313 // If the animation is a rotation, set the character direction he should have at the end of the animation.
2314 if(_CurrentState->Rotation)
2316 if(isUser())
2318 nldebug("<CCharacterCL::endAnimTransition> rotation : set dir as end anim dir");
2321 dir(dirEndAnim());
2323 // Fit the current direction to the target when attacking.
2324 if(_CurrentState->Attack)
2326 dir(front());
2328 // If user is in range attack and not moving, set dir to target
2329 if( isUser() )
2331 if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK && _Mode==MBEHAV::COMBAT && !UserControls.isMoving())
2333 dir( dirToTarget() );
2335 float frontYawBefore = frontYaw();
2336 front(dir());
2337 float frontYawAfter = frontYaw();
2338 float deltaYaw= frontYawAfter - frontYawBefore;
2339 UserControls.appendCameraDeltaYaw(-deltaYaw);
2341 if( ClientCfg.AutomaticCamera )
2343 UserControls.resetSmoothCameraDeltaYaw();
2348 // If the next mode in the automaton != Current Mode
2349 if(_CurrentState->NextMode != _Mode)
2351 // Undo previous behaviour
2352 if (_Mode == MBEHAV::DEATH)
2354 // Restore collisions.
2355 if (_Primitive)
2357 // TODO: Without this dynamic cast
2358 if (dynamic_cast<CPlayerCL *>(this))
2359 _Primitive->setOcclusionMask(MaskColPlayer);
2360 else
2361 _Primitive->setOcclusionMask(MaskColNpc);
2365 if (ClientCfg.UsePACSForAll && _Primitive)
2366 _Primitive->setCollisionMask(MaskColNone);
2368 //// ADDED ////
2369 switch(_CurrentState->NextMode)
2371 // Combat
2372 case MBEHAV::COMBAT:
2373 case MBEHAV::COMBAT_FLOAT:
2374 if(ClientCfg.UsePACSForAll && _Primitive)
2375 _Primitive->setCollisionMask(MaskColPlayer | MaskColNpc | MaskColDoor); // Collision with player and npc.
2376 break;
2377 // Mount
2378 case MBEHAV::MOUNT_NORMAL:
2379 parent(_Mount);
2380 // Remove collisions if needed.
2381 if(_Mount != CLFECOMMON::INVALID_SLOT)
2383 if(_Primitive)
2384 _Primitive->setOcclusionMask(MaskColNone);
2386 break;
2387 // Death
2388 case MBEHAV::DEATH:
2389 setDead();
2390 break;
2391 // UNKNOWN
2392 case MBEHAV::UNKNOWN_MODE:
2393 if(ClientCfg.Check)
2394 nlstop;
2395 // NO BREAK is Normal here
2396 // Normal
2397 case MBEHAV::NORMAL:
2398 // _Mount = CLFECOMMON::INVALID_SLOT;
2399 // _Rider = CLFECOMMON::INVALID_SLOT;
2400 parent(CLFECOMMON::INVALID_SLOT);
2401 dir(front());
2403 front(CVector(1.f, 0.f, 0.f));
2404 dir(front());
2406 break;
2407 default:
2408 break;
2410 // Change the current mode.
2411 if ( _ModeWanted != MBEHAV::UNKNOWN_MODE )
2413 _Mode = _CurrentState->NextMode;
2415 //else
2416 // nlinfo( "NO mode change from %u to %u, %s S%hu", _Mode, _CurrentState->NextMode, _SheetId.toString().c_str(), _Slot );
2417 // Change the current automaton.
2418 computeAutomaton();
2419 // Update the animation set according to the new automaton.
2420 computeAnimSet();
2422 // Select the Default Next Animation.
2423 setAnim(_CurrentState->NextState);
2424 }// endAnimTransition //
2426 //-----------------------------------------------
2427 // onMove :
2428 //-----------------------------------------------
2429 CCharacterCL::TOnMove CCharacterCL::onMove(const CAutomatonStateSheet &curAnimState)
2431 // Animation is breakable if the distance to destination is long enough (at least > 0).
2432 if(curAnimState.BreakableOnMove && dist2Dest()>0.0)
2434 // \todo GUIGUI : take the next position to current one (it could be possible this position was the same as the first).
2435 CVectorD dirToFirstPos = _FirstPos-pos();
2436 dirToFirstPos.z = 0.0;
2437 if(dirToFirstPos != CVectorD::Null)
2438 dirToFirstPos.normalize();
2439 else
2441 nlwarning("CH:onMove:%d: First pos == pos -> take the dir(%f,%f,%f) instead.", _Slot, dir().x, dir().y, dir().z);
2442 dirToFirstPos = dir();
2445 // Compute Angle between the front and the first position.
2446 double angToDest = computeShortestAngle(atan2(front().y, front().x), atan2(dirToFirstPos.y, dirToFirstPos.x));
2447 if(!_MaxLoop)
2449 // Strafe Left
2450 if(curAnimState.OnMoveLeft != CAnimationStateSheet::UnknownState)
2451 if((angToDest>Pi/3.0) && (angToDest<2.0*Pi/3.0))
2452 return OnMoveLeft;
2453 // Strafe Right
2454 if(curAnimState.OnMoveRight != CAnimationStateSheet::UnknownState)
2455 if((angToDest<-Pi/3.0) && (angToDest>-2.0*Pi/3.0))
2456 return OnMoveRight;
2457 // Backward
2458 if(curAnimState.OnMoveBackward != CAnimationStateSheet::UnknownState)
2459 if(fabs(angToDest)>1.92)
2460 return OnMoveBackward;
2462 // Forward (default)
2463 if(curAnimState.OnMoveForward != CAnimationStateSheet::UnknownState)
2465 // if(_MaxLoop || fabs(angToDest)<=1.92)
2466 return OnMoveForward;
2468 else
2469 nlwarning("CH:onMove:%d: the current state is breakable on Move and the dist to dest is not Null, but there is no animation to move.", _Slot);
2471 // No Move
2472 return OnMoveNone;
2473 }// onMove //
2475 //-----------------------------------------------
2476 // onRotation :
2477 //-----------------------------------------------
2478 CCharacterCL::TOnRot CCharacterCL::onRotation(const CAutomatonStateSheet &curAnimState, CVector &dirEndA)
2480 // Turn and About Face
2481 if(curAnimState.BreakableOnRotation)
2483 CVector bodyDir = dir();
2484 CVector destDir = front();
2486 // when user is attacking, his dest is his target
2487 if( isUser() && _CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK && _Mode==MBEHAV::COMBAT && !UserControls.isMoving() )
2489 destDir = dirToTarget();
2492 // Compute the angle between the current heading and computed heading.
2493 double angToDest = computeShortestAngle(atan2(bodyDir.y, bodyDir.x), atan2(destDir.y, destDir.x));
2494 // Rotation to the left.
2495 if(angToDest >= (ClientCfg.AnimatedAngleThreshold*Pi/180.0))
2497 dirEndA = destDir;
2498 return OnRotLeft;
2500 // Rotation to the right.
2501 if(angToDest <= -(ClientCfg.AnimatedAngleThreshold*Pi/180.0))
2503 dirEndA = destDir;
2504 return OnRotRight;
2507 // No Rot
2508 return OnRotNone;
2509 }// onRotation //
2511 //-----------------------------------------------
2512 // onBigBend :
2513 //-----------------------------------------------
2514 CCharacterCL::TOnBigBend CCharacterCL::onBigBend(const CAutomatonStateSheet &curAnimState, CVector &dirEndA)
2516 // If the current direction is too different of the direction to the first destination -> play a rotation.
2517 if(curAnimState.BrkOnBigBend)
2519 CVector dirToFirstPos;
2520 if(setVect(dirToFirstPos, _FirstPos - pos(), true, false))
2522 dirToFirstPos = curAnimState.getMatrix()*dirToFirstPos;
2523 double angToDest = computeShortestAngle(atan2(dir().y, dir().x), atan2(dirToFirstPos.y, dirToFirstPos.x));
2524 // Rotation to the left.
2525 if(angToDest >= 1.5)
2527 dirEndA = dirToFirstPos;
2528 return OnBendLeft;
2530 // Rotation to the right.
2531 if(angToDest <= -1.5)
2533 dirEndA = dirToFirstPos;
2534 return OnBendRight;
2536 // Smooth dir
2537 if(fabs(angToDest) > 0.1)
2539 double newAngle = atan2(dir().y, dir().x) + angToDest/2.0;
2540 dir(CVector((float)cos(newAngle), (float)sin(newAngle), dir().z));
2542 else
2543 dir(dirToFirstPos);
2546 // No Bend
2547 return OnBendNone;
2548 }// onBigBend //
2550 //-----------------------------------------------
2551 // onBadHeading :
2552 //-----------------------------------------------
2553 bool CCharacterCL::onBadHeading(const CAutomatonStateSheet &curAnimState)
2555 // Bad Heading
2556 if(curAnimState.BreakableOnBadHeading && !_MaxLoop)
2558 CVector dirToFirstPos;
2559 if(setVect(dirToFirstPos, _FirstPos - pos(), true, false))
2561 // Compute the angle between the front and the next body direction.
2562 double angToDest = computeShortestAngle(atan2(front().y, front().x), atan2(dirToFirstPos.y, dirToFirstPos.x));
2563 if(curAnimState.BadHeadingMin <= curAnimState.BadHeadingMax)
2565 if((angToDest<curAnimState.BadHeadingMin) || (angToDest>curAnimState.BadHeadingMax))
2566 return true;
2568 else
2570 if((angToDest<curAnimState.BadHeadingMin) && (angToDest>curAnimState.BadHeadingMax))
2571 return true;
2575 // No bad Heading
2576 return false;
2577 }// onBadHeading //
2579 //-----------------------------------------------
2580 // setAnim :
2581 // Select a new animation for the entity.
2582 // \todo GUIGUI : better manage when there is no skeleton
2583 // \todo GUIGUI : simplify head control
2584 //-----------------------------------------------
2585 ADD_METHOD(void CCharacterCL::setAnim(TAnimStateKey newKey, TAnimStateKey subKey, uint animID))
2586 if(ClientCfg.Light)
2587 return;
2589 // Debug Animation for the target
2590 if(VerboseAnimSelection && _Slot == UserEntity->selection())
2591 nlinfo("CH:setAnim:%d: state '%s'.", _Slot, CAnimationState::getAnimationStateName (newKey).c_str());
2594 // RELEASE THE OLD ANIMATION
2595 TAnimStateId lastAnimStateId = animState(MOVE); // Bkup the current anim state Id.
2597 // \todo GUIGUI : Faire une fonction pour savoir si on a changer de type d'animation.
2598 // Reset loop counter
2599 if((newKey != animState(MOVE)) || (_OldAutomaton != _CurrentAutomaton))
2600 _NbLoopAnim = 0;
2602 // RESET THE CURRENT ANIMATION
2603 _HideSkin = false;
2604 CAnimation::TAnimId buIndex = animIndex(MOVE);
2605 animIndex (MOVE, CAnimation::UnknownAnim); // Reset the current animation Index
2606 animState (MOVE, CAnimationStateSheet::UnknownState); // Reset the current animation state
2607 animOffset(MOVE, 0.0); // Reset the current animation offset
2608 animIndex (MOVE_BLEND_OUT, CAnimation::UnknownAnim); // Reset the current animation Index
2609 animState (MOVE_BLEND_OUT, CAnimationStateSheet::UnknownState); // Reset the current animation state
2610 animOffset(MOVE_BLEND_OUT, 0.0); // Reset the current animation offset
2611 _RotationFactor = 1.0; // New animation -> default rotation factor for the time.
2612 _RightFXActivated = false;
2613 _LeftFXActivated = false;
2614 _RotationFactor = 1.0f; // Rotation factor as the animation for the time.
2615 _SubStateKey = CAnimationStateSheet::UnknownState; // No SubStateKey for the time.
2616 dirEndAnim(dir()); // For the time the direction at the end of the animation is the current direction.
2618 // CHECK
2619 // If there is no animation set ->There is nothing we can do properly.
2620 if(_CurrentAnimSet[MOVE] == 0)
2621 return;
2624 // INITIALIZE
2625 if(!animationStateKey(MOVE, newKey))
2627 nlwarning("CH:setAnim:%d: automaton '%s': animation state key '%s' asked is not valid -> trying Idle.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (newKey).c_str());
2628 if(!animationStateKey(MOVE, CAnimationStateSheet::Idle))
2629 nlwarning("CH:setAnim:%d: Idle is not warking too", _Slot);
2632 // Compute Speed
2633 double speedToDest = computeSpeed();
2634 // Compute the direction to the first position (direction = front if there is not first pos)
2635 CVector dirToFirstPos;
2636 if(dist2FirstPos() > 0.0)
2637 dirToFirstPos = (_FirstPos - pos()).normed();
2638 else
2639 dirToFirstPos = front();
2641 uint antiFreezeCounter = 0;
2642 // Loop until the process find a steady state.
2643 KeyChosen:
2644 // Get the state for the current animation.
2645 const CAutomatonStateSheet *state = EAM->mState(_CurrentAutomaton, animState(MOVE));
2646 if(state == 0)
2648 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot, CAnimationStateSheet::getAnimationStateName(animState(MOVE)).c_str(), _CurrentAutomaton.c_str());
2649 // No animation playing
2650 if(_PlayList)
2651 _PlayList->setAnimation(MOVE, UPlayList::empty);
2652 return;
2654 const CAutomatonStateSheet &curAnimState = *state;
2655 //--------------------//
2656 //--------------------//
2657 // ANTI-FREEZE SYSTEM //
2658 // If too many loop, display some infos
2659 if(antiFreezeCounter > 10)
2662 nlwarning("CH:setAnim:anitFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
2663 nlwarning("CH:setAnim:anitFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
2664 nlwarning("CH:setAnim:anitFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
2665 nlwarning("CH:setAnim:anitFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
2666 nlwarning("CH:setAnim:anitFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
2667 nlwarning("CH:setAnim:anitFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationStateSheet::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
2669 // Once too many more time reached, leave the method.
2670 if(antiFreezeCounter > 20)
2671 return;
2673 // Update antiFreezeCounter.
2674 ++antiFreezeCounter;
2675 // ANTI-FREEZE SYSTEM //
2676 //--------------------//
2677 //--------------------//
2678 // Is there a mode in the queue
2679 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
2681 // Is the current mode not already the mode wanted.
2682 if((_Mode != _ModeWanted) && (dist2Dest()==INVALID_DIST))
2684 TAnimStateKey transition;
2685 // Check Default Mode Connection.
2686 if(curAnimState.getModeConnection(_ModeWanted, transition))
2688 // Mode Mount need to be synchro
2689 if(_ModeWanted == MBEHAV::MOUNT_NORMAL || _ModeWanted == MBEHAV::MOUNT_SWIM)
2691 // The Mount
2692 if(_Rider != CLFECOMMON::INVALID_SLOT)
2694 if(animationStateKey(MOVE, transition))
2695 goto KeyChosen;
2696 else
2697 nlwarning("CH:setAnim:%d: Mode Wanted '%d' : automaton '%s': state '%s': Transition '%s' is not valid.", _Slot, _ModeWanted, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName (transition).c_str());
2699 // The Rider
2700 else if(_Mount != CLFECOMMON::INVALID_SLOT)
2702 CEntityCL *mountTmp = EntitiesMngr.entity(_Mount);
2703 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(mountTmp);
2704 if(mount)
2706 if(mountTmp->mode() == _ModeWanted)
2708 if(animationStateKey(MOVE, transition))
2709 goto KeyChosen;
2710 else
2712 nlwarning("CH:setAnim:%d: automaton '%s': state '%s': MountDefaultModeTransition '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName (transition).c_str());
2716 else
2718 nlwarning("CH:setAnim:%d: Mode Wanted '%s' but the mount does not exist.", _Slot, MBEHAV::modeToString(_ModeWanted).c_str());
2722 // Other modes have the same code.
2723 else
2725 if(animationStateKey(MOVE, transition))
2726 goto KeyChosen;
2727 else
2728 nlwarning("CH:setAnim:%d: Mode Wanted '%d' : automaton '%s': state '%s': Transition '%s' is not valid.", _Slot, _ModeWanted, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName (transition).c_str());
2735 // Should we stop the animation once at destination.
2736 if(curAnimState.BrkAtDest && (dist2Dest() <= ClientCfg.DestThreshold))
2738 _MaxLoop = false;
2739 if(animationStateKey(MOVE, CAnimationStateSheet::Idle))
2740 goto KeyChosen;
2741 else
2742 nlwarning("CH:setAnim:%d: automaton '%s': state '%s': BrkAtDest 'idle' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str());
2745 // On Move
2746 switch(onMove(curAnimState))
2748 // On Move Forward
2749 case OnMoveForward:
2750 if(animationStateKey(MOVE, curAnimState.OnMoveForward))
2751 goto KeyChosen;
2752 else
2753 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveForeward '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnMoveForward).c_str());
2754 break;
2755 // On Move Backward
2756 case OnMoveBackward:
2757 if(animationStateKey(MOVE, curAnimState.OnMoveBackward))
2758 goto KeyChosen;
2759 else
2760 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveBackward '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnMoveBackward).c_str());
2761 break;
2762 // On Move Left
2763 case OnMoveLeft:
2764 if(animationStateKey(MOVE, curAnimState.OnMoveLeft))
2765 goto KeyChosen;
2766 else
2767 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveLeft '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnMoveLeft).c_str());
2768 break;
2769 // On Move Right
2770 case OnMoveRight:
2771 if(animationStateKey(MOVE, curAnimState.OnMoveRight))
2772 goto KeyChosen;
2773 else
2774 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnMoveRight '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnMoveRight).c_str());
2775 break;
2776 default:
2777 break;
2780 // On Rotation/About Face
2781 CVector dirEndA(0.0f, 0.0f, 0.0f);
2782 switch(onRotation(curAnimState, dirEndA))
2784 // On Rot Left
2785 case OnRotLeft:
2786 if(animationStateKey(MOVE, curAnimState.OnLeftRotation))
2788 dirEndAnim(dirEndA);
2789 goto KeyChosen;
2791 else
2792 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnLeftRotation '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnLeftRotation).c_str());
2793 break;
2794 // On Rot Right
2795 case OnRotRight:
2796 if(animationStateKey(MOVE, curAnimState.OnRightRotation))
2798 dirEndAnim(dirEndA);
2799 goto KeyChosen;
2801 else
2802 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnRightRotation '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnRightRotation).c_str());
2803 break;
2804 default:
2805 break;
2808 // Max Loop
2809 if(curAnimState.MaxLoop && curAnimState.MaxLoop<=_NbLoopAnim)
2811 if(animationStateKey(MOVE, CAnimationStateSheet::Idle))
2813 _MaxLoop = true;
2814 _NbLoopAnim = 0;
2815 goto KeyChosen;
2819 // On Bad Heading
2820 if(onBadHeading(curAnimState))
2822 if(animationStateKey(MOVE, CAnimationStateSheet::Idle))
2823 goto KeyChosen;
2824 else
2825 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': 'Idle' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str());
2828 // On Big Bend
2829 switch(onBigBend(curAnimState, dirEndA))
2831 // On Bend Left
2832 case OnBendLeft:
2833 if(animationStateKey(MOVE, curAnimState.OnBigBendLeft))
2835 dirEndAnim(dirEndA);
2836 goto KeyChosen;
2838 else
2839 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnBigBendLeft '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnBigBendLeft).c_str());
2840 break;
2841 // On Bend Right
2842 case OnBendRight:
2843 if(animationStateKey(MOVE, curAnimState.OnBigBendRight))
2845 dirEndAnim(dirEndA);
2846 goto KeyChosen;
2848 else
2849 nlwarning("CH::setAnim:%d: automaton '%s': state '%s': OnBigBendRight '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName(curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName(curAnimState.OnBigBendRight).c_str());
2850 break;
2851 default:
2852 break;
2855 // If the animation change according to a high speed and speed high enough or oo.
2856 if(ClientCfg.BlendForward == false)
2858 if(curAnimState.OnMaxSpeed.Breakable)
2860 if(speedToDest >= _CurrentAnimSet[MOVE]->speedToRun() || speedToDest == -1.0)
2862 if(animationStateKey(MOVE, curAnimState.OnMaxSpeed.NextStateKey))
2863 goto KeyChosen;
2864 else
2865 nlwarning("CH::setAnim: Char '%d': automaton '%s': state '%s': OnMaxSpeed.NextStateKey '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName (curAnimState.OnMaxSpeed.NextStateKey).c_str());
2870 // If the animation can change with a low speed.
2871 if(curAnimState.OnMinSpeed.Breakable)
2873 // If the speed is low enough (check this is not -1 (infinite speed)) -> update the animation to play.
2874 if(speedToDest != -1.0 && speedToDest <= _CurrentAnimSet[MOVE]->speedToWalk())
2876 if(animationStateKey(MOVE, curAnimState.OnMinSpeed.NextStateKey))
2877 goto KeyChosen;
2878 else
2879 nlwarning("CH::setAnim: Char '%d': automaton '%s': state '%s': OnMinSpeed.NextStateKey '%s' is not valid.", _Slot, _CurrentAutomaton.c_str(), CAnimationState::getAnimationStateName (curAnimState.MoveState).c_str(), CAnimationState::getAnimationStateName (curAnimState.OnMinSpeed.NextStateKey).c_str());
2882 //// END LOOP /////
2885 // \todo GUIGUI : better manage automate change
2886 // Current animation is not of the same kind as the old one.
2887 bool sameAnim = lastAnimStateId == animState(MOVE) && _OldAutomaton == _CurrentAutomaton;
2888 if(!sameAnim)
2890 // Stop FXs.
2891 stopItemAttackFXs();
2892 stopAttachedFXForCurrrentAnim(true); // stop all anim fx, including looping fxs
2894 else
2896 stopAttachedFXForCurrrentAnim(false); // stop all anim fxs, but do not stop looping fxs (because the same anim is repeating)
2899 // Compute the current animation state.
2900 _CurrentState = EAM->mState(_CurrentAutomaton, animState(MOVE));
2901 // If the state does not exist.
2902 if(_CurrentState == 0)
2904 nlwarning("CH:setAnim:%d: State '%s' not in the automaton '%s'.", _Slot, CAnimationStateSheet::getAnimationStateName (animState(MOVE)).c_str(), _CurrentAutomaton.c_str());
2906 // No animation playing
2907 if(_PlayList)
2908 _PlayList->setAnimation(MOVE, UPlayList::empty);
2910 // The state is valid.
2911 else
2913 double angToDest = 0.0;
2914 // If the new state is a rotation.
2915 if(_CurrentState->Rotation)
2916 angToDest = computeShortestAngle(atan2(dir().y, dir().x), atan2(dirEndAnim().y, dirEndAnim().x));
2917 // Get the right animation state and choose an animation.
2919 // Get the animation state
2920 const CAnimationState *animationState = 0;
2921 if(animState(MOVE) == CAnimationStateSheet::Emote)
2923 _SubStateKey = subKey;
2924 animationState = _CurrentAnimSet[MOVE]->getAnimationState(_SubStateKey);
2926 else
2927 animationState = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
2928 if(animationState)
2930 // Choose the animation
2931 CAnimation::TAnimId index;
2932 if(sameAnim)
2933 index = buIndex;
2934 else
2935 index = CAnimation::UnknownAnim;
2936 animIndex(MOVE, animationState->chooseAnim(_AnimJobSpecialisation, people(), getGender(), angToDest, index));
2937 if(animID != NL3D::UAnimationSet::NotFound)
2938 animId(MOVE, animID);
2941 // Should the objects in hands be displayed ?
2942 _ObjectsVisible = animationState->areObjectsVisible();
2943 showOrHideBodyParts( _ObjectsVisible );
2945 // in case of user manage the internal view
2946 if( isUser() )
2948 UserEntity->updateVisualDisplay();
2952 // Initialize the animation
2953 if(animIndex(MOVE) != CAnimation::UnknownAnim)
2955 // If the new state is a rotation.
2956 if(_CurrentState->Rotation)
2958 // Get the animation rotation.
2959 double animAngle = CAnimationMisc::getAnimationRotation(EAM->getAnimationSet(), animId(MOVE));
2960 // Compute the rotation factor.
2961 if(animAngle != 0.0)
2962 _RotationFactor = fabs(angToDest/animAngle);
2963 else
2964 _RotationFactor = -1.0; // \todo GUIGUI : see which value we should use if we have a rot anim without rot and which should rotate character
2967 // If the animation is an atk or forage extraction -> Start all dynamic FXs.
2968 if (_CurrentBehaviour.Behaviour == MBEHAV::EXTRACTING)
2970 // True Extract Animation only for Use AnimationState type
2971 // \todo yoyo: ugly?
2972 if( animState(MOVE)==CAnimationStateSheet::UseLoop )
2974 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(_CurrentBehaviour.ForageExtraction.Level);
2975 startItemAttackFXs(true, 1);
2978 else if (_CurrentState->Attack)
2980 if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK)
2982 startItemAttackFXs(_CurrentBehaviour.Range.ImpactIntensity != 0, _CurrentBehaviour.Range.ImpactIntensity);
2984 else
2986 startItemAttackFXs(_CurrentBehaviour.Combat.ImpactIntensity != 0 && _CurrentBehaviour.Combat.HitType != HITTYPE::Failed, _CurrentBehaviour.Combat.ImpactIntensity);
2991 // Initialize the new animation.
2992 if(_PlayList)
2994 // Blend the last animation and the new one.
2995 if(ClientCfg.BlendFrameNumber && _BlendRemaining <= 0 // Blend On ?
2996 && animIndex(ACTION) != CAnimation::UnknownAnim // Last Animation Valid ?
2997 && ((lastAnimStateId != animState(MOVE)) || (_OldAutomaton != _CurrentAutomaton)
2998 || (animState(MOVE) == CAnimationStateSheet::Emote)) // Last anim != or automaton != ?
2999 && !isRiding()) // No Blend on a mount. \todo GUIGUI trouver un meilleur moyen.
3001 // Get the rotation of the last animation for the blend.
3002 if(!_Skeleton.empty())
3003 _OldRotQuat = _Skeleton.getRotQuat();
3004 _BlendRemaining = ClientCfg.BlendFrameNumber;
3005 // Set animation
3006 _PlayList->setAnimation(ACTION, animId(ACTION));
3007 // Compute weight step.
3008 float w = 1.f/((float)(ClientCfg.BlendFrameNumber+1));
3009 // Set Old Anim Weight.
3010 _PlayList->setWeight(ACTION, 1.f-w);
3011 // Set New Anim Weight.
3012 _PlayList->setWeight(MOVE, w);
3013 // Copy Reverse
3014 _AnimReversed[ACTION] = _AnimReversed[MOVE];
3016 // Set Animation.
3017 _PlayList->setAnimation (MOVE, animId(MOVE));
3018 // Blend between Walk and Run.
3019 if(ClientCfg.BlendForward && (animState(MOVE) == CAnimationStateSheet::Walk))
3021 _CurrentAnimSet[MOVE_BLEND_OUT] = _CurrentAnimSet[MOVE];
3022 animState(MOVE_BLEND_OUT, CAnimationStateSheet::Run);
3023 const CAnimationState *animationBlendState = _CurrentAnimSet[MOVE_BLEND_OUT]->getAnimationState(animState(MOVE_BLEND_OUT));
3024 if(animationBlendState)
3026 animIndex(MOVE_BLEND_OUT, animationBlendState->chooseAnim(_AnimJobSpecialisation, people(), getGender()));
3027 _PlayList->setAnimation(MOVE_BLEND_OUT, animId(MOVE_BLEND_OUT));
3028 _PlayList->setWeight(MOVE_BLEND_OUT, 1.0f); // \todo GUIGUI : verify what is happening if animId is "empty".
3030 else
3031 nlwarning("setAnim:%d: animationBlendState is Null.", _Slot);
3034 // Set children animation.
3035 std::list<CEntityCL *>::iterator it = _Children.begin();
3036 while(it != _Children.end())
3038 if(*it)
3040 CCharacterCL *child = dynamic_cast<CCharacterCL *>(*it);
3041 if(child)
3043 // Set the Child as the parent.
3044 child->computeAnimSet();
3045 child->currentAutomaton() = _CurrentAutomaton; // \todo GUIGUI : CA VA PAS MARCHER A CAUSE DU TYPE !!!
3046 child->animOffset(MOVE, animOffset(MOVE));
3047 child->animState(MOVE, animState(MOVE));
3048 child->currentState(currentState());
3049 child->animIndex(MOVE, CAnimation::UnknownAnim);
3050 const CAnimationState *animStatePtr = child->currentAnimSet()[MOVE]->getAnimationState(child->animState(MOVE));
3051 if(animStatePtr)
3053 child->animIndex(MOVE, animStatePtr->chooseAnim(_AnimJobSpecialisation, people(), getGender(), angToDest));
3054 child->playList()->setAnimation(MOVE, child->animId(MOVE));
3056 // TEMP : \todo GUIGUI : Pour le moment on enlever le Blend sur les montures.
3058 child->playList()->setAnimation(ACTION, UPlayList::empty);
3059 child->currentAnimSet()[ACTION] = child->currentAnimSet()[MOVE];
3060 child->animState (ACTION, child->animState (MOVE));
3061 child->animIndex (ACTION, child->animIndex (MOVE));
3062 child->animOffset(ACTION, child->animOffset(MOVE));
3066 else
3067 nlwarning("CH:setAnim:%d: Child is not a 'CCharacterCL'.", _Slot);
3069 else
3070 nlwarning("CH:setAnim:%d: Child not allocated.", _Slot);
3072 // Next Child
3073 ++it;
3077 // Get the animation.
3078 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState((animState(MOVE) == CAnimationStateSheet::Emote)?subKey:animState(MOVE));
3079 if(animStatePtr)
3081 const CAnimation *anim = animStatePtr->getAnimation(animIndex(MOVE));
3082 if(anim)
3084 _HideSkin = anim->hideAtEndAnim();
3085 // Reverse ?
3086 _AnimReversed[MOVE] = anim->isReverse();
3087 // Is the head controlable.
3088 _TargetAnimCtrl.Enabled = anim->headControlable();
3089 // Select the sound ID
3090 _SoundId[MOVE] = anim->soundId();
3091 // look in behaviour if there's a spell to play
3092 if (_CurrentAttack)
3094 if (_CurrentAttackInfo.Intensity >= 1 && _CurrentAttackInfo.Intensity <= MAGICFX::NUM_SPELL_POWER)
3096 MAGICFX::TSpellCastStage attackStage;
3097 switch(newKey)
3099 case CAnimationStateSheet::OffensiveCastBegin:
3100 case CAnimationStateSheet::CurativeCastBegin:
3101 case CAnimationStateSheet::MixedCastBegin:
3102 case CAnimationStateSheet::AcidCastInit:
3103 case CAnimationStateSheet::BlindCastInit:
3104 case CAnimationStateSheet::ColdCastInit:
3105 case CAnimationStateSheet::ElecCastInit:
3106 case CAnimationStateSheet::FearCastInit:
3107 case CAnimationStateSheet::FireCastInit:
3108 case CAnimationStateSheet::HealHPCastInit:
3109 case CAnimationStateSheet::MadCastInit:
3110 case CAnimationStateSheet::PoisonCastInit:
3111 case CAnimationStateSheet::RootCastInit:
3112 case CAnimationStateSheet::RotCastInit:
3113 case CAnimationStateSheet::ShockCastInit:
3114 case CAnimationStateSheet::SleepCastInit:
3115 case CAnimationStateSheet::SlowCastInit:
3116 case CAnimationStateSheet::StunCastInit:
3117 attackStage = MAGICFX::CastBegin;
3118 break;
3119 case CAnimationStateSheet::OffensiveCastLoop:
3120 case CAnimationStateSheet::CurativeCastLoop:
3121 case CAnimationStateSheet::MixedCastLoop:
3122 case CAnimationStateSheet::AcidCastLoop:
3123 case CAnimationStateSheet::BlindCastLoop:
3124 case CAnimationStateSheet::ColdCastLoop:
3125 case CAnimationStateSheet::ElecCastLoop:
3126 case CAnimationStateSheet::FearCastLoop:
3127 case CAnimationStateSheet::FireCastLoop:
3128 case CAnimationStateSheet::HealHPCastLoop:
3129 case CAnimationStateSheet::MadCastLoop:
3130 case CAnimationStateSheet::PoisonCastLoop:
3131 case CAnimationStateSheet::RootCastLoop:
3132 case CAnimationStateSheet::RotCastLoop:
3133 case CAnimationStateSheet::ShockCastLoop:
3134 case CAnimationStateSheet::SleepCastLoop:
3135 case CAnimationStateSheet::SlowCastLoop:
3136 case CAnimationStateSheet::StunCastLoop:
3137 attackStage = MAGICFX::CastLoop;
3138 break;
3139 case CAnimationStateSheet::OffensiveCastSuccess:
3140 case CAnimationStateSheet::CurativeCastSuccess:
3141 case CAnimationStateSheet::MixedCastSuccess:
3142 case CAnimationStateSheet::AcidCastEnd:
3143 case CAnimationStateSheet::BlindCastEnd:
3144 case CAnimationStateSheet::ColdCastEnd:
3145 case CAnimationStateSheet::ElecCastEnd:
3146 case CAnimationStateSheet::FearCastEnd:
3147 case CAnimationStateSheet::FireCastEnd:
3148 case CAnimationStateSheet::HealHPCastEnd:
3149 case CAnimationStateSheet::MadCastEnd:
3150 case CAnimationStateSheet::PoisonCastEnd:
3151 case CAnimationStateSheet::RootCastEnd:
3152 case CAnimationStateSheet::RotCastEnd:
3153 case CAnimationStateSheet::ShockCastEnd:
3154 case CAnimationStateSheet::SleepCastEnd:
3155 case CAnimationStateSheet::SlowCastEnd:
3156 case CAnimationStateSheet::StunCastEnd:
3157 attackStage = MAGICFX::CastEnd;
3158 break;
3159 case CAnimationStateSheet::OffensiveCastFail:
3160 case CAnimationStateSheet::OffensiveCastFumble:
3161 case CAnimationStateSheet::CurativeCastFail:
3162 case CAnimationStateSheet::CurativeCastFumble:
3163 case CAnimationStateSheet::MixedCastFail:
3164 case CAnimationStateSheet::MixedCastFumble:
3165 case CAnimationStateSheet::AcidCastFail:
3166 case CAnimationStateSheet::BlindCastFail:
3167 case CAnimationStateSheet::ColdCastFail:
3168 case CAnimationStateSheet::ElecCastFail:
3169 case CAnimationStateSheet::FearCastFail:
3170 case CAnimationStateSheet::FireCastFail:
3171 case CAnimationStateSheet::HealHPCastFail:
3172 case CAnimationStateSheet::MadCastFail:
3173 case CAnimationStateSheet::PoisonCastFail:
3174 case CAnimationStateSheet::RootCastFail:
3175 case CAnimationStateSheet::RotCastFail:
3176 case CAnimationStateSheet::ShockCastFail:
3177 case CAnimationStateSheet::SleepCastFail:
3178 case CAnimationStateSheet::SlowCastFail:
3179 case CAnimationStateSheet::StunCastFail:
3180 attackStage = MAGICFX::CastFail;
3181 break;
3182 // attacks
3183 case CAnimationStateSheet::DefaultAtkLow:
3184 case CAnimationStateSheet::DefaultAtkMiddle:
3185 case CAnimationStateSheet::DefaultAtkHigh:
3186 case CAnimationStateSheet::PowerfulAtkLow:
3187 case CAnimationStateSheet::PowerfulAtkMiddle:
3188 case CAnimationStateSheet::PowerfulAtkHigh:
3189 case CAnimationStateSheet::AreaAtkLow:
3190 case CAnimationStateSheet::AreaAtkMiddle:
3191 case CAnimationStateSheet::AreaAtkHigh:
3192 case CAnimationStateSheet::Attack1:
3193 case CAnimationStateSheet::Attack2:
3194 case CAnimationStateSheet::FirstPersonAttack:
3195 attackStage = MAGICFX::CastEnd;
3196 break;
3197 default:
3198 attackStage = MAGICFX::SpellCastStageCount;
3199 break;
3201 const CAnimationFXSet *afs = NULL;
3202 switch(attackStage)
3204 case MAGICFX::CastBegin: afs = &_CurrentAttack->AttackBeginFX; break;
3205 case MAGICFX::CastLoop: afs = &_CurrentAttack->AttackLoopFX; break;
3206 case MAGICFX::CastEnd: afs = &_CurrentAttack->AttackEndFX; break;
3207 case MAGICFX::CastFail: afs = &_CurrentAttack->AttackFailFX; break;
3208 default: break;
3210 playCastFX(afs, _CurrentAttackInfo.Intensity);
3213 // start forage prospection anim fx(s)
3214 if ( (behaviour() == MBEHAV::PROSPECTING) || (behaviour() == MBEHAV::PROSPECTING_END) )
3216 const CAnimationFXSet& fxSet = anim->getFXSet();
3217 std::vector<UParticleSystemInstance> fxInstances;
3218 CAttachedFX::CBuildInfo bi;
3219 CAttachedFX::CTargeterInfo ti;
3220 for (uint k = 0; k < fxSet.FX.size(); ++k)
3222 CAttachedFX::TSmartPtr fx = new CAttachedFX;
3223 bi.Sheet = &(fxSet.FX[k]);
3224 fx->create(*this, bi, ti);
3225 if (!fx->FX.empty())
3228 attachFXInternal(fx,FXListCurrentAnim);
3229 if (k == 0)
3231 // Set user param for size of prospection area
3233 fx->FX.setUserParam( 0, ((float)(_CurrentBehaviour.ForageProspection.Range)) / 127.0f );
3234 float up [3];
3235 switch ( _CurrentBehaviour.ForageProspection.Angle )
3237 case 0: up[2] = up[1] = up[0] = 0.0f; break;
3238 case 1: up[0]=1.0f; up[1]=0.0f; up[2]=0.0f; break;
3239 case 2: up[0]=1.0f; up[1]=1.0f; up[2]=0.0f; break;
3240 default: up[0]=1.0f; up[1]=1.0f; up[2]=1.0f; break;
3242 fx->FX.setUserParam( 1, up[0] );
3243 fx->FX.setUserParam( 2, up[1] );
3244 fx->FX.setUserParam( 3, up[2] );
3245 //nlinfo( "Prospection %s %u %u", behaviour()==MBEHAV::PROSPECTING?"PROSPTG":(behaviour()==MBEHAV::PROSPECTING_END?"PRO_END":"OTHER"), _CurrentBehaviour.ForageProspection.Range, _CurrentBehaviour.ForageProspection.Angle );
3248 else if (k == 1)
3250 // Set user param for level of prospection
3251 float up [4];
3252 switch ( _CurrentBehaviour.ForageProspection.Level )
3254 case 0: up[3] = up[2] = up[1] = up[0] = 0.0f; break;
3255 case 1: up[0]=0.0f; up[1]=1.0f; up[2]=0.0f; up[3]=0.0f; break;
3256 case 2: up[0]=1.0f; up[1]=1.0f; up[2]=0.0f; up[3]=0.0f; break;
3257 case 3: up[0]=1.0f; up[1]=1.0f; up[2]=1.0f; up[3]=0.0f; break;
3258 default: up[3] = up[2] = up[1] = up[0] = 1.0f; break;
3260 fx->FX.setUserParam( 0, up[0] );
3261 fx->FX.setUserParam( 1, up[1] );
3262 fx->FX.setUserParam( 2, up[2] );
3263 fx->FX.setUserParam( 3, up[3] );
3268 // start standard anim fx
3269 else
3271 CAttachedFX::CBuildInfo bi;
3272 CAttachedFX::CTargeterInfo ti;
3273 bi.MaxNumAnimCount = MAX_FX_ANIM_COUNT;
3274 for (uint k = 0; k < anim->getFXSet().FX.size(); ++k)
3276 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
3277 if (anim->getFXSet().FX[k].Sheet->RepeatMode == CAnimationFXSheet::Loop && sameAnim) continue;
3278 CAttachedFX::TSmartPtr fx = new CAttachedFX;
3279 bi.Sheet = &(anim->getFXSet().FX[k]);
3280 //bi.StickMode = &bi.Sheet->FX[k].StickMode;
3281 if (anim->getFXSet().FX[k].Sheet)
3283 bi.StickMode = &(anim->getFXSet().FX[k].Sheet->StickMode);
3285 fx->create(*this, bi, ti);
3286 if (!fx->FX.empty())
3288 attachFXInternal(fx, FXListAuto);
3294 else
3295 nlwarning("CH:setAnim:%d: cannot get the pointer on the animation.", _Slot);
3297 // No animation found -> check if it is just a transition or is there really an animation missing
3298 else
3300 // INFO : Verbose mode for Animations of the selection.
3301 if(VerboseAnimSelection && _Slot == UserEntity->selection())
3302 nlinfo("CH:setAnim:%d: Anim Not Found, state '%s'.", _Slot, CAnimationState::getAnimationStateName(animState(MOVE)).c_str());
3304 // If the next animation or automaton is not the same -> this is a transition -> no animation needed.
3305 if(_CurrentState->MoveState != _CurrentState->NextState || (_CurrentState->NextMode != _Mode))
3307 // Choose the next animation.
3308 endAnimTransition();
3310 // Else -> Animation Missing.
3313 // Set the LOD Animation.
3314 setAnimLOD(((lastAnimStateId != animState(MOVE)) || (_OldAutomaton != _CurrentAutomaton)));
3315 }// setAnim //
3318 //-----------------------------------------------
3319 // playCastFX
3320 //-----------------------------------------------
3321 void CCharacterCL::playCastFX(const CAnimationFXSet *afs, uint power)
3323 if (!afs) return;
3324 if (power <= 0 || power > 5) return;
3325 static const float castFXUserParams[MAGICFX::NUM_SPELL_POWER][4] =
3327 { 0.f, 0.f, 0.f, 0.f},
3328 { 1.f, 0.f, 0.f, 0.f},
3329 { 1.f, 1.f, 0.f, 0.f},
3330 { 1.f, 1.f, 1.f, 0.f},
3331 { 1.f, 1.f, 1.f, 1.f}
3333 CAttachedFX::CBuildInfo bi;
3334 CAttachedFX::CTargeterInfo ti;
3335 for (uint k = 0; k < afs->FX.size(); ++k)
3337 CAttachedFX::TSmartPtr fx = new CAttachedFX;
3338 bi.Sheet = &afs->FX[k];
3339 fx->create(*this, bi, ti);
3340 if (!fx->FX.empty())
3342 for(uint l = 0; l < 4; ++l)
3344 fx->FX.setUserParam(l, castFXUserParams[power - 1][l]);
3346 attachFXInternal(fx, FXListCurrentAnim);
3352 //-----------------------------------------------
3353 // showOrHideBodyParts
3354 //-----------------------------------------------
3355 void CCharacterCL::showOrHideBodyParts( bool objectsVisible )
3357 // UnHide all user body parts.
3358 for(uint i=0; i<_Instances.size(); ++i)
3359 if(!_Instances[i].Current.empty())
3360 _Instances[i].Current.show();
3362 // hide or show the face
3363 if( _Items[SLOTTYPE::HEAD_SLOT].Sheet && _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family == ITEMFAMILY::ARMOR )
3365 // Get the face
3366 SInstanceCL *face = getFace ();
3368 // hide if helmet
3369 if(face)
3371 if(!face->Current.empty())
3372 face->Current.hide();
3373 else
3374 face->KeepHiddenWhenLoaded = true;
3377 else
3379 // Get the face
3380 SInstanceCL *face = getFace ();
3382 // hide if helmet
3383 if(face)
3385 if(!face->Current.empty())
3386 face->Current.show();
3387 else
3388 face->KeepHiddenWhenLoaded = false;
3392 // get the instance index for right hand and left hand
3393 uint32 rHandInstIdx;
3394 uint32 lHandInstIdx;
3395 if( isPlayer() || isUser() )
3397 rHandInstIdx = SLOTTYPE::RIGHT_HAND_SLOT;
3398 lHandInstIdx = SLOTTYPE::LEFT_HAND_SLOT;
3400 // hide gloves(armor) if player has magician amplifier
3401 if( _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet && (_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->ItemType == ITEM_TYPE::MAGICIAN_STAFF) )
3403 if( !_Instances[SLOTTYPE::HANDS_SLOT].Current.empty() )
3404 _Instances[SLOTTYPE::HANDS_SLOT].Current.hide();
3405 else
3406 _Instances[SLOTTYPE::HANDS_SLOT].KeepHiddenWhenLoaded = true;
3409 else
3411 rHandInstIdx = _RHandInstIdx;
3412 lHandInstIdx = _LHandInstIdx;
3415 // if not already hidden and need to hide :
3416 if( !objectsVisible )
3418 // Right Hand
3419 nlassert(SLOTTYPE::RIGHT_HAND_SLOT < _Items.size() && SLOTTYPE::LEFT_HAND_SLOT < _Items.size());
3420 if(rHandInstIdx<_Instances.size())
3421 if( !(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet && _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->NeverHideWhenEquipped ) )
3422 if(!_Instances[rHandInstIdx].Current.empty())
3424 _Instances[rHandInstIdx].Current.hide();
3425 _Instances[rHandInstIdx].hideStaticFXs();
3427 // Left Hand
3428 if(lHandInstIdx <_Instances.size())
3429 if( !(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet && _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->NeverHideWhenEquipped ) )
3430 if(!_Instances[lHandInstIdx].Current.empty())
3432 _Instances[lHandInstIdx].Current.hide();
3433 _Instances[lHandInstIdx].hideStaticFXs();
3436 else
3438 // Right Hand
3439 if(rHandInstIdx<_Instances.size())
3440 if(!_Instances[rHandInstIdx].Current.empty())
3442 _Instances[rHandInstIdx].Current.show();
3443 _Instances[rHandInstIdx].showStaticFXs();
3445 // Left Hand
3446 if(lHandInstIdx <_Instances.size())
3447 if(!_Instances[lHandInstIdx].Current.empty())
3449 _Instances[lHandInstIdx].Current.show();
3450 _Instances[lHandInstIdx].showStaticFXs();
3455 //-----------------------------------------------
3456 // setAnimLOD :
3457 // Set the LOD animation.
3458 //-----------------------------------------------
3459 ADD_METHOD(void CCharacterCL::setAnimLOD(bool changed))
3460 // reset LOD animation.
3461 _LodCharacterAnimEnabled = false;
3462 // Setup new LOD animation.
3463 if(skeleton())
3465 // if the entity has a lod Character
3466 sint lodId = skeleton()->getLodCharacterShape();
3467 if(lodId >= 0)
3469 // Setup Lod anim.
3470 _LodCharacterAnimEnabled = true;
3471 _LodCharacterMasterAnimSlot = MOVE;
3472 _LodCharacterAnimTimeOffset = 0;
3473 // do complex stuff only if the anim state has really changed.
3474 if(changed)
3476 // get the anim state from the set.
3477 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
3478 // if animStatePtr found.
3479 if(animStatePtr)
3481 // get Lod Character animation Name from the anim state.
3482 const string &lodAnimName = animStatePtr->getLodCharacterAnimation();
3483 // Find the anim in the UScene LodCharacterManager
3484 sint animId = Scene->getCLodAnimIdByName(lodId, lodAnimName);
3485 // if Anim not found, get the "idle" anim, with the Id 0.
3486 if(animId < 0)
3487 animId = 0;
3488 // setup the skeleton.
3489 skeleton()->setLodCharacterAnimId(animId);
3494 }// setAnimLOD //
3497 //-----------------------------------------------
3498 // updateAnimationState :
3499 // \todo GUIGUI : precalculate distance to destination when receiving Stages.
3500 // \todo GUIGUI : improve, we are setting often 'idle' to recompute orientation at the end of animation instead of doing directly the right one.
3501 //-----------------------------------------------
3502 ADD_METHOD(void CCharacterCL::updateAnimationState())
3503 // If the current state is invalid -> return.
3504 if(_CurrentState == 0)
3505 return;
3507 // Get the Animation Length.
3508 double animLength = EAM->getAnimationLength(animId(MOVE));
3509 // Check Anim length
3510 if(animOffset(MOVE) >= animLength)
3512 // Choose the next animation.
3513 endAnimTransition();
3515 // Last animation not finished check for some chances to break current animation.
3516 else
3518 // Check Modes.
3519 // if(_IsThereAMode && (dist2Dest()==INVALID_DIST))
3521 // Is the current mode not already the mode wanted.
3522 if((_Mode != _ModeWanted) && (dist2Dest()==INVALID_DIST))
3524 if((_ModeWanted != MBEHAV::MOUNT_NORMAL && _ModeWanted != MBEHAV::MOUNT_SWIM) || (_Rider != CLFECOMMON::INVALID_SLOT))
3526 // Is there a possible connection with the mode wanted.
3527 TAnimStateKey transition;
3528 if(_CurrentState->getModeConnection(_ModeWanted, transition))
3530 setAnim(transition);
3531 return;
3534 else
3536 //InfoLog->displayRawNL("(%d)_Rider=%d _Mount=%d _ModeWanted=%s _Mode=%s offset=%f length=%f",_Slot,_Rider,_Mount,MBEHAV::modeToString(_ModeWanted).c_str(),MBEHAV::modeToString(_Mode).c_str(),animOffset(MOVE),animLength);
3537 // anti-bug : sometimes _rider is not set yet and we stay in an infinite move
3538 _Rider = _TheoreticalRider;
3543 // Should we stop the animation once at destination.
3544 if(_CurrentState->BrkAtDest && dist2Dest() <= ClientCfg.DestThreshold)
3546 _MaxLoop = false;
3547 setAnim(CAnimationStateSheet::Idle);
3548 return;
3551 // ON MOVE
3552 switch(onMove(*_CurrentState))
3554 // On Move Forward
3555 case OnMoveForward:
3556 setAnim(_CurrentState->OnMoveForward);
3557 return;
3558 // On Move Backward
3559 case OnMoveBackward:
3560 setAnim(_CurrentState->OnMoveBackward);
3561 return;
3562 // On Move Left
3563 case OnMoveLeft:
3564 setAnim(_CurrentState->OnMoveLeft);
3565 return;
3566 // On Move Right
3567 case OnMoveRight:
3568 setAnim(_CurrentState->OnMoveRight);
3569 return;
3570 default:
3571 break;
3574 // ON ROTATION
3575 CVector tmp;
3576 switch(onRotation(*_CurrentState, tmp))
3578 // Rotation Left
3579 case OnRotLeft:
3580 setAnim(CAnimationStateSheet::Idle);
3581 return;
3582 // Rotation Right
3583 case OnRotRight:
3584 setAnim(CAnimationStateSheet::Idle);
3585 return;
3586 default:
3587 break;
3590 // ON BAD HEADING
3591 if(onBadHeading(*_CurrentState))
3593 setAnim(CAnimationStateSheet::Idle);
3594 return;
3597 // ON BIG BEND
3598 switch(onBigBend(*_CurrentState, tmp))
3600 // Big Bend Left and Right
3601 case OnBendLeft:
3602 case OnBendRight:
3603 setAnim(_CurrentState->MoveState);
3604 return;
3605 default:
3606 break;
3609 // \todo GUIGUI : changer de place cette partie je pense.
3610 // Adjust the direction to fit the front
3611 if( !(isUser() && ClientCfg.AutomaticCamera==false) )
3613 if(_CurrentState->AdjustOri)
3615 // Adjust before the half of the attack animation.
3616 const double animL = animLength/2.0;
3617 // Half already reatch, dir should be as the front now
3618 if(animOffset(MOVE) > animL)
3619 dir(front());
3620 else
3622 double ang = computeShortestAngle(atan2(dir().y, dir().x), atan2(front().y, front().x));
3623 if(ang)
3625 double ang2 = (animOffset(MOVE)/animL)*ang/animL;
3626 double angleZ = ang2+atan2(dir().y, dir().x);
3627 dir(CVector((float)cos(angleZ), (float)sin(angleZ), 0.f));
3633 }// updateAnimationState //
3635 //-----------------------------------------------
3636 // computeMotion :
3637 //-----------------------------------------------
3638 ADD_METHOD(double CCharacterCL::computeMotion(const double &oldMovingTimeOffset, TAnimationType channel) const)
3639 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Compute_Motion )
3641 // Check the state is valid.
3642 if(_CurrentState == 0)
3643 return 0.0;
3645 // Calculate movement for given animation segment.
3646 if(_CurrentState->Move)
3648 // Animation is unknown, no move in it.
3649 if(animIndex(channel) == CAnimation::UnknownAnim)
3650 return 0.0;
3652 CVector oldPos, newPos;
3653 uint animID = animId(channel);
3654 if(EAM->interpolate(animID, oldMovingTimeOffset, oldPos))
3656 if(EAM->interpolate(animID, animOffset(channel), newPos))
3658 CVector mov = newPos - oldPos;
3659 // Scale it by the CharacterScalePos, if needed, according to the animation.
3660 bool mustApplyCharacterScalePosFactor = true;
3661 const CAnimationState *animStatePtr = _CurrentAnimSet[channel]->getAnimationState(animState(channel));
3662 if(animStatePtr)
3664 const CAnimation *anim = animStatePtr->getAnimation(animIndex(channel));
3665 if(anim)
3666 mustApplyCharacterScalePosFactor = anim->applyCharacterScalePosFactor();
3668 // scale it according to the species.
3669 if(mustApplyCharacterScalePosFactor)
3670 mov*= _CharacterScalePos;
3671 // Scale according to the gabarit.
3672 mov *= _CustomScalePos;
3673 // Return a significant move.
3674 double distDone = mov.norm();
3675 if(distDone>0.0 && distDone<ClientCfg.SignificantDist)
3676 distDone = ClientCfg.SignificantDist;
3677 return distDone;
3681 // Miss the position's track.
3682 nlwarning("CCharacterCL::computeMotion : Animation should have a track for the position.");
3683 return 0.0;
3685 // The animation is not one to move.
3686 else
3687 return 0.0;
3688 }// computeMotion //
3690 //-----------------------------------------------
3691 // beginCast :
3692 //-----------------------------------------------
3693 void CCharacterCL::beginCast(const MBEHAV::CBehaviour &behaviour)
3695 // if the player has a target, force him to face this target
3696 CEntityCL *target = EntitiesMngr.entity(targetSlot());
3697 if(target && !target->isUser() )
3699 CVectorD dirToTarget = target->pos() - pos();
3700 dirToTarget.z = 0;
3701 dirToTarget.normalize();
3702 front( dirToTarget );
3703 dir( dirToTarget );
3706 switch(behaviour.Behaviour)
3708 ////////////////
3709 // BEGIN CAST //
3710 // OFFENSIVE CAST BEGIN
3711 case MBEHAV::CAST_OFF:
3712 setAnim(CAnimationStateSheet::OffensiveCastInit);
3713 break;
3714 // CURATIVE CAST BEGIN
3715 case MBEHAV::CAST_CUR:
3716 setAnim(CAnimationStateSheet::CurativeCastInit);
3717 break;
3718 // MIXED CAST BEGIN
3719 case MBEHAV::CAST_MIX:
3720 setAnim(CAnimationStateSheet::MixedCastInit);
3721 break;
3722 // IDLE
3723 case MBEHAV::IDLE:
3724 setAnim(CAnimationStateSheet::Idle);
3725 break;
3727 case MBEHAV::CAST_ACID:
3728 setAnim(CAnimationStateSheet::AcidCastInit);
3729 break;
3730 case MBEHAV::CAST_BLIND:
3731 setAnim(CAnimationStateSheet::BlindCastInit);
3732 break;
3733 case MBEHAV::CAST_COLD:
3734 setAnim(CAnimationStateSheet::ColdCastInit);
3735 break;
3736 case MBEHAV::CAST_ELEC:
3737 setAnim(CAnimationStateSheet::ElecCastInit);
3738 break;
3739 case MBEHAV::CAST_FEAR:
3740 setAnim(CAnimationStateSheet::FearCastInit);
3741 break;
3742 case MBEHAV::CAST_FIRE:
3743 setAnim(CAnimationStateSheet::FireCastInit);
3744 break;
3745 case MBEHAV::CAST_HEALHP:
3746 setAnim(CAnimationStateSheet::HealHPCastInit);
3747 break;
3748 case MBEHAV::CAST_MAD:
3749 setAnim(CAnimationStateSheet::MadCastInit);
3750 break;
3751 case MBEHAV::CAST_POISON:
3752 setAnim(CAnimationStateSheet::PoisonCastInit);
3753 break;
3754 case MBEHAV::CAST_ROOT:
3755 setAnim(CAnimationStateSheet::RootCastInit);
3756 break;
3757 case MBEHAV::CAST_ROT:
3758 setAnim(CAnimationStateSheet::RotCastInit);
3759 break;
3760 case MBEHAV::CAST_SHOCK:
3761 setAnim(CAnimationStateSheet::ShockCastInit);
3762 break;
3763 case MBEHAV::CAST_SLEEP:
3764 setAnim(CAnimationStateSheet::SleepCastInit);
3765 break;
3766 case MBEHAV::CAST_SLOW:
3767 setAnim(CAnimationStateSheet::SlowCastInit);
3768 break;
3769 case MBEHAV::CAST_STUN:
3770 setAnim(CAnimationStateSheet::StunCastInit);
3771 break;
3772 default:
3773 break;
3775 }// beginCast //
3777 //-----------------------------------------------
3778 // endCast :
3779 //-----------------------------------------------
3780 void CCharacterCL::endCast(const MBEHAV::CBehaviour &behaviour, const MBEHAV::CBehaviour &lastBehaviour)
3782 if( !(isUser() && isSit()) )
3784 switch(lastBehaviour.Behaviour)
3786 case MBEHAV::CAST_ACID:
3787 setAnim(CAnimationStateSheet::AcidCastEnd);
3788 break;
3789 case MBEHAV::CAST_BLIND:
3790 setAnim(CAnimationStateSheet::BlindCastEnd);
3791 break;
3792 case MBEHAV::CAST_COLD:
3793 setAnim(CAnimationStateSheet::ColdCastEnd);
3794 break;
3795 case MBEHAV::CAST_ELEC:
3796 setAnim(CAnimationStateSheet::ElecCastEnd);
3797 break;
3798 case MBEHAV::CAST_FEAR:
3799 setAnim(CAnimationStateSheet::FearCastEnd);
3800 break;
3801 case MBEHAV::CAST_FIRE:
3802 setAnim(CAnimationStateSheet::FireCastEnd);
3803 break;
3804 case MBEHAV::CAST_HEALHP:
3805 setAnim(CAnimationStateSheet::HealHPCastEnd);
3806 break;
3807 case MBEHAV::CAST_MAD:
3808 setAnim(CAnimationStateSheet::MadCastEnd);
3809 break;
3810 case MBEHAV::CAST_POISON:
3811 setAnim(CAnimationStateSheet::PoisonCastEnd);
3812 break;
3813 case MBEHAV::CAST_ROOT:
3814 setAnim(CAnimationStateSheet::RootCastEnd);
3815 break;
3816 case MBEHAV::CAST_ROT:
3817 setAnim(CAnimationStateSheet::RotCastEnd);
3818 break;
3819 case MBEHAV::CAST_SHOCK:
3820 setAnim(CAnimationStateSheet::ShockCastEnd);
3821 break;
3822 case MBEHAV::CAST_SLEEP:
3823 setAnim(CAnimationStateSheet::SleepCastEnd);
3824 break;
3825 case MBEHAV::CAST_SLOW:
3826 setAnim(CAnimationStateSheet::SlowCastEnd);
3827 break;
3828 case MBEHAV::CAST_STUN:
3829 setAnim(CAnimationStateSheet::StunCastEnd);
3830 break;
3831 case MBEHAV::IDLE:
3832 setAnim(CAnimationStateSheet::Idle);
3833 break;
3834 // Old One
3835 default:
3837 switch(behaviour.Behaviour)
3839 //////////////
3840 // END CAST //
3841 // OFFENSIVE CAST END
3842 case MBEHAV::CAST_OFF_FAIL:
3843 setAnim(CAnimationStateSheet::OffensiveCastFail);
3844 break;
3845 case MBEHAV::CAST_OFF_FUMBLE:
3846 setAnim(CAnimationStateSheet::OffensiveCastFumble);
3847 break;
3848 case MBEHAV::CAST_OFF_SUCCESS:
3849 setAnim(CAnimationStateSheet::OffensiveCastSuccess);
3850 break;
3851 case MBEHAV::CAST_OFF_LINK:
3852 setAnim(CAnimationStateSheet::OffensiveCastLink);
3853 break;
3854 // CURATIVE CAST END
3855 case MBEHAV::CAST_CUR_FAIL:
3856 setAnim(CAnimationStateSheet::CurativeCastFail);
3857 break;
3858 case MBEHAV::CAST_CUR_FUMBLE:
3859 setAnim(CAnimationStateSheet::CurativeCastFumble);
3860 break;
3861 case MBEHAV::CAST_CUR_SUCCESS:
3862 setAnim(CAnimationStateSheet::CurativeCastSuccess);
3863 break;
3864 case MBEHAV::CAST_CUR_LINK:
3865 setAnim(CAnimationStateSheet::CurativeCastLink);
3866 break;
3867 // MIXED CAST END
3868 case MBEHAV::CAST_MIX_FAIL:
3869 setAnim(CAnimationStateSheet::MixedCastFail);
3870 break;
3871 case MBEHAV::CAST_MIX_FUMBLE:
3872 setAnim(CAnimationStateSheet::MixedCastFumble);
3873 break;
3874 case MBEHAV::CAST_MIX_SUCCESS:
3875 setAnim(CAnimationStateSheet::MixedCastSuccess);
3876 break;
3877 case MBEHAV::CAST_MIX_LINK:
3878 setAnim(CAnimationStateSheet::MixedCastLink);
3879 break;
3880 default:
3881 break;
3884 break;
3887 }// endCast //
3890 // *************************************************************************************************
3891 void CCharacterCL::updateCurrentAttack()
3893 // This is a behaviour for the magic.
3894 if(_CurrentBehaviour.isMagic())
3896 _CurrentAttackID.Type = CAttackIDSheet::Magic;
3897 _CurrentAttackID.SpellInfo.Mode = (MAGICFX::TSpellMode) _CurrentBehaviour.Spell.SpellMode;
3898 _CurrentAttackID.SpellInfo.ID = (MAGICFX::TMagicFx) _CurrentBehaviour.Spell.SpellId;
3900 // This is a behaviour for the combat.
3901 else if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK)
3903 _CurrentAttackID.Type = CAttackIDSheet::Range;
3904 _CurrentAttackID.RangeWeaponType = (RANGE_WEAPON_TYPE::TRangeWeaponType) _CurrentBehaviour.Range.WeaponType;
3906 else if (_CurrentBehaviour.isCombat())
3908 _CurrentAttackID.Type = CAttackIDSheet::Melee;
3910 else if (_CurrentBehaviour.isCreatureAttack())
3912 _CurrentAttackID.Type = CAttackIDSheet::Creature;
3913 _CurrentAttackID.CreatureAttackIndex = _CurrentBehaviour.Behaviour == MBEHAV::CREATURE_ATTACK_0 ? 0 : 1;
3915 else
3917 // the behaviour does not generate an attack
3918 _CurrentAttack = NULL;
3919 _CurrentAttackID.Type = CAttackIDSheet::Unknown;
3920 return;
3922 _CurrentAttack = getAttack(_CurrentAttackID);
3923 // update current attack infos
3924 if(_CurrentBehaviour.isMagic())
3926 _CurrentAttackInfo.Intensity = _CurrentBehaviour.Spell.SpellIntensity;
3927 // physical damge are irrelevant for magic
3928 _CurrentAttackInfo.DamageType = DMGTYPE::UNDEFINED;
3929 _CurrentAttackInfo.HitType = HITTYPE::Undefined;
3930 _CurrentAttackInfo.Localisation = BODY::UnknownBodyPart;
3931 _CurrentAttackInfo.PhysicalImpactIntensity = 0;
3933 // This is a behaviour for the combat.
3934 else if(_CurrentBehaviour.Behaviour == MBEHAV::RANGE_ATTACK)
3936 _CurrentAttackInfo.Intensity = _CurrentBehaviour.Range.ImpactIntensity;
3937 _CurrentAttackInfo.DamageType = DMGTYPE::UNDEFINED;
3938 _CurrentAttackInfo.HitType = HITTYPE::Undefined;
3939 _CurrentAttackInfo.Localisation = (BODY::TBodyPart) _CurrentBehaviour.Range.Localisation;
3940 _CurrentAttackInfo.PhysicalImpactIntensity = 0;
3942 else if (_CurrentBehaviour.isCombat())
3944 _CurrentAttackInfo.Intensity = _CurrentBehaviour.Combat.ImpactIntensity;
3945 _CurrentAttackInfo.DamageType = (DMGTYPE::EDamageType) _CurrentBehaviour.Combat2.DamageType;
3946 _CurrentAttackInfo.HitType = (HITTYPE::THitType) _CurrentBehaviour.Combat.HitType;
3947 _CurrentAttackInfo.Localisation = (BODY::TBodyPart) _CurrentBehaviour.Combat.Localisation;
3948 _CurrentAttackInfo.PhysicalImpactIntensity = _CurrentBehaviour.Combat.ImpactIntensity;
3950 else if (_CurrentBehaviour.isCreatureAttack())
3952 // creature attack is the most general form
3953 _CurrentAttackInfo.Intensity = _CurrentBehaviour.CreatureAttack.MagicImpactIntensity;
3954 _CurrentAttackInfo.DamageType = (DMGTYPE::EDamageType) _CurrentBehaviour.CreatureAttack2.DamageType;
3955 _CurrentAttackInfo.HitType = (HITTYPE::THitType) _CurrentBehaviour.CreatureAttack2.HitType;
3956 _CurrentAttackInfo.Localisation = (BODY::TBodyPart) _CurrentBehaviour.CreatureAttack.Localisation;
3957 _CurrentAttackInfo.PhysicalImpactIntensity = _CurrentBehaviour.CreatureAttack.ImpactIntensity;
3959 _CurrentAttackInfo.Side = (rand() & 1) ? BODY::Left : BODY::Right;
3963 // utility function for performCurrentAttackEnd
3964 inline static void getResistAndDistance(uint8 packedInfo, bool isDirectAttack, bool isCombat, bool &resist, uint &distance)
3966 // get the distance from attacker to defender (consider 0 if a direct attack)
3967 if (isDirectAttack)
3968 distance = 0;
3969 else
3970 distance = packedInfo & 127;
3971 // resisted?
3972 if(isCombat)
3973 resist= false;
3974 else
3975 resist = (packedInfo & 128) != 0;
3978 // *********************************************************************************************
3979 void CCharacterCL::performCurrentAttackEnd(const CBehaviourContext &bc, bool directOffensifSpell, vector<double> &targetHitDates, TAnimStateKey animForCombat)
3981 if (!_CurrentAttack) return;
3982 if (!_CurrentAttack->Sheet) return;
3983 if (bc.Targets.Targets.empty())
3985 nlwarning ("No target available for current attack.");
3986 return;
3988 nlassert(targetHitDates.size() == bc.Targets.Targets.size());
3990 CAttackInfo attackInfo = _CurrentAttackInfo;
3992 const CAttackSheet &sheet = *_CurrentAttack->Sheet;
3995 // should cast FX for static Objects like towers be used ?
3996 bool usesStaticCastFX = _Sheet && !_Sheet->ProjectileCastRay.empty();
3998 CProjectileBuild pb;
4001 // ray of cast for static object with several cast points
4002 CVector castWorldOrigin;
4003 CVector castWorldPos;
4004 CVector additionnalOffset = NLMISC::CVector::Null;
4005 bool castRayValid = false;
4006 uint mainCastFXIntensity = attackInfo.Intensity;
4009 // *** Compute castStartTime (time at which the projectile is casted)
4011 // *** Compute castStartTime (time at which the projectile is casted)
4013 float delay = 0.f; // by default, no delay before impact
4014 double timeFactor = 1;
4016 // Combat (range or melee) => no resist (yoyo: legacy code. Actually the server should set the resist bit to 0 in Target.Info)
4017 bool isCombat= _CurrentBehaviour.isCombat();
4019 // An attack is said 'direct' when no projectile is casted (Generally those are melee attack, but also pistol range attack for instance)
4020 // In this case, all targets are hit at the same time, no matter what date is supplied for them.
4021 bool isDirectAttack = sheet.ProjectileFX.empty();
4023 if (isDirectAttack)
4025 ///////////////////
4026 // DIRECT ATTACK //
4027 ///////////////////
4028 // compute impact date. It is given by the trigger on right hand in the animation
4029 // Get the current animation id.
4030 if (sheet.ForceUseProjectileDelay)
4032 delay = sheet.ProjectileDelay;
4034 else
4035 if (!directOffensifSpell)
4037 if ((_CurrentAttackID.Type != CAttackIDSheet::Range) && (_PlayList != NULL))
4039 // default
4040 delay= 0.5f;
4041 // if the animation has been correctly chosen
4042 if(animForCombat!=CAnimationStateSheet::UnknownState)
4044 // try to get the MeleeImpactDelay that is stored in the sheet (given by graphists)
4045 const CAnimationSet *animSet= currentAnimSet()[MOVE];
4046 if(animSet)
4048 const CAnimationState *animState = animSet->getAnimationState(animForCombat);
4049 if(animState)
4050 delay= animState->getMeleeImpactDelay();
4054 else
4056 // see if weapon is known, and in this case, delay the impact by the same amount
4057 const CItemSheet *sheet = _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
4058 if (sheet) delay = sheet->FX.ImpactFXDelay;
4061 // all target are reached at the same time
4062 timeFactor = 0;
4064 else
4066 // there's a projectile
4067 if (usesStaticCastFX)
4069 delay = sheet.StaticObjectProjectileDelay;
4071 else
4073 if (!directOffensifSpell)
4075 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4076 // projectile cast from a character
4077 delay = sheet.ProjectileDelay;
4082 // then castStartTime is just start of behav + animation delay
4083 double castStartTime;
4084 // Special for direct attack. because of lag sometimes behavTime<TimeInSec, which results
4085 // on too early played hits regarding animation played
4086 if(isDirectAttack)
4088 // start at max(behavTime, TimeInSec), but with a limit of bc.BehavTime+0.5 sec
4089 if(TimeInSec > bc.BehavTime+0.5)
4090 castStartTime= bc.BehavTime + 0.5;
4091 else if(TimeInSec > bc.BehavTime)
4092 castStartTime= TimeInSec;
4093 else
4094 castStartTime= bc.BehavTime;
4095 // add the delay
4096 castStartTime+= delay;
4098 else
4100 // In case of magic or range (with projectile), this is not required because the projectilemanager should take cares of
4101 // cast start time and current time (and therfore advance the projectile if required)
4102 castStartTime= bc.BehavTime + delay;
4105 // *** Casts projectiles and Impacts
4106 bool resist;
4107 uint distance;
4108 // there's a projectile
4109 switch(sheet.ProjectileMode)
4111 // TODO: homin code for projectile look quite similar, maybe can merge ?
4112 case MAGICFX::Bomb:
4114 getResistAndDistance(bc.Targets.Targets[0].Info, isDirectAttack, isCombat, resist, distance);
4115 double mainStartDate = castStartTime;
4116 double mainEndDate = mainStartDate + timeFactor * double(distance * MULTI_TARGET_DISTANCE_UNIT / MAGICFX::PROJECTILE_SPEED);
4117 CCharacterCL *mainTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[0].TargetSlot));
4118 if (mainTarget)
4120 computeTargetStickMode(*_CurrentAttack->Sheet, attackInfo, pb.ProjectileAimingPoint, *mainTarget);
4121 if (usesStaticCastFX)
4123 // compute the cast pos
4124 computeBestCastRay(*mainTarget, pb.ProjectileAimingPoint, castWorldOrigin, castWorldPos, additionnalOffset);
4125 castRayValid = true;
4128 // the target hit date
4129 targetHitDates[0]= mainEndDate;
4131 // Add the projectile to queue
4132 if (createCurrentAttackEndPart(pb,
4133 _CurrentAttack,
4134 *mainTarget,
4135 NULL,
4136 mainStartDate,
4137 mainEndDate,
4138 true,
4139 sheet.PlayImpactAnim,
4140 resist,
4141 sheet.IsImpactLocalised,
4142 attackInfo,
4143 additionnalOffset
4146 CProjectileManager::getInstance().addProjectileToQueue(pb);
4149 attackInfo.Intensity = attackInfo.Intensity != 0 ? 1 : 0;
4150 attackInfo.PhysicalImpactIntensity = attackInfo.PhysicalImpactIntensity != 0 ? 1 : 0;
4151 // all subsequent projectiles are casted from the secondary target, from the first impact point, and have level 1
4152 for(uint k = 1; k < bc.Targets.Targets.size(); ++k)
4154 getResistAndDistance(bc.Targets.Targets[k].Info, isDirectAttack, isCombat, resist, distance);
4155 double secondaryEndDate = mainEndDate + timeFactor * ((double) (distance * MULTI_TARGET_DISTANCE_UNIT / MAGICFX::PROJECTILE_SPEED));
4156 CCharacterCL *secondaryTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4157 if (secondaryTarget)
4159 // the target hit date
4160 targetHitDates[k]= secondaryEndDate;
4162 // Add the projectile to queue
4163 if (mainTarget->createCurrentAttackEndPart(pb,
4164 _CurrentAttack,
4165 *secondaryTarget,
4166 &pb.ProjectileAimingPoint,
4167 mainEndDate,
4168 secondaryEndDate,
4169 k != 0 ? !sheet.PlayImpactFXOnlyOnMainTarget : true,
4170 sheet.PlayImpactAnim,
4171 resist,
4172 sheet.IsImpactLocalised,
4173 attackInfo
4176 computeTargetStickMode(*_CurrentAttack->Sheet, attackInfo, pb.ProjectileAimingPoint, *secondaryTarget);
4177 CProjectileManager::getInstance().addProjectileToQueue(pb);
4183 break;
4184 case MAGICFX::Chain:
4186 double currDate = castStartTime;
4187 CCharacterCL *currCaster = this;
4188 const CFXStickMode *projectileStartPoint = NULL; // by default, start at caster hand
4189 CFXStickMode currStickMode;
4190 for(uint k = 0; k < bc.Targets.Targets.size(); ++k)
4192 if (k == 1)
4194 // for secondary impacts, intensity is 0 or 1
4195 attackInfo.Intensity = attackInfo.Intensity != 0 ? 1 : 0;
4196 attackInfo.PhysicalImpactIntensity = attackInfo.PhysicalImpactIntensity != 0 ? 1 : 0;
4198 getResistAndDistance(bc.Targets.Targets[k].Info, isDirectAttack, isCombat, resist, distance);
4199 double nextDate = currDate + timeFactor * ((double) (distance * MULTI_TARGET_DISTANCE_UNIT / MAGICFX::PROJECTILE_SPEED));
4200 CCharacterCL *currTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4201 if (!currTarget) break;
4204 computeTargetStickMode(*_CurrentAttack->Sheet, attackInfo, pb.ProjectileAimingPoint, *currTarget);
4207 if (k == 0 && usesStaticCastFX)
4209 // compute the initial cast pos for objects with multiple possible cast positions
4210 computeBestCastRay(*currTarget, pb.ProjectileAimingPoint, castWorldOrigin, castWorldPos, additionnalOffset);
4211 castRayValid = true;
4214 // the target hit date
4215 targetHitDates[k]= nextDate;
4217 // Add the projectile to queue
4218 if (currCaster->createCurrentAttackEndPart(pb,
4219 _CurrentAttack,
4220 *currTarget,
4221 projectileStartPoint,
4222 currDate,
4223 nextDate,
4224 k != 0 ? !sheet.PlayImpactFXOnlyOnMainTarget : true,
4225 sheet.PlayImpactAnim,
4226 resist,
4227 sheet.IsImpactLocalised,
4228 attackInfo,
4229 k == 0 ? additionnalOffset : CVector::Null
4232 CProjectileManager::getInstance().addProjectileToQueue(pb);
4234 currStickMode = pb.ProjectileAimingPoint;
4235 projectileStartPoint = &currStickMode;
4236 currCaster = currTarget;
4237 if (!currCaster) break;
4238 currDate = nextDate;
4241 break;
4242 case MAGICFX::Spray:
4244 for(uint k = 0; k < bc.Targets.Targets.size(); ++k)
4246 getResistAndDistance(bc.Targets.Targets[k].Info, isDirectAttack, isCombat, resist, distance);
4247 double startDate = castStartTime;
4248 double endDate = startDate + timeFactor * ((double) (distance * MULTI_TARGET_DISTANCE_UNIT / MAGICFX::PROJECTILE_SPEED));
4249 CCharacterCL *currTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4250 if (k == 1)
4252 // for secondary impacts, intensity is 0 or 1
4253 attackInfo.Intensity = attackInfo.Intensity != 0 ? 1 : 0;
4254 attackInfo.PhysicalImpactIntensity = attackInfo.PhysicalImpactIntensity != 0 ? 1 : 0;
4256 if (currTarget)
4258 computeTargetStickMode(*_CurrentAttack->Sheet, attackInfo, pb.ProjectileAimingPoint, *currTarget);
4259 if (k == 0 && usesStaticCastFX)
4261 // compute the initial cast pos for objects with multiple possible cast positions
4262 computeBestCastRay(*currTarget, pb.ProjectileAimingPoint, castWorldOrigin, castWorldPos, additionnalOffset);
4263 castRayValid = true;
4266 // the target hit date
4267 targetHitDates[k]= endDate;
4269 // nb : only main target display the spell with full power
4270 if (createCurrentAttackEndPart(pb,
4271 _CurrentAttack,
4272 *currTarget,
4273 NULL,
4274 startDate,
4275 endDate,
4276 k != 0 ? !sheet.PlayImpactFXOnlyOnMainTarget : true,
4277 sheet.PlayImpactAnim,
4278 resist,
4279 sheet.IsImpactLocalised,
4280 attackInfo,
4281 k == 0 ? additionnalOffset : CVector::Null
4284 CProjectileManager::getInstance().addProjectileToQueue(pb);
4289 break;
4290 default:
4291 break;
4294 // if object has a list of cast rays, then we assume it is a static object (like guard towers)
4295 if (usesStaticCastFX && castRayValid)
4297 buildStaticObjectCastFX(castWorldOrigin, castWorldPos, *_CurrentAttack->Sheet, mainCastFXIntensity);
4301 // *** Play damage shields when melee attack is done
4303 // TODO: to finalize (server code not done).
4304 if (ClientCfg.DamageShieldEnabled && _CurrentBehaviour.isCombat() && _CurrentBehaviour.todoNotRange())
4306 for(uint k = 0; k < bc.Targets.Targets.size(); ++k)
4308 uint power = bc.Targets.Targets[k].Info & 7;
4309 if (power)
4311 uint dmType = bc.Targets.Targets[k].Info >> 3;
4313 CAttackIDSheet damageShieldID;
4314 damageShieldID.Type = CAttackIDSheet::DamageShield;
4315 damageShieldID.DamageShieldType = dmType;
4316 CCharacterCL *currTarget = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(bc.Targets.Targets[k].TargetSlot));
4317 if (!currTarget) continue;
4318 const CAttack *damageShieldReaction = currTarget->getAttack(damageShieldID);
4319 if (!damageShieldReaction) continue;
4320 CAttackInfo attackInfo;
4321 attackInfo.Intensity = power;
4322 attackInfo.PhysicalImpactIntensity = 0;
4323 attackInfo.Localisation = BODY::UnknownBodyPart;
4324 // no physical part
4325 const CAttackSheet &damageShieldSheet = *damageShieldReaction->Sheet;
4326 computeTargetStickMode(damageShieldSheet, attackInfo, pb.ProjectileAimingPoint, *this);
4327 if (currTarget->createCurrentAttackEndPart(pb,
4328 damageShieldReaction,
4329 *this,
4330 NULL,
4331 castStartTime,
4332 castStartTime,
4333 true,
4334 damageShieldSheet.PlayImpactAnim,
4335 false,
4336 false,
4337 attackInfo))
4339 // play "CastEnd" at the good date (if any ...)
4340 pb.CastAspect = &damageShieldReaction->AttackEndFX;
4341 pb.CastPower = power;
4342 pb.ForcePlayImpact = true;
4343 CProjectileManager::getInstance().addProjectileToQueue(pb);
4351 // *********************************************************************************************
4352 void CCharacterCL::buildStaticObjectCastFX(const NLMISC::CVector &castWorldOrigin, NLMISC::CVector &castWorldPos, const CAttackSheet &/* sheet */, uint intensity)
4354 if (intensity == 0) return;
4355 const float *userParams = CProjectileManager::getProjectileFXUserParams(intensity);
4356 // create additionnal cast fxs on the tower or other static object (if any)
4357 // Build lookat matrix (with respect to axis) looking from castWorldOrigin to castWorldPos
4358 CVector dir = castWorldPos - castWorldOrigin;
4359 CVector I = dir.normed();
4360 CVector K = (CVector::K - (I * CVector::K) * I).normed();
4361 CMatrix castMat;
4362 castMat.setPos(castWorldPos);
4363 castMat.setRot(I, K ^ I, K);
4364 CAttachedFX::CBuildInfo bi;
4365 CAttachedFX::CTargeterInfo ti;
4366 bi.StaticMatrix = &castMat;
4367 const CAnimationFXSet &afs =_CurrentAttack->AttackStaticObjectCastFX;
4368 for (uint k = 0; k < afs.FX.size(); ++k)
4370 // if the fx is in looping mode, & the anim has already done a loop, then don't recreate it
4371 CAttachedFX::TSmartPtr fx = new CAttachedFX;
4372 bi.Sheet = &afs.FX[k];
4373 fx->create(*this, bi, ti);
4374 if (!fx->FX.empty())
4376 if (userParams)
4378 for(uint l = 0; l < 4; ++l)
4380 fx->FX.setUserParam(l, userParams[l]);
4383 attachFX(fx);
4388 // *********************************************************************************************
4389 void CCharacterCL::computeTargetStickMode(const CAttackSheet &sheet, const CAttackInfo &attackInfo, CFXStickMode &dest, CEntityCL &target)
4391 bool hasPhysicalImpact = false;
4392 if (attackInfo.Localisation != BODY::UnknownBodyPart &&
4393 attackInfo.PhysicalImpactIntensity >= 1 &&
4394 attackInfo.PhysicalImpactIntensity <= MAGICFX::NUM_SPELL_POWER &&
4395 attackInfo.HitType != HITTYPE::Failed &&
4396 attackInfo.HitType != HITTYPE::Undefined &&
4397 attackInfo.DamageType != DMGTYPE::UNDEFINED)
4399 hasPhysicalImpact = true;
4402 if (sheet.IsImpactLocalised || hasPhysicalImpact)
4404 // get projectile impact point from localisation
4405 // & generate stick mode
4406 const char *targetBoneName = target.getBoneNameFromBodyPart(attackInfo.Localisation, attackInfo.Side);
4407 if (targetBoneName)
4409 if (sheet.DefaultAimingPoint.Mode == CFXStickMode::UserBoneOrientedTowardTargeter)
4411 dest.Mode = CFXStickMode::UserBoneOrientedTowardTargeter;
4413 else
4415 dest.Mode = CFXStickMode::UserBone;
4417 dest.UserBoneName = CStringMapper::map(targetBoneName);
4420 else
4422 // use default aiming point given in sheet
4423 dest = sheet.DefaultAimingPoint;
4428 // *********************************************************************************************
4429 bool CCharacterCL::createCurrentAttackEndPart(CProjectileBuild &destPB,
4430 const CAttack *currentAttack,
4431 const CCharacterCL &target,
4432 const CFXStickMode *sm,
4433 double spawnDate,
4434 double hitDate,
4435 bool playImpactFX,
4436 bool playImpactAnim,
4437 bool magicResist,
4438 bool /* mainImpactIsLocalised */,
4439 const CAttackInfo &attackInfo,
4440 const NLMISC::CVector &additionnalOffset /*= NLMISC::CVector::Null*/
4443 if (!currentAttack) return false;
4444 if (!currentAttack->Sheet) return false;
4447 const CAttackSheet &sheet = *currentAttack->Sheet;
4449 // dates
4450 destPB.StartDate = spawnDate;
4451 destPB.EndDate = hitDate;
4452 // choose fx for projectile
4453 if (attackInfo.Intensity >= 1 && attackInfo.Intensity <= MAGICFX::NUM_SPELL_POWER)
4455 destPB.ProjectileAspect = &currentAttack->ProjectileFX;
4457 else
4459 destPB.ProjectileAspect = NULL;
4461 // choose fx for impact
4462 if (!playImpactFX)
4464 destPB.ImpactAspect = NULL;
4466 else
4468 if (attackInfo.Intensity >= 1 && attackInfo.Intensity <= MAGICFX::NUM_SPELL_POWER) // impact has same intensity than projectile
4470 destPB.ImpactAspect = &currentAttack->ImpactFX;
4472 else
4474 destPB.ImpactAspect = NULL;
4478 // FILL PROJECTILE BUILD
4479 destPB.AttackInfo = attackInfo;
4480 destPB.TargeterInfo.Slot = slot();
4483 destPB.LocalizedImpact = sheet.IsImpactLocalised;
4484 // If this is a secondary projectile, it may start from another location, which is the impact point of the previous projectile
4485 // (so it doesn't start from the caster hand, or any around settings that is read from the spell sheet)
4486 if (sm) // start stickmode wanted ?
4488 destPB.TargeterInfo.StickMode = *sm;
4490 else
4492 // if no stick mode is not forced, then use the one given in the projectile sheet
4493 if (destPB.ProjectileAspect && !destPB.ProjectileAspect->FX.empty())
4495 destPB.TargeterInfo.StickMode = destPB.ProjectileAspect->FX[0].Sheet->StickMode;
4497 else
4499 // if no projectile is given, then uses the default casting point
4500 destPB.TargeterInfo.StickMode = sheet.DefaultCastingPoint;
4503 destPB.Mode = sheet.ProjectileMode;
4504 destPB.Target.Slot = target.slot();
4505 destPB.TargeterInfo.StickOffset = CVector::Null;
4506 destPB.PlayImpactAnim = playImpactAnim;
4507 destPB.LetProjectileStickedOnTarget = sheet.LetProjectileStickedOnTarget;
4508 destPB.TargeterInfo.DefaultPos = pos().asVector();
4510 destPB.MagicResist = magicResist;
4511 // offset if projectile is launched from a range weapon and projectile is sticked to box_arme
4512 if (destPB.TargeterInfo.StickMode.Mode == CFXStickMode::UserBone && sheet.ApplyItemOffsetToWeaponBone)
4514 // should be fired from the 'box_arme' bone, which means it is fired from a weapon
4515 if (CStringMapper::unmap(destPB.TargeterInfo.StickMode.UserBoneName) == "box_arme")
4517 NLMISC::CVector projectileOffset = NLMISC::CVector::Null;
4518 const CItemSheet *is = getRightHandItemSheet();
4519 if (is)
4521 destPB.TargeterInfo.StickOffset = is->FX.AttackFXOffset;
4523 destPB.TargeterInfo.StickOffset += additionnalOffset;
4526 destPB.TargeterInfo.StickOffset += sheet.AdditionnalStartOffset;
4527 return true;
4531 // *********************************************************************************************
4532 void CCharacterCL::computeBestCastRay(CEntityCL &targetEntity,
4533 const CFXStickMode &targetStickMode,
4534 NLMISC::CVector &castWorldOrigin,
4535 NLMISC::CVector &castWorldPos,
4536 NLMISC::CVector &worldOffsetToCasterPivot
4537 ) const
4539 // additionnal offset taken from sheet. Useful for towers that have no bones, but can fire projectiles anyway
4540 nlassert(_Sheet && !_Sheet->ProjectileCastRay.empty());
4541 // if several offsets are provided, then choose the one that has the smallest angle towards target
4542 CVector target;
4543 CProjectileManager::evalFXPosition(&targetStickMode, targetEntity, target);
4544 float maxDP3 = -FLT_MAX;
4545 // NB : the offset is relative to object pivot, not to a bone
4546 CMatrix casterMatrix;
4547 buildAlignMatrix(casterMatrix);
4548 for(uint k = 0; k < _Sheet->ProjectileCastRay.size(); ++k)
4550 CVector currCastWorldPos = casterMatrix * _Sheet->ProjectileCastRay[k].Pos;
4551 CVector currCastWorldOrigin = casterMatrix * _Sheet->ProjectileCastRay[k].Origin;
4552 float dp3 = (target - currCastWorldPos).normed() * (currCastWorldPos - currCastWorldOrigin).normed();
4553 if (dp3 > maxDP3)
4555 maxDP3 = dp3;
4556 worldOffsetToCasterPivot = casterMatrix.mulVector(_Sheet->ProjectileCastRay[k].Pos);
4557 castWorldOrigin = currCastWorldOrigin;
4558 castWorldPos = currCastWorldPos;
4563 // *********************************************************************************************
4564 bool CCharacterCL::isCurrentBehaviourAttackEnd() const
4566 switch(_CurrentBehaviour.Behaviour)
4568 case MBEHAV::CAST_OFF_SUCCESS:
4569 case MBEHAV::CAST_OFF_LINK:
4570 case MBEHAV::CAST_CUR_SUCCESS:
4571 case MBEHAV::CAST_CUR_LINK:
4572 case MBEHAV::CAST_MIX_SUCCESS:
4573 case MBEHAV::CAST_MIX_LINK:
4574 case MBEHAV::RANGE_ATTACK:
4575 case MBEHAV::CREATURE_ATTACK_0:
4576 case MBEHAV::CREATURE_ATTACK_1:
4577 case MBEHAV::DEFAULT_ATTACK:
4578 case MBEHAV::POWERFUL_ATTACK:
4579 case MBEHAV::AREA_ATTACK:
4580 return true;
4581 default:
4582 break;
4584 return false;
4588 // ***************************************************************************
4589 void CCharacterCL::applyBehaviourFlyingHPs(const CBehaviourContext &bc, const MBEHAV::CBehaviour &behaviour,
4590 const vector<double> &targetHitDates)
4592 nlassert(targetHitDates.size()==bc.Targets.Targets.size());
4594 if (behaviour.DeltaHP == 0 || bc.Targets.Targets.empty())
4595 return;
4597 CRGBA deltaHPColor(0, 0, 0);
4598 for (size_t i=0; i<bc.Targets.Targets.size(); ++i)
4600 if(bc.Targets.Targets[i].DeltaHP == 0)
4601 continue;
4603 CEntityCL *target2 = EntitiesMngr.entity(bc.Targets.Targets[i].TargetSlot);
4604 if(target2)
4606 // if it's a hit
4607 if(bc.Targets.Targets[i].DeltaHP < 0)
4609 // if the behaviour is casted by the user
4610 if( slot() == 0 )
4612 deltaHPColor = ClientCfg.SystemInfoParams["dgm"].Color;
4614 else
4615 // if the behaviour is casted by an entity that target the user
4616 if( targetSlot() == 0 )
4618 CEntityCL *actor = EntitiesMngr.entity(slot());
4619 if( actor )
4621 // if actor is player : use pvp color
4622 if( actor->isPlayer() )
4623 deltaHPColor = ClientCfg.SystemInfoParams["dgp"].Color;
4624 else
4625 deltaHPColor = ClientCfg.SystemInfoParams["dg"].Color;
4628 else
4630 deltaHPColor = CRGBA(127,127,127);
4633 else
4635 deltaHPColor = CRGBA(0,220,0);
4638 target2->addHPOutput(bc.Targets.Targets[i].DeltaHP, deltaHPColor, float(targetHitDates[i]-TimeInSec));
4644 //-----------------------------------------------
4645 // Apply the behaviour.
4646 // \param behaviour : the behaviour to apply.
4647 //-----------------------------------------------
4648 void CCharacterCL::applyBehaviour(const CBehaviourContext &bc) // virtual
4650 // Backup the current behaviour.
4651 CBehaviour previousBehaviour = _CurrentBehaviour;
4652 _CurrentBehaviour = bc.Behav;
4653 const CBehaviour &behaviour = bc.Behav;
4655 // check if self-target
4656 bool selfSpell = false;
4657 if (bc.Targets.Targets.size() == 1)
4659 if (bc.Targets.Targets[0].TargetSlot == 0)
4661 selfSpell = true;
4664 bool isOffensif;
4665 switch(behaviour.Behaviour)
4667 case MBEHAV::CAST_OFF:
4668 case MBEHAV::CAST_OFF_FAIL:
4669 case MBEHAV::CAST_OFF_SUCCESS:
4670 case MBEHAV::CAST_OFF_LINK:
4671 isOffensif = true;
4672 break;
4673 default:
4674 isOffensif = false;
4675 break;
4678 // Get a pointer on the target.
4679 CEntityCL *target = EntitiesMngr.entity(targetSlot());
4682 // ***** Choose Combat Animation to apply
4684 TAnimStateKey combatAnimState= CAnimationStateSheet::UnknownState;
4685 bool isMovingCombatAnimState= false;
4686 // if the behaviour is for combat, compute now the animation to apply
4687 if( !behaviour.isMagic() && ( behaviour.isCombat() || behaviour.isCreatureAttack() ) )
4689 // Atk Animation when moving
4690 if(_CurrentState && _CurrentState->Move && (_CurrentState->OnAtk != CAnimationStateSheet::UnknownState))
4692 combatAnimState= _CurrentState->OnAtk;
4693 isMovingCombatAnimState= true;
4695 // Atk Animation when NOT moving
4696 else
4698 // select the combat animation.
4699 switch(behaviour.Behaviour)
4701 case CREATURE_ATTACK_0:
4702 combatAnimState= CAnimationStateSheet::Attack1;
4703 break;
4704 case CREATURE_ATTACK_1:
4705 combatAnimState= CAnimationStateSheet::Attack2;
4706 break;
4707 // Default Animation
4708 case DEFAULT_ATTACK:
4709 switch(getAttackHeight(target, (BODY::TBodyPart)behaviour.Combat.Localisation, BODY::Right))
4711 // LOW
4712 case CCharacterCL::AtkLow:
4713 combatAnimState= CAnimationStateSheet::DefaultAtkLow;
4714 break;
4715 // HIGH
4716 case CCharacterCL::AtkHigh:
4717 combatAnimState= CAnimationStateSheet::DefaultAtkHigh;
4718 break;
4719 // MIDDLE or Default
4720 case CCharacterCL::AtkMiddle:
4721 default:
4722 combatAnimState= CAnimationStateSheet::DefaultAtkMiddle;
4723 break;
4725 break;
4726 // Powerful Animation
4727 case POWERFUL_ATTACK:
4728 switch(getAttackHeight(target, (BODY::TBodyPart)behaviour.Combat.Localisation, BODY::Right))
4730 // LOW
4731 case CCharacterCL::AtkLow:
4732 combatAnimState= CAnimationStateSheet::PowerfulAtkLow;
4733 break;
4734 // HIGH
4735 case CCharacterCL::AtkHigh:
4736 combatAnimState= CAnimationStateSheet::PowerfulAtkHigh;
4737 break;
4738 // MIDDLE or Default
4739 case CCharacterCL::AtkMiddle:
4740 default:
4741 combatAnimState= CAnimationStateSheet::PowerfulAtkMiddle;
4742 break;
4744 break;
4745 // Area Animation
4746 case AREA_ATTACK:
4747 switch(getAttackHeight(target, (BODY::TBodyPart)behaviour.Combat.Localisation, BODY::Right))
4749 // LOW
4750 case CCharacterCL::AtkLow:
4751 combatAnimState= CAnimationStateSheet::AreaAtkLow;
4752 break;
4753 // HIGH
4754 case CCharacterCL::AtkHigh:
4755 combatAnimState= CAnimationStateSheet::AreaAtkHigh;
4756 break;
4757 // MIDDLE or Default
4758 case CCharacterCL::AtkMiddle:
4759 default:
4760 combatAnimState= CAnimationStateSheet::AreaAtkMiddle;
4761 break;
4763 break;
4764 // Range Animation
4765 case RANGE_ATTACK:
4766 combatAnimState= CAnimationStateSheet::Attack1;
4767 break;
4768 default:
4769 break;
4775 // ***** Compute Impact delays and cast missiles
4777 // Default target hit dates
4778 static vector<double> targetHitDates;
4779 targetHitDates.clear();
4780 targetHitDates.resize(bc.Targets.Targets.size(), TimeInSec);
4782 // Update Attack Projectiles and FXs
4783 updateCurrentAttack();
4784 if (isCurrentBehaviourAttackEnd())
4786 // retrieve target hit dates, so flying HPs have the correct ones
4787 performCurrentAttackEnd(bc, selfSpell && isOffensif, targetHitDates, combatAnimState);
4790 // INFO : display some debug information.
4791 if((VerboseAnimUser && _Slot==0) || (VerboseAnimSelection && _Slot == UserEntity->selection()))
4792 nlinfo("CH:applyBeh:%d: '%d(%s)'", _Slot, (sint)behaviour.Behaviour, behaviourToString((EBehaviour)behaviour.Behaviour).c_str());
4795 // ***** Apply the behaviour according to type
4797 // This is a behaviour for the magic.
4798 if(behaviour.isMagic())
4800 // Execute the magic behaviour.
4801 switch(behaviour.Behaviour)
4803 ////////////////
4804 // BEGIN CAST //
4805 case MBEHAV::CAST_OFF:
4806 case MBEHAV::CAST_CUR:
4807 case MBEHAV::CAST_MIX:
4808 case MBEHAV::CAST_ACID:
4809 case MBEHAV::CAST_BLIND:
4810 case MBEHAV::CAST_COLD:
4811 case MBEHAV::CAST_ELEC:
4812 case MBEHAV::CAST_FEAR:
4813 case MBEHAV::CAST_FIRE:
4814 case MBEHAV::CAST_HEALHP:
4815 case MBEHAV::CAST_MAD:
4816 case MBEHAV::CAST_POISON:
4817 case MBEHAV::CAST_ROOT:
4818 case MBEHAV::CAST_ROT:
4819 case MBEHAV::CAST_SHOCK:
4820 case MBEHAV::CAST_SLEEP:
4821 case MBEHAV::CAST_SLOW:
4822 case MBEHAV::CAST_STUN:
4823 beginCast(behaviour);
4824 break;
4825 //////////////
4826 // END CAST //
4827 case MBEHAV::CAST_OFF_FAIL:
4828 case MBEHAV::CAST_OFF_FUMBLE:
4829 if (!selfSpell) endCast(behaviour, previousBehaviour);
4830 break;
4831 case MBEHAV::CAST_OFF_SUCCESS:
4832 case MBEHAV::CAST_OFF_LINK:
4833 endCast(behaviour, previousBehaviour);
4834 break;
4835 case MBEHAV::CAST_CUR_FAIL:
4836 case MBEHAV::CAST_CUR_FUMBLE:
4837 endCast(behaviour, previousBehaviour);
4838 break;
4839 case MBEHAV::CAST_CUR_SUCCESS:
4840 case MBEHAV::CAST_CUR_LINK:
4841 endCast(behaviour, previousBehaviour);
4842 break;
4843 case MBEHAV::CAST_MIX_FAIL:
4844 case MBEHAV::CAST_MIX_FUMBLE:
4845 endCast(behaviour, previousBehaviour);
4846 break;
4847 case MBEHAV::CAST_MIX_SUCCESS:
4848 case MBEHAV::CAST_MIX_LINK:
4849 endCast(behaviour, previousBehaviour);
4850 break;
4851 default:
4852 break;
4854 // DeltaHP
4855 applyBehaviourFlyingHPs(bc, behaviour, targetHitDates);
4857 // This is a behaviour for the combat.
4858 else if(behaviour.isCombat() || behaviour.isCreatureAttack())
4860 float frontYawBefore = 0.f;
4861 float frontYawAfter = 0.f;
4863 // Atk Animation when NOT moving?
4864 if(target && !isMovingCombatAnimState)
4866 // orientate to target
4867 CVectorD dirToTarget = target->pos() - pos();
4868 dirToTarget.z = 0;
4869 dirToTarget.normalize();
4870 if( !(isUser() && ClientCfg.AutomaticCamera == false) )
4872 // backup front yaw
4873 frontYawBefore = frontYaw();
4874 front( dirToTarget );
4876 dir( dirToTarget );
4879 // Apply the state animation chosen before
4880 if(combatAnimState!=CAnimationStateSheet::UnknownState)
4881 setAnim(combatAnimState);
4883 // move camera so view doesn't change
4884 if( isUser() && frontYawBefore != 0.f )
4886 frontYawAfter = frontYaw();
4887 float deltaYaw = frontYawAfter - frontYawBefore;
4888 if( deltaYaw !=0 )
4890 UserControls.appendCameraDeltaYaw(-deltaYaw);
4894 // reset yaw smoothly to center view behind user
4895 if( isUser() && target && !target->isUser() && ClientCfg.AutomaticCamera )
4897 UserControls.resetSmoothCameraDeltaYaw();
4900 // DeltaHP
4901 applyBehaviourFlyingHPs(bc, behaviour, targetHitDates);
4903 // Emote
4904 else if(behaviour.isEmote())
4906 if(ClientCfg.Light==false && ClientCfg.EAMEnabled)
4908 TAnimStateId emot;
4909 if (EAM)
4912 // old code : fxs attached to emotes
4913 uint emoteIndex = behaviour.Behaviour-EMOTE_BEGIN;
4914 CTextEmotListSheet *pTELS = dynamic_cast<CTextEmotListSheet*>(SheetMngr.get(CSheetId("list.text_emotes")));
4915 if (pTELS)
4917 if (emoteIndex < pTELS->TextEmotList.size())
4919 const CTextEmotListSheet::STextEmot &emot = pTELS->TextEmotList[emoteIndex];
4920 if (!emot.FXToSpawn.empty())
4922 // Compute the direction Matrix
4923 CMatrix fxMatrix;
4924 CVector vi = dir() ^ CVector::K;
4925 CVector vk = vi ^ dir();
4926 fxMatrix.setRot(vi, UserEntity->dir(), vk, true);
4927 fxMatrix.setPos(pos().asVector() + fxMatrix.getJ() * emot.FXSpawnDist);
4928 FXMngr.deferFX(emot.FXToSpawn, fxMatrix, emot.FXSpawnDelay);
4933 if(EAM->getEmot(behaviour.Behaviour-EMOTE_BEGIN, emot))
4934 setAnim(CAnimationStateSheet::Emote, emot);
4935 else
4936 nlwarning("CH:applyBeh:%d: Emot '%d' unknown.", _Slot, behaviour.Behaviour-EMOTE_BEGIN);
4940 // Others
4941 else
4943 switch(behaviour.Behaviour)
4945 // Loot Begin
4946 case MBEHAV::LOOT_INIT:
4947 setAnim(CAnimationStateSheet::LootInit);
4948 break;
4949 // Loot End
4950 case MBEHAV::LOOT_END:
4951 setAnim(CAnimationStateSheet::LootEnd);
4952 break;
4953 // Prospecting Begin
4954 case MBEHAV::PROSPECTING:
4955 setAnim(CAnimationStateSheet::ProspectingInit);
4956 break;
4957 // Prospecting End
4958 case MBEHAV::PROSPECTING_END:
4959 setAnim(CAnimationStateSheet::ProspectingEnd);
4960 break;
4961 // Extracting Begin
4962 case MBEHAV::EXTRACTING:
4963 // DeltaHP
4964 if(target)
4965 if(behaviour.DeltaHP != 0)
4966 target->addHPOutput(behaviour.DeltaHP,CRGBA(0,220,0));
4967 // If receiving a new DeltaHP in the current extraction, don't reset the animation
4968 if ( previousBehaviour.Behaviour != _CurrentBehaviour.Behaviour )
4969 setAnim(CAnimationStateSheet::UseInit);
4970 break;
4971 // Extracting End
4972 case MBEHAV::EXTRACTING_END:
4973 setAnim(CAnimationStateSheet::UseEnd);
4974 break;
4975 // Care Begin
4976 case MBEHAV::CARE:
4977 setAnim(CAnimationStateSheet::CareInit);
4978 break;
4979 // Care End
4980 case MBEHAV::CARE_END:
4981 setAnim(CAnimationStateSheet::CareEnd);
4982 break;
4984 // Begin to use a tool
4985 case MBEHAV::HARVESTING:
4986 case MBEHAV::FABER:
4987 case MBEHAV::REPAIR:
4988 case MBEHAV::REFINE:
4989 case MBEHAV::TRAINING:
4990 setAnim(CAnimationStateSheet::UseInit);
4991 break;
4993 // End to use a tool
4994 case MBEHAV::HARVESTING_END:
4995 case MBEHAV::FABER_END:
4996 case MBEHAV::REPAIR_END:
4997 case MBEHAV::REFINE_END:
4998 case MBEHAV::TRAINING_END:
4999 setAnim(CAnimationStateSheet::UseEnd);
5000 break;
5002 // Begin Stun
5003 case MBEHAV::STUNNED:
5004 setAnim(CAnimationStateSheet::StunBegin);
5005 break;
5007 // End Stun
5008 case MBEHAV::STUN_END:
5009 setAnim(CAnimationStateSheet::StunEnd);
5010 break;
5012 // Idle
5013 case IDLE:
5014 break;
5016 // Unknown behaviour -> idle.
5017 case UNKNOWN_BEHAVIOUR:
5018 default:
5019 nlwarning("CH::computeBehaviour : Entity in slot %d has an unknown behaviour %d to manage.", _Slot, (sint)behaviour.Behaviour);
5020 break;
5023 }// computeBehaviour //
5025 //-----------------------------------------------
5026 // impact :
5027 // Play an impact on the entity
5028 // \param impactType : 0=magic, 1=melee
5029 // \param type : see behaviour for spell
5030 // \param intensity : see behaviour for spell
5031 // \param id : see behaviour for spell
5032 //-----------------------------------------------
5033 void CCharacterCL::impact(uint /* impactType */, uint type, uint id, uint intensity) // virtual
5035 // Display Magic Debug Infos
5036 if(Verbose & VerboseMagic)
5037 nlinfo("CH:impact:%d: type: %d, id: %d, intensity: %d", _Slot, type, id, intensity);
5038 // No Intensity -> No Impact
5039 if(intensity==0)
5040 return;
5041 // Invalid Intensity -> No Impact
5042 else if(intensity>5)
5044 nlwarning("CH:impact:%d: invalid intensity %u", _Slot, intensity);
5045 return;
5047 // RESIST : temp until resist is in the enum.
5048 if(type==0)
5050 // Create the FX
5051 NL3D::UInstance resistFX = Scene->createInstance("Sp_Resist_Lev5.ps");
5052 if(!resistFX.empty())
5053 resistFX.setPos(pos());
5054 return;
5056 // Compute the impact name
5057 string impact;
5058 if(id < ClientCfg.OffImpactFX.size())
5059 impact = ClientCfg.OffImpactFX[id];
5060 // Create the FX
5061 if(!impact.empty())
5064 NL3D::UInstance impactFX = Scene->createInstance(impact);
5065 if(!impactFX.empty())
5067 impactFX.setPos(pos());
5068 UParticleSystemInstance instFX;
5069 instFX.cast (impactFX);
5070 if(!instFX.empty())
5072 // UserParam | Intensity 1 | Intensity 2 | Intensity 3 | Intensity 4 | Intensity 5
5073 // 0 | 0 | 0 | 1 | 1 | 1
5074 // 1 | 0 | 1 | 1 | 1 | 1
5075 // 2 | 0 | 0 | 0 | 1 | 1
5076 // 3 | 0 | 0 | 0 | 0 | 1
5077 float userParam0 = 0.0f;
5078 float userParam1 = 0.0f;
5079 float userParam2 = 0.0f;
5080 float userParam3 = 0.0f;
5081 // WARNING : there is no break and this is correct.
5082 switch(intensity)
5084 case 5:
5085 userParam3 = 1.0f;
5086 case 4:
5087 userParam2 = 1.0f;
5088 case 3:
5089 userParam0 = 1.0f;
5090 case 2:
5091 userParam1 = 1.0f;
5093 instFX.setUserParam(0, userParam0);
5094 instFX.setUserParam(1, userParam1);
5095 instFX.setUserParam(2, userParam2);
5096 instFX.setUserParam(3, userParam3);
5100 }// impact //
5102 //-----------------------------------------------
5103 // meleeImpact ::
5104 //-----------------------------------------------
5105 void CCharacterCL::meleeImpact(const CAttackInfo &attack)
5107 if (_Skeleton.empty()) return;
5108 if (attack.PhysicalImpactIntensity < 1 || attack.PhysicalImpactIntensity > 5) return;
5109 if (attack.HitType == HITTYPE::Failed) return;
5110 const char *boneName = getBoneNameFromBodyPart(attack.Localisation, attack.Side);
5111 if (!boneName) return;
5112 sint boneId = _Skeleton.getBoneIdByName(std::string(boneName));
5113 if (boneId == -1) return;
5114 if (!Scene) return;
5115 // choose good fx depending on the kind of damage
5116 NL3D::UInstance instance;
5117 switch(attack.DamageType)
5119 case DMGTYPE::BLUNT: instance = Scene->createInstance("mel_impactblunt.ps"); break;
5120 case DMGTYPE::SLASHING: instance = Scene->createInstance("mel_impactslashing.ps"); break;
5121 case DMGTYPE::PIERCING: instance = Scene->createInstance("mel_impactpiercing.ps"); break;
5122 default:
5123 return; // other types not supported
5124 break;
5126 if (instance.empty()) return;
5127 UParticleSystemInstance impact;
5128 impact.cast (instance);
5129 if (impact.empty())
5131 Scene->deleteInstance(instance);
5132 return;
5134 // the 2 first user params of the fx are used to modulate intensity
5135 static const float intensityUP[5][2] =
5137 { 0.f, 0.f},
5138 { 0.f, 0.5f},
5139 { 0.5f, 0.5f},
5140 { 0.5f, 1.f},
5141 { 1.f, 1.f}
5143 impact.setUserParam(0, intensityUP[attack.PhysicalImpactIntensity - 1][0]);
5144 impact.setUserParam(1, intensityUP[attack.PhysicalImpactIntensity - 1][1]);
5145 impact.setUserParam(2, (attack.HitType == HITTYPE::CriticalHit || attack.HitType == HITTYPE::CriticalHitResidual) ? 1.f : 0.f);
5146 impact.setUserParam(3, (attack.HitType == HITTYPE::HitResidual || attack.HitType == HITTYPE::CriticalHitResidual) ? 1.f : 0.f);
5148 _Skeleton.stickObject(impact, boneId);
5149 // delegate managment of the impact to the fx manager
5150 FXMngr.fx2remove(impact);
5151 }// meleeImpact //
5154 //-----------------------------------------------
5155 // magicImpact :
5156 // Play the magic impact on the entity
5157 // \param type : type of the impact (host/good/neutral).
5158 // \param intensity : intensity of the impact.
5159 //-----------------------------------------------
5160 void CCharacterCL::magicImpact(uint type, uint intensity) // virtual
5162 // --- FX --- //
5163 // Choose the FX.
5164 string impact;
5165 switch(type)
5167 // Resist
5168 case 0:
5169 impact = "Sp_Resist_Lev";
5170 break;
5171 // Good
5172 case 1:
5173 impact = "Sp_Bien_Cure_Lev";
5174 break;
5175 // Neutral
5176 case 2:
5177 impact = "Sp_Neutre_Protect_Lev";
5178 break;
5179 // Bad
5180 case 3:
5181 impact = "Sp_Host_Hurt_Lev";
5182 break;
5183 default:
5184 nlwarning("CH:magicImpact:%d: Unknown type '%d'.", _Slot, type);
5185 return;
5187 // Intensity
5188 switch(intensity)
5190 // Too weak
5191 case INTENSITY_TYPE::IMPACT_NONE:
5192 return;
5194 case INTENSITY_TYPE::IMPACT_INSIGNIFICANT:
5195 impact += "1.ps";
5196 break;
5197 case INTENSITY_TYPE::IMPACT_VERY_WEAK:
5198 impact += "2.ps";
5199 break;
5200 case INTENSITY_TYPE::IMPACT_WEAK:
5201 impact += "3.ps";
5202 break;
5203 case INTENSITY_TYPE::IMPACT_AVERAGE:
5204 impact += "4.ps";
5205 break;
5206 case INTENSITY_TYPE::IMPACT_STRONG:
5207 impact += "5.ps";
5208 break;
5210 // Unknown
5211 default:
5212 nlwarning("CH:magicImpact:%d: Unknown intensity '%d'.", _Slot, intensity);
5214 // Create the FX
5215 if(!impact.empty())
5217 NL3D::UInstance resistFX = Scene->createInstance(impact);
5218 if(!resistFX.empty())
5219 resistFX.setPos(pos());
5221 }// resist //
5224 //-----------------------------------------------
5225 // getMaxSpeed :
5226 // Return the basic max speed for the entity in meter per sec
5227 //-----------------------------------------------
5228 double CCharacterCL::getMaxSpeed() const // virtual
5230 return _Sheet->MaxSpeed;
5231 }// getMaxSpeed //
5234 //-----------------------------------------------
5235 // mode :
5236 // Method called to change the mode (Combat/Mount/etc.).
5237 //-----------------------------------------------
5238 bool CCharacterCL::mode(MBEHAV::EMode m)
5240 // DEBUG INFOS
5241 if(VerboseAnimSelection && _Slot == UserEntity->selection())
5243 nlinfo("CH::mode:%d: m'%s(%d)', _ModeWanted'%s(%d)', _Mode'%s(%d)'", _Slot,
5244 MBEHAV::modeToString(m ).c_str(), m,
5245 MBEHAV::modeToString(_ModeWanted).c_str(), _ModeWanted,
5246 MBEHAV::modeToString(_Mode ).c_str(), _Mode);
5248 // Is the mode wanted valid ?
5249 if(m == MBEHAV::UNKNOWN_MODE || m >= MBEHAV::NUMBER_OF_MODES)
5251 nlwarning("CH::mode:%d: Invalid Mode Wanted '%s(%d)' -> keep '%s(%d)'.",
5252 _Slot, MBEHAV::modeToString(m).c_str(), m, MBEHAV::modeToString(_Mode).c_str(), _Mode);
5253 return false;
5255 // Set the mode wanted.
5256 _ModeWanted = m;
5257 if(_CurrentState == 0)
5258 _Mode = _ModeWanted;
5259 return true;
5260 }// mode //
5263 //-----------------------------------------------
5264 // applyStage :
5265 // Apply stage modifications.
5266 // \todo GUIGUI ; ameliorer gestion mode.
5267 //-----------------------------------------------
5268 bool CCharacterCL::applyStage(CStage &stage)
5270 bool stageDone = true;
5272 // If the Stage has a position, backup the position and the stage time.
5273 if(stage.getPos(_OldPos))
5275 // Backup the time
5276 _OldPosTime = stage.time();
5277 // Remove the property.
5278 stage.removeProperty(PROPERTY_POSX);
5279 stage.removeProperty(PROPERTY_POSY);
5280 stage.removeProperty(PROPERTY_POSZ);
5283 // Apply orientation.
5284 pair<bool, sint64> resultTeta = stage.property(PROPERTY_ORIENTATION);
5285 if(resultTeta.first)
5287 C64BitsParts parts;
5288 parts.i64[0] = resultTeta.second;
5289 float angleZ = parts.f[0];
5290 // server forces the entity orientation even if it cannot turn
5291 front(CVector((float)cos(angleZ), (float)sin(angleZ), 0.f), true, true, true);
5293 _TargetAngle = (float)angleZ;
5294 // Remove the property.
5295 stage.removeProperty(PROPERTY_ORIENTATION);
5298 // Apply Mode.
5299 pair<bool, sint64> resultMode = stage.property(PROPERTY_MODE);
5300 if(resultMode.first)
5302 // Get the mode from stage.
5303 C64BitsParts parts;
5304 parts.i64[0] = resultMode.second;
5305 uint8 mo = parts.u8[0];
5306 // If the mode wanted is not the same, change the mode wanted.
5307 if(mo != _ModeWanted)
5309 if(mode((MBEHAV::EMode)mo))
5311 //stageDone = false;
5312 if(_Mode != _ModeWanted)
5313 stageDone = false;
5314 else
5315 stage.removeProperty(PROPERTY_MODE);
5317 else
5318 stage.removeProperty(PROPERTY_MODE);
5320 // If the mode wanted is not the same as the current mode -> Stage not done.
5321 else if(_Mode != _ModeWanted)
5322 stageDone = false;
5323 // Property applied -> Remove the property form stage.
5324 else
5325 stage.removeProperty(PROPERTY_MODE);
5328 // Apply Behaviour.
5329 pair<bool, sint64> resultBehaviour = stage.property(PROPERTY_BEHAVIOUR);
5330 if(resultBehaviour.first)
5332 CBehaviourContext bc;
5333 bc.Behav = CBehaviour(resultBehaviour.second);
5334 bc.BehavTime = stage.time();
5335 // See if there's a list of target associated with that behaviour (for multitarget spells)
5336 uint64 spellTarget[4];
5337 uint numTargets = 0;
5338 for(uint k = 0; k < 4; ++k)
5340 pair<bool, sint64> stProp = stage.property(PROPERTY_TARGET_LIST_0 + k);
5341 if (!stProp.first) break;
5342 spellTarget[k] = (uint64) stProp.second;
5343 ++ numTargets;
5344 stage.removeProperty(PROPERTY_TARGET_LIST_0 + k);
5347 if (numTargets > 0)
5349 // get the list of targets from the visual properties
5350 bc.Targets.unpack(spellTarget, numTargets);
5352 // Compute the beheviour.
5354 applyBehaviour(bc);
5355 // Remove the property.
5356 stage.removeProperty(PROPERTY_BEHAVIOUR);
5359 // Apply the target.
5360 pair<bool, sint64> resultTarget = stage.property(PROPERTY_TARGET_ID);
5361 if(resultTarget.first)
5363 // Change the entity target.
5364 targetSlot((CLFECOMMON::TCLEntityId)resultTarget.second);
5365 // Remove the property.
5366 stage.removeProperty(PROPERTY_TARGET_ID);
5369 // The Mount
5370 pair<bool, sint64> resultParent = stage.property(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID);
5371 if(resultParent.first)
5373 _Mount = (CLFECOMMON::TCLEntityId)resultParent.second;
5375 // Remove the property.
5376 stage.removeProperty(CLFECOMMON::PROPERTY_ENTITY_MOUNTED_ID);
5379 // The Rider
5380 pair<bool, sint64> resultRider = stage.property(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID);
5381 if(resultRider.first)
5383 _Rider = (CLFECOMMON::TCLEntityId)resultRider.second;
5385 // Remove the property.
5386 stage.removeProperty(CLFECOMMON::PROPERTY_RIDER_ENTITY_ID);
5389 // visual fxs : links and auras
5390 pair<bool, sint64> resultVisualFX = stage.property(CLFECOMMON::PROPERTY_VISUAL_FX);
5391 if (resultVisualFX.first)
5393 applyVisualFX(resultVisualFX.second);
5394 stage.removeProperty(CLFECOMMON::PROPERTY_VISUAL_FX);
5398 return stageDone;
5399 }// applyStage //
5401 //-----------------------------------------------
5402 // applyCurrentStage :
5403 // Apply The Current Stage (first stage).
5404 //-----------------------------------------------
5405 bool CCharacterCL::applyCurrentStage()
5407 bool bRet = true;
5409 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
5410 if(it != _Stages._StageSet.end())
5412 // Apply the Stage and remove it if stage done.
5413 if(applyStage((*it).second))
5414 _Stages._StageSet.erase(it);
5415 else
5416 bRet = false;
5418 else
5419 nlwarning("CCharacterCL::applyCurrentStage: there is no stage.");
5421 // Update information from remaining stages.
5422 updateStages();
5424 return bRet;
5425 }// applyCurrentStage //
5428 //-----------------------------------------------
5429 // applyAllStagesToFirstPos :
5430 // Apply all stages to the first stage with a position.
5431 //-----------------------------------------------
5432 void CCharacterCL::applyAllStagesToFirstPos()
5434 CVectorD stagePos;
5435 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
5436 while(it != _Stages._StageSet.end() && !(*it).second.getPos(stagePos))
5438 // Apply the Stage.
5439 if(!applyStage((*it).second))
5441 updateStages();
5442 return;
5445 // Backup the iterator to remove
5446 CStageSet::TStageSet::iterator itTmp = it;
5447 // Next Stage.
5448 ++it;
5449 // Remove the stage done.
5450 _Stages._StageSet.erase(itTmp);
5453 // Apply the stage with the position.
5454 if(it != _Stages._StageSet.end())
5456 // Apply the Stage.
5457 if(applyStage((*it).second))
5458 // Remove the stage done.
5459 _Stages._StageSet.erase(it);
5461 else
5462 nlwarning("CH:applyAllStagesToFirstPos:%d: There is no stage with a position.", _Slot);
5464 // Upate information from remaining stages.
5465 updateStages();
5466 }// applyAllStagesToFirstPos //
5469 //-----------------------------------------------
5470 // playToEndAnim :
5471 // Play the time step for the loop and truncate to End Anim if Time Step too big.
5472 //-----------------------------------------------
5473 ADD_METHOD(void CCharacterCL::playToEndAnim(const double &startTimeOffset, double &length))
5474 // Average Speed
5475 double speedToDest = speed();
5476 // Blend Walk/Run
5477 if(ClientCfg.BlendForward && (animState(MOVE) == CAnimationStateSheet::Walk))
5479 uint animWalkId = animId(MOVE);
5480 uint animRunId = animId(MOVE_BLEND_OUT);
5481 double animWalkSpeed = EAM->getAnimationAverageSpeed(animWalkId)*getSheetScale()*_CustomScalePos*_CharacterScalePos;
5482 double animRunSpeed = EAM->getAnimationAverageSpeed(animRunId)*getSheetScale()*_CustomScalePos*_CharacterScalePos;
5483 if(animWalkSpeed<=animRunSpeed)
5485 double startTimeOffRun = animOffset(MOVE_BLEND_OUT);
5486 double animWalkLength = EAM->getAnimationLength(animWalkId);
5487 double animRunLength = EAM->getAnimationLength(animRunId);
5488 // Current Speed <= Walk Speed, so use the walk animation only.
5489 if(speed() <= animWalkSpeed)
5491 runFactor(0.0);
5492 double speedFactor = speed()/animWalkSpeed;
5493 double animTimeOffWalk = animOffset(MOVE) + length*speedFactor;
5494 if(animTimeOffWalk > animWalkLength)
5496 animOffset(MOVE, animWalkLength);
5497 animOffset(MOVE_BLEND_OUT, animRunLength);
5498 length = (animWalkLength - startTimeOffset) / speedFactor;
5500 // Adjust Time Offset for the Run Channel
5501 else
5503 animOffset(MOVE, animTimeOffWalk);
5504 animOffset(MOVE_BLEND_OUT, animRunLength*(animTimeOffWalk/animWalkLength));
5507 // Current Speed >= Run Speed, so use the run animation only.
5508 else if(speed() >= animRunSpeed)
5510 runFactor(1.0);
5511 double speedFactor = speed()/animRunSpeed;
5512 double animTimeOffRun = animOffset(MOVE_BLEND_OUT) + length*speedFactor;
5513 if(animTimeOffRun > animRunLength)
5515 animOffset(MOVE, animWalkLength);
5516 animOffset(MOVE_BLEND_OUT, animRunLength);
5517 length = (animRunLength - startTimeOffRun) / speedFactor;
5519 // Adjust Time Offset for the Walk Channel
5520 else
5522 animOffset(MOVE, animWalkLength*(animTimeOffRun/animRunLength));
5523 animOffset(MOVE_BLEND_OUT, animTimeOffRun);
5526 // Current Speed > Walk Speed & < Run Speed, so mix Walk and Run animation.
5527 else
5529 double t1 = animRunSpeed-animWalkSpeed;
5530 double t2 = speed()-animWalkSpeed;
5531 runFactor(t2/t1);
5532 double mixLength = runFactor()*animRunLength + (1.0-runFactor())*animWalkLength;
5533 double animTimeOffWalk = animOffset(MOVE) + animWalkLength/mixLength*length;
5534 if(animTimeOffWalk > animWalkLength)
5536 animOffset(MOVE, animWalkLength);
5537 animOffset(MOVE_BLEND_OUT, animRunLength);
5538 length = (animWalkLength - startTimeOffset) / (animWalkLength/mixLength);
5540 else
5542 animOffset(MOVE, animTimeOffWalk);
5543 animOffset(MOVE_BLEND_OUT, animRunLength*animTimeOffWalk/animWalkLength); // Same percentage in the animation than the Walk one.
5546 return;
5548 //else
5549 //nlwarning("playToEndAnim:%d: animWalkSpeed > animRunSpeed", _Slot);
5551 // No Mix between Walk and Run.
5552 runFactor(0.0);
5553 // Speed Factor
5554 double speedFactor = computeSpeedFactor(speedToDest);
5555 // Compute the desired new time offset.
5556 double animTimeOffMove = animOffset(MOVE) + length * speedFactor;
5557 // Truncate animation time offset if it over-runs end of animation and change the loopTimeOffset too.
5558 double animationLength = EAM->getAnimationLength(animId(MOVE));
5559 if(animTimeOffMove > animationLength)
5561 animOffset(MOVE, animationLength);
5562 length = (animationLength - startTimeOffset) / speedFactor;
5564 else
5565 animOffset(MOVE, animTimeOffMove);
5566 }// playToEndAnim //
5568 //-----------------------------------------------
5569 // updateStages :
5570 // Call this method to give a time for each stage, compute distance to destination and some more information.
5571 // \todo GUIGUI : clean up
5572 //-----------------------------------------------
5573 void CCharacterCL::updateStages()
5575 H_AUTO ( RZ_Client_Entity_CL_updateStages );
5577 _FirstPos = INVALID_POS; // No First Position
5578 _FirstTime = INVALID_TIME; //- No First Position
5579 dist2FirstPos(INVALID_DIST); // No First Position
5580 _DestPos = INVALID_POS; // No Destination
5581 _DestTime = INVALID_TIME; // No Destination
5582 dist2Dest(INVALID_DIST); // No Destination
5583 CVectorD posTmp = pos();
5584 _IsThereAMode = false;
5585 _ImportantStepTime= 0.0;
5588 // ***** update predicted interval: if a new pos B is found after a pos A, then the interval B-A is known!
5589 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
5590 CStageSet::TStageSet::iterator itPosPrec= _Stages._StageSet.end();
5591 bool somePosFoundEarly= false;
5592 while(it != _Stages._StageSet.end())
5594 // if this stage has a position
5595 if(it->second.isPresent(PROPERTY_POSITION))
5597 somePosFoundEarly= true;
5598 // then it's cool we can set the new accurate interval to the prec stage which has a pos
5599 if(itPosPrec!=_Stages._StageSet.end())
5601 uint dgc= it->first - itPosPrec->first;
5602 itPosPrec->second.predictedInterval(dgc);
5604 // bkup
5605 itPosPrec= it;
5607 it++;
5611 // ***** Compute the current LCT Impact for this character
5612 // NB: used only in mode CClientConfig::StageUsePosOnlyLCT
5613 sint32 charLCTI;
5614 // If disabled, full LCT impact
5615 if(_StartDecreaseLCTImpact==0)
5616 charLCTI= 256;
5617 else
5619 const sint32 decreaseTick= 20; // 2 seconds
5620 // blend according to the start of decrease
5621 sint32 dt= NetMngr.getCurrentServerTick() - _StartDecreaseLCTImpact;
5622 if(dt<=0)
5623 charLCTI= 256;
5624 else if(dt>=decreaseTick)
5625 charLCTI= 0;
5626 else
5627 charLCTI= ((decreaseTick-dt)*256)/decreaseTick;
5628 // hence, at end of blend, charLCTI is 0
5632 // ***** Compute Stages to give them a time and get some information from those stages.
5633 // yoyo: use any stage with no LCT, until it is to be played AFTER a position
5634 bool stageForceLCTFound= false;
5635 CStageSet::TStageSet::iterator itTmp;
5636 it = _Stages._StageSet.begin();
5637 while(it != _Stages._StageSet.end())
5639 CStage &stage = (*it).second;
5641 // *** retrieve position in stage if any
5642 CVectorD posInStage;
5643 bool hasPos= stage.getPos(posInStage);
5644 // check the first pos is correct
5645 if(hasPos && dist2Dest()==INVALID_DIST)
5647 // Compute the distance to the first position
5648 double distToFirst = (CVectorD(posInStage.x, posInStage.y, 0.0) - CVectorD( posTmp.x, posTmp.y, 0.0)).norm();
5649 double distToLimiter = (CVectorD(posInStage.x, posInStage.y, 0.0) - CVectorD(_PositionLimiter.x, _PositionLimiter.y, 0.0)).norm();
5650 // Check if the first pos is Not the same as the current entity pos
5651 if((distToFirst < ClientCfg.DestThreshold)
5652 || (distToLimiter <= ClientCfg.PositionLimiterRadius))
5654 // The FIRST POSITION is the SAME as the CURRENT entity POSITION -> REMOVE POSITION in the stage
5655 stage.removeProperty(CLFECOMMON::PROPERTY_POSX);
5656 stage.removeProperty(CLFECOMMON::PROPERTY_POSY);
5657 stage.removeProperty(CLFECOMMON::PROPERTY_POSZ);
5658 hasPos= false;
5660 if(VerboseAnimSelection && _Slot == UserEntity->selection())
5661 nlinfo("CH:updateStages:%d: Bad First, distToFirst(%f), ClientCfg.DestThreshold(%f), distToLimiter(%f), ClientCfg.PositionLimiterRadius(%f)", _Slot,
5662 distToFirst, ClientCfg.DestThreshold, distToLimiter, ClientCfg.PositionLimiterRadius);
5666 // stage has pos? => force LCT for it and after
5667 stageForceLCTFound= stageForceLCTFound || hasPos;
5670 // *** Compute the estimated Time for the Stage.
5671 // Compute difference in Game Cycle Between the current Game Cycle and the current Stage.
5672 sint32 t;
5673 if(ClientCfg.StageLCTUsage==CClientConfig::StageUseAllLCT)
5674 t= (*it).first - NetMngr.getCurrentClientTick();
5675 else if(ClientCfg.StageLCTUsage==CClientConfig::StageUseNoLCT)
5676 t= (*it).first - NetMngr.getCurrentServerTick();
5677 else
5679 // Update LCTImpact for the stage
5680 if(stageForceLCTFound)
5682 // Force full impact for stage after or including a POS
5683 stage.setLCTImpact(256);
5685 else
5687 /* minimize the LCT impact with the current char one
5688 Hence, if the stage had a lct impact lowered, but LCT decrease was canceled,
5689 the stage will keep its LCT impact
5691 stage.setLCTImpact(min(stage.getLCTImpact(), charLCTI));
5694 sint32 lcti= stage.getLCTImpact();
5696 // if full impact
5697 if(lcti>=256)
5698 t= (*it).first - NetMngr.getCurrentClientTick();
5699 // if no impact
5700 else if(lcti<=0)
5701 t= (*it).first - NetMngr.getCurrentServerTick();
5702 // else blend
5703 else
5705 sint32 twlct= (*it).first - NetMngr.getCurrentClientTick();
5706 sint32 twolct= (*it).first - NetMngr.getCurrentServerTick();
5707 t= (twlct*lcti + twolct*(256-lcti))>>8;
5710 // Compute the estimated Time for the Stage.
5711 stage.time( (double)(NetMngr.getMachineTimeAtTick() + t*NetMngr.getMsPerTick())/1000.0 );
5714 // *** Important step is used for "Panic mode" animation acceleration. skip the first stage
5715 if(_ImportantStepTime==0.0 && it!=_Stages._StageSet.begin())
5717 // Important steps are ones that takes times (pos, orientation, mode, animation etc....)
5718 if( stage.isPresent(PROPERTY_POSITION) ||
5719 stage.isPresent(PROPERTY_MODE) ||
5720 stage.isPresent(PROPERTY_ORIENTATION) ||
5721 stage.isPresent(PROPERTY_ENTITY_MOUNTED_ID) ||
5722 stage.isPresent(PROPERTY_RIDER_ENTITY_ID) ||
5723 stage.isPresent(PROPERTY_BEHAVIOUR))
5724 _ImportantStepTime= stage.time();
5728 // *** Compute dist2dest (until a mode) if has pos
5729 if((_IsThereAMode==false) && hasPos)
5731 // Set the destination pos and time.
5732 _DestPos= posInStage;
5733 _DestTime = stage.time() + (double)(stage.predictedInterval()*NetMngr.getMsPerTick())/1000.0;
5734 // Update First Pos.
5735 if(dist2Dest() == INVALID_DIST)
5737 _FirstPos = _DestPos;
5738 _FirstTime = stage.time();
5739 // Compute the distance to the first position
5740 double distToFirst = (CVectorD(_DestPos.x, _DestPos.y, 0.0) - CVectorD(posTmp.x,posTmp.y, 0.0)).norm();
5741 // Set the Distance to the Destination as the distance to the first position.
5742 dist2Dest(distToFirst);
5743 // Set the distance to First Stage.
5744 dist2FirstPos(distToFirst);
5746 // Increase distance to destination.
5747 else
5748 dist2Dest(dist2Dest() + (CVectorD(_DestPos.x, _DestPos.y, 0.0) - CVectorD(posTmp.x, posTmp.y, 0.0)).norm());
5749 // Backup the last pos.
5750 posTmp = _DestPos;
5752 // Stop if there is a mode in the stage.
5753 if(stage.isPresent(CLFECOMMON::PROPERTY_MODE))
5754 _IsThereAMode = true;
5757 // *** NEXT STAGE
5758 itTmp = it;
5759 ++it;
5760 // REMOVE EMPTY STAGE (because only position and the same as the current one).
5761 if(stage.empty())
5762 _Stages._StageSet.erase(itTmp);
5765 // If there is no mode in queue, mode wanted is the current mode
5766 // It must usually be the theorical one except for some mode used only by the client like SWIM
5767 if(!_IsThereAMode)
5768 _ModeWanted = _Mode;
5770 // ***** update _StartDecreaseLCTImpact
5771 // If a stage that force LCT has been found in the list
5772 if(stageForceLCTFound)
5773 // Decrease of LCT is disabled
5774 _StartDecreaseLCTImpact= 0;
5775 else if(_StartDecreaseLCTImpact==0)
5776 // Start to decrease LCT
5777 _StartDecreaseLCTImpact= NetMngr.getCurrentServerTick();
5780 // ***** compute _RunStartTimeNoPop (see _RunStartTimeNoPop)
5781 _RunStartTimeNoPop= INVALID_TIME;
5782 // only if have some pos in the queue
5783 if(somePosFoundEarly)
5785 double d2fp = 0.0;
5786 double fpTime= INVALID_TIME;
5787 // if the first pos is computed, use it
5788 if(_FirstTime!=INVALID_TIME)
5790 d2fp= dist2FirstPos();
5791 fpTime= _FirstTime;
5793 // else try to compute the first pos, WIHTOUT regarding if there is a mode or not
5794 // (because even mode anim can be accelerated....)
5795 else
5797 it = _Stages._StageSet.begin();
5798 while(it != _Stages._StageSet.end())
5800 CStage &stage = (*it).second;
5801 CVectorD firstPos;
5802 if(stage.getPos(firstPos))
5804 fpTime = stage.time() + (double)(stage.predictedInterval()*NetMngr.getMsPerTick())/1000.0;
5805 d2fp= CVectorD(firstPos.x-pos().x, firstPos.y-pos().y, 0.0).norm();
5806 break;
5808 it++;
5812 // with d2fp, fpTime, and maxSpeed, we can estimate the moment where the run should start
5813 if(fpTime!=INVALID_TIME)
5815 float maxSpeed= (float)getMaxSpeed();
5816 if(maxSpeed>0)
5818 // compute at which time the first move should begin so it doesn't have to accelerate
5819 _RunStartTimeNoPop= fpTime - d2fp/maxSpeed;
5824 }// updateStages //
5826 //-----------------------------------------------
5827 // beginImpact :
5828 // Return if the impact must be played.
5829 // \param anim : pointer on the current animation (MUST NOT BE NULL).
5830 // \param currentTime : current time in the animation.
5831 // \param triggerName : name of the trigger to check.
5832 // \param isActive : read (and can change) the state.
5833 // \param timeFactor : when to activate the impact if there is no track (value has to be between 0 and 1 to be valid).
5834 // \return bool : true if the trigger is valid.
5835 // \warning This method does not check if the animation is Null.
5836 //-----------------------------------------------
5837 ADD_METHOD(bool CCharacterCL::beginImpact(NL3D::UAnimation *anim, NL3D::TAnimationTime currentTime, const std::string &triggerName, bool &isActive, float timeFactor))
5838 // Is the impact already activeated.
5839 if(isActive)
5840 return false;
5842 // Try to find the impact trigger in the animation.
5843 UTrack *Track = anim->getTrackByName(triggerName.c_str());
5844 // No track -> just check with 2/3 animation
5845 if(Track)
5847 if(Track->interpolate(currentTime, isActive))
5848 return isActive;
5849 else
5850 nlwarning("CH:beginImpact:%d: Wrong type asked.", _Slot);
5853 // No Track or pb with it so try with the animation length.
5854 float length = (float)(anim->getEndTime()-anim->getBeginTime());
5855 isActive = (animOffset(MOVE) >= length*timeFactor);
5856 return isActive;
5857 }// beginImpact //
5859 //-----------------------------------------------
5860 // animEventsProcessing :
5861 // Manage Events that could be created by the animation (like sound).
5862 // \param startTime : time to start processing events from the current animation.
5863 // \param stopTime : time to stop processing events from the current animation.
5864 // \todo GUIGUI : Optimize FXs launch when we would have time
5865 //-----------------------------------------------
5866 void CCharacterCL::animEventsProcessing(double startTime, double stopTime)
5868 if (_CurrentState == 0)
5869 return;
5871 // \todo Vianney : temp le temps de savoir comment on joue les son pour le propre joueur
5872 // No sound for the player.
5873 if(_Slot != 0 || _Mode != MBEHAV::NORMAL)
5875 // Retreive the surface material
5876 uint32 matId= getGroundType();
5877 // Set the material id var
5878 _SoundContext.Args[0] = matId;
5879 if(_Sheet)
5881 // Set the sound family var
5882 _SoundContext.Args[2] = _Sheet->SoundFamily;
5883 // Set the sound variation var
5884 _SoundContext.Args[3] = _Sheet->SoundVariation;
5886 else
5888 // Set the sound family var
5889 _SoundContext.Args[2] = 0;
5890 // Set the sound variation var
5891 _SoundContext.Args[3] = 0;
5893 // Sound Process.
5894 CSoundAnimManager* sndMngr = CSoundAnimManager::instance();
5895 if(_SoundContext.Args[2] != 999 && sndMngr && (_SoundId[MOVE] != CSoundAnimationNoId))
5897 _SoundContext.Position = pos();
5898 // Look for the cluster(s) containing this character...
5899 std::vector<NL3D::CCluster*> clusters;
5900 if (!_Instance.empty())
5902 // single meshed
5903 _Instance.getLastParentClusters(clusters);
5905 else if (!_Skeleton.empty())
5907 // Skel meshed
5908 _Skeleton.getLastParentClusters(clusters);
5910 CCluster *pcluster = 0;
5911 // use the first cluster if at leat one available
5912 if (!clusters.empty())
5913 pcluster = clusters.front();
5914 sndMngr->playAnimation(_SoundId[MOVE], (float) startTime, (float) stopTime, pcluster, _SoundContext);
5918 }// animEventsProcessing //
5920 //-----------------------------------------------
5921 // updatePreCollision :
5922 // Method called each frame to manage the entity.
5923 // \param time : current time of the frame.
5924 // \parem target : pointer on the current entity target.
5925 //-----------------------------------------------
5926 void CCharacterCL::updatePreCollision(const TTime &currentTimeInMs, CEntityCL *target) // virtual
5928 H_AUTO ( RZ_Client_Entity_CL_Update_Pre_Collision );
5929 // Set the Last frame PACS Pos.
5930 if(_Primitive)
5931 _Primitive->getGlobalPosition(_LastFramePACSPos, dynamicWI);
5932 // Set the previous position before changing the current one.
5933 _LastFramePos = _Position;
5935 // Turn towards the target when in COMBAT_FLOAT mode.
5936 if(_Mode == MBEHAV::COMBAT_FLOAT)
5938 H_AUTO ( RZ_Client_Entity_CL_Update_Combat )
5940 // Check there is a valid target and it's not the entity itself.
5941 if(targetSlot() != CLFECOMMON::INVALID_SLOT
5942 && targetSlot() != slot()
5943 && target)
5944 // Set the new entity direction
5945 front(target->pos() - pos(), true, false);
5948 // Update Position if not a child & displayable.
5949 if(parent() == CLFECOMMON::INVALID_SLOT && _Displayable)
5951 H_AUTO ( RZ_Client_Entity_CL_Update_Pos )
5952 updatePos(currentTimeInMs, target);
5954 }// updatePreCollision //
5956 //-----------------------------------------------
5957 // updateFX :
5958 // Apply track on fxs
5959 // Check if some FX should be removed.
5960 //-----------------------------------------------
5961 inline void CCharacterCL::updateFX()
5963 updateAttachedFX();
5964 }// updateFX //
5967 //-----------------------------------------------
5968 // updateAttachedFX :
5969 // Apply track on animated fxs
5970 // Remove those that should be removed
5971 //-----------------------------------------------
5972 void CCharacterCL::updateAttachedFX()
5974 // build align matrix
5975 CMatrix alignMatrix;
5976 buildAlignMatrix(alignMatrix);
5978 std::list<CAttachedFX::CBuildInfo>::iterator itAttachedFxToStart = _AttachedFXListToStart.begin();
5979 while(itAttachedFxToStart != _AttachedFXListToStart.end())
5981 if ((*itAttachedFxToStart).DelayBeforeStart < (float)(TimeInSec - (*itAttachedFxToStart).StartTime))
5983 uint index = (*itAttachedFxToStart).MaxNumAnimCount;
5984 (*itAttachedFxToStart).MaxNumAnimCount = 0;
5985 CAttachedFX::TSmartPtr fx = new CAttachedFX;
5986 fx->create(*this, (*itAttachedFxToStart), CAttachedFX::CTargeterInfo());
5987 if (!fx->FX.empty())
5989 _AuraFX[index] = fx;
5991 itAttachedFxToStart = _AttachedFXListToStart.erase(itAttachedFxToStart);
5993 else
5995 ++itAttachedFxToStart;
5999 // update tracks & pos for anim attachedfxs
6000 std::list<CAttachedFX::TSmartPtr>::iterator itAttachedFx = _AttachedFXListForCurrentAnim.begin();
6001 while(itAttachedFx != _AttachedFXListForCurrentAnim.end())
6003 nlassert(*itAttachedFx);
6004 CAttachedFX &attachedFX = **itAttachedFx;
6005 attachedFX.update(*this, alignMatrix);
6006 if (!attachedFX.FX.empty()) attachedFX.FX.setUserMatrix(alignMatrix);
6007 ++itAttachedFx;
6010 // Try to remove animation FXs still not removed.
6011 itAttachedFx = _AttachedFXListToRemove.begin();
6012 while(itAttachedFx != _AttachedFXListToRemove.end())
6014 // If the FX is not present or valid -> remove the FX.
6015 bool mustDelete = false;
6016 CAttachedFX &attachedFX = **itAttachedFx;
6017 if (attachedFX.SpawnTime != TimeInSec)
6019 if(attachedFX.FX.empty() || !attachedFX.FX.isSystemPresent() || !attachedFX.FX.isValid())
6021 mustDelete = true;
6024 if (attachedFX.TimeOutDate != 0)
6026 if (TimeInSec >= attachedFX.TimeOutDate)
6028 mustDelete = true;
6031 if (mustDelete)
6033 // Remove from the list.
6034 itAttachedFx = _AttachedFXListToRemove.erase(itAttachedFx);
6036 else
6038 attachedFX.update(*this, alignMatrix);
6039 if (!attachedFX.FX.empty()) attachedFX.FX.setUserMatrix(alignMatrix);
6040 ++itAttachedFx;
6044 // update the aura fx
6045 for(uint k = 0; k < MaxNumAura; ++k)
6047 if (_AuraFX[k])
6049 if (_AuraFX[k]->TimeOutDate != 0.f) // we use that flag to mark the aura as 'shutting down'
6051 if (TimeInSec >= _AuraFX[k]->TimeOutDate)
6053 _AuraFX[k] = NULL;
6055 else
6057 float lifeRatio = (float) ((_AuraFX[k]->TimeOutDate - TimeInSec) / AURA_SHUTDOWN_TIME);
6058 if (!_AuraFX[k]->FX.empty()) _AuraFX[k]->FX.setUserParam(0, 1.f - lifeRatio);
6061 if (_AuraFX[k]) // not deleted yet ?
6063 // update position & orientation
6064 _AuraFX[k]->update(*this, alignMatrix);
6069 // update the link fx
6070 if (_LinkFX)
6072 _LinkFX->update(*this, alignMatrix);
6074 if (_StaticFX)
6076 _StaticFX->FX->update(*this, alignMatrix);
6083 //-----------------------------------------------
6084 // updateVisible :
6085 //-----------------------------------------------
6086 void CCharacterCL::updateVisible (const TTime &currentTimeInMs, CEntityCL *target)
6088 if (_StepCustomScale != 0.f)
6089 scale();
6091 // Changes the skeleton state
6092 if(!_Skeleton.empty())
6094 _Skeleton.show();
6096 // Changes the instance position.
6097 else if(!_Instance.empty())
6099 _Instance.show();
6102 // Snap the entity to the ground.
6104 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground )
6105 snapToGround();
6108 // Apply the new entity position to the visual of the entity (apply x and z movement due to animation).
6109 if(parent() == CLFECOMMON::INVALID_SLOT)
6111 H_AUTO ( RZ_Client_Entity_CL_Update_Display )
6112 updateDisplay();
6115 // Change the cluster of the entity.
6117 H_AUTO ( RZ_Client_Entity_CL_Update_Cluster )
6118 updateCluster();
6121 // Update the LodCharacter Animation.
6122 if(_LodCharacterAnimEnabled)
6124 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation )
6126 // set this value to the skeleton
6127 if(skeleton())
6128 skeleton()->setLodCharacterAnimTime(_LodCharacterAnimTimeOffset);
6131 // Update FX
6133 H_AUTO ( RZ_Client_Entity_CL_Update_FX )
6134 updateFX();
6137 // Update Modifiers
6138 if(!_HPModifiers.empty())
6140 HPMD mod;
6141 mod.CHPModifier::operator= (*_HPModifiers.begin());
6142 mod.Time = TimeInSec + mod.DeltaT;
6143 _HPDisplayed.push_back(mod);
6144 _HPModifiers.erase(_HPModifiers.begin());
6147 // Parent
6148 CEntityCL::updateVisible(currentTimeInMs, target);
6149 }// updateVisible //
6151 //-----------------------------------------------
6152 // updateSomeClipped :
6153 //-----------------------------------------------
6154 void CCharacterCL::updateSomeClipped (const TTime &currentTimeInMs, CEntityCL *target)
6156 // Snap the entity to the ground.
6158 H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground )
6159 snapToGround();
6162 // Changes the skeleton position.
6163 if(!_Skeleton.empty())
6165 _Skeleton.setPos(pos());
6166 _Skeleton.hide();
6168 // Changes the instance position.
6169 else if(!_Instance.empty())
6171 _Instance.setPos(pos());
6172 _Instance.hide();
6175 if(!ClientCfg.Light)
6177 // Update texture Async Loading
6178 updateAsyncTexture();
6179 // Update lod Texture
6180 updateLodTexture();
6182 // Update FX
6184 H_AUTO ( RZ_Client_Entity_CL_Update_FX )
6185 updateFX();
6189 // Remove Modifiers.
6190 _HPModifiers.clear();
6191 _HPDisplayed.clear();
6193 // Parent
6194 CEntityCL::updateSomeClipped(currentTimeInMs, target);
6195 }// updateSomeClipped //
6197 //-----------------------------------------------
6198 // updateClipped :
6199 //-----------------------------------------------
6200 void CCharacterCL::updateClipped (const TTime &currentTimeInMs, CEntityCL *target)
6202 // hide the scene interface
6203 if (_InSceneUserInterface)
6205 if (_InSceneUserInterface->getActive())
6206 _InSceneUserInterface->setActive (false);
6208 if (_CurrentBubble)
6210 if (_CurrentBubble->getActive())
6211 _CurrentBubble->setActive (false);
6214 // parent
6215 CEntityCL::updateClipped(currentTimeInMs, target);
6216 }// updateClipped //
6218 //-----------------------------------------------
6219 // updateVisiblePostPos :
6220 // Update the entity after all positions done.
6221 //-----------------------------------------------
6222 void CCharacterCL::updateVisiblePostPos(const NLMISC::TTime &currentTimeInMs, CEntityCL *target) // virtual
6224 // Stuff to do only when alive.
6225 if(!isDead())
6227 // Update the head direction.
6229 H_AUTO ( RZ_Client_Entity_CL_Update_Head_Direction )
6230 updateHeadDirection(target);
6232 // Update Blink.
6234 H_AUTO ( RZ_Client_Entity_CL_Update_Blink )
6235 updateBlink(currentTimeInMs);
6239 // Update in scene interface
6240 if(_InSceneUserInterface || _CurrentBubble)
6242 // Draw the entity Name if asked or under the cursor.
6243 bool showIS = mustShowInsceneInterface( (!_Sheet) || (_Sheet->DisplayOSD) );
6244 bool showBubble = true;
6246 // Don't show bubble if lod
6247 if (!_Skeleton.empty() && _Skeleton.isDisplayedAsLodCharacter())
6249 showBubble = false;
6252 // If the name of the character is unknown, no user info
6253 if (_EntityName.empty() && _Title.empty())
6254 showIS = false;
6256 // if mounted : don't display name
6257 if( _Rider != CLFECOMMON::INVALID_SLOT)
6259 showIS = false;
6263 // User Info
6264 if (_InSceneUserInterface)
6266 // Activate
6267 _InSceneUserInterface->setActive (showIS);
6269 if (showIS)
6271 // Update dynamic data
6272 _InSceneUserInterface->updateDynamicData ();
6274 NLMISC::CVectorD pos;
6275 if (getNamePos(pos))
6277 // Check the pos validity
6278 if((isValidDouble(pos.x) && isValidDouble(pos.y) && isValidDouble(pos.z)) == false)
6280 nlwarning("CH:updateVisiblePostPos:%d: invalid pos %f %f %f", _Slot, pos.x, pos.y, pos.z);
6281 nlstop;
6283 _InSceneUserInterface->Position = pos;
6285 else
6287 pos = (box().getMin() + box().getMax())/2;
6288 pos.z = box().getMax().z;
6289 nlassert(isValidDouble(pos.x) && isValidDouble(pos.y) && isValidDouble(pos.z));
6290 _InSceneUserInterface->Position = pos;
6295 // Bubble ?
6296 if (_CurrentBubble)
6298 showBubble &= _CurrentBubble->canBeShown();
6300 // Activate
6301 if (_CurrentBubble->getActive() != showBubble)
6302 _CurrentBubble->setActive (showBubble);
6304 if (showBubble)
6306 // Offset X
6307 sint offsetX = 0;
6308 if (_InSceneUserInterface)
6309 offsetX = - 10 - (_InSceneUserInterface->getWReal() / 2);
6310 _CurrentBubble->setOffsetX (offsetX);
6312 NLMISC::CVectorD pos;
6313 if (!getNamePos(pos))
6315 pos = (box().getMin() + box().getMax())/2;
6316 pos.z = box().getMax().z;
6319 CRaceStatsSheet *sheet = const_cast<CRaceStatsSheet*>(UserEntity->playerSheet());
6320 float namePosZ = sheet->GenderInfos[UserEntity->getGender()].NamePosZNormal;
6321 if (pos.z > box().getMin().z + namePosZ)
6322 pos.z = box().getMin().z + namePosZ;
6323 nlassert(isValidDouble(pos.x) && isValidDouble(pos.y) && isValidDouble(pos.z));
6324 _CurrentBubble->Position = pos;
6329 // parent
6330 CEntityCL::updateVisiblePostPos(currentTimeInMs, target);
6331 }// updateVisiblePostPos //
6334 //-----------------------------------------------
6335 // updatePostCollision :
6336 // Method called each frame to manage the entity.
6337 // \param time : current time of the frame.
6338 // \parem target : pointer on the current entity target.
6339 //-----------------------------------------------
6340 void CCharacterCL::updatePostCollision(const TTime &/* currentTimeInMs */, CEntityCL * /* target */) // virtual
6342 H_AUTO ( RZ_Client_Entity_CL_Update_Post_Collision )
6344 // Finalize PACS position
6346 H_AUTO ( RZ_Client_Entity_CL_Update_Finalize_Move )
6347 pacsFinalizeMove();
6348 // \todo GUIGUI : fait rapidement pour voir les autres se baigner pour la video, faire mieux.
6350 // changed : Malkav , also do this for mektoub (as they can swim)
6351 if(PACS && _Primitive
6352 && (isPlayer() || isNPC()
6353 || (_Sheet && (_Sheet->Race == EGSPD::CPeople::MektoubMount || _Sheet->Race == EGSPD::CPeople::MektoubPacker))
6357 // Is in water ?
6358 if(GR)
6360 UGlobalPosition gPos;
6361 _Primitive->getGlobalPosition(gPos, dynamicWI);
6362 float waterHeight;
6363 if(GR->isWaterPosition(gPos, waterHeight))
6365 if(isSwimming()==false)
6367 if(isDead())
6369 _Mode = MBEHAV::SWIM_DEATH;
6371 else if (isRiding())
6373 _Mode = MBEHAV::MOUNT_SWIM;
6375 // also change mounted entity mode
6376 if (_Mount != CLFECOMMON::INVALID_SLOT)
6378 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
6379 if(mount)
6381 // Set the mount.
6382 mount->setMode(MBEHAV::MOUNT_SWIM);
6383 mount->computeAutomaton();
6384 mount->computeAnimSet();
6385 mount->setAnim(CAnimationStateSheet::Idle);
6389 else
6391 _Mode = MBEHAV::SWIM;
6394 // Compute the current automaton
6395 computeAutomaton();
6396 // Update the animation set according to the mode.
6397 computeAnimSet();
6398 // Animset changed -> update current animation
6399 setAnim(CAnimationStateSheet::Idle);
6402 else
6404 if(isSwimming())
6406 if(isDead())
6408 _Mode = MBEHAV::DEATH;
6410 else if (isRiding())
6412 _Mode = MBEHAV::MOUNT_NORMAL;
6413 // also change mounted entity mode
6414 if (_Mount != CLFECOMMON::INVALID_SLOT)
6416 CCharacterCL *mount = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(_Mount));
6417 if(mount)
6419 // Set the mount.
6420 mount->setMode(MBEHAV::MOUNT_NORMAL);
6421 mount->computeAutomaton();
6422 mount->computeAnimSet();
6423 mount->setAnim(CAnimationStateSheet::Idle);
6427 else
6429 _Mode = MBEHAV::NORMAL;
6432 // Compute the current automaton
6433 computeAutomaton();
6434 // Update the animation set according to the mode.
6435 computeAnimSet();
6436 // Animset changed -> update current animation
6437 setAnim(CAnimationStateSheet::Idle);
6443 }// updatePostCollision //
6446 //-----------------------------------------------
6447 // getTheMove :
6448 //-----------------------------------------------
6449 double CCharacterCL::getTheMove(double loopTimeStep, double oldMovingTimeOffset, double oldMovingTimeOffsetRun) const
6451 double move;
6452 if(_CurrentState)
6454 // A Real Move State
6455 if(_CurrentState->Move)
6457 // Get the covered distance from the animation.
6458 move = getTheMove(loopTimeStep, oldMovingTimeOffset, MOVE);
6459 if(runFactor() > 0.0)
6461 // Blend the 2 move (Walk & Run).
6462 move = move*(1.0-runFactor()) + getTheMove(loopTimeStep, oldMovingTimeOffsetRun, MOVE_BLEND_OUT)*runFactor();
6463 // The move must be significant.
6464 if((move>0.0) && (move<ClientCfg.SignificantDist))
6465 move = ClientCfg.SignificantDist;
6468 // Slide
6469 else if(_CurrentState->Slide)
6471 move = speed() * loopTimeStep;
6472 // The move must be significant.
6473 if((move>0.0) && (move<ClientCfg.SignificantDist))
6474 move = ClientCfg.SignificantDist;
6476 else
6477 move = 0.0;
6479 else
6481 move = speed() * loopTimeStep;
6482 // The move must be significant.
6483 if((move>0.0) && (move<ClientCfg.SignificantDist))
6484 move = ClientCfg.SignificantDist;
6486 // Check the move is significant.
6487 CHECK(!((move>0.0) && (move<ClientCfg.SignificantDist)));
6488 // Return the move done by the entity since last time.
6489 return move;
6490 }// getTheMove //
6491 //-----------------------------------------------
6492 // getTheMove :
6493 //-----------------------------------------------
6494 double CCharacterCL::getTheMove(double loopTimeStep, double oldMovingTimeOffset, TAnimationType channel) const
6496 double move;
6497 // Compute a linear motion when the animation is missing.
6498 if(animIndex(channel) == CAnimation::UnknownAnim)
6500 double offsetT = _DestTime - _LastFrameTime;
6501 if(offsetT <= 0.0)
6503 // \todo GUIGUI : in this case, 'loopTimeStep' should not decrease so FIX IT.
6504 move = dist2Dest();
6506 else
6508 move = dist2Dest() * (loopTimeStep / offsetT);
6509 // The move must be significant.
6510 if((move>0.0) && (move<ClientCfg.SignificantDist))
6511 move = ClientCfg.SignificantDist;
6514 // Get the motion done by the animation.
6515 else
6516 move = computeMotion(oldMovingTimeOffset, channel);
6518 CHECK(!(move>0.0 && move<ClientCfg.SignificantDist));
6519 return move;
6520 }// getTheMove //
6522 //-----------------------------------------------
6523 // updatePosCombatFloat :
6524 //-----------------------------------------------
6525 void CCharacterCL::updatePosCombatFloat(double /* frameTimeRemaining */, CEntityCL *target) // virtual
6527 H_AUTO_USE ( RZ_Client_Character_CL_Update_Pos_Combat_Float )
6529 // The target is valid
6530 if(target)
6532 // Get the position where the attacker should go to attack his target according to the attack angle.
6533 CVectorD dirToTarget = target->pos() - pos();
6534 dirToTarget.z = 0.0;
6535 if(ClientCfg.Local
6536 || ((dirToTarget != CVectorD::Null)
6537 && fabs(target->pos().x-target->lastFramePos().x)>0.01
6538 && fabs(target->pos().y-target->lastFramePos().y)>0.01))
6540 double angToTarget = atan2(dirToTarget.y, dirToTarget.x);
6541 _DestPos = target->getAttackerPos(angToTarget, attackRadius() + ClientCfg.AttackDist);
6543 else
6544 _DestPos = target->getAttackerPos(_TargetAngle, attackRadius() + ClientCfg.AttackDist);
6545 // Compute the distance to destination.
6546 CVectorD vectToDest = _DestPos - pos();
6547 vectToDest.z = 0.0;
6548 // Distance to destination is big enough.
6549 if(vectToDest.norm() > ClientCfg.DestThreshold)
6551 dist2Dest(vectToDest.norm());
6552 // Compute the time to reach the destination at the max speed.
6553 double lengthOfTimeToDest = 0.0; // 0 = No Speed Limit
6554 _FirstPos = _DestPos;
6555 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6556 _FirstTime = _DestTime;
6558 // The time remaining will be enough to reach the destination
6559 if(frameTimeRemaining >= lengthOfTimeToDest)
6561 _FirstPos = _DestPos;
6562 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6563 _FirstTime = _DestTime;
6565 // The time remaining is not enough to reach the destination at max speed -> compute a first pos possible to reach.
6566 else
6568 _FirstPos = pos() + vectToDest*frameTimeRemaining/lengthOfTimeToDest;
6569 _DestTime = _LastFrameTime + lengthOfTimeToDest + ClientCfg.ChaseReactionTime;
6570 _FirstTime = _LastFrameTime + frameTimeRemaining + ClientCfg.ChaseReactionTime;
6573 // Compute the distance to the first position.
6574 CVectorD tmp2computeDist2FirstPos = _FirstPos-pos();
6575 tmp2computeDist2FirstPos.z = 0.0;
6576 dist2FirstPos(tmp2computeDist2FirstPos.norm());
6578 updatePosCombatFloatChanged(target);
6580 // Destination is too close (will consider to be at destination.
6581 else
6583 _FirstPos = _DestPos = pos();
6584 dist2Dest(0.0);
6585 dist2FirstPos(0.0);
6586 _FirstTime = _DestTime = _LastFrameTime;
6589 // The target is not allocated.
6590 else
6592 _FirstPos = _DestPos = pos();
6593 dist2Dest(0.0);
6594 dist2FirstPos(0.0);
6595 _FirstTime = _DestTime = _LastFrameTime;
6597 }// updatePosCombatFloat //
6599 //-----------------------------------------------
6600 // updatePos :
6601 // Upadte the player position
6602 // \param time : Time for the position of the entity after the motion.
6603 // \param target : pointer on the current target.
6604 // \todo GUIGUI : compute it when receiving a new stage instead of every frame (should be faster).
6605 // \todo GUIGUI: recompute distance to destination even if the Stage not reached.
6606 //-----------------------------------------------
6607 ADD_METHOD(void CCharacterCL::updatePos(const TTime &currentTimeInMs, CEntityCL *target))
6608 _OldAutomaton = _CurrentAutomaton;
6609 // Compute the Time Step.
6610 double frameTimeRemaining = computeTimeStep(((double)currentTimeInMs)*0.001);
6612 // Update the LodCharacter Animation.
6613 if(_LodCharacterAnimEnabled)
6615 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Lod_Animation );
6616 // \todo GUIGUI : replace 'getSpeedFactor' by the correct speed factor !!
6617 // update lod anim time. multiply by speed factor of the most important slot.
6618 _LodCharacterAnimTimeOffset += DT * _PlayList->getSpeedFactor(_LodCharacterMasterAnimSlot);
6620 // BLEND
6621 if(_PlayList)
6623 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Set_Play_List );
6624 // \todo GUIGUI : do something better for the blend (mounts there).
6625 if(isRiding() || ClientCfg.BlendFrameNumber == 0 || _BlendRemaining <= 0)
6627 _BlendRemaining = 0;
6628 _PlayList->setAnimation(ACTION, UPlayList::empty);
6629 _PlayList->setWeight(ACTION, 0.0f);
6630 if(runFactor() < 0.5 || (_CurrentAnimSet[MOVE_BLEND_OUT]==0))
6632 if(_CurrentAnimSet[MOVE])
6634 _CurrentAnimSet[ACTION] = _CurrentAnimSet [MOVE];
6635 animState (ACTION, animState (MOVE));
6636 animIndex (ACTION, animIndex (MOVE)); // This also call "animId" and set it.
6637 animOffset(ACTION, animOffset(MOVE));
6639 else
6641 _CurrentAnimSet[ACTION] = 0;
6642 animState (ACTION, CAnimationStateSheet::UnknownState);
6643 animIndex (ACTION, CAnimation::UnknownAnim); // This also call "animId" and set it.
6644 animOffset(ACTION, 0.0);
6647 else
6649 _CurrentAnimSet[ACTION] = _CurrentAnimSet [MOVE_BLEND_OUT];
6650 animState (ACTION, animState (MOVE_BLEND_OUT));
6651 animIndex (ACTION, animIndex (MOVE_BLEND_OUT)); // This also call "animId" and set it.
6652 animOffset(ACTION, animOffset(MOVE_BLEND_OUT));
6654 _AnimReversed[ACTION] = false;
6656 else
6658 double animLength = EAM->getAnimationLength(animId(ACTION));
6659 // Check Anim length
6660 if(animOffset(ACTION)+frameTimeRemaining > animLength)
6661 animOffset(ACTION, animLength);
6662 else
6663 animOffset(ACTION, animOffset(ACTION)+frameTimeRemaining);
6664 // Compute weight step.
6665 float w = (float)_BlendRemaining/(float)(ClientCfg.BlendFrameNumber+1);
6666 // Set Old Anim Weight.
6667 _PlayList->setWeight(ACTION, w);
6668 // Set New Anim Weight.
6669 _PlayList->setWeight(MOVE, 1.f-w);
6672 uint antiFreezeCounter = 0;
6673 // While the time Step is not Null.
6674 while(frameTimeRemaining > 0)
6676 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_WhileStep );
6678 //--------------------//
6679 //--------------------//
6680 // ANTI-FREEZE SYSTEM //
6681 // If too many loop, display some infos
6682 if(antiFreezeCounter > 50)
6685 nlwarning("CH:updatePos:antiFreeze:%d: frameTimeRemaining '%f'", _Slot, frameTimeRemaining);
6686 nlwarning("CH:updatePos:antiFreeze:%d: Automaton '%s'", _Slot, _CurrentAutomaton.c_str());
6687 nlwarning("CH:updatePos:antiFreeze:%d: _IsThereAMode '%s'", _Slot, _IsThereAMode?"true":"false");
6688 nlwarning("CH:updatePos:antiFreeze:%d: dist2Dest '%f'", _Slot, dist2Dest());
6689 nlwarning("CH:updatePos:antiFreeze:%d: Mode '%s(%d)'", _Slot, modeToString(_Mode).c_str(), _Mode);
6690 nlwarning("CH:updatePos:antiFreeze:%d: Mode Wanted '%s(%d)'", _Slot, modeToString(_ModeWanted).c_str(), _ModeWanted);
6691 nlwarning("CH:updatePos:antiFreeze:%d: Anim State Move '%s(%d)'", _Slot, CAnimationState::getAnimationStateName(animState(MOVE)).c_str(), animState(MOVE));
6693 // Once too many more time reached, leave the method.
6694 if(antiFreezeCounter > 60)
6695 break;
6697 // Update antiFreezeCounter.
6698 ++antiFreezeCounter;
6699 // ANTI-FREEZE SYSTEM //
6700 //--------------------//
6701 //--------------------//
6702 // \todo GUIGUI : improve dist2first and dist2dest
6703 // Update Stages
6704 updateStages();
6705 // \todo GUIGUI : Bug with _TargetAngle in fight float, we overwrite here angle sent by the server ?
6706 // If the entity is too far (orientation not received yet), set the front vector as the moving direction.
6707 CVectorD distToUser = pos()-UserEntity->pos();
6708 distToUser.z = 0.0;
6709 if(distToUser.norm()*1000.0 > CLFECOMMON::THRESHOLD_ORIENTATION*0.9)
6711 if(_FirstPos != INVALID_POS)
6713 CVectorD dirToFirstP = _FirstPos-pos();
6714 dirToFirstP.z = 0.0;
6715 if(dirToFirstP != CVectorD::Null)
6717 front(dirToFirstP.normed(), false, false);
6718 _TargetAngle = atan2(front().y, front().x);
6722 // Mode Combat Float :
6723 if(!_IsThereAMode && (_Mode == MBEHAV::COMBAT_FLOAT))
6725 // Update the position in combat float.
6726 updatePosCombatFloat(frameTimeRemaining, target);
6728 // Compute the average speed to the destination.
6729 // double spd =
6730 computeSpeed();
6733 bool stageReach = false;
6734 bool allToFirstPos = false;
6735 // Compute time to Stage or full Time Step if Stage too far.
6736 double loopTimeStep = frameTimeRemaining;
6737 double buLoopTimeStep = 0.0;
6738 double checkLoopTimeStep = loopTimeStep;
6739 // Update the animation used according to the speed/end anim/etc..
6740 updateAnimationState();
6741 // Backup the old time offset.
6742 double oldMovingTimeOffset = animOffset(MOVE);
6743 double oldMovingTimeOffsetRun = animOffset(MOVE_BLEND_OUT);
6744 // WARNING -> Unknown Animation Selected.
6745 // Play the time step for the loop and truncate to End Anim if Time Step too big.
6746 if((_CurrentState != 0) && (animIndex(MOVE) != CAnimation::UnknownAnim))
6747 playToEndAnim(oldMovingTimeOffset, loopTimeStep);
6748 /////////////////
6749 // -- CHECK -- //
6750 if(loopTimeStep > checkLoopTimeStep)
6752 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > checkLoopTimeStep(%f).", _Slot, loopTimeStep, checkLoopTimeStep);
6753 if(ClientCfg.Check)
6754 nlstop;
6755 loopTimeStep = checkLoopTimeStep;
6757 checkLoopTimeStep = loopTimeStep;
6758 // -- END CHECK -- //
6759 /////////////////////
6760 // (DEBUG) : Backup the Animation Time Offset after the adjustment with end anim to make some checks.
6761 double backupAnimTimeOff = animOffset(MOVE);
6763 bool posInStage = false;
6764 double stageTime = -1.0;
6765 if(!_Stages._StageSet.empty())
6767 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Move );
6768 // Get the reference on the current stage.
6769 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
6770 CStage &stage = (*it).second;
6771 if(_Mode == MBEHAV::COMBAT_FLOAT && !_IsThereAMode)
6772 posInStage = false;
6773 else
6774 posInStage = stage.isPresent(CLFECOMMON::PROPERTY_POSITION);
6775 stageTime = stage.time();
6777 // dist2FirstPos() should not be Null if the destination is not Null (because of the code in updateStage).
6778 if(dist2FirstPos() > 0.0)
6780 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_dist2FirstPos_gt_0 );
6782 // Get the covered distance from the animation.
6783 double move = getTheMove(loopTimeStep, oldMovingTimeOffset, oldMovingTimeOffsetRun);
6784 // The move is big enough to reach the first step with motion.
6785 if(move >= dist2FirstPos()) // dist2FirstPos() > 0 -> move > 0.
6787 double percent = dist2FirstPos() / move;
6788 // Adjust loopTimeStep
6789 loopTimeStep *= percent;
6790 if(loopTimeStep > checkLoopTimeStep) // prevent bugs because of the double's precision
6791 loopTimeStep = checkLoopTimeStep;
6792 if(loopTimeStep < 0.0)
6793 loopTimeStep = 0.0;
6794 // Update Animation Time Offset (move greater than the dist to next stage; update animation time to get them equal).
6795 animOffset(MOVE, oldMovingTimeOffset + (animOffset(MOVE) -oldMovingTimeOffset )*percent);
6796 animOffset(MOVE_BLEND_OUT, oldMovingTimeOffsetRun + (animOffset(MOVE_BLEND_OUT)-oldMovingTimeOffsetRun)*percent);
6797 // \todo GUIGUI : check if the following line is necessary
6798 buLoopTimeStep = loopTimeStep;
6799 // First Position Reached
6800 pos(_FirstPos);
6801 dist2FirstPos(0.0); // Current entity position is now the same as the First position so dis is Null.
6802 // Complete the Stage.
6803 if(_Mode != MBEHAV::COMBAT_FLOAT || _IsThereAMode)
6805 if(posInStage)
6806 stageReach = true;
6807 else
6808 allToFirstPos = true;
6811 // Even if the movement is not enough to reach the first position, move the entity to this position.
6812 else if(move > 0.0)
6814 // Compute the vector to the first stage with a position.
6815 CVectorD vectToFirstPos = _FirstPos - pos();
6816 vectToFirstPos.z = 0.0f;
6817 // Update entity position.
6818 if(vectToFirstPos != CVectorD::Null)
6819 pos(pos() + vectToFirstPos*(move/dist2FirstPos()));
6821 // Else : There is no move.
6823 else
6825 CHECK(posInStage==false && dist2Dest()<=0.0);
6828 // If there is no position in the next stage and the stage should be done already.
6829 if(!_Stages._StageSet.empty() && !posInStage && !stageReach && !allToFirstPos && ((_LastFrameTime+loopTimeStep) >= stageTime))
6831 // Backup 'loopTimeStep' just in case of the stage could not be done.
6832 buLoopTimeStep = loopTimeStep;
6833 // Adjust loopTimeStep
6834 loopTimeStep = stageTime - _LastFrameTime;
6835 if(loopTimeStep > checkLoopTimeStep) // prevent bugs because of the double's precision
6836 loopTimeStep = checkLoopTimeStep;
6837 if(loopTimeStep < 0.0)
6838 loopTimeStep = 0.0;
6840 // \todo GUIGUI : adjust timeOffset, because we stopped the loop before
6842 // Stage complete.
6843 stageReach = true;
6845 /////////////////
6846 // -- CHECK -- //
6847 // Check the Animation Time Offset is not became greater than the old.
6848 if(animOffset(MOVE) > backupAnimTimeOff)
6850 nlwarning("CH:updtPos:%d: backupAnimTimeOff(%f) < AnimationsTimeOffset(%f) animLen(%f) -> animOffset(MOVE) = backupAnimTimeOff",
6851 _Slot, backupAnimTimeOff, animOffset(MOVE), EAM->getAnimationLength(animId(MOVE)));
6852 if(ClientCfg.Check)
6853 nlstop;
6854 animOffset(MOVE, backupAnimTimeOff);
6856 // Check loopTimeStep is not < 0;
6857 if(loopTimeStep < 0.0)
6859 nlwarning("CH:updtPos:%d: loopTimeStep(%f) < 0 -> loopTimeStep=0.0.", _Slot, loopTimeStep);
6860 if(ClientCfg.Check)
6861 nlstop;
6862 loopTimeStep = 0.0;
6864 // time spent could not be bigger than the time remaining
6865 if(loopTimeStep > frameTimeRemaining)
6867 nlwarning("CH:updtPos:%d: loopTimeStep(%f) > frameTimeRemaining(%f) -> loopTimeStep=frameTimeRemaining.", _Slot, loopTimeStep, frameTimeRemaining);
6868 if(ClientCfg.Check)
6869 nlstop;
6870 loopTimeStep = frameTimeRemaining;
6872 // -- END CHECK -- //
6873 /////////////////////
6874 // Manage Events that could be created by the animation (like sound).
6876 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Anim_Event )
6877 animEventsProcessing(oldMovingTimeOffset, animOffset(MOVE));
6879 // Apply all stages until the first stage with a pos.
6880 if(allToFirstPos)
6882 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_All_Stage_To_First_Pos );
6883 applyAllStagesToFirstPos();
6885 // Stage is complete, apply modifications.
6886 else if(stageReach)
6888 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Apply_Current_Stage );
6889 if(!applyCurrentStage())
6890 loopTimeStep = buLoopTimeStep;
6892 // Compute the remaining Time Step.
6893 frameTimeRemaining -= loopTimeStep;
6894 // Update the last Time.
6895 _LastFrameTime += loopTimeStep;
6896 }// while(frameTimeRemaining > 0) //
6898 ////////////////////////////////
6899 // UPDATE THE ENTITY POSITION //
6900 // Set the new position into PACS.
6902 H_AUTO_USE ( RZ_Client_Entity_CL_Update_Pos_Pacs );
6903 pacsMove(pos());
6906 /// (DEBUG) ///
6907 // Check frameTimeRemaining is perfectly equal to 0.
6908 if(frameTimeRemaining < 0.0)
6909 nlwarning("CCharacterCL::updatePos : frameTimeRemaining(%f) < 0 ! This should never happen.", frameTimeRemaining);
6910 /// (END DEBUG) ///
6912 // Update the children display.
6914 H_AUTO ( RZ_Client_Entity_CL_Update_Pos_Children );
6915 std::list<CEntityCL *>::iterator itTmp, it = _Children.begin();
6916 while(it != _Children.end())
6918 itTmp = it;
6919 // Next Child (done before just in case child is detached during his processFrame).
6920 it++;
6922 if(*itTmp)
6924 CCharacterCL *child = dynamic_cast<CCharacterCL *>(*itTmp);
6925 if(child)
6927 if ( ! ClientCfg.Light )
6929 // Update the animation offset for the child.
6930 double animLength = EAM->getAnimationLength(animId(MOVE));
6931 if(animLength > 0.0)
6933 double factor = animOffset(MOVE) / animLength;
6934 if(factor > 1.0)
6935 factor = 1.0;
6936 double childTimeOffset = factor*EAM->getAnimationLength(child->animId(MOVE));
6937 child->animOffset(MOVE, childTimeOffset);
6939 else
6940 child->animOffset(MOVE, 0.0);
6941 child->processFrame(currentTimeInMs);
6943 child->pacsMove(pos()); // Move the child at the same position than the parent.
6945 else
6946 nlwarning("Character '%d': Child is not a 'CCharacterCL'.", _Slot);
6948 else
6949 nlwarning("Character '%d': Child not allocated.", _Slot);
6952 }// updatePos //
6954 //-----------------------------------------------
6955 // updateVisiblePostRender :
6956 // Update the entity after the render like for the head offset.
6957 //-----------------------------------------------
6958 void CCharacterCL::updateVisiblePostRender() // virtual
6960 // Compute the headoffset
6961 if(_HeadBoneId != -1 && !_Skeleton.empty())
6963 if(_Skeleton.getLastClippedState() && _Skeleton.isBoneComputed(_HeadBoneId))
6965 UBone headBone=_Skeleton.getBone(_HeadBoneId);
6966 const CMatrix &headMat = headBone.getLastWorldMatrixComputed();
6967 _HeadOffset = headMat.getPos()-pos();
6968 _HeadOffsetComputed= true;
6971 }// updateVisiblePostRender //
6973 //-----------------------------------------------
6975 void CCharacterCL::updateAllPostRender()
6979 //-----------------------------------------------
6980 // processFrame :
6981 //-----------------------------------------------
6982 void CCharacterCL::processFrame(const TTime &currentTimeInMs)
6984 // Prepare stages and update information from them.
6985 updateStages();
6987 // Compute the time remaining until frame completely processed.
6988 double timeRemaining = computeTimeStep(((double)currentTimeInMs)*0.001);
6990 // Compute the time spent between 2 frames.
6991 while(timeRemaining > 0.0)
6993 // Time already processed until now.
6994 double timeProcessed = timeRemaining;
6996 // Process Stages.
6997 if(!_Stages._StageSet.empty())
6999 // Get the reference on the current stage.
7000 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
7001 CStage &stage = (*it).second;
7003 // Check if the stage should be done already.
7004 if((_LastFrameTime+timeProcessed) >= stage.time())
7006 // Stage Done during the Frame.
7007 if(stage.time() > _LastFrameTime)
7008 timeProcessed = stage.time() - _LastFrameTime;
7009 // This Stage should have been done already before last frame
7010 else
7011 timeProcessed = 0.0;
7013 // Process the stage.
7014 processStage(stage);
7016 // Stage complete.
7017 _Stages._StageSet.erase(it);
7021 // Compute the remaining Time Step.
7022 timeRemaining -= timeProcessed;
7023 // Update the last Time.
7024 _LastFrameTime += timeProcessed;
7027 // Just to be sure, Last frame time = current time once all is done.
7028 _LastFrameTime = ((double)currentTimeInMs)*0.001;
7029 }// processFrame //
7031 //-----------------------------------------------
7032 // processStage :
7033 // Process the stage.
7034 //-----------------------------------------------
7035 void CCharacterCL::processStage(CStage &stage)
7037 // Apply Mode (if there is a mode, there is a position too).
7038 pair<bool, sint64> resultMode = stage.property(PROPERTY_MODE);
7039 if(resultMode.first)
7041 uint8 mo = *(uint8 *)(&resultMode.second);
7042 MBEHAV::EMode mode = (MBEHAV::EMode)mo;
7043 if(mode != _Mode)
7045 // release the old mode.
7046 if ( (_Mode == MBEHAV::MOUNT_NORMAL || _Mode == MBEHAV::MOUNT_SWIM)
7047 && (mode != MBEHAV::MOUNT_NORMAL && mode != MBEHAV::MOUNT_SWIM)
7050 // Unlink the mount and the rider.
7051 parent(CLFECOMMON::INVALID_SLOT);
7052 _Mount = CLFECOMMON::INVALID_SLOT;
7053 _Rider = CLFECOMMON::INVALID_SLOT;
7055 // Restore collisions.
7056 if(_Primitive)
7058 // \todo GUIGUI : do that without dynamic cast
7059 if(dynamic_cast<CPlayerCL *>(this))
7060 _Primitive->setOcclusionMask(MaskColPlayer);
7061 else
7062 _Primitive->setOcclusionMask(MaskColNpc);
7065 // Get stage position.
7066 if(stage.getPos(_OldPos))
7068 // Backup the time
7069 _OldPosTime = stage.time();
7070 // Unseat the entity at the position given in the stage.
7071 pacsMove(_OldPos);
7073 else
7074 nlwarning("CH:processStage:%d: The stage should have a position with the mode.", _Slot);
7077 // Set the new mode.
7078 _Mode = mode;
7079 _ModeWanted = mode;
7080 // Compute the automaton
7081 computeAutomaton();
7082 computeAnimSet();
7083 setAnim(CAnimationStateSheet::Idle);
7086 // Not a mode -> so search for a position.
7087 }// processStage //
7090 //-----------------------------------------------
7091 // updateBlink :
7092 // Update the player blink state
7093 //-----------------------------------------------
7094 void CCharacterCL::updateBlink(const TTime &currentTimeInMs)
7096 // Only for homine
7097 GSGENDER::EGender gender = getGender();
7098 if ((gender == GSGENDER::male) || (gender == GSGENDER::female))
7100 float blend;
7102 // Some parameters
7103 static const double blinkTime = 100.f;
7104 static const double minBlinkLength = 500.f;
7105 static const double maxBlinkLength = 5000.f;
7107 // Next blink time is valid ?
7108 bool validTime = (_NextBlinkTime + blinkTime >= currentTimeInMs) && (_NextBlinkTime <= (currentTimeInMs + maxBlinkLength));
7110 // Blink end ?
7111 bool blinkEnd = (currentTimeInMs >= _NextBlinkTime + blinkTime);
7113 // Blink is finished or next blink time is invalid ?
7114 if ( blinkEnd || !validTime )
7116 blend = 0;
7118 // Compute next time
7119 _NextBlinkTime = (TTime)(((double)rand () / (double)RAND_MAX) * (maxBlinkLength - minBlinkLength) + minBlinkLength + (double)currentTimeInMs);
7121 else
7123 // Blink time ?
7124 if (currentTimeInMs >= _NextBlinkTime)
7126 blend = 100.f;
7128 else
7130 // Do nothing
7131 return;
7135 // Get the face
7136 SInstanceCL *face = getFace ();
7138 // Set the blend shape
7139 if(face && !face->Current.empty())
7140 face->Current.setBlendShapeFactor ("visage_100", blend, true);
7142 }// updateBlink //
7145 //-----------------------------------------------
7146 // getFace :
7147 // Update eyes blink. For the moment, called by updatePos.
7148 //-----------------------------------------------
7149 CEntityCL::SInstanceCL *CCharacterCL::getFace ()
7151 // Implemented in CPlayerCL
7152 return idx2Inst(_FaceIdx);
7156 //---------------------------------------------------
7157 // updateDisplay :
7158 // Get the entity position and set all visual stuff with it.
7159 // \todo GUIGUI : put this method 'virtual' to have a different code for the user (no playlist).
7160 // \todo GUIGUI : manage the parent better.
7161 //---------------------------------------------------
7162 ADD_METHOD(void CCharacterCL::updateDisplay(CEntityCL *parent))
7163 // Animable ?
7164 if(_PlayList)
7166 // Reverse the animation if needed.
7167 const double animOffsetMOV = _AnimReversed[MOVE] ? EAM->getAnimationLength(animId(MOVE)) - animOffset(MOVE) : animOffset(MOVE);
7168 const double animOffsetACT = _AnimReversed[ACTION] ? EAM->getAnimationLength(animId(ACTION)) - animOffset(ACTION) : animOffset(ACTION);
7169 const double animOffsetBLE = _AnimReversed[MOVE_BLEND_OUT] ? EAM->getAnimationLength(animId(MOVE_BLEND_OUT)) - animOffset(MOVE_BLEND_OUT): animOffset(MOVE_BLEND_OUT);
7170 // Update Speed Factor
7171 _PlayList->setTimeOrigin(MOVE, TimeInSec-animOffsetMOV);
7172 _PlayList->setTimeOrigin(ACTION, TimeInSec-animOffsetACT);
7173 _PlayList->setTimeOrigin(MOVE_BLEND_OUT, TimeInSec-animOffsetBLE);
7174 float weight;
7175 if(_BlendRemaining)
7176 weight = _PlayList->getLocalWeight(MOVE, TimeInSec);
7177 else
7178 weight = 1.0f;
7179 _PlayList->setWeight(MOVE, weight*(float)(1.0-runFactor()));
7180 _PlayList->setWeight(MOVE_BLEND_OUT, weight*(float)runFactor());
7181 // If the animation exist update the display.
7182 if(animIndex(MOVE) != CAnimation::UnknownAnim)
7184 // POSITION //
7185 // Get the 3D position for the current time in the animation (Vector Null if animation has no move).
7186 CVector currentAnimPos;
7187 if(!EAM->interpolate(animId(MOVE), animOffsetMOV, currentAnimPos))
7188 currentAnimPos = CVector::Null;
7189 else
7191 // If the current animation state is a move, do not take the Y move in the animation because it's the code that compute the move.
7192 if(_CurrentState && _CurrentState->Move)
7194 CVector currentAnimPosStart;
7195 if(!EAM->interpolate(animId(MOVE), 0.0, currentAnimPosStart))
7196 currentAnimPosStart = CVector::Null;
7197 if(_CurrentState->XFactor) currentAnimPos.x = currentAnimPosStart.x;
7198 if(_CurrentState->YFactor) currentAnimPos.y = currentAnimPosStart.y;
7199 if(_CurrentState->ZFactor) currentAnimPos.z = currentAnimPosStart.z;
7201 // Scale the animation with the Character Scale Pos if the animation need it.
7203 bool applyCharacterScalePosFactor = true;
7204 // \todo GUIGUI : faire cette histoire de emote beaucoup mieux, C NULL.
7205 const CAnimationState *animStatePtr;
7206 // If the current animation is an emote, get the right animation state.
7207 if(animState(MOVE) == CAnimationStateSheet::Emote)
7208 animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(_SubStateKey);
7209 else
7210 animStatePtr = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
7211 if(animStatePtr)
7213 const CAnimation *anim = animStatePtr->getAnimation(animIndex(MOVE));
7214 if(anim)
7215 applyCharacterScalePosFactor = anim->applyCharacterScalePosFactor();
7217 // scale it.
7218 if(applyCharacterScalePosFactor)
7219 currentAnimPos *= _CharacterScalePos;
7221 // Scale according to the gabarit (choose at the character creation).
7222 currentAnimPos *= _CustomScalePos;
7224 // Blend Walk/Run
7225 if(runFactor() > 0.0)
7227 CVector currentAnimPosRun;
7228 if(!EAM->interpolate(animId(MOVE_BLEND_OUT), animOffsetBLE, currentAnimPosRun))
7229 currentAnimPosRun = CVector::Null;
7230 else
7232 // If the current animation state is a move, do not take the Y move in the animation because it's the code that compute the move.
7233 if(_CurrentState && _CurrentState->Move)
7235 CVector currentAnimPosStart;
7236 if(!EAM->interpolate(animId(MOVE_BLEND_OUT), 0.0, currentAnimPosStart))
7237 currentAnimPosStart = CVector::Null;
7238 if(_CurrentState->XFactor) currentAnimPosRun.x = currentAnimPosStart.x;
7239 if(_CurrentState->YFactor) currentAnimPosRun.y = currentAnimPosStart.y;
7240 if(_CurrentState->ZFactor) currentAnimPosRun.z = currentAnimPosStart.z;
7242 // Scale it by the CharacterScalePos, if needed, according to the animation.
7243 bool applyCharacterScalePosFactor = true;
7244 const CAnimationState *animStatePtr = _CurrentAnimSet[MOVE_BLEND_OUT]->getAnimationState(animState(MOVE_BLEND_OUT));
7245 if(animStatePtr)
7247 const CAnimation *anim = animStatePtr->getAnimation(animIndex(MOVE_BLEND_OUT));
7248 if(anim)
7249 applyCharacterScalePosFactor = anim->applyCharacterScalePosFactor();
7251 // scale it.
7252 if(applyCharacterScalePosFactor)
7253 currentAnimPosRun *= _CharacterScalePos;
7254 // Scale according to the gabarit.
7255 currentAnimPosRun *= _CustomScalePos;
7258 currentAnimPos = currentAnimPos*(float)(1.0-runFactor()) + currentAnimPosRun*(float)runFactor();
7260 // ROTATION //
7261 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7262 CQuat currentAnimRot;
7263 if(!EAM->interpolate(animId(MOVE), animOffsetMOV, currentAnimRot))
7264 currentAnimRot = CQuat::Identity;
7265 else
7267 // If the animation is a rotation -> Do just a part of the animation.
7268 if(parent==0 && _CurrentState && _CurrentState->Rotation && _RotationFactor!=-1.0)
7270 // Get the Rotation at the beginning of the animation.
7271 CQuat currentAnimRotStart;
7272 if(!EAM->interpolate(_PlayList->getAnimation(MOVE), 0.0, currentAnimRotStart))
7273 currentAnimRotStart = CQuat::Identity;
7275 double animLength = EAM->getAnimationLength(animId(MOVE));
7277 // Get the Rotation at the beginning of the animation.
7278 CQuat currentAnimRotEnd;
7279 if(!EAM->interpolate(_PlayList->getAnimation(MOVE), animLength, currentAnimRotEnd))
7280 currentAnimRotEnd = CQuat::Identity;
7282 // Get the angle done by the animation from the beginning
7283 CQuat rotStartTmp = currentAnimRotStart;
7284 rotStartTmp.invert();
7285 CQuat rotTmp = rotStartTmp * currentAnimRot;
7286 float ang = rotTmp.getAngle();
7288 currentAnimRot = applyRotationFactor(currentAnimRot, (float)_RotationFactor, currentAnimRotStart, currentAnimRotEnd, (float)(animOffsetMOV/animLength));
7290 // Get the angle done once scaled.
7291 rotTmp = rotStartTmp * currentAnimRot;
7292 CMatrix rotMat;
7293 rotMat.identity();
7294 rotMat.rotateZ(_CurrentState->RotFactor*(ang-rotTmp.getAngle()));
7296 // Apply the scaled rotation to the position.
7297 currentAnimPos = rotMat*currentAnimPos;
7298 //// OLD ////
7300 // Get the Rotation at the beginning of the animation.
7301 CQuat currentAnimRotStart;
7302 if(!EAM->interpolate(_PlayList->getAnimation(MOVE), 0.0, currentAnimRotStart))
7303 currentAnimRotStart = CQuat::Identity;
7305 // Find the closest quat.
7306 // currentAnimRotStart.makeClosest(currentAnimRot);
7308 // Get the angle done by the animation from the beginning
7309 CQuat rotStartTmp = currentAnimRotStart;
7310 rotStartTmp.invert();
7311 CQuat rotTmp = rotStartTmp * currentAnimRot;
7312 float ang = rotTmp.getAngle();
7314 // Get the Rotation scaled.
7315 currentAnimRot = CQuat::slerp(currentAnimRotStart, currentAnimRot, (float)_RotationFactor);
7317 // Get the angle done once scaled.
7318 rotTmp = rotStartTmp * currentAnimRot;
7319 CMatrix rotMat;
7320 rotMat.identity();
7321 rotMat.rotateZ(_CurrentState->RotFactor*(ang-rotTmp.getAngle()));
7323 // Apply the scaled rotation to the position.
7324 currentAnimPos = rotMat*currentAnimPos;
7326 //// FIN OLD ////
7329 // Blend Walk/Run
7330 if(runFactor() > 0.0)
7332 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7333 CQuat currentAnimRotRun;
7334 if(!EAM->interpolate(animId(MOVE_BLEND_OUT), animOffsetBLE, currentAnimRotRun))
7335 currentAnimRotRun = CQuat::Identity;
7336 currentAnimRotRun.makeClosest(currentAnimRot);
7337 currentAnimRot = CQuat::slerp(currentAnimRot, currentAnimRotRun, (float)runFactor());
7340 // Animation Matrix
7341 CMatrix AnimMatrixRot;
7342 AnimMatrixRot.identity();
7343 AnimMatrixRot.setRot(currentAnimRot);
7345 // Rotation 180 degrees Matrix
7346 CMatrix rot180;
7347 rot180.identity();
7348 if(parent == 0)
7349 rot180.rotateZ((float)Pi);
7351 // Logical entity Matrix.
7352 CMatrix current;
7353 if(parent == 0)
7354 current = _DirMatrix;
7355 else
7356 current.identity();
7358 // Convert the anim position in a world position.
7359 currentAnimPos = (current*rot180)*currentAnimPos;
7361 rot180 *= AnimMatrixRot;
7362 current *= rot180;
7365 // Get the rotation for the current time in the animation (Rotation Null if animation has no rotation).
7366 if(ClientCfg.BlendFrameNumber && _BlendRemaining > 0)
7368 CQuat tmpQuat;
7369 _OldRotQuat.makeClosest(current.getRot());
7370 tmpQuat = CQuat::slerp(current.getRot(), _OldRotQuat, ((float)_BlendRemaining/(float)(ClientCfg.BlendFrameNumber+1)));
7371 current.setRot(tmpQuat);
7372 // 1 more frame played.
7373 _BlendRemaining--;
7377 // Compute the position for the instance.
7378 CVectorD tmpPos;
7379 if(parent == 0)
7381 tmpPos = pos();
7382 tmpPos += currentAnimPos;
7384 // If the entity is on a mount, just adjust the position with the animation.
7385 else
7386 tmpPos = currentAnimPos;
7387 // Set the skeleton position and rotation.
7388 if(!_Skeleton.empty())
7390 _Skeleton.setRotQuat(current.getRot());
7391 _Skeleton.setPos(tmpPos);
7393 // Only Instances with no skeleton (objects).
7394 else if(!_Instances.empty() && !_Instances[0].Current.empty())
7396 _Instances[0].Current.setRotQuat(current.getRot());
7397 _Instances[0].Current.setPos(tmpPos);
7399 // Set the instance position and rotation.
7400 else if(!_Instance.empty())
7402 _Instance.setRotQuat(current.getRot());
7403 _Instance.setPos(tmpPos);
7405 else
7407 static bool once = false;
7409 if (!once)
7411 nlwarning("CH::updtDisp:%d: no instance nor skeleton. Sheet Id '%d(%s)'.", _Slot, _SheetId.asInt(), _SheetId.toString().c_str());
7412 once = true;
7416 // Else Keep the lastest correct display.
7417 else
7419 H_AUTO ( RZ_Client_Entity_CL_Update_Display_Unknown_Anim )
7421 // Rotation 90 degrees Matrix
7422 CMatrix rot90;
7423 rot90.identity();
7424 if(parent == 0)
7425 rot90.rotateZ((float)(Pi/2.0));
7427 // Logical entity Matrix.
7428 CMatrix current;
7429 if(parent == 0)
7430 current = _DirMatrix;
7431 // else
7432 // current.identity();
7434 current *= rot90;
7436 // Changes the skeleton position.
7437 if(!_Skeleton.empty())
7439 _Skeleton.setRotQuat(current.getRot());
7440 if(parent == 0)
7441 _Skeleton.setPos(pos());
7442 // else
7443 // _Skeleton.setPos(currentAnimPos);
7445 // Only Instances with no skeleton (objects).
7446 else if(!_Instances.empty() && !_Instances[0].Current.empty())
7448 _Instances[0].Current.setRotQuat(current.getRot());
7449 if(parent == 0)
7450 _Instances[0].Current.setPos(pos());
7454 else
7456 // Changes the skeleton position.
7457 if(!_Skeleton.empty())
7459 _Skeleton.setPos(pos());
7461 // Only Instances with no skeleton (objects).
7462 else if(!_Instances.empty() && !_Instances[0].Current.empty())
7464 // Logical entity Matrix.
7465 CMatrix current;
7466 if(parent == 0)
7467 current = _DirMatrix;
7469 _Instances[0].Current.setRotQuat(current.getRot());
7470 _Instances[0].Current.setPos(pos());
7472 // Changes the instance position.
7473 else if(!_Instance.empty())
7475 _Instance.setPos(pos());
7479 if(!ClientCfg.Light)
7481 // update texture Async Loading
7482 updateAsyncTexture();
7483 // update lod Texture
7484 updateLodTexture();
7488 // Update the children display.
7489 std::list<CEntityCL *>::iterator it = _Children.begin();
7490 while(it != _Children.end())
7492 // Update the display for the child
7493 (*it)->updateDisplay(this);
7494 // Next Child.
7495 ++it;
7497 }// updateDisplay //
7500 //---------------------------------------------------
7501 // getHeadPos :
7502 // Method to get the position of the head (in the world).
7503 // \param headPos: will be set with the head position if succeed.
7504 // \return 'true' if the param has been updated.
7505 // \warning this method do NOT check if there is a skeleton.
7506 //---------------------------------------------------
7507 bool CCharacterCL::getHeadPos(NLMISC::CVector &headPos)
7509 // if never computed (eg: clipped or lod)
7510 if(!_HeadOffsetComputed)
7512 // force compute the bone
7513 if(_HeadBoneId != -1 && !_Skeleton.empty())
7515 _Skeleton.forceComputeBone(_HeadBoneId);
7516 UBone headBone=_Skeleton.getBone(_HeadBoneId);
7517 const CMatrix &headMat = headBone.getLastWorldMatrixComputed();
7518 _HeadOffset = headMat.getPos()-pos();
7520 _HeadOffsetComputed= true;
7523 // return the pos with the last head offset computed
7524 headPos = pos()+_HeadOffset;
7526 return true;
7527 }// getHeadPos //
7529 //---------------------------------------------------
7530 // updateHeadDirection :
7531 // Update the head Direction.
7532 //---------------------------------------------------
7533 void CCharacterCL::updateHeadDirection(CEntityCL *target)
7535 // Does the entity got a target to track with the head ?
7536 // No head targeting if the target slot is the same as the entity slot
7537 if(_TargetSlot!=CLFECOMMON::INVALID_SLOT && _TargetSlot!=_Slot)
7539 // Is the target allocated.
7540 if(target != 0)
7542 // Do not orientate the head to the target if too far.
7543 CVectorD vectDist = target->pos() - pos();
7544 if((fabs(vectDist.x) + fabs(vectDist.y)) <= ClientCfg.MaxHeadTargetDist && vectDist != CVectorD::Null)
7546 // Do not orientate the head to the target if behind.
7547 vectDist.normalize();
7548 if(fabs(angleBetween2Vect(dir(), vectDist)) < Pi/3.0)
7550 CVector targetheadPos;
7551 if(target->getHeadPos(targetheadPos))
7553 _TargetAnimCtrl.Mode = CTargetAnimCtrl::TargetMode;
7554 _TargetAnimCtrl.WorldTarget = targetheadPos;
7555 return;
7562 _TargetAnimCtrl.Mode = CTargetAnimCtrl::DirectionMode;
7563 CMatrix frontMat;
7564 frontMat.setRot(CVector::I, front(), CVector::K, true);
7565 frontMat.normalize(CMatrix::YZX);
7566 _TargetAnimCtrl.CurrentWorldDirection = frontMat.getRot();
7567 }// updateHeadDirection //
7570 //---------------------------------------------------
7571 // displayName :
7572 // Display the entity name.
7573 //---------------------------------------------------
7574 void CCharacterCL::displayName()
7576 // There is no Context -> Cannot display a name.
7577 if(TextContext == 0)
7578 return;
7580 NLMISC::CVector namePos;
7581 // There is a skeleton.
7582 if(!_Skeleton.empty() && !ClientCfg.Light)
7584 // Only if the skeleton is visible.
7585 if(!isVisible())
7586 return;
7587 // Do not display the name in LoD.
7588 if(_Skeleton.isDisplayedAsLodCharacter())
7589 return;
7590 // If the entity was not displayed last frame (clipped) -> do not display the name.
7591 if(!_Skeleton.getLastClippedState())
7592 return;
7594 // Is there a Bone for the Name ?
7595 if(_NameBoneId != -1)
7596 namePos = _Skeleton.getBone(_NameBoneId).getLastWorldMatrixComputed().getPos();
7597 // No Bone for the name -> Draw it at a default position.
7598 else
7599 namePos = pos() + CVector(0.f, 0.f, 2.0f);
7601 // If there is no skeleton -> compute a position for the name.
7602 else
7604 namePos = pos() + CVector(0.f, 0.f, 2.0f);
7607 // Create the matrix and set the orientation according to the camera.
7608 CMatrix matrix;
7609 matrix.identity();
7610 matrix.setRot(MainCam.getRotQuat());
7611 matrix.setPos(namePos);
7613 CVector distPos = MainCam.getPos()-pos();
7614 float scale = distPos.norm();
7616 // don't display too far names
7617 if (ClientCfg.Light && scale > 20)
7618 return;
7620 if(scale <= 1.0f)
7621 scale = 1.0f;
7622 else if(scale > ClientCfg.ConstNameSizeDist)
7623 scale = ClientCfg.ConstNameSizeDist;
7624 // Too Far to display a name.
7625 else if(scale > ClientCfg.MaxNameDist)
7626 return;
7627 // Compute the final scale.
7628 matrix.scale(ClientCfg.NameScale*scale);
7630 // Draw the name.
7631 drawName(matrix);
7632 }// displayName //
7634 //---------------------------------------------------
7635 // drawName :
7636 // Draw the name.
7637 //---------------------------------------------------
7638 void CCharacterCL::drawName(const NLMISC::CMatrix &mat) // virtual
7640 const string &ucname = getEntityName();
7641 if(!getEntityName().empty())
7643 // If there is no extended name, just display the name
7644 if(_NameEx.empty())
7645 TextContext->render3D(mat, ucname);
7646 // If there is an extended name, display the extended name at the name place and the name above.
7647 else
7649 // Display the Extended Name at right place.
7650 TextContext->render3D(mat, _NameEx);
7651 // Compute the position for the Name.
7652 CMatrix mat2;
7653 mat2.identity();
7654 mat2.setRot(MainCam.getRotQuat());
7655 CVector v = mat.getPos()+mat.getK().normed()*ClientCfg.NamePos*mat.getScaleUniform();
7656 mat2.setPos(v);
7657 mat2.scale(mat.getScaleUniform());
7658 // Diaplay the name.
7659 TextContext->render3D(mat2, ucname);
7662 // Name from Sheet
7663 else
7665 if(_Sheet != 0)
7667 const char *name = STRING_MANAGER::CStringManagerClient::getCreatureLocalizedName(_Sheet->Id);
7668 if (!FINAL_VERSION || !NLMISC::startsWith(name, "<NotExist:"))
7669 TextContext->render3D(mat, name);
7672 }// drawName //
7675 //---------------------------------------------------
7676 // displayModifiers :
7677 // Display the Hp Bar
7678 //---------------------------------------------------
7679 void CCharacterCL::displayModifiers() // virtual
7681 // if none, no op
7682 if( _HPDisplayed.empty())
7683 return;
7685 // **** get the name pos
7686 NLMISC::CVectorD namePos;
7687 if(!getNamePos(namePos))
7688 namePos = pos() + CVector(0.f, 0.f, 2.0f);
7689 // remove but keep the Z to the ground
7690 float currentDeltaZ= float(namePos.z - pos().z);
7691 CVector groundPos= namePos;
7692 groundPos.z-= currentDeltaZ;
7695 // **** compute the scale
7696 float dist = (MainCam.getPos()-pos()).norm();
7697 float scale= 1.f;
7698 if(dist > ClientCfg.MaxNameDist)
7699 return;
7700 if ( dist < ClientCfg.ConstNameSizeDist )
7701 scale = 1.0f;
7702 else
7703 scale = ClientCfg.ConstNameSizeDist / dist;
7706 // **** Display HP modifiers.
7707 CInterfaceManager *pIM= CInterfaceManager::getInstance();
7708 std::list<HPMD>::iterator itTmp;
7709 std::list<HPMD>::iterator it = _HPDisplayed.begin();
7710 while(it != _HPDisplayed.end())
7712 HPMD &mod = *it;
7714 const float totalDuration= 3.f;
7715 const float noFadeDuration= 1.f;
7716 const float fadeDuration= totalDuration-noFadeDuration;
7717 if(TimeInSec > (mod.Time+totalDuration))
7719 itTmp = it;
7720 ++it;
7721 _HPDisplayed.erase(itTmp);
7723 else if (TimeInSec >= mod.Time)
7725 string hpModifier;
7726 if (mod.Text.empty())
7727 hpModifier = toString("%d", mod.Value);
7728 else
7729 hpModifier = mod.Text;
7730 double t = TimeInSec-mod.Time;
7731 // for character, keep the deltaZ the first time it is displayed, and apply the same each frame
7732 // (avoid Z movement of the flying text because of animation)
7733 if(mod.DeltaZ==-FLT_MAX)
7734 mod.DeltaZ= currentDeltaZ;
7735 // Compute the position for the Modifier.
7736 float dynT= sqrtf((float)t/totalDuration); // a sqrt just so it looks much more "jumpy"
7737 CVector pos= groundPos + CVector(0.0f, 0.0f, mod.DeltaZ + dynT*1.f);
7738 // fade
7739 if(t<noFadeDuration)
7740 mod.Color.A= 255;
7741 else
7742 mod.Color.A= 255-(uint8)((t-noFadeDuration)*255.0/fadeDuration);
7744 // Display the hp modifier. display with a X offset according if user or not, for more readability
7745 sint deltaX= -pIM->FlyingTextManager.getOffsetXForCharacter();
7746 if(UserEntity && UserEntity->slot()==slot())
7747 deltaX*= -1;
7748 pIM->FlyingTextManager.addFlyingText(&mod, hpModifier, pos, mod.Color, scale, deltaX);
7750 // Next
7751 ++it;
7753 else
7755 // Next
7756 ++it;
7759 }// displayModifiers //
7762 //---------------------------------------------------
7763 // drawPath :
7764 // Draw Pathes
7765 //---------------------------------------------------
7766 void CCharacterCL::drawPath() // virtual
7768 // Pivot
7769 CLineColor line;
7770 CVector pl0 = pos();
7771 CVector pl1 = pos()+CVector(0.f, 0.f, 2.f);
7772 line = CLine(pl0, pl1);
7773 line.Color0 = CRGBA(150,0,255);
7774 line.Color1 = CRGBA(150,0,255);
7775 Driver->drawLine(line, GenericMat);
7777 line = CLine(_PositionLimiter, _PositionLimiter+CVector(0.f, 0.f, 2.f));
7778 line.Color0 = CRGBA(255,64,128);
7779 line.Color1 = CRGBA(255,64,128);
7780 Driver->drawLine(line, GenericMat);
7782 CVector p0 = pos();
7783 p0.z += 1.f;
7785 // Draw Front
7786 line = CLine(p0, p0+front());
7787 line.Color0 = CRGBA(0,255,0);
7788 line.Color1 = CRGBA(0,255,0);
7789 Driver->drawLine(line, GenericMat);
7791 // Draw Direction
7792 line = CLine(p0, p0+dir());
7793 line.Color0 = CRGBA(255,255,0);
7794 line.Color1 = CRGBA(255,255,0);
7795 Driver->drawLine(line, GenericMat);
7797 // Go to the First Stage.
7798 CStageSet::TStageSet::iterator it = _Stages._StageSet.begin();
7799 // Compute the distance over all Stages.
7800 while(it != _Stages._StageSet.end())
7802 // Compute Distance.
7803 CVectorD stagePos;
7804 if((*it).second.getPos(stagePos))
7806 CVector p1 = stagePos;
7807 CVector p2 = p0;
7808 p2.z = (float)stagePos.z;
7809 p2 = p2 + (stagePos-p2).normed();
7811 getCollisionEntity()->snapToGround(p1);
7812 p1.z += 0.05f;
7813 getCollisionEntity()->snapToGround(p2);
7814 p2.z += 0.03f;
7816 line = CLine(p0, p2);
7817 line.Color0 = CRGBA(0,0,255);
7818 line.Color1 = CRGBA(0,0,255);
7819 Driver->drawLine(line, GenericMat);
7821 p1.z += 0.03f;
7822 line = CLine(p0, p1);
7823 line.Color0 = CRGBA(255,0,0);
7824 line.Color1 = CRGBA(255,0,0);
7825 Driver->drawLine(line, GenericMat);
7827 p0 = p1;
7830 // Next Stage.
7831 ++it;
7833 }// drawPath //
7836 //---------------------------------------------------
7837 // drawBox :
7838 // Draw the selection Box.
7839 //---------------------------------------------------
7840 void CCharacterCL::drawBox() // virtual
7842 if(!ClientCfg.Light)
7843 ::drawBox(_Aabbox.getMin(), _Aabbox.getMax(), CRGBA(0,250,0));
7845 // Draw the PACS box (adjust the color according to the PACS valid or not).
7846 NLMISC::CAABBox PACSBox = _Aabbox;
7847 CVector halfSize = PACSBox.getHalfSize();
7848 halfSize.x = 0; halfSize.y = 0;
7849 PACSBox.setCenter(_FinalPacsPos+halfSize);
7850 UGlobalPosition gPos;
7851 if(_Primitive)
7852 _Primitive->getGlobalPosition(gPos, dynamicWI);
7853 ::drawBox(PACSBox.getMin(), PACSBox.getMax(), ((gPos.InstanceId == -1) && (T1%1000)>500)?CRGBA(255,0,0):CRGBA(0,250,250));
7855 if(!ClientCfg.Light)
7857 ::drawBox(selectBox().getMin(), selectBox().getMax(), CRGBA(250,250,0));
7858 // Draw the clip Sphere
7859 CVector clipPos = _Aabbox.getCenter();
7860 clipPos.z+= _ClipDeltaZ - _Aabbox.getHalfSize().z; // _ClipDeltaZ is relative to pos on ground
7861 ::drawSphere(clipPos, _ClipRadius, CRGBA(0,0,250));
7863 }// drawBox //
7865 //---------------------------------------------------
7866 // selectBox :
7867 // Return the selection box.
7868 //---------------------------------------------------
7869 const NLMISC::CAABBox &CCharacterCL::selectBox() // virtual
7871 // recompute the selection box?
7872 if(_LastSelectBoxComputeTime<T1)
7874 _LastSelectBoxComputeTime=T1;
7875 bool found= false;
7877 // if skeleton, compute aabox from precise skeleton method
7878 if(!_Skeleton.empty())
7880 // Don't compute if in LOD form (else flick because sometimes valid because of shadow animation)
7881 if(!_Skeleton.isDisplayedAsLodCharacter() &&
7882 _Skeleton.computeRenderedBBoxWithBoneSphere(_SelectBox))
7883 found= true;
7885 // else compute from static bot object
7886 else
7888 UInstance inst;
7889 // try with _Instances array first
7890 if(!_Instances.empty())
7891 inst= _Instances[0].Current;
7892 // Fallback to _Instance (??)
7893 if(inst.empty())
7894 inst= _Instance;
7896 // if static instance found
7897 if(!inst.empty())
7899 CAABBox bbox;
7900 inst.getShapeAABBox(bbox);
7901 // if supported (ie bbox not null)
7902 if(bbox.getHalfSize()!=CVector::Null)
7904 // Transform bbox to world
7905 _SelectBox= CAABBox::transformAABBox(inst.getLastWorldMatrixComputed(), bbox);
7906 found= true;
7911 // if not found, fallback to default bbox
7912 if(!found)
7913 _SelectBox = _Aabbox;
7916 // Return the selection box.
7917 return _SelectBox;
7918 }// selectBox //
7921 //---------------------------------------------------
7922 // updateAttachedFXListForSlotRemoved :
7923 //---------------------------------------------------
7924 void CCharacterCL::updateAttachedFXListForSlotRemoved(std::list<CAttachedFX::TSmartPtr> &fxList, const CLFECOMMON::TCLEntityId &slotRemoved)
7926 std::list<CAttachedFX::TSmartPtr>::iterator it = fxList.begin();
7927 while (it != fxList.end())
7929 std::list<CAttachedFX::TSmartPtr>::iterator tmpIt = it;
7930 ++ it;
7931 if ((*tmpIt)->StickMode &&
7932 ((*tmpIt)->StickMode == CFXStickMode::OrientedTowardTargeter ||
7933 (*tmpIt)->StickMode == CFXStickMode::UserBoneOrientedTowardTargeter ||
7934 (*tmpIt)->StickMode == CFXStickMode::UserBoneRay
7938 if ((*tmpIt)->TargeterInfo.Slot == slotRemoved)
7940 fxList.erase(tmpIt);
7946 //---------------------------------------------------
7947 // slotRemoved :
7948 // To Inform about an entity removed (to remove from selection for example).
7949 // This will remove the entity from the target.
7950 // \param slot : Slot of the entity that will be removed.
7951 //---------------------------------------------------
7952 void CCharacterCL::slotRemoved(const CLFECOMMON::TCLEntityId &slotRemoved)
7954 // If the target is the entity that will be removed -> no target
7955 if(_TargetSlot == slotRemoved)
7956 _TargetSlot = CLFECOMMON::INVALID_SLOT;
7957 // invalidate targeter slots in anim fxs
7958 updateAttachedFXListForSlotRemoved(_AttachedFXListForCurrentAnim, slotRemoved);
7959 updateAttachedFXListForSlotRemoved(_AttachedFXListToRemove, slotRemoved);
7960 }// slotRemoved //
7962 //---------------------------------------------------
7963 // nbStage :
7964 // Return number of stage remaining.
7965 //---------------------------------------------------
7966 uint CCharacterCL::nbStage()
7968 return (uint)_Stages._StageSet.size();
7969 }// nbStage //
7972 //---------------------------------------------------
7973 // attackRadius :
7974 // Method to return the attack radius of an entity (take the scale into account).
7975 //---------------------------------------------------
7976 double CCharacterCL::attackRadius() const
7978 return _Sheet->DistToFront * getScale();
7979 }// attackRadius //
7981 //---------------------------------------------------
7982 // getAttackerPos :
7983 // Return the position the attacker should have to combat according to the attack angle.
7984 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
7985 // \todo : GUIGUI precalculate entity matrix
7986 //---------------------------------------------------
7987 CVectorD CCharacterCL::getAttackerPos(double ang, double dist) const
7989 // Compute the local angle
7990 ang = computeShortestAngle(atan2(front().y, front().x), ang);
7991 ang += Pi;
7992 if(ang > Pi)
7993 ang -= 2*Pi;
7995 // Compute the local position.
7996 CVectorD p;
7997 float distToSide, distToFront, distToBack;
7998 bool useComplexShape = false;
7999 if (useComplexShape) // Keep this code for when AIS become complex shape aware
8001 distToSide = _Sheet->DistToSide;
8002 distToFront = _Sheet->DistToFront;
8003 distToBack = _Sheet->DistToBack;
8005 else // use round shape here
8007 distToSide = _Sheet->ColRadius;
8008 distToFront = _Sheet->ColRadius;
8009 distToBack = _Sheet->ColRadius;
8011 p.x = getScale()*distToSide*sin(-ang) + dist*sin(-ang); // or: pos.x = _Sheet->DistToSide*cos(ang) + dist*cos(ang); but 0 should be right side.
8012 p.y = dist*cos(ang);
8013 if(fabs(ang) <= Pi/2.0)
8014 p.y += getScale()*distToFront * cos(ang);
8015 else
8016 p.y += getScale()*distToBack * cos(ang);
8017 p.z = 0.0;
8020 // Compute the world position.
8021 // Create the target matrix.
8022 CVector vj = front();
8023 vj.z = 0;
8024 CVector vk(0,0,1);
8025 CVector vi = vj^vk;
8026 CMatrix bodyBase;
8027 bodyBase.setRot(vi,vj,vk,true);
8028 bodyBase.setPos(pos());
8030 // Get the destination in the world.
8031 return bodyBase * p;
8032 }// getAttackerPos //
8034 //---------------------------------------------------
8035 // isPlacedToFight :
8036 // Return true if the opponent is well placed.
8037 //---------------------------------------------------
8038 bool CCharacterCL::isPlacedToFight(const NLMISC::CVectorD &posAtk, const NLMISC::CVector &dirAtk, double attackerRadius) const // virtual
8040 NLMISC::CVectorD vDist = pos()-posAtk;
8041 if(vDist != NLMISC::CVectorD::Null)
8043 // Attacker Distance
8044 const double distToAttacker = vDist.norm();
8045 // Get the Ideal Position
8046 vDist.normalize();
8047 CVectorD rightPos = getAttackerPos(atan2(vDist.y, vDist.x), attackerRadius);
8048 // Vector from the Ideal Position
8049 NLMISC::CVectorD vDist2 = pos()-rightPos;
8050 // Check the Distance.
8051 if(distToAttacker <= vDist2.norm()+ClientCfg.FightAreaSize)
8053 // Check the orientation.
8054 NLMISC::CVector vAng = dirAtk;
8055 vAng.z = 0.0f;
8056 vDist.z = 0.0;
8057 return (fabs(angleBetween2Vect(vAng, vDist)) <= NLMISC::Pi/3.0);
8060 // User is on the target, do not check dist or angle
8061 else
8062 return true;
8063 // Something wrong
8064 return false;
8065 // NLMISC::CVectorD vDist = pos()-posAtk;
8066 // const double dist = vDist.norm();
8067 // double radius;
8068 // // Get current entity radius
8069 // if(_Primitive)
8070 // radius = _Primitive->getRadius();
8071 // else
8072 // radius = 0.0;
8073 // // Attack is possible if not too close or too far.
8074 // if(dist>=radius && dist<=(radius+ClientCfg.FightAreaSize))
8075 // {
8076 // // Check Angle
8077 // NLMISC::CVector vAng = dirAtk;
8078 // vAng.z = 0.0f;
8079 // vDist.z = 0.0;
8080 // return (fabs(angleBetween2Vect(vAng, vDist)) <= NLMISC::Pi/3.0);
8081 // }
8082 // return false;
8083 }// isPlacedToFight //
8086 //---------------------------------------------------
8087 // \param pos : result given in this variable. Only valid if return 'true'.
8088 // \return bool : 'true' if the 'pos' has been filled.
8089 //---------------------------------------------------
8090 bool CCharacterCL::getNamePos(CVectorD &pos) // virtual
8092 // If there is no skeleton -> cannot display the name.
8093 if(_Skeleton.empty())
8094 return false;
8096 // If the entity was not displayed last frame (clipped) -> do not display the name.
8097 if(!_Skeleton.getLastClippedState())
8098 return false;
8100 if(_NameBoneId == -1)
8101 return false;
8103 // Take x and y in pos() else we a have a frame late.
8104 pos.x = this->pos().x;
8105 pos.y = this->pos().y;
8107 float namePosZ;
8108 if (ClientCfg.StaticNameHeight)
8109 namePosZ = getNamePosZ();
8110 else
8111 namePosZ = 0.f;
8113 // use bone position if no name position is given
8114 if (namePosZ == 0.f)
8116 // if displayed as lod, the NameId bone may not be computed
8117 float skeletonZ = _Skeleton.getLastWorldMatrixComputed().getPos().z;
8118 if(_Skeleton.isDisplayedAsLodCharacter())
8120 // if never computed
8121 if(_NameCLodDeltaZ==NameCLodDeltaZNotComputed)
8123 _Skeleton.forceComputeBone(_NameBoneId);
8124 float boneZ= _Skeleton.getBone(_NameBoneId).getLastWorldMatrixComputed().getPos().z;
8125 _NameCLodDeltaZ= boneZ - skeletonZ;
8127 pos.z= skeletonZ + _NameCLodDeltaZ;
8129 else
8131 float boneZ= _Skeleton.getBone(_NameBoneId).getLastWorldMatrixComputed().getPos().z;
8132 pos.z = boneZ;
8133 // update delta Z, for when enter in CLod form
8134 _NameCLodDeltaZ= boneZ - skeletonZ;
8137 // reset name pos history
8138 if (_NamePosHistory.isInitialized())
8140 _NamePosHistory.LastNamePosZ = 0.f;
8141 _NamePosHistory.LastBonePosZ = 0.f;
8144 return true;
8147 const float baseZ = float( this->pos().z );
8149 // if displayed as lod, skip smooth transition stuff
8150 if (_Skeleton.isDisplayedAsLodCharacter())
8152 pos.z = baseZ + namePosZ;
8154 // reset name pos history
8155 if (_NamePosHistory.isInitialized())
8157 _NamePosHistory.LastNamePosZ = 0.f;
8158 _NamePosHistory.LastBonePosZ = 0.f;
8161 return true;
8164 const float boneZ = _Skeleton.getBone(_NameBoneId).getLastWorldMatrixComputed().getPos().z;
8166 float deltaNamePosZ;
8167 float deltaBonePosZ;
8168 if (_NamePosHistory.isInitialized())
8170 deltaNamePosZ = namePosZ - _NamePosHistory.LastNamePosZ;
8171 deltaBonePosZ = (boneZ - baseZ) - _NamePosHistory.LastBonePosZ;
8173 else
8175 deltaNamePosZ = 0.f;
8176 deltaBonePosZ = 0.f;
8179 if (deltaNamePosZ != 0.f)
8181 // generate a smooth transition following the bone movement
8182 if (deltaBonePosZ != 0.f && (deltaBonePosZ > 0.f) == (deltaNamePosZ > 0.f))
8184 namePosZ = _NamePosHistory.LastNamePosZ + deltaBonePosZ;
8186 else
8188 const float defaultSpeed = 1.f; // in meters per sec.
8189 float deltaZ = defaultSpeed * DT;
8190 if (deltaNamePosZ < 0.f)
8191 deltaZ = -deltaZ;
8193 if ( fabs(deltaZ) < fabs(deltaNamePosZ) )
8194 namePosZ = _NamePosHistory.LastNamePosZ + deltaZ;
8198 pos.z = baseZ + namePosZ;
8200 // update history
8201 _NamePosHistory.LastNamePosZ = namePosZ;
8202 _NamePosHistory.LastBonePosZ = boneZ - baseZ;
8204 return true;
8205 }// getNamePos //
8207 //---------------------------------------------------
8208 // Return name position on Z axis defined in sheet
8209 //---------------------------------------------------
8210 float CCharacterCL::getNamePosZ() const
8212 if (!_Sheet)
8213 return 0.f;
8215 float namePosZ;
8216 switch (_ModeWanted)
8218 case MBEHAV::DEATH:
8219 case MBEHAV::SIT:
8220 case MBEHAV::REST:
8221 namePosZ = _Sheet->NamePosZLow;
8222 break;
8224 case MBEHAV::MOUNT_NORMAL:
8225 case MBEHAV::MOUNT_SWIM:
8226 namePosZ = _Sheet->NamePosZHigh;
8227 break;
8229 default:
8230 namePosZ = _Sheet->NamePosZNormal;
8231 break;
8234 if (namePosZ == 0.f)
8235 namePosZ = _Sheet->NamePosZNormal;
8237 return namePosZ * getScale();
8238 }// getNamePosZ //
8240 //---------------------------------------------------
8241 // \param pos : result given in this variable. Only valid if return 'true'.
8242 // \return bool : 'true' if the 'pos' has been filled.
8243 //---------------------------------------------------
8244 bool CCharacterCL::getChestPos(CVectorD &pos) const // virtual
8246 // If there is no skeleton -> cannot display the Chest.
8247 if(_Skeleton.empty())
8248 return false;
8250 // If the entity was not displayed last frame (clipped) -> do not display the Chest.
8251 if(!_Skeleton.getLastClippedState())
8252 return false;
8254 if(_ChestBoneId == -1)
8255 return false;
8257 // Take x and y in pos() else we a have a frame late.
8258 pos.x = this->pos().x;
8259 pos.y = this->pos().y;
8260 pos.z = _Skeleton.getBone(_ChestBoneId).getLastWorldMatrixComputed().getPos().z;
8261 return true;
8262 }// getChestPos //
8265 //---------------------------------------------------
8266 // getSheetScale :
8267 // Return the entity sheet scale. (return 1.0 if there is any problem).
8268 //---------------------------------------------------
8269 float CCharacterCL::getSheetScale() const // virtual
8271 if(!_Sheet)
8272 return 1.f;
8273 else
8274 return _Sheet->Scale;
8276 } // getSheetScale //
8279 //---------------------------------------------------
8280 // getColRadius :
8281 // Return the entity collision radius. (return 0.5 if there is any problem).
8282 //---------------------------------------------------
8283 float CCharacterCL::getSheetColRadius() const
8285 if(!_Sheet)
8286 return 0.5f;
8287 else
8288 return _Sheet->ColRadius;
8292 //---------------------------------------------------
8293 // getScale :
8294 // Return the entity scale. (return 1.0 if there is any problem).
8295 //---------------------------------------------------
8296 float CCharacterCL::getScale() const // virtual
8298 switch( _OwnerPeople )
8300 case MOUNT_PEOPLE::Fyros : return getSheetScale() * ClientCfg.FyrosScale * _CustomScale;
8301 case MOUNT_PEOPLE::Matis : return getSheetScale() * ClientCfg.MatisScale * _CustomScale;
8302 case MOUNT_PEOPLE::Tryker : return getSheetScale() * ClientCfg.TrykerScale * _CustomScale;
8303 case MOUNT_PEOPLE::Zorai : return getSheetScale() * ClientCfg.ZoraiScale * _CustomScale;
8304 default:
8305 return getSheetScale() * _CustomScale;
8307 }// getScale //
8313 ///////////
8314 // DEBUG //
8315 ///////////
8316 //---------------------------------------------------
8317 // currentAnimationName :
8318 // Return the current animation name.
8319 //---------------------------------------------------
8320 const std::string &CCharacterCL::currentAnimationName() const
8322 if(_PlayList)
8324 uint idCurrentAnimation = _PlayList->getAnimation(MOVE);
8325 if(idCurrentAnimation != UPlayList::empty)
8326 if(EAM && EAM->getAnimationSet())
8327 return EAM->getAnimationSet()->getAnimationName(idCurrentAnimation);
8330 // No animation yet.
8331 return CCharacterCL::_EmptyString;
8332 }// currentAnimationName //
8334 //---------------------------------------------------
8335 // currentAnimationSetName :
8336 // Return the current animation set name.
8337 //---------------------------------------------------
8338 std::string CCharacterCL::currentAnimationSetName(TAnimationType animType) const
8340 if( animType < animTypeCount )
8342 if( uint(animType) < _CurrentAnimSet.size() )
8344 if( _CurrentAnimSet[animType] )
8346 return _CurrentAnimSet[animType]->getSheetName();
8350 return CCharacterCL::_EmptyString;
8351 }// currentAnimationSetName //
8353 /////////////
8354 // PRIVATE //
8355 //---------------------------------------------------
8356 // shapeFromItem :
8357 // Return the shape pointer from tha item and according to the character gender.
8358 // \param itemSheet : reference on the item sheet.
8359 // \return string & : reference on the shape name.
8360 //---------------------------------------------------
8361 std::string CCharacterCL::shapeFromItem(const CItemSheet &itemSheet) const
8363 string sheet = "";
8365 if(_Gender == GSGENDER::male)
8367 if(_Sheet)
8368 switch(_Sheet->Race)
8370 case EGSPD::CPeople::Fyros:
8371 sheet = itemSheet.getShapeFyros();
8372 break;
8373 case EGSPD::CPeople::Matis:
8374 sheet = itemSheet.getShapeMatis();
8375 break;
8376 case EGSPD::CPeople::Tryker:
8377 sheet = itemSheet.getShapeTryker();
8378 break;
8379 case EGSPD::CPeople::Zorai:
8380 sheet = itemSheet.getShapeZorai();
8381 break;
8384 else
8386 if(_Sheet)
8387 switch(_Sheet->Race)
8389 case EGSPD::CPeople::Fyros:
8390 sheet = itemSheet.getShapeFyrosFemale();
8391 break;
8392 case EGSPD::CPeople::Matis:
8393 sheet = itemSheet.getShapeMatisFemale();
8394 break;
8395 case EGSPD::CPeople::Tryker:
8396 sheet = itemSheet.getShapeTrykerFemale();
8397 break;
8398 case EGSPD::CPeople::Zorai:
8399 sheet = itemSheet.getShapeZoraiFemale();
8400 break;
8402 if (sheet.empty())
8403 sheet = itemSheet.getShapeFemale();
8405 if (sheet.empty())
8406 sheet = itemSheet.getShape();
8408 return sheet;
8410 }// shapeFromItem //
8413 //---------------------------------------------------
8414 // createItemInstance :
8415 // Create the instance from an item
8416 //---------------------------------------------------
8417 uint32 CCharacterCL::createItemInstance(const CItemSheet &itemSheet, uint32 instIdx, SLOTTYPE::EVisualSlot visualSlot, const string &bindBone, sint8 texture, sint color)
8419 uint32 idx = CEntityCL::BadIndex;
8420 // Get the right shape according to the gender of the character.
8421 const string &shape = shapeFromItem(itemSheet);
8423 // Check the shape.
8424 if(!shape.empty())
8426 // Check the item need a shape.
8427 if(shape != "none.shape")
8429 UInstance instance;
8430 // Get the instance
8431 idx = addColoredInstance(shape, bindBone, texture, instIdx, color);
8432 SInstanceCL *pInst = idx2Inst(idx);
8433 nlassert( (pInst == NULL) || (pInst != NULL && !pInst->Loading.empty()) );
8434 if (pInst != NULL)
8435 instance = pInst->Loading;
8436 // Check the shape creation has been is well done.
8437 if(!instance.empty())
8439 // Create the FX associated to the item in a given visual slot.
8440 _Items[visualSlot].initFXs(visualSlot, instance);
8442 else
8443 nlwarning("CH:createItemInstance: cannot create the instance for the shape '%s'.", shape.c_str());
8446 else
8447 nlwarning("CH:createItemInstance: the item has no shape.");
8449 return idx;
8450 }// createItemInstance //
8453 //-----------------------------------------------
8454 // setAlive :
8455 // Method to Flag the character as alive and do everything needed.
8456 //-----------------------------------------------
8457 void CCharacterCL::setAlive() // virtual
8459 }// setAlive //
8463 //---------------------------------------------------
8464 // displayDebug :
8465 // Display Debug Information.
8466 //---------------------------------------------------
8467 ADD_METHOD(void CCharacterCL::displayDebug(float x, float &y, float lineStep)) // virtual
8468 CInterfaceManager *IM = CInterfaceManager::getInstance ();
8470 CEntityCL::displayDebug(x, y, lineStep);
8471 // Mode Wanted
8472 // Display the Target Mode.
8473 TextContext->printfAt(x, y, "Mode Wanted: %d(%s)", (sint)_ModeWanted, MBEHAV::modeToString(_ModeWanted).c_str());
8474 y += lineStep;
8475 // Stage Remaining
8476 TextContext->printfAt(x, y, "Stages remaining: %d", _Stages._StageSet.size());
8477 y += lineStep;
8478 // Current Automaton
8479 TextContext->printfAt(x, y, "Automaton: %s", _CurrentAutomaton.c_str());
8480 y += lineStep;
8481 // Current Speed
8482 TextContext->printfAt(x, y, "Speed: %f (_DestTime(%f) - _LastFrameTime(%f)) = %f", speed(), _DestTime, _LastFrameTime, _DestTime-_LastFrameTime);
8483 y += lineStep;
8484 // Display the Run Factor.
8485 TextContext->printfAt(x, y, "(Walk)Run Factor: %f", runFactor());
8486 y += lineStep;
8487 // Display the current animation name(id)(offset)(nbloop) for channel MOVE.
8488 TextContext->printfAt(x, y, "Current Animation: %s(%u)(%lf)(%u loops)", animId(MOVE)==std::numeric_limits<uint>::max()?"[NONE]":currentAnimationName().c_str(), animId(MOVE), animOffset(MOVE), _NbLoopAnim);
8489 y += lineStep;
8490 // First Pos
8491 if(_First_Pos)
8492 TextContext->printfAt(x, y, "No Position Received", _First_Pos);
8493 else
8494 TextContext->printfAt(x, y, "At least 1 Position Received", _First_Pos);
8495 y += lineStep;
8496 // Primitive Ptr
8497 TextContext->printfAt(x, y, "Prim Ptr: %p", _Primitive);
8498 y += lineStep;
8499 // Primitive Position
8500 if(_Primitive)
8502 CVectorD primFinalPos = _Primitive->getFinalPosition(dynamicWI);
8503 TextContext->printfAt(x, y, "Prim Pos: %f %f %f", primFinalPos.x, primFinalPos.y, primFinalPos.z);
8504 y += lineStep;
8506 // Skeleton Ptr, Animset Ptr and Current State Ptr
8507 TextContext->printfAt(x, y, "Skel Ptr: %p - AnimSet Ptr: %p - State Ptr: %p", &_Skeleton, _CurrentAnimSet[MOVE], _CurrentState);
8508 y += lineStep;
8509 // Display the target mount and rider.
8510 TextContext->printfAt(x, y, "Mount: %3u(Theoretical: %3u) Rider: %3u(Theoretical: %3u)", mount(), _TheoreticalMount, rider(), _TheoreticalRider);
8511 y += lineStep;
8512 // VPA
8513 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
8514 if(isPlayer() || isUser())
8516 SPropVisualA visualA = *(SPropVisualA *)(&prop);
8517 TextContext->printfAt(x, y, "VPA: %" NL_I64 "X : Chest(%d,%d) Legs(%d,%d) Arms(%d,%d) Hat(%d,%d) RH(%d) LH(%d)", prop,
8518 (uint)visualA.PropertySubData.JacketModel, (uint)visualA.PropertySubData.JacketColor,
8519 (uint)visualA.PropertySubData.TrouserModel, (uint)visualA.PropertySubData.TrouserColor,
8520 (uint)visualA.PropertySubData.ArmModel, (uint)visualA.PropertySubData.ArmColor,
8521 (uint)visualA.PropertySubData.HatModel, (uint)visualA.PropertySubData.HatColor,
8522 (uint)visualA.PropertySubData.WeaponRightHand,
8523 (uint)visualA.PropertySubData.WeaponLeftHand);
8525 else
8526 TextContext->printfAt(x, y, "VPA: %" NL_I64 "X", prop);
8527 y += lineStep;
8528 // VPB
8529 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->getValue64();
8530 if(isPlayer() || isUser())
8532 SPropVisualB visualB = *(SPropVisualB *)(&prop);
8533 TextContext->printfAt(x, y, "VPB: %" NL_I64 "X : Hands(%d,%d) Feet(%d,%d).", prop,
8534 (uint)visualB.PropertySubData.HandsModel, (uint)visualB.PropertySubData.HandsColor,
8535 (uint)visualB.PropertySubData.FeetModel, (uint)visualB.PropertySubData.FeetColor);
8537 else
8538 TextContext->printfAt(x, y, "VPB: %" NL_I64 "X", prop);
8539 y += lineStep;
8540 // VPC
8541 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPC))->getValue64();
8542 if(isPlayer() || isUser())
8544 SPropVisualC visualC = *(SPropVisualC *)(&prop);
8545 TextContext->printfAt(x, y, "VPC: %" NL_I64 "X : EyesColor(%d) Tattoo(%d).", prop, visualC.PropertySubData.EyesColor, visualC.PropertySubData.Tattoo);
8547 else
8548 TextContext->printfAt(x, y, "VPC: %" NL_I64 "X", prop);
8549 y += lineStep;
8550 }// displayDebug //
8554 //-----------------------------------------------
8555 // displayDebugPropertyStages
8556 //-----------------------------------------------
8557 void CCharacterCL::displayDebugPropertyStages(float x, float &y, float lineStep)
8559 CStageSet::TStageSet::iterator it= _Stages._StageSet.begin();
8560 for(;it!=_Stages._StageSet.end();it++)
8562 CStage &stage= it->second;
8563 uint32 gc= it->first % 100;
8564 // build the string of props present in this stage
8565 string strProps;
8566 for(uint i=0;i<CLFECOMMON::NB_VISUAL_PROPERTIES;i++)
8568 if(i!=CLFECOMMON::PROPERTY_POSY && i!=CLFECOMMON::PROPERTY_POSZ && stage.isPresent(i))
8569 strProps+= string(CLFECOMMON::getPropShortText(i)) + " ";
8571 TextContext->printfAt(x, y, "%02d %s", gc, strProps.c_str());
8572 y += lineStep;
8577 //---------------------------------------------------
8578 // readWrite :
8579 // Read/Write Variables from/to the stream.
8580 //---------------------------------------------------
8581 void CCharacterCL::readWrite(NLMISC::IStream &f)
8583 CEntityCL::readWrite(f);
8585 // PUBLIC
8587 // PROTECTED
8588 f.serial(_Stages);
8589 // std::vector<CAnimation::TAnimId> _Animations;
8590 // std::vector<NLSOUND::TSoundAnimId> _SoundId;
8591 // NLSOUND::CSoundContext _SoundContext;
8592 // std::vector<double> _AnimationsTimeOffset;
8593 // std::vector<CAnimationState::TAnimStateId> _AnimationsStateKey;
8594 // const CAnimationSet *_CurrentAnimSet;
8595 f.serial(_LastFrameTime);
8596 f.serial(_LodCharacterAnimEnabled);
8597 f.serial(_LodCharacterAnimTimeOffset);
8598 // uint _LodCharacterMasterAnimSlot;
8599 f.serial(_CharacterScalePos);
8600 f.serial(_FirstPos);
8601 f.serial(_FirstTime);
8602 f.serial(_DistToFirst);
8603 f.serial(_DestPos);
8604 f.serial(_DestTime);
8605 f.serial(_DistToDest);
8606 f.serial(_OldPos);
8607 f.serial(_OldPosTime);
8608 // GSGENDER::EGender _Gender;
8609 // sint _NameBoneId;
8610 // NL3D::UTransform _NameTransform;
8611 // std::vector<CItemSheet *> _Items;
8612 f.serial(_HeadIdx);
8613 f.serial(_FaceIdx);
8614 // sint _HeadBoneId;
8615 f.serial(_RotationFactor);
8616 f.serial(_DirEndAnim);
8617 f.serial(_RotAngle);
8618 f.serial(_CurrentAutomaton);
8619 // const CAutomatonStateSheet *_CurrentState;
8620 // MBEHAV::EMode _ModeWanted;
8621 // sint _BlendRemaining;
8622 f.serial(_OldAutomaton);
8623 f.serial(_OldRotQuat);
8624 f.serial(_CustomScalePos);
8625 // TTime _NextBlinkTime;
8626 f.serial(_NbLoopAnim);
8627 // std::vector<CFXStruct *> _FXs;
8628 // std::list<UParticleSystemInstance> _CurrentAnimFXList;
8629 // std::list<UParticleSystemInstance> _RemoveAnimFXList;
8630 // NL3D::UParticleSystemInstance _CurrentAnimFX;
8631 f.serial(_RightFXActivated);
8632 f.serial(_LeftFXActivated);
8633 // sint _IndexRightFX;
8634 // sint _IndexLeftFX;
8635 f.serial(_Mount);
8636 f.serial(_Rider);
8637 f.serial(_IsThereAMode);
8638 f.serial(_HairColor);
8639 f.serial(_EyesColor);
8640 f.serial(_HairIndex);
8641 f.serial(_LookRdy);
8642 f.serial(_Speed);
8643 f.serial(_RunFactor);
8645 // PRIVATE
8646 // uint32 _RHandInstIdx;
8647 // uint32 _LHandInstIdx;
8648 }// readWrite //
8650 //---------------------------------------------------
8651 // load :
8652 // To call after a read from a stream to re-initialize the entity.
8653 //---------------------------------------------------
8654 void CCharacterCL::load() // virtual
8656 CInterfaceManager *IM = CInterfaceManager::getInstance ();
8658 // If the entity should be in the world already
8659 if(_First_Pos == false)
8661 // Insert the primitive into the world.
8662 if(_Primitive)
8663 _Primitive->insertInWorldImage(dynamicWI);
8664 // Insert the entity into PACS
8665 pacsPos(pos());
8668 if(_LookRdy)
8670 _LookRdy = false;
8671 // Visual properties A
8672 _HeadIdx = CEntityCL::BadIndex;
8673 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
8674 updateVisualPropertyVpa(0, prop);
8676 }// load //
8678 //---------------------------------------------------
8679 // buildPlaylist :
8680 //---------------------------------------------------
8681 void CCharacterCL::buildPlaylist()
8683 computeAnimSet();
8684 // Release the old animation playlist.
8685 if(_PlayList)
8687 EAM->deletePlayList(_PlayList);
8688 _PlayList = 0;
8690 // Create the new animation playlist.
8691 _PlayList = EAM->createPlayList();
8692 if(!_PlayList)
8694 nlwarning("Cannot create a playlist for the entity.");
8695 return;
8697 // Register the skeleton to the playlist.
8698 _PlayList->registerTransform(_Skeleton);
8699 // Animation should not move alone.
8700 uint posChannel = EAM->getAnimationSet()->getChannelIdByName("pos");
8701 if(posChannel != NL3D::UAnimationSet::NotFound)
8702 _PlayList->enableChannel(posChannel, false);
8703 else
8704 nlwarning("Channel 'pos' not found.");
8705 // Animation should not rotate alone.
8706 uint rotquatChannel = EAM->getAnimationSet()->getChannelIdByName("rotquat");
8707 if(rotquatChannel != NL3D::UAnimationSet::NotFound)
8708 _PlayList->enableChannel(rotquatChannel, false);
8709 else
8710 nlwarning("Channel 'rotquat' not found.");
8711 // Initialize the new playlist.
8712 // MOVE Channel
8713 _PlayList->setSpeedFactor (MOVE, 1.f);
8714 _PlayList->setWrapMode (MOVE, NL3D::UPlayList::Clamp);
8715 // ACTION Channel
8716 _PlayList->setAnimation (ACTION, NL3D::UPlayList::empty);
8717 _PlayList->setSpeedFactor (ACTION, 1.f);
8718 _PlayList->setWrapMode (ACTION, NL3D::UPlayList::Clamp);
8719 // Compute the current animation state.
8720 _CurrentState = EAM->mState(_CurrentAutomaton, animState(MOVE));
8721 if(_CurrentState == 0)
8723 _PlayList->setAnimation(MOVE, NL3D::UPlayList::empty);
8724 return;
8726 // Get the right animation state and choose an animation.
8728 // Get the animation state
8729 const CAnimationState *animationState = 0;
8730 if(animState(MOVE) == CAnimationStateSheet::Emote)
8731 animationState = _CurrentAnimSet[MOVE]->getAnimationState(_SubStateKey);
8732 else
8733 animationState = _CurrentAnimSet[MOVE]->getAnimationState(animState(MOVE));
8734 if(animationState)
8736 // Choose the animation
8737 animIndex(MOVE, animationState->chooseAnim(_AnimJobSpecialisation, people(), getGender(), 0.0));
8738 // Should the objects in hands be displayed ?
8739 _ObjectsVisible = animationState->areObjectsVisible();
8742 // Set animation
8743 _PlayList->setAnimation(MOVE, animId(MOVE));
8744 }// buildPlaylist //
8746 //--------------//
8747 // ENTITY INFOS //
8748 //--------------//
8749 //-----------------------------------------------
8750 // getSpeed :
8751 // Return the entity speed
8752 //-----------------------------------------------
8753 double CCharacterCL::getSpeed() const // virtual
8755 return speed();
8756 }// getSpeed //
8758 //-----------------------------------------------
8759 // dist2FirstPos :
8760 // Set the Distance from the current entity position to the First Position.
8761 //-----------------------------------------------
8762 void CCharacterCL::dist2FirstPos(double d2FP)
8764 CHECK((d2FP == INVALID_DIST) || (d2FP == 0.0) || (d2FP >= ClientCfg.DestThreshold));
8765 _DistToFirst = d2FP;
8766 }// dist2FirstPos //
8767 //-----------------------------------------------
8768 // dist2FirstPos :
8769 // Return the Distance from the current entity position to the First Position.
8770 //-----------------------------------------------
8771 double CCharacterCL::dist2FirstPos() const
8773 CHECK((_DistToFirst == INVALID_DIST) || (_DistToFirst == 0.0) || (_DistToFirst >= ClientCfg.DestThreshold));
8774 return _DistToFirst;
8775 }// dist2FirstPos //
8777 //-----------------------------------------------
8778 // dist2Dest :
8779 // Set the Distance from the current entity position to the Destination.
8780 //-----------------------------------------------
8781 void CCharacterCL::dist2Dest(double d2D)
8783 CHECK((d2D == INVALID_DIST) || (d2D == 0.0) || (d2D >= ClientCfg.DestThreshold));
8784 _DistToDest = d2D;
8785 }// dist2Dest //
8787 //-----------------------------------------------
8788 // dist2Dest :
8789 // Return the Distance from the current entity position to the Destination.
8790 //-----------------------------------------------
8791 double CCharacterCL::dist2Dest() const
8793 CHECK((_DistToDest == INVALID_DIST) || (_DistToDest == 0.0) || (_DistToDest >= ClientCfg.DestThreshold));
8794 return _DistToDest;
8795 }// dist2Dest //
8797 //-----------------------------------------------
8798 // speed :
8799 // Set the Entity Current Speed.
8800 //-----------------------------------------------
8801 void CCharacterCL::speed(double s)
8803 CHECK((s == -1.0) || (s == 0.0) || ((s >= 0.001) && (s <= 1000.0)));
8804 _Speed = s;
8805 }// speed //
8807 //-----------------------------------------------
8808 // speed :
8809 // Return the Entity Current Speed.
8810 //-----------------------------------------------
8811 double CCharacterCL::speed() const // virtual
8813 CHECK((_Speed == -1.0) || (_Speed == 0.0) || ((_Speed >= 0.001) && (_Speed <= 1000.0)));
8814 return _Speed;
8815 }// getSpeed //
8817 //-----------------------------------------------
8818 // Build the in scene interface
8819 //-----------------------------------------------
8820 void CCharacterCL::buildInSceneInterface ()
8822 // Delete previous interface
8823 releaseInSceneInterfaces();
8825 _InSceneUserInterface = CGroupInSceneUserInfo::build (this);
8827 // parent
8828 CEntityCL::buildInSceneInterface();
8831 //-----------------------------------------------
8833 void CCharacterCL::setBubble (CGroupInSceneBubble *bubble)
8835 if (_CurrentBubble != NULL)
8837 CGroupInSceneBubble *old = _CurrentBubble;
8838 _CurrentBubble = NULL;
8839 old->unlink();
8841 nlassert (_CurrentBubble == NULL);
8842 _CurrentBubble = bubble;
8845 //-----------------------------------------------
8847 //-----------------------------------------------
8848 // runFactor :
8849 // Set the Factor between Walk & Run
8850 //-----------------------------------------------
8851 void CCharacterCL::runFactor(double factor)
8853 CHECK((factor >= 0.0) && (factor <= 1.0));
8854 CHECK((factor == 0.0) || (animState(MOVE) == CAnimationStateSheet::Walk));
8855 _RunFactor = factor;
8856 }// runFactor //
8858 //-----------------------------------------------
8859 // runFactor :
8860 // Get the Factor between Walk & Run
8861 //-----------------------------------------------
8862 double CCharacterCL::runFactor() const
8864 CHECK((_RunFactor >= 0.0) && (_RunFactor <= 1.0));
8865 CHECK((_RunFactor == 0.0) || (animState(MOVE) == CAnimationStateSheet::Walk));
8866 return _RunFactor;
8867 }// runFactor //
8869 //-----------------------------------------------
8870 // animOffset :
8871 // Set the animation time offset for an animation channel.
8872 //-----------------------------------------------
8873 void CCharacterCL::animOffset(TAnimationType channel, double timeOffset)
8875 if(ClientCfg.Light)
8876 return;
8877 // Check the channel
8878 CHECK((uint)channel < _AnimOffset.size());
8879 // Check Animation Time Offset is greater or equal to 0.
8880 CHECK(timeOffset >= 0.0);
8881 // Check the animation time offset is not greater to the animation length.
8882 CHECK(timeOffset <= EAM->getAnimationLength(animId(channel)));
8883 // Set the Value
8884 _AnimOffset[channel] = timeOffset;
8885 }// animOffset //
8887 //-----------------------------------------------
8888 // animOffset :
8889 // Return the animation time offset for an animation channel.
8890 //-----------------------------------------------
8891 double CCharacterCL::animOffset(TAnimationType channel) const
8893 if(ClientCfg.Light)
8894 return 0.0;
8895 // Check the channel
8896 CHECK((uint)channel < _AnimOffset.size());
8897 // Check Animation Time Offset is greater or equal to 0.
8898 CHECK(_AnimOffset[channel] >= 0.0);
8899 // Check the animation time offset is not greater to the animation length.
8900 CHECK(_AnimOffset[channel] <= EAM->getAnimationLength(animId(channel)));
8901 // Return the Value
8902 return _AnimOffset[channel];
8903 }// animOffset //
8905 //---------------------------------------------------
8906 // animationStateKey :
8907 // Set the animation state key.
8908 // \todo GUIGUI : a remplacer par animState directement.
8909 //---------------------------------------------------
8910 bool CCharacterCL::animationStateKey(TAnimationType channel, TAnimStateId value)
8912 // Is the new key valid ?
8913 if(value == CAnimationStateSheet::UnknownState)
8915 nlwarning("CH::animationStateKey: Char '%d': new state key is Null.", _Slot);
8916 return false;
8918 // Set the new key.
8919 animState(channel, value);
8920 // Debug Animation for the selection
8921 if(VerboseAnimSelection && _Slot == UserEntity->selection())
8922 nlinfo("CH:animationStateKey:%d: state '%s'.", _Slot, CAnimationState::getAnimationStateName(value).c_str());
8924 return true;
8925 }// animationStateKey //
8927 //-----------------------------------------------
8928 // animState :
8929 // Set the Animation 'State' for an animation channel.
8930 //-----------------------------------------------
8931 void CCharacterCL::animState(TAnimationType channel, TAnimStateId state)
8933 // Check the channel
8934 CHECK((uint)channel < _AnimState.size());
8935 // Set the new State
8936 _AnimState[channel] = state;
8937 // Reset the Run Factor when the state change.
8938 runFactor(0.0);
8939 }// animState //
8941 //-----------------------------------------------
8942 // animState :
8943 // Get the Animation 'State' for an animation channel.
8944 //-----------------------------------------------
8945 TAnimStateId CCharacterCL::animState(TAnimationType channel) const
8947 // Check the channel
8948 CHECK((uint)channel < _AnimState.size());
8949 // Return the Animation State
8950 return _AnimState[channel];
8951 }// animState //
8953 //-----------------------------------------------
8954 // animIndex :
8955 // Set the Animation 'Index' in the 'State' for an animation channel.
8956 //-----------------------------------------------
8957 void CCharacterCL::animIndex(TAnimationType channel, CAnimation::TAnimId index)
8959 // Check the channel
8960 CHECK((uint)channel < _AnimState.size());
8961 // Set the animation index in the state
8962 _AnimIndex[channel] = index;
8963 // Set the animation Id
8964 // If the current animation Index is not a valid one, return empty
8965 if(_AnimIndex[channel] == CAnimation::UnknownAnim)
8966 animId(channel, NL3D::UPlayList::empty);
8967 else
8969 // Check the AnimSet needed to get the animation Id.
8970 CHECK(_CurrentAnimSet[channel] != NULL);
8971 // Get the Pointer on the animation state, if Null, return empty
8972 const CAnimationState *animStatePtr = _CurrentAnimSet[channel]->getAnimationState( (animState(channel)==CAnimationStateSheet::Emote)?_SubStateKey:animState(channel));
8973 if(animStatePtr == 0)
8974 animId(channel, NL3D::UPlayList::empty);
8975 else
8977 // Get the Animation Pointer, if Null, return Empty
8978 const CAnimation *anim = animStatePtr->getAnimation(animIndex(channel));
8979 if(anim == 0)
8980 animId(channel, NL3D::UPlayList::empty);
8981 // Return The Animation ID
8982 else
8983 animId(channel, anim->id());
8986 }// animIndex //
8988 //-----------------------------------------------
8989 // animIndex :
8990 // Get the Animation 'Index' in the 'State' for an animation channel.
8991 //-----------------------------------------------
8992 CAnimation::TAnimId CCharacterCL::animIndex(TAnimationType channel) const
8994 // Check the channel
8995 CHECK((uint)channel < _AnimState.size());
8996 // Return the Animation Index in the State
8997 return _AnimIndex[channel];
8998 }//animIndex //
9000 //-----------------------------------------------
9001 // animId :
9002 // Set the Animation 'Id' among all the animations for an animation channel.
9003 //-----------------------------------------------
9004 void CCharacterCL::animId(TAnimationType channel, uint id)
9006 // Check the channel
9007 CHECK((uint)channel < _AnimId.size());
9008 // Set the Id
9009 _AnimId[channel] = id;
9010 }// animId //
9012 //-----------------------------------------------
9013 // animId :
9014 // Get the Animation 'Id' among all the animations for an animation channel.
9015 //-----------------------------------------------
9016 uint CCharacterCL::animId(TAnimationType channel) const
9018 // Check the channel
9019 CHECK((uint)channel < _AnimId.size());
9020 // Get the Id
9021 return _AnimId[channel];
9022 }// animId //
9026 //-----------------------------------------------
9027 // Align the given FX so that it is oriented like that entity
9028 //-----------------------------------------------
9029 void CCharacterCL::alignFX(UParticleSystemInstance instance, float scale /* = 1.f */, const NLMISC::CVector &localOffset /*= NLMISC::CVector::Null*/) const
9031 // copy matrix from parent
9032 CMatrix fxMatrix;
9033 fxMatrix.identity();
9034 buildAlignMatrix(fxMatrix);
9035 alignFX(instance, fxMatrix, scale, localOffset);
9039 void CCharacterCL::alignFX(UParticleSystemInstance instance, const CMatrix &matrix, float scale /*=1.f*/, const NLMISC::CVector &localOffset /*=NLMISC::CVector::Null*/) const
9041 if(instance.empty())
9042 return;
9043 CMatrix fxMatrix = matrix;
9044 if (scale != 1.f) fxMatrix.scale(scale);
9045 fxMatrix.setPos(fxMatrix.getPos() + fxMatrix.mulVector(localOffset));
9046 instance.setTransformMode(NL3D::UTransform::DirectMatrix);
9047 instance.setMatrix(fxMatrix);
9048 if(!_Skeleton.empty())
9050 instance.setClusterSystem(_Skeleton.getClusterSystem());
9055 //------------------------------------------------------------
9056 // build a matrix aligned on that entity (includes dir & pos)
9057 //------------------------------------------------------------
9058 void CCharacterCL::buildAlignMatrix(NLMISC::CMatrix &dest) const
9060 // if not in clod, pelvis bone has been computed -> use it to get the current orientation
9061 // use the dir matrix otherwise
9062 /*CVector forward;
9063 if (pelvisBone() != -1 && _Skeleton)
9065 if (_Skeleton.isBoneComputed(pelvisBone()))
9067 // the direction is given by the y axis
9068 NL3D::UBone pelvisBone = _Skeleton.getBone(pelvisBone());
9069 forward = pelvisBone->getMatrix().getJ();
9070 forward.z = 0.f; // project onto XY plane
9071 forward.normalize();
9073 else
9075 // must be in clod -> use the direction
9076 forward = _Front;
9079 else
9081 // bone not found, use the direction
9082 forward = _Front;
9084 dest.setRot(- forward, forward ^ CVector::K, CVector::K);
9086 dest.setRot(CVector::K ^ _Front, - _Front, CVector::K);
9087 dest.setPos(pos());
9091 //-----------------------------------------------
9092 // attachFXInternal
9093 //-----------------------------------------------
9094 void CCharacterCL::attachFXInternal(const CAttachedFX::TSmartPtr fx, TAttachedFXList targetList)
9096 if (!fx) return;
9097 switch(targetList)
9099 case FXListToRemove:
9100 fx->TimeOutDate += TimeInSec; // in remove list timeout date is absolute
9101 _AttachedFXListToRemove.push_front(fx);
9102 break;
9103 case FXListCurrentAnim:
9104 _AttachedFXListForCurrentAnim.push_front(fx);
9105 break;
9106 case FXListAuto:
9108 if (fx->AniFX->Sheet)
9110 switch(fx->AniFX->Sheet->RepeatMode)
9112 case CAnimationFXSheet::Respawn:
9113 fx->TimeOutDate += TimeInSec; // in remove list timeout date is absolute
9114 _AttachedFXListToRemove.push_front(fx);
9115 break;
9116 default:
9117 _AttachedFXListForCurrentAnim.push_front(fx);
9118 break;
9121 else
9123 fx->TimeOutDate += TimeInSec; // in remove list timeout date is absolute
9124 _AttachedFXListToRemove.push_front(fx);
9127 break;
9128 default:
9129 nlassert(0);
9130 break;
9134 //-----------------------------------------------
9135 // attachFX
9136 //-----------------------------------------------
9137 void CCharacterCL::attachFX(const CAttachedFX::TSmartPtr fx)
9139 attachFXInternal(fx, FXListToRemove);
9146 // ***********************************************************************************************************************
9147 void CCharacterCL::setAuraFX(uint index, const CAnimationFX *sheet)
9149 nlassert(index < MaxNumAura);
9150 // no-op if same aura
9151 if (_AuraFX[index] && _AuraFX[index]->AniFX == sheet) return;
9153 if (sheet == NULL)
9155 std::list<CAttachedFX::CBuildInfo>::iterator itAttachedFxToStart = _AttachedFXListToStart.begin();
9156 while(itAttachedFxToStart != _AttachedFXListToStart.end())
9158 if ((*itAttachedFxToStart).MaxNumAnimCount == index)
9159 itAttachedFxToStart = _AttachedFXListToStart.erase(itAttachedFxToStart);
9160 else
9161 ++itAttachedFxToStart;
9163 // if there's already an aura attached, and if it is not already shutting down
9164 if (_AuraFX[index] && _AuraFX[index]->TimeOutDate == 0.f)
9166 _AuraFX[index]->TimeOutDate = TimeInSec + AURA_SHUTDOWN_TIME;
9169 else
9171 std::list<CAttachedFX::CBuildInfo>::iterator itAttachedFxToStart = _AttachedFXListToStart.begin();
9172 while(itAttachedFxToStart != _AttachedFXListToStart.end())
9174 if ((*itAttachedFxToStart).MaxNumAnimCount == index)
9175 return;
9177 // remove previous aura
9178 _AuraFX[index] = NULL;
9179 CAttachedFX::CBuildInfo bi;
9180 bi.Sheet = sheet;
9181 bi.TimeOut = 0.f;
9183 if (sheet->Sheet->PSName == "misc_caravan_teleportout.ps")
9185 bi.MaxNumAnimCount = index;
9186 bi.StartTime = TimeInSec;
9187 bi.DelayBeforeStart = 12.5f;
9188 _AttachedFXListToStart.push_front(bi);
9190 else if (sheet->Sheet->PSName == "misc_kami_teleportout.ps")
9192 bi.MaxNumAnimCount = index;
9193 bi.StartTime = TimeInSec;
9194 bi.DelayBeforeStart = 11.5f;
9195 _AttachedFXListToStart.push_front(bi);
9197 else
9199 CAttachedFX::TSmartPtr fx = new CAttachedFX;
9200 fx->create(*this, bi, CAttachedFX::CTargeterInfo());
9201 if (!fx->FX.empty())
9203 _AuraFX[index] = fx;
9209 // ***********************************************************************************************************************
9210 void CCharacterCL::setLinkFX(const CAnimationFX *fx, const CAnimationFX *dispell)
9212 // no-op if same link
9213 if (_LinkFX && _LinkFX->AniFX == fx) return;
9214 if (_LinkFX)
9216 if (dispell)
9218 CAttachedFX::TSmartPtr fx = new CAttachedFX;
9219 CAttachedFX::CBuildInfo bi;
9220 bi.Sheet = dispell;
9221 fx->create(*this, bi, CAttachedFX::CTargeterInfo());
9222 attachFX(fx);
9225 _LinkFX = NULL;
9226 if (!fx) return;
9227 CAttachedFX::TSmartPtr linkFX = new CAttachedFX;
9228 CAttachedFX::CBuildInfo bi;
9229 bi.Sheet = fx;
9230 linkFX->create(*this, bi, CAttachedFX::CTargeterInfo());
9231 if (!linkFX->FX.empty())
9233 _LinkFX = linkFX;
9239 // ***********************************************************************************************************************
9240 void CCharacterCL::startItemAttackFXs(bool activateTrails, uint intensity)
9242 uint numItems = (uint)_Items.size();
9243 forceEvalAnim(); // force to eval bones at least once when fx are created
9244 for(uint k = 0; k < numItems; ++k)
9246 _Items[k].startAttackFX(_Skeleton, intensity, (SLOTTYPE::EVisualSlot) k, activateTrails);
9250 // ***********************************************************************************************************************
9251 void CCharacterCL::stopItemAttackFXs()
9253 uint numItems = (uint)_Items.size();
9254 for(uint k = 0; k < numItems; ++k)
9256 if (_Items[k].Sheet)
9258 _Items[k].stopAttackFX();
9264 //-----------------------------------------------
9265 // isNeutral :
9266 //-----------------------------------------------
9267 bool CCharacterCL::isNeutral() const
9269 return !isEnemy();
9272 //-----------------------------------------------
9273 // isFriend :
9274 //-----------------------------------------------
9275 bool CCharacterCL::isFriend () const
9277 return !isEnemy();
9280 //-----------------------------------------------
9281 // isEnemy :
9282 //-----------------------------------------------
9283 bool CCharacterCL::isEnemy () const
9285 // Suppose enemy if attackable
9286 if( properties().attackable() )
9288 return true;
9291 return isAnOutpostEnemy();
9295 //-----------------------------------------------
9296 // isAlly :
9297 //-----------------------------------------------
9298 bool CCharacterCL::isAlly () const
9300 return this->isAnOutpostAlly();
9304 //-----------------------------------------------
9305 // applyVisualFX
9306 //-----------------------------------------------
9307 void CCharacterCL::applyVisualFX(sint64 prop)
9309 CVisualFX vfx;
9310 vfx.unpack(prop);
9311 const CAnimationFX *auraFX = NULL;
9312 if (vfx.Aura != 0)
9314 auraFX = CAttackListManager::getInstance().getAuras().getFX(vfx.Aura);
9316 setAuraFX(0, auraFX);
9317 const CAnimationFX *auraReceiptFX = NULL;
9318 if (vfx.AuraReceipt)
9320 auraReceiptFX = CAttackListManager::getInstance().getAuras().getFX(0);
9322 setAuraFX(1, auraReceiptFX);
9323 const CAnimationFX *linkFX = NULL;
9324 if (vfx.Link != 0)
9326 linkFX = CAttackListManager::getInstance().getLinks().getFX(vfx.Link);
9328 const CAnimationFX *dispellFX = NULL;
9329 dispellFX = CAttackListManager::getInstance().getLinks().getFX(0);
9330 setLinkFX(linkFX, dispellFX);
9333 // *********************************************************************************************
9334 const char *CCharacterCL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const
9336 if (!_Sheet) return CEntityCL::getBoneNameFromBodyPart(part, side);
9337 return _Sheet->BodyToBone.getBoneName(part, side);
9341 // *********************************************************************************************
9342 const CItemSheet *CCharacterCL::getRightHandItemSheet() const
9344 if (_RHandInstIdx == CEntityCL::BadIndex) return NULL;
9345 return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
9348 // *********************************************************************************************
9349 const CItemSheet *CCharacterCL::getLeftHandItemSheet() const
9351 if (_LHandInstIdx == CEntityCL::BadIndex) return NULL;
9352 return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
9355 // ***************************************************************************
9356 void CCharacterCL::resetAllSoundAnimId()
9358 for(uint i=0;i<_SoundId.size();i++)
9360 _SoundId[i]= CSoundAnimationNoId;
9365 /////////////////////////////
9366 // CCharacterCL::CWornItem //
9367 /////////////////////////////
9370 // ***********************************************************************************************************************
9371 void CCharacterCL::CWornItem::startAttackFX(NL3D::USkeleton skeleton, uint intensity, SLOTTYPE::EVisualSlot visualSlot, bool activateTrail)
9373 if (intensity < 1 || intensity > 5) return;
9374 // activate trail
9375 if (activateTrail && !Trail.empty())
9377 Trail.start();
9379 // create the attack fx
9380 if (Sheet)
9382 const CItemFXSheet &fxSheet = Sheet->FX;
9383 std::string shapeName = fxSheet.getAttackFX();
9384 if (!shapeName.empty())
9386 const char *stickPoint = NULL;
9387 if(!skeleton.empty())
9389 switch(visualSlot)
9391 case SLOTTYPE::RIGHT_HAND_SLOT:
9392 if( Sheet->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
9393 stickPoint = "box_arme";
9394 break;
9395 case SLOTTYPE::LEFT_HAND_SLOT:
9396 if(Sheet && Sheet->getAnimSet()=="s")
9397 stickPoint = "Box_bouclier";
9398 else
9399 stickPoint = "box_arme_gauche";
9400 break;
9401 default:
9402 break;
9405 if (stickPoint)
9407 sint boneId = skeleton.getBoneIdByName(std::string(stickPoint));
9408 if (boneId != -1)
9410 NL3D::UInstance instance = Scene->createInstance(shapeName);
9411 if (!instance.empty())
9413 instance.show();
9414 if (skeleton.getVisibility() == UTransform::Hide)
9416 // force to compute the bone at least once
9417 skeleton.forceComputeBone(boneId);
9419 UParticleSystemInstance atk;
9420 atk.cast (instance);
9421 if (!atk.empty())
9423 // set the user params
9424 static const float up[5][4] =
9426 { 0.f, 0.f, 0.f, 0.f},
9427 { 1.f, 0.f, 0.f, 0.f},
9428 { 1.f, 1.f, 0.f, 0.f},
9429 { 1.f, 1.f, 1.f, 0.f},
9430 { 1.f, 1.f, 1.f, 1.f}
9432 for(uint k = 0; k < 4; ++k)
9434 atk.setUserParam(k, up[intensity - 1][k]);
9436 atk.setTransformMode(UTransform::RotEuler);
9437 atk.setPos(fxSheet.AttackFXOffset);
9438 const float degreeToRad = (float) Pi / 180.f;
9439 atk.setRotEuler(fxSheet.AttackFXRot.x * degreeToRad, fxSheet.AttackFXRot.y * degreeToRad, fxSheet.AttackFXRot.z * degreeToRad);
9440 skeleton.stickObject(atk, boneId);
9441 // delegate mng of this object lifetime to the fx manager
9442 FXMngr.fx2remove(atk);
9444 else
9446 Scene->deleteInstance(atk);
9455 // ***********************************************************************************************************************
9456 void CCharacterCL::CWornItem::stopAttackFX()
9458 if (!Trail.empty()) Trail.stop();
9461 // ***********************************************************************************************************************
9462 void CCharacterCL::CWornItem::initFXs(SLOTTYPE::EVisualSlot /* visualSlot */, NL3D::UInstance parent)
9464 releaseFXs();
9465 if (!Sheet) return;
9466 // create trail fx
9467 const CItemFXSheet &sheet = Sheet->FX;
9468 std::string shapeName = sheet.getTrail();
9469 if (!shapeName.empty())
9471 Trail = Scene->createInstance(shapeName);
9472 if (Trail.empty())
9474 nlwarning("Cannot create instance %s", shapeName.c_str());
9475 return;
9477 // Initialize the remanence. Must substract the object matrix since parent(parent)
9478 CMatrix mat = parent.getMatrix();
9479 mat.invert();
9480 mat *= Trail.getMatrix();
9481 Trail.setTransformMode(UTransformable::DirectMatrix);
9482 Trail.setMatrix(mat);
9483 // Initialize instance.
9484 Trail.parent(parent);
9488 // ***********************************************************************************************************************
9489 void CCharacterCL::CWornItem::enableAdvantageFX(NL3D::UInstance parent)
9491 if (!Sheet) return;
9493 bool enabled = Sheet->HasFx;
9495 if ((enabled && !AdvantageFX.empty()) || (!enabled && AdvantageFX.empty()))
9496 return; // state did not change
9497 if (!enabled)
9499 // well, it is unlikely that player will loses its ability to master an item after he gained it, but manage the case anyway.
9500 if (!AdvantageFX.removeByID(NELID("STOP")) && !AdvantageFX.removeByID(NELID("main")))
9502 AdvantageFX.activateEmitters(false);
9504 FXMngr.fx2remove(AdvantageFX);
9505 AdvantageFX = NULL;
9507 else
9509 std::string shapeName = Sheet->FX.getAdvantageFX();
9510 if (!shapeName.empty())
9512 NL3D::UInstance fx = Scene->createInstance(shapeName);
9513 if (fx.empty()) return;
9514 AdvantageFX.cast (fx);
9515 if (AdvantageFX.empty())
9517 Scene->deleteInstance(fx);
9518 return;
9520 CMatrix mat = parent.getMatrix();
9521 mat.invert();
9522 mat *= AdvantageFX.getMatrix();
9523 AdvantageFX.setTransformMode(UTransformable::DirectMatrix);
9524 AdvantageFX.setMatrix(mat);
9525 AdvantageFX.parent(parent);
9530 // ***********************************************************************************************************************
9531 void CCharacterCL::CWornItem::releaseFXs()
9533 if (Scene)
9535 if (!Trail.empty())
9536 Scene->deleteInstance(Trail);
9537 if (!AdvantageFX.empty())
9538 Scene->deleteInstance(AdvantageFX);
9542 // ***********************************************************************************************************************
9543 void CCharacterCL::CWornItem::setTrailSize(uint size)
9545 if (Trail.empty()) return;
9546 if (!Sheet) return;
9547 clamp(size, (uint) 0, (uint) 15);
9548 if (size == 0)
9550 Trail.setSliceTime(0.f);
9552 else
9554 float ratio = (size - 1) / 15.f;
9555 Trail.setSliceTime(ratio * Sheet->FX.TrailMaxSliceTime + (1.f - ratio) * Sheet->FX.TrailMinSliceTime);
9560 // ***********************************************************************************************************************
9561 const CAttack *CCharacterCL::getAttack(const CAttackIDSheet &id) const
9563 if (!_Sheet) return NULL;
9564 return getAttack(id, _Sheet->AttackLists);
9567 // ***********************************************************************************************************************
9568 const CAttack *CCharacterCL::getAttack(const CAttackIDSheet &id, const std::vector<NLMISC::TSStringId> &attackList) const
9570 for(std::vector<NLMISC::TSStringId>::const_reverse_iterator it = attackList.rbegin(); it != attackList.rend(); ++it)
9572 const CAttackList *al = CAttackListManager::getInstance().getAttackList(ClientSheetsStrings.get(*it));
9573 if (al)
9575 const CAttack *attk = al->getAttackFromID(id);
9576 if (attk) return attk;
9579 return NULL;
9583 // ***********************************************************************************************************************
9584 void CCharacterCL::initStaticFX()
9586 _StaticFX = NULL;
9587 if (ClientCfg.Light) return;
9588 std::string staticFX = _Sheet->getStaticFX();
9589 if (!staticFX.empty())
9591 CEntitySheet *sheet = SheetMngr.get(NLMISC::CSheetId(staticFX));
9592 if (sheet)
9594 if (sheet->Type == CEntitySheet::ANIMATION_FX)
9596 CAnimationFXSheet *afs = NLMISC::safe_cast<CAnimationFXSheet *>(sheet);
9597 _StaticFX = new CStaticFX;
9598 _StaticFX->AF.init(afs, EAM ? EAM->getAnimationSet() : NULL);
9599 _StaticFX->FX = new CAttachedFX;
9600 CAttachedFX::CBuildInfo bi;
9601 bi.Sheet = &_StaticFX->AF;
9602 _StaticFX->FX->create(*this, bi, CAttachedFX::CTargeterInfo());
9603 if (_StaticFX->FX->FX.empty())
9605 _StaticFX = NULL;
9606 return;
9611 else
9613 nlwarning("Static fx %s not found", staticFX.c_str());
9618 // ***************************************************************************
9619 EGSPD::CPeople::TPeople CCharacterCL::people() const
9621 if(_Sheet)
9622 return _Sheet->Race;
9623 else
9624 return EGSPD::CPeople::Unknown;
9627 // ***************************************************************************
9628 void CCharacterCL::setPeople(EGSPD::CPeople::TPeople /* people */)
9632 // ***************************************************************************
9634 #if !FINAL_VERSION
9636 // temp : begin cast of projectile (start begin anim and loop anim)
9637 NLMISC_COMMAND(beginCast, "Start spell cast", "<spell_id> <strenght> [<mode> <caster slot> <behaviour>]")
9639 if (args.size() < 2) return false;
9640 if (args.size() > 5) return false;
9641 uint casterSlot = 0;
9642 if (args.size() >= 4)
9644 fromString(args[3], casterSlot);
9646 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(casterSlot));
9647 if (!ch) return false;
9648 CBehaviourContext bc;
9649 // TODO : the type of missile is contained in the spell id
9650 if (args.size() >= 5)
9652 sint missileType;
9653 fromString(args[4], missileType);
9654 bc.Behav.Behaviour = (MBEHAV::EBehaviour) (MBEHAV::CAST_OFF + missileType);
9656 else
9658 bc.Behav.Behaviour = MBEHAV::CAST_OFF;
9661 uint16 spellId;
9662 fromString(args[0], spellId); // spell id is unused
9663 bc.Behav.Spell.SpellId = spellId;
9664 //bc.Behav.Spell.Resist = false;
9665 uint16 spellIntensity;
9666 fromString(args[1], spellIntensity);
9667 bc.Behav.Spell.SpellIntensity = spellIntensity;
9668 if (args.size() == 3)
9670 uint16 spellMode;
9671 fromString(args[2], spellMode);
9672 bc.Behav.Spell.SpellMode = spellMode;
9674 else
9676 bc.Behav.Spell.SpellMode = MAGICFX::Bomb; // 'bomb' is the default
9678 ch->applyBehaviour(bc);
9679 return true;
9682 // temp : test fail of a cast
9683 NLMISC_COMMAND(failCast, "Simulate failure of a spell cast", "<spell_id> <strenght> [<mode>]")
9685 if (args.size() < 2) return false;
9686 if (args.size() > 3) return false;
9687 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(0));
9688 if (!ch) return false;
9689 CBehaviourContext bc;
9690 // TODO : the type of missile is contained in the spell id
9691 bc.Behav.Behaviour = MBEHAV::CAST_OFF_FAIL;
9693 uint16 spellId;
9694 fromString(args[0], spellId); // spell id is unused
9695 bc.Behav.Spell.SpellId = spellId;
9696 //bc.Behav.Spell.Resist = false;
9697 uint16 spellIntensity;
9698 fromString(args[1], spellIntensity);
9699 bc.Behav.Spell.SpellIntensity = spellIntensity;
9700 if (args.size() == 3)
9702 uint16 spellMode;
9703 fromString(args[2], spellMode);
9704 bc.Behav.Spell.SpellMode = spellMode;
9706 else
9708 bc.Behav.Spell.SpellMode = MAGICFX::Bomb; // 'bomb' is the default
9710 ch->applyBehaviour(bc);
9711 return true;
9714 // temp to test cast of a projectile on another entity
9715 NLMISC_COMMAND(projectile, "Cast a projectile on another entity", "<spellID> <strenght> [<mode> <target entity> <resist> <source entity>]" )
9717 if (args.size() < 2) return false;
9718 if (args.size() > 6) return false;
9719 uint8 casterSlot = 0;
9720 if (args.size() > 5)
9722 fromString(args[5], casterSlot);
9724 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(casterSlot));
9725 if (!ch) return false;
9726 // create a new behaviour to apply to the user
9727 CBehaviourContext bc;
9728 uint16 spellId, spellIntensity;
9729 fromString(args[0], spellId);
9730 bc.Behav.Spell.SpellId = spellId;
9731 fromString(args[1], spellIntensity);
9732 bc.Behav.Spell.SpellIntensity = spellIntensity;
9733 // TODO : the type of missile is contained in the spell id
9734 bc.Behav.Behaviour = MBEHAV::CAST_OFF_SUCCESS;
9736 if (args.size() > 2)
9738 uint16 spellMode;
9739 fromString(args[2], spellMode);
9740 bc.Behav.Spell.SpellMode = spellMode;
9742 else
9744 bc.Behav.Spell.SpellMode = MAGICFX::Bomb; // 'bomb' is the default
9746 if (args.size() > 3)
9748 uint targetSlot;
9749 fromString(args[3], targetSlot);
9750 if (targetSlot >= CLFECOMMON::INVALID_SLOT) return false;
9751 CEntityCL *target = EntitiesMngr.entity(targetSlot);
9752 if (!target) return false;
9753 double dist = (target->pos() - ch->pos()).norm();
9754 bool resist = false;
9755 if (args.size() > 4) fromString(args[4], resist);
9756 bc.Targets.Targets.push_back(CMultiTarget::CTarget(targetSlot, resist, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), -11));
9758 bc.BehavTime = TimeInSec;
9759 ch->applyBehaviour(bc);
9760 return true;
9763 NLMISC_COMMAND(mtProjectile, "Cast a projectile on one or several entities", "<caster> <spellID> <strenght> <mode> <target0> [<target n>]*" )
9765 if (args.size() < 5) return false;
9766 uint8 casterSlot;
9767 fromString(args[0], casterSlot);
9768 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(casterSlot));
9769 if (!ch) return false;
9770 // create a new behaviour to apply to the user
9771 CBehaviourContext bc;
9772 uint16 spellId, spellIntensity, spellMode;
9774 fromString(args[1], spellId);
9775 bc.Behav.Spell.SpellId = spellId;
9777 fromString(args[2], spellIntensity);
9778 bc.Behav.Spell.SpellIntensity = spellIntensity;
9780 fromString(args[3], spellMode);
9781 bc.Behav.Spell.SpellMode = spellMode;
9782 // get targets and their dist depending on the mode
9783 switch(bc.Behav.Spell.SpellMode)
9785 case MAGICFX::Bomb:
9787 uint mainTargetSlot;
9788 fromString(args[4], mainTargetSlot);
9789 if (mainTargetSlot >= CLFECOMMON::INVALID_SLOT) return false;
9790 CEntityCL *mainTarget = EntitiesMngr.entity(mainTargetSlot);
9791 if (mainTarget)
9793 double dist = (mainTarget->pos() - ch->pos()).norm();
9794 bc.Targets.Targets.push_back(CMultiTarget::CTarget(mainTargetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), 1));
9795 for(sint k = 1; k < (sint) (args.size() - 4); ++k)
9797 uint secondaryTargetSlot;
9798 fromString(args[4 + k], secondaryTargetSlot);
9799 if (secondaryTargetSlot >= CLFECOMMON::INVALID_SLOT) return false;
9800 CEntityCL *secondaryTarget = EntitiesMngr.entity(secondaryTargetSlot);
9801 if (secondaryTarget)
9803 dist = (secondaryTarget->pos() - mainTarget->pos()).norm();
9804 bc.Targets.Targets.push_back(CMultiTarget::CTarget(secondaryTargetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), k+1));
9809 break;
9810 case MAGICFX::Spray:
9812 for(sint k = 0; k < (sint) (args.size() - 4); ++k)
9814 uint targetSlot;
9815 fromString(args[4 + k], targetSlot);
9816 if (targetSlot >= CLFECOMMON::INVALID_SLOT) return false;
9817 CEntityCL *target = EntitiesMngr.entity(targetSlot);
9818 if (target)
9820 double dist = (target->pos() - ch->pos()).norm();
9821 bc.Targets.Targets.push_back(CMultiTarget::CTarget(targetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), k+1));
9825 break;
9826 case MAGICFX::Chain:
9828 CEntityCL *startSlot = ch;
9829 for(sint k = 0; k < (sint) (args.size() - 4); ++k)
9831 uint targetSlot;
9832 fromString(args[4 + k], targetSlot);
9833 if (targetSlot >= CLFECOMMON::INVALID_SLOT) return false;
9834 CEntityCL *target = EntitiesMngr.entity(targetSlot);
9835 if (target)
9837 double dist = (target->pos() - startSlot->pos()).norm();
9838 bc.Targets.Targets.push_back(CMultiTarget::CTarget(targetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), k+1));
9839 startSlot = target;
9843 break;
9845 bc.BehavTime = TimeInSec;
9846 // TODO : the type of missile is contained in the spell id
9847 bc.Behav.Behaviour = MBEHAV::CAST_OFF_SUCCESS;
9849 ch->applyBehaviour(bc);
9850 return true;
9853 // temp to test cast of multitarget projectile on another entity
9854 NLMISC_COMMAND(aura, "enable / disable aura on an entity", "<slot> <aura>")
9856 if (args.size() != 2) return false;
9857 uint slot;
9858 fromString(args[0], slot);
9859 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(slot));
9860 if (!ch) return false;
9861 const CAnimationFX *fx = NULL;
9862 sint auraIndex;
9863 fromString(args[1], auraIndex);
9864 if (auraIndex != -1)
9866 fx = CAttackListManager::getInstance().getAuras().getFX(auraIndex);
9867 if (!fx) return false;
9869 ch->setAuraFX(0, fx);
9870 return true;
9873 NLMISC_COMMAND(link, "enable / disable link on an entity", "<slot> <link>")
9875 if (args.size() != 2) return false;
9876 uint slot;
9877 fromString(args[0], slot);
9878 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(slot));
9879 if (!ch) return false;
9880 const CAnimationFX *link = NULL;
9881 sint linkIndex;
9882 fromString(args[1], linkIndex);
9883 if (linkIndex != -1)
9885 link = CAttackListManager::getInstance().getLinks().getFX(linkIndex + 1);
9886 if (!link) return false;
9888 const CAnimationFX *linkBreak = CAttackListManager::getInstance().getLinks().getFX(0);
9889 if (!linkBreak) return false;
9890 ch->setLinkFX(link, linkBreak);
9891 return true;
9894 NLMISC_COMMAND(auraReceipt, "enable / disable aura receipt on an entity", "<slot> <aura> <0=on/1=off>")
9896 if (args.size() != 2) return false;
9897 uint slot;
9898 fromString(args[0], slot);
9899 CCharacterCL *ch = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(slot));
9900 if (!ch) return false;
9901 const CAnimationFX *fx = NULL;
9902 bool enableAura;
9903 fromString(args[1], enableAura);
9904 if (enableAura)
9906 fx = CAttackListManager::getInstance().getAuras().getFX(0); // 0 is special for aura receipt
9907 if (!fx) return false;
9909 ch->setAuraFX(1, fx);
9910 return true;
9913 ////////////////////////////
9914 // test for melee weapons //
9915 ////////////////////////////
9917 // these are helpers (the same can be done with /vp, or altLook)
9918 NLMISC_COMMAND(weapon, "change the weapon in hand", "<slot> <hand> <weapon>")
9920 if (args.size() != 3) return false;
9921 CInterfaceManager *im = CInterfaceManager::getInstance();
9922 uint slot;
9923 fromString(args[0], slot);
9924 CCDBNodeLeaf *propA = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPA), false);
9925 if (!propA) return false;
9926 sint64 valueA = propA->getValue64();
9928 CCDBNodeLeaf *propB = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPB), false);
9929 if (!propB) return false;
9930 sint64 valueB = propB->getValue64();
9932 uint hand;
9933 fromString(args[1], hand);
9935 // the VP is dependent of Entity actual type
9936 if(dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(slot)))
9938 SPropVisualA vpa = (SPropVisualA &) valueA;
9939 SPropVisualB vpb = (SPropVisualB &) valueB;
9940 if (hand == 0)
9942 uint16 weaponRightHand;
9943 fromString(args[2], weaponRightHand);
9944 vpa.PropertySubData.WeaponRightHand = weaponRightHand;
9945 vpb.PropertySubData.RTrail = 1;
9947 else
9949 uint16 weaponLeftHand;
9950 fromString(args[2], weaponLeftHand);
9951 vpa.PropertySubData.WeaponLeftHand = weaponLeftHand;
9952 vpb.PropertySubData.LTrail = 1;
9954 propA->setValue64((sint64) vpa.PropertyA);
9955 propB->setValue64((sint64) vpb.PropertyB);
9957 // CharacterCL: use a SAltLook
9958 else
9960 SAltLookProp vpalt = (SAltLookProp&) valueA;
9961 if (hand == 0)
9963 uint16 weaponRightHand;
9964 fromString(args[2], weaponRightHand);
9965 vpalt.Element.WeaponRightHand = weaponRightHand;
9966 vpalt.Element.RTrail = 1;
9968 else
9970 uint16 weaponLeftHand;
9971 fromString(args[2], weaponLeftHand);
9972 vpalt.Element.WeaponLeftHand = weaponLeftHand;
9973 vpalt.Element.LTrail = 1;
9975 propA->setValue64((sint64) vpalt.Summary);
9978 // Force to update property
9979 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPA);
9980 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPB);
9982 // display name of weapon sheet
9984 return true;
9987 NLMISC_COMMAND(advantageFX, "turn on / off the advantage fx for an item in hand", "<slot> <hand> <on = 1/ off = 0>")
9989 if (args.size() != 3) return false;
9990 CInterfaceManager *im = CInterfaceManager::getInstance();
9991 uint slot;
9992 fromString(args[0], slot);
9995 CCDBNodeLeaf *prop = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPA), false);
9996 if (!prop) return false;
9997 sint64 value = prop->getValue64();
9998 uint hand;
9999 fromString(args[1], hand);
10000 // the VP is dependent of Entity actual type
10001 if(dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(slot)))
10003 SPropVisualA vpa = (SPropVisualA &) value;
10004 if (hand == 0)
10006 fromString(args[2], vpa.PropertySubData.RWeaponFX);
10008 else
10010 fromString(args[2], vpa.PropertySubData.LWeaponFX);
10012 prop->setValue64((sint64) vpa.PropertyA);
10014 // CharacterCL: use a SAltLook
10015 else
10017 SAltLookProp vpa = (SAltLookProp&) value;
10018 if (hand == 0)
10020 fromString(args[2], vpa.Element.RWeaponFX);
10022 else
10024 fromString(args[2], vpa.Element.LWeaponFX);
10026 prop->setValue64((sint64) vpa.Summary);
10030 // Force to update property
10031 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPA);
10033 return true;
10036 NLMISC_COMMAND(trailLength, "set length of trail for one weapon in hand", "<slot> <hand> <power = 0..15>")
10038 if (args.size() != 3) return false;
10039 CInterfaceManager *im = CInterfaceManager::getInstance();
10040 uint slot;
10041 fromString(args[0], slot);
10043 CCDBNodeLeaf *propA = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPA), false);
10044 if (!propA) return false;
10045 sint64 valueA = propA->getValue64();
10047 CCDBNodeLeaf *propB = NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:Entities:E%d:P%d", (int) slot, (int) PROPERTY_VPB), false);
10048 if (!propB) return false;
10049 sint64 valueB = propB->getValue64();
10051 uint hand;
10052 fromString(args[1], hand);
10054 // the VP is dependent of Entity actual type
10055 if(dynamic_cast<CPlayerCL*>(EntitiesMngr.entity(slot)))
10057 SPropVisualA vpa = (SPropVisualA &) valueA;
10058 SPropVisualB vpb = (SPropVisualB &) valueB;
10059 if (hand == 0)
10061 uint16 rTrail;
10062 fromString(args[2], rTrail);
10063 vpb.PropertySubData.RTrail = rTrail;
10064 propB->setValue64((sint64) vpb.PropertyB);
10066 else
10068 uint16 lTrail;
10069 fromString(args[2], lTrail);
10070 vpb.PropertySubData.LTrail = lTrail / 2;
10071 propB->setValue64((sint64) vpb.PropertyB);
10074 else
10076 SAltLookProp vpalt = (SAltLookProp &) valueA;
10077 if (hand == 0)
10079 uint16 rTrail;
10080 fromString(args[2], rTrail);
10081 vpalt.Element.RTrail = rTrail;
10083 else
10085 uint16 lTrail;
10086 fromString(args[2], lTrail);
10087 vpalt.Element.LTrail = lTrail / 2;
10089 propA->setValue64((sint64) vpalt.Summary);
10092 // Force to update property
10093 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPA);
10094 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_VPB);
10096 return true;
10100 // simulate an attack behaviour for the given slot
10101 NLMISC_COMMAND(attack, "simulate an attack", "<slot> <intensity> <hit_type> <localisation> <dmg_type> <damage_shield_power> <damage_shield_id>")
10103 if (args.size() < 2) return false;
10104 if (args.size() > 7) return false;
10105 CBehaviourContext bc;
10106 bc.Behav.Behaviour = MBEHAV::DEFAULT_ATTACK;
10107 bc.Behav.Combat.ActionDuration = 0;
10108 uint16 impactIntensity;
10109 fromString(args[1], impactIntensity);
10110 bc.Behav.Combat.ImpactIntensity = impactIntensity;
10111 bc.Behav.Combat.HitType = HITTYPE::Hit;
10112 bc.Behav.Combat.Localisation = BODY::HHead;
10113 bc.Behav.Combat2.DamageType = 0;
10114 if (args.size() > 2)
10116 uint16 hitType;
10117 fromString(args[2], hitType);
10118 bc.Behav.Combat.HitType = hitType + 1;
10120 if (args.size() > 3)
10122 uint16 localisation;
10123 fromString(args[3], localisation);
10124 bc.Behav.Combat.Localisation = localisation;
10126 if (args.size() > 4)
10128 uint16 damageType;
10129 fromString(args[4], damageType);
10130 bc.Behav.Combat2.DamageType = damageType;
10132 uint dsPower = 0;
10133 uint dsType = 0;
10134 if (args.size() > 5)
10136 fromString(args[5], dsPower);
10138 if (args.size() > 6)
10140 fromString(args[6], dsType);
10143 uint slot;
10144 fromString(args[0], slot);
10145 CCharacterCL *entity = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(slot));
10146 if (!entity) return false;
10147 // push current slection as main target
10148 CMultiTarget::CTarget target;
10149 target.TargetSlot = UserEntity->selection();
10150 target.Info = dsPower | (dsType << 3);
10151 target.DeltaHP = -20;
10152 bc.Targets.Targets.push_back(target);
10153 bc.BehavTime = TimeInSec;
10154 bc.Behav.DeltaHP = -20;
10155 entity->applyBehaviour(bc);
10156 return true;
10159 // simulate a range attack from the current slor to the current selection
10160 NLMISC_COMMAND(rangeAttack, "simulate a range attack", "<slot> [intensity] [localisation] [range_weapon_type_if_unequipped]")
10162 if (args.size() < 1) return false;
10163 if (args.size() > 4) return false;
10164 uint slot;
10165 fromString(args[0], slot);
10166 CCharacterCL *entity = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(slot));
10167 if (!entity) return false;
10168 const CItemSheet *weaponSheet = entity->getRightHandItemSheet();
10169 CBehaviourContext bc;
10170 if (!weaponSheet || weaponSheet->Family != ITEMFAMILY::RANGE_WEAPON)
10173 uint16 weaponType = 0;
10174 if (args.size() > 3)
10176 fromString(args[3], weaponType);
10178 bc.Behav.Range.WeaponType = weaponType;
10180 else
10182 bc.Behav.Range.WeaponType = weaponSheet->RangeWeapon.RangeWeaponType;
10184 bc.Behav.Behaviour = MBEHAV::RANGE_ATTACK;
10185 bc.Behav.Range.ImpactIntensity = 1;
10186 bc.Behav.Range.Localisation = BODY::HHead;
10187 bc.BehavTime = TimeInSec;
10188 if (args.size() > 1)
10190 uint16 impactIntensity;
10191 fromString(args[1], impactIntensity);
10192 bc.Behav.Range.ImpactIntensity = impactIntensity;
10194 if (args.size() > 2)
10196 uint16 localisation;
10197 fromString(args[2], localisation);
10198 bc.Behav.Range.Localisation = localisation;
10200 // if not a generic range weapon, add a single target (this is the current selection)
10201 uint8 targetSlot = UserEntity->targetSlot();
10202 if (targetSlot >= CLFECOMMON::INVALID_SLOT) return false;
10203 CEntityCL *target = EntitiesMngr.entity(targetSlot);
10204 if (!target) return false;
10205 double dist = (target->pos() - entity->pos()).norm();
10206 bc.Targets.Targets.push_back(CMultiTarget::CTarget(targetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), -10));
10207 bc.Behav.DeltaHP = -10;
10208 entity->applyBehaviour(bc);
10209 return true;
10213 // simulate a creature attack
10214 NLMISC_COMMAND(creatureAttack, "simulate a creature attack (2 attaques per creature)", "<casterSlot> <targetSlot> [attk=0/1] [magicIntensity] [physicalIntensity] [localisation] [damageType] [hitType] [resist=1/0]")
10216 if (args.size() < 2) return false;
10217 if (args.size() > 9) return false;
10218 CBehaviourContext bc;
10219 bc.Behav.Behaviour = MBEHAV::CREATURE_ATTACK_0;
10220 if (args.size() > 2)
10222 uint attk;
10223 fromString(args[2], attk);
10224 bc.Behav.Behaviour = attk == 0 ? MBEHAV::CREATURE_ATTACK_0 : MBEHAV::CREATURE_ATTACK_1;
10226 uint8 casterSlot;
10227 fromString(args[0], casterSlot);
10228 CCharacterCL *caster = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(casterSlot));
10229 if (!caster) return false;
10230 uint8 targetSlot;
10231 fromString(args[1], targetSlot);
10232 CCharacterCL *target = dynamic_cast<CCharacterCL *>(EntitiesMngr.entity(targetSlot));
10233 if (!target) return false;
10234 double dist = (target->pos() - caster->pos()).norm();
10235 bool resist = false;
10236 if (args.size() > 8)
10238 fromString(args[8], resist);
10240 bc.Targets.Targets.push_back(CMultiTarget::CTarget(targetSlot, false, (uint8) ceilf((float) (dist / MULTI_TARGET_DISTANCE_UNIT)), -15));
10241 bc.Behav.CreatureAttack.ActionDuration = 0;
10242 uint magicImpactIntensity = 1;
10243 if (args.size() > 3)
10245 fromString(args[3], magicImpactIntensity);
10247 bc.Behav.CreatureAttack.MagicImpactIntensity = magicImpactIntensity;
10248 uint physicalImpactIntensity = 0;
10249 if (args.size() > 4)
10251 fromString(args[4], physicalImpactIntensity);
10253 bc.Behav.CreatureAttack.ImpactIntensity = physicalImpactIntensity;
10254 BODY::TBodyPart localisation = BODY::HHead;
10255 if (args.size() > 5)
10257 sint tmp;
10258 fromString(args[5], tmp);
10259 localisation = (BODY::TBodyPart) tmp;
10261 bc.Behav.CreatureAttack.Localisation = localisation;
10262 DMGTYPE::EDamageType dmgType = DMGTYPE::BLUNT;
10263 if (args.size() > 6)
10265 sint tmp;
10266 fromString(args[6], tmp);
10267 dmgType = (DMGTYPE::EDamageType) tmp;
10269 bc.Behav.CreatureAttack2.DamageType = dmgType;
10270 HITTYPE::THitType hitType = HITTYPE::Hit;
10271 if (args.size() > 7)
10273 sint tmp;
10274 fromString(args[7], tmp);
10275 hitType = (HITTYPE::THitType) tmp;
10277 bc.Behav.CreatureAttack2.HitType = hitType;
10278 bc.BehavTime = TimeInSec;
10279 bc.Behav.DeltaHP = -15;
10280 caster->applyBehaviour(bc);
10281 return true;
10284 NLMISC_COMMAND(setNamePosZ, "", "<low/high/normal> <value>")
10286 if (args.size() != 2) return false;
10288 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
10289 if (!target)
10290 return true;
10292 float *namePosZ = NULL;
10293 string sheetName, skelName;
10294 if (target->Type == CEntityCL::Player)
10296 CPlayerCL *playerTarget = dynamic_cast<CPlayerCL*>(target);
10297 if (playerTarget)
10299 CRaceStatsSheet *sheet = const_cast<CRaceStatsSheet*>(playerTarget->playerSheet());
10300 if (sheet)
10302 if (toLowerAscii(args[0]) == "low")
10303 namePosZ = &sheet->GenderInfos[playerTarget->getGender()].NamePosZLow;
10304 else if (toLowerAscii(args[0]) == "normal")
10305 namePosZ = &sheet->GenderInfos[playerTarget->getGender()].NamePosZNormal;
10306 else if (toLowerAscii(args[0]) == "high")
10307 namePosZ = &sheet->GenderInfos[playerTarget->getGender()].NamePosZHigh;
10309 sheetName = sheet->Id.toString();
10310 skelName = sheet->GenderInfos[playerTarget->getGender()].Skelfilename;
10314 else
10316 CCharacterCL *creatureTarget = dynamic_cast<CCharacterCL*>(target);
10317 if (creatureTarget)
10319 CCharacterSheet *sheet = const_cast<CCharacterSheet*>(creatureTarget->getSheet());
10320 if (sheet)
10322 if (toLowerAscii(args[0]) == "low")
10323 namePosZ = &sheet->NamePosZLow;
10324 else if (toLowerAscii(args[0]) == "normal")
10325 namePosZ = &sheet->NamePosZNormal;
10326 else if (toLowerAscii(args[0]) == "high")
10327 namePosZ = &sheet->NamePosZHigh;
10329 sheetName = sheet->Id.toString();
10330 skelName = sheet->getSkelFilename();
10335 if (namePosZ)
10337 fromString(args[1], *namePosZ);
10338 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName.c_str(), skelName.c_str(), args[0].c_str(), *namePosZ);
10341 return true;
10344 NLMISC_COMMAND(setMyNamePosZ, "", "<low/high/normal> <value>")
10346 if (args.size() != 2) return false;
10348 float *namePosZ = NULL;
10349 string sheetName, skelName;
10350 CRaceStatsSheet *sheet = const_cast<CRaceStatsSheet*>(UserEntity->playerSheet());
10351 if (sheet)
10353 if (toLowerAscii(args[0]) == "low")
10354 namePosZ = &sheet->GenderInfos[UserEntity->getGender()].NamePosZLow;
10355 else if (toLowerAscii(args[0]) == "normal")
10356 namePosZ = &sheet->GenderInfos[UserEntity->getGender()].NamePosZNormal;
10357 else if (toLowerAscii(args[0]) == "high")
10358 namePosZ = &sheet->GenderInfos[UserEntity->getGender()].NamePosZHigh;
10360 sheetName = sheet->Id.toString();
10361 skelName = sheet->GenderInfos[UserEntity->getGender()].Skelfilename;
10364 if (namePosZ)
10366 fromString(args[1], *namePosZ);
10367 nlinfo("NAMEPOSZ: sheet: %s, skel: %s, NamePosZ%s = %g", sheetName.c_str(), skelName.c_str(), args[0].c_str(), *namePosZ);
10370 return true;
10374 NLMISC_COMMAND(pvpMode, "modify pvp mode", "[<pvp mode> <state>]")
10376 if (args.size() != 0 && args.size() != 2) return false;
10378 CInterfaceManager *IM = CInterfaceManager::getInstance();
10380 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
10381 if (!target)
10383 IM->displaySystemInfo(toString("<pvpMode> no target"));
10384 return false;
10386 if (target->Type != CEntityCL::Player && target->Type != CEntityCL::User)
10388 IM->displaySystemInfo(toString("<pvpMode> target is not a player"));
10389 return false;
10391 CPlayerCL *playerTarget = dynamic_cast<CPlayerCL*>(target);
10392 if (!playerTarget)
10393 return false;
10395 if (args.empty())
10397 uint16 pvpMode = playerTarget->getPvpMode();
10398 string str;
10399 if( pvpMode&PVP_MODE::PvpDuel )
10400 str+="duel ";
10401 if( pvpMode&PVP_MODE::PvpChallenge)
10402 str+="challenge ";
10403 if( pvpMode&PVP_MODE::PvpZoneFree)
10404 str+="free ";
10405 if( pvpMode&PVP_MODE::PvpZoneFaction)
10406 str+="zone_faction ";
10407 if( pvpMode&PVP_MODE::PvpZoneGuild)
10408 str+="zone_guild ";
10409 if( pvpMode&PVP_MODE::PvpZoneOutpost)
10410 str+="outpost ";
10411 if( pvpMode&PVP_MODE::PvpFaction)
10412 str+="faction ";
10413 if( pvpMode&PVP_MODE::PvpFactionFlagged)
10414 str+="faction_flagged ";
10415 if( pvpMode&PVP_MODE::PvpZoneSafe)
10416 str+="in_safe_zone ";
10417 if( pvpMode&PVP_MODE::PvpSafe)
10418 str+="safe ";
10419 IM->displaySystemInfo(str);
10420 nlinfo("<pvpMode> %s",str.c_str());
10422 else
10424 PVP_MODE::TPVPMode pvpMode = PVP_MODE::fromString(args[0]);
10425 bool state;
10426 fromString(args[1], state);
10427 if( state )
10429 uint16 currentPVPMode = playerTarget->getPvpMode();
10430 currentPVPMode |= pvpMode;
10431 playerTarget->setPvpMode(currentPVPMode);
10432 IM->displaySystemInfo(toString("<pvpMode> adding pvp mode %s",args[0].c_str()));
10434 else
10436 uint16 currentPVPMode = playerTarget->getPvpMode();
10437 currentPVPMode &= ~pvpMode;
10438 playerTarget->setPvpMode(currentPVPMode);
10439 IM->displaySystemInfo(toString("<pvpMode> removing pvp mode %s",args[0].c_str()));
10442 playerTarget->buildInSceneInterface();
10443 return true;
10447 NLMISC_COMMAND(pvpClan, "modify pvp clan", "<pvp clan>")
10449 if (args.size() != 1) return false;
10451 CInterfaceManager *IM = CInterfaceManager::getInstance();
10453 CEntityCL *target = EntitiesMngr.entity(UserEntity->targetSlot());
10454 if (!target)
10456 IM->displaySystemInfo(toString("<pvpClan> no target"));
10457 return false;
10459 if (target->Type != CEntityCL::Player && target->Type != CEntityCL::User)
10461 IM->displaySystemInfo(toString("<pvpMode> target is not a player"));
10462 return false;
10464 CPlayerCL *playerTarget = dynamic_cast<CPlayerCL*>(target);
10465 if (!playerTarget)
10466 return false;
10468 // PVP_CLAN::TPVPClan clan = PVP_CLAN::fromString(args[0]);
10469 // playerTarget->setPvpClan(clan);
10470 playerTarget->buildInSceneInterface();
10472 return true;
10475 #endif // !FINAL_VERSION
10477 #include "r2/editor.h"
10479 //---------------------------------------------------
10480 // setDead :
10481 // Method to Flag the character as dead and do everything needed.
10482 //---------------------------------------------------
10483 void CCharacterCL::setDead() // virtual
10485 // If the entity dead is the user -> switch to dead mode.
10486 if(_Slot == UserEntity->slot())
10487 UserControls.mode(CUserControls::DeathMode);
10489 // If the entity killed was the current user target, we update context cursor
10490 if(_Slot == UserEntity->selection())
10492 bool nextContextSelected = false;
10493 // Quartering
10494 if (!R2::getEditor().isDMing())
10496 if(_Properties.harvestable())
10497 nextContextSelected = ContextCur.context("QUARTER");
10498 // Loot
10499 else if(_Properties.lootable())
10500 nextContextSelected = ContextCur.context("LOOT");
10501 // Pick Up
10502 else if(_Properties.liftable())
10503 nextContextSelected = ContextCur.context("PICKUP");
10504 if( !nextContextSelected )
10505 ContextCur.context("STAND BY");
10509 // The character now won't be an obstacle anymore
10510 _Primitive->setOcclusionMask(MaskColNone);
10511 }// setDead //