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