Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / player_cl.cpp
blobd2782b4520a59779804e1e4da90fb1d7d996a5f9
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) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /////////////
25 // INCLUDE //
26 /////////////
27 #include "stdpch.h"
28 // Misc
29 #include "nel/misc/time_nl.h"
30 // Client.
31 #include "player_cl.h"
32 #include "ingame_database_manager.h"
33 #include "net_manager.h"
34 #include "time_client.h"
35 #include "entity_animation_manager.h"
36 #include "sheet_manager.h"
37 #include "color_slot_manager.h"
38 #include "debug_client.h"
39 #include "gabarit.h"
40 #include "interface_v3/interface_manager.h"
41 #include "misc.h"
42 #include "pacs_client.h"
43 #include "motion/user_controls.h"
44 #include "client_cfg.h"
45 #include "user_entity.h"
46 #include "faction_war_manager.h"
47 // Client Sheets
48 #include "client_sheets/player_sheet.h"
49 // 3D
50 #include "nel/3d/u_scene.h"
51 #include "nel/3d/u_instance_material.h"
52 #include "nel/3d/u_play_list.h"
53 #include "nel/3d/u_bone.h"
54 #include "nel/3d/u_particle_system_instance.h"
55 #include "nel/3d/u_point_light.h"
56 // game share
57 #include "game_share/player_visual_properties.h"
58 #include "game_share/gender.h"
59 #include "game_share/bot_chat_types.h"
62 ///////////
63 // USING //
64 ///////////
65 using namespace NLMISC;
66 using namespace NL3D;
67 using namespace NLPACS;
68 using namespace std;
71 ////////////
72 // EXTERN //
73 ////////////
74 extern UScene *Scene;
75 extern CEntityAnimationManager *EAM;
76 extern UTextContext *TextContext;
77 extern UCamera MainCam;
80 //-----------------------------------------------
81 // CPlayerCL :
82 // Constructor.
83 //-----------------------------------------------
84 CPlayerCL::CPlayerCL()
85 : CCharacterCL()
87 Type = Player;
89 // Resize _Instances to the number of visual slots.
90 _Instances.resize(SLOTTYPE::NB_SLOT);
92 // No sheet pointed.
93 _Sheet = 0;
94 _PlayerSheet = 0;
96 // Some default colors.
97 _HairColor = 0;
98 _EyesColor = 0;
100 // Not enough information to display the player.
101 _WaitForAppearance = true;
103 _PlayerCLAsyncTextureLoading= false;
105 // Light Off and not allocated
106 _LightOn = false;
107 }// CPlayerCL //
110 //-----------------------------------------------
111 // ~CPlayerCL :
112 // Destructor.
113 //-----------------------------------------------
114 CPlayerCL::~CPlayerCL()
116 // No more sheet pointed.
117 _PlayerSheet = NULL;
119 // Remove the light
120 if(!_Light.empty())
122 if(Scene)
123 Scene->deletePointLight(_Light);
127 //---------------------------------------------------
128 // getScale :
129 // Return the entity scale. (return 1.0 if there is any problem).
130 //---------------------------------------------------
131 float CPlayerCL::getScale() const // virtual
133 // Default Scale.
134 return _CharacterScalePos;
135 }// getScale //
139 //-----------------------------------------------
140 // getGroundFX :
141 // retrieve ground fxs for the entity depending on the ground
142 //-----------------------------------------------
143 const std::vector<CGroundFXSheet> *CPlayerCL::getGroundFX() const
145 switch (getGender())
147 case 0: return &(_PlayerSheet->GenderInfos[0].GroundFX);
148 case 1: return &(_PlayerSheet->GenderInfos[1].GroundFX);
149 default: break;
151 return NULL;
155 //-----------------------------------------------
156 // isNeutral :
157 // Return true if this entity is a neutral entity(pvp or not)
158 //-----------------------------------------------
159 bool CPlayerCL::isNeutral() const
161 return (!isEnemy() && !isAlly());
165 //-----------------------------------------------
166 // isFriend :
167 // Return true if this entity is a user's friend.
168 //-----------------------------------------------
169 bool CPlayerCL::isFriend () const
171 return isNeutral() || isAlly();
175 //-----------------------------------------------
176 // isEnemy :
177 // true if at least enemy in on pvp mode
178 //-----------------------------------------------
179 bool CPlayerCL::isEnemy () const
181 // Challenge i.e. SOLO FULL PVP
182 if( getPvpMode()&PVP_MODE::PvpChallenge ||
183 UserEntity->getPvpMode()&PVP_MODE::PvpChallenge )
185 return true;
189 // if one of 2 players is not in pvp they can't be enemies
190 if( UserEntity->getPvpMode() == PVP_MODE::None ||
191 getPvpMode() == PVP_MODE::None )
193 return false;
196 // if one of 2 players is safe they can't be enemies
197 if( UserEntity->getPvpMode()&PVP_MODE::PvpSafe ||
198 getPvpMode()&PVP_MODE::PvpSafe )
200 return false;
203 // if one of 2 players are in safe zone and not flagged they can't be enemies
204 if ((UserEntity->getPvpMode()&PVP_MODE::PvpZoneSafe &&
205 ((UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0))
207 (getPvpMode()&PVP_MODE::PvpZoneSafe &&
208 ((getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0)))
210 return false;
213 // Duel
214 if( getPvpMode()&PVP_MODE::PvpDuel &&
215 UserEntity->getPvpMode()&PVP_MODE::PvpDuel )
217 return true; // TODO
220 // Outpost
221 if ( isAnOutpostEnemy() )
223 return true;
226 // Zone Free
227 if( getPvpMode()&PVP_MODE::PvpZoneFree &&
228 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree )
230 // If not in same Team and not in same League => ennemy
231 if( !isInTeam() && !isInSameLeague() )
232 return true;
235 // Zone Guild
236 if( getPvpMode()&PVP_MODE::PvpZoneGuild &&
237 UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild )
239 // If in same Guild but different Leagues => ennemy
240 if ( isInSameGuild() && oneInLeague() && !isInSameLeague() )
241 return true;
243 if( !isInTeam() && !isInSameLeague() )
244 return true;
247 // Zone Faction
248 if( getPvpMode()&PVP_MODE::PvpZoneFaction &&
249 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction )
251 if (getPvpClan() != UserEntity->getPvpClan())
252 return true;
255 // Free PVP : Ennemis are not in team AND not in league
256 if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) &&
257 (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged))
259 // If in same Guild but different Leagues => ennemy
260 if ( isInSameGuild() && oneInLeague() && !isInSameLeague() )
261 return true;
263 if (!isInTeam() && !isInSameLeague())
264 return true;
267 return false;
269 } // isEnemy //
272 //-----------------------------------------------
273 // isAlly :
274 // true if at least ally in one pvp mode
275 //-----------------------------------------------
276 bool CPlayerCL::isAlly() const
279 // Challenge i.e. SOLO FULL PVP
280 if( getPvpMode()&PVP_MODE::PvpChallenge ||
281 UserEntity->getPvpMode()&PVP_MODE::PvpChallenge )
283 return false;
286 // if one of 2 players is not in pvp they can't be allies
287 if( UserEntity->getPvpMode() == PVP_MODE::None ||
288 getPvpMode() == PVP_MODE::None )
290 return false;
293 // if one of 2 players is in safe zone and not other they can't be allies
294 if ((UserEntity->getPvpMode()&PVP_MODE::PvpSafe) != (getPvpMode()&PVP_MODE::PvpSafe))
296 return false;
299 // Outpost
300 if ( isAnOutpostAlly() )
302 return true;
305 // Zone Free
306 if( getPvpMode()&PVP_MODE::PvpZoneFree &&
307 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree )
309 if( isInTeam() || isInSameLeague() )
310 return true;
313 // Zone Guild
314 if( getPvpMode()&PVP_MODE::PvpZoneGuild &&
315 UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild )
317 if( isInTeam() || isInSameLeague() )
318 return true;
320 if ( isInSameGuild() && !oneInLeague() )
321 return true;
325 // Zone Faction
326 if( getPvpMode()&PVP_MODE::PvpZoneFaction &&
327 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction )
329 if (getPvpClan() == UserEntity->getPvpClan())
330 return true;
333 // Free PVP : Allies are in team OR in league
334 if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) &&
335 (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged))
337 if (isInTeam() || isInSameLeague())
338 return true;
340 if ( isInSameGuild() && !oneInLeague() )
341 return true;
344 return false;
346 } // isAlly //
349 //-----------------------------------------------
350 // isNeutralPVP :
351 //-----------------------------------------------
352 bool CPlayerCL::isNeutralPVP() const
354 // if player is not in pvp they can't be neutral pvp
355 if( getPvpMode() == PVP_MODE::None )
357 return false;
360 if( UserEntity->getPvpMode() == PVP_MODE::None )
362 return false;
365 return (!isEnemy() && !isAlly());
369 //-----------------------------------------------
370 // build :
371 // Build the entity from a sheet.
372 //-----------------------------------------------
373 bool CPlayerCL::build(const CEntitySheet *sheet) // virtual
375 // Cast the sheet in the right type.
376 _PlayerSheet = dynamic_cast<const CRaceStatsSheet *>(sheet);
377 if(_PlayerSheet==0)
379 pushDebugStr(NLMISC::toString("Player '%d' sheet is not a '.race_stats' -> BIG PROBLEM.", _Slot));
380 return false;
382 else
383 pushInfoStr(NLMISC::toString("Player '%d' sheet is valid.", _Slot));
384 // Get the DB Entry
385 if(IngameDbMngr.getNodePtr())
387 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
388 if(nodeRoot)
390 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
391 if(_DBEntry == 0)
392 pushDebugStr("Cannot get a pointer on the DB entry.");
396 // Compute the first automaton.
397 _CurrentAutomaton = automatonType() + "_normal.automaton";
399 // Initialize the player look.
400 init3d();
401 // Compute the primitive
402 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
403 // Create the collision entity (used to snap the entity to the ground).
404 computeCollisionEntity();
406 // Initialize properties of the client.
407 initProperties();
408 // Entity Created.
409 return true;
410 }// build //
412 //-----------------------------------------------
413 // automatonType :
414 // Return the automaton type of the entity (homin, creature, etc.)
415 //-----------------------------------------------
416 std::string CPlayerCL::automatonType() const // virtual
418 return _PlayerSheet->Automaton;
419 }// automatonType //
422 //-----------------------------------------------
423 // init3d :
424 // Initialize the graphic for the player.
425 //-----------------------------------------------
426 void CPlayerCL::init3d()
428 createPlayList();
429 // Initialize the internal time.
430 _LastFrameTime = ((double)T1) * 0.001;
431 }// init3d //
434 //-----------------------------------------------
435 // initProperties :
436 // Initialize properties of the entity (according to the class).
437 //-----------------------------------------------
438 void CPlayerCL::initProperties()
440 properties().selectable(true);
441 properties().attackable(false);
442 properties().givable(true);
443 properties().invitable(true);
444 properties().canExchangeItem(true);
445 }// initProperties //
448 //-----------------------------------------------
449 // equip :
450 // Set the equipmenent worn.
451 //-----------------------------------------------
452 void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, const std::string &shapeName, const CItemSheet *item, sint color)
454 // Check slot.
455 if(slot == SLOTTYPE::HIDDEN_SLOT || slot >= SLOTTYPE::NB_SLOT)
457 nlwarning("CCharacterCL::equip : slot %d is not valid.", (uint)slot);
458 return;
461 uint s = (uint)slot;
463 // If exactly the same than before -> return
465 if (!_Instances[s].Loading.empty())
467 if ((_Instances[s].LoadingName == shapeName) && (_Instances[s].FXItemSheet == item))
468 return;
470 else if (!_Instances[s].Current.empty())
472 if ((_Instances[s].CurrentName == shapeName) && (_Instances[s].FXItemSheet == item))
473 return;
476 // Attach to the skeleton.
477 string stickPoint;
478 if(!_Skeleton.empty())
480 switch(slot)
482 case SLOTTYPE::RIGHT_HAND_SLOT:
483 if( item && item->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
484 stickPoint = "box_arme";
485 break;
487 case SLOTTYPE::LEFT_HAND_SLOT:
488 if(_Items[slot].Sheet && _Items[slot].Sheet->getAnimSet()=="s")
489 stickPoint = "Box_bouclier";
490 else
491 stickPoint = "box_arme_gauche";
492 break;
494 default:
495 break;
499 /* If the object is sticked (ie not a skin), decide to delete the Current instance. Why? because the animation
500 is changed according to the equipped item.
502 Hence, For example, if a sword would be changed for a gun, then the new gun animation would take place,
503 while Keeping the old sword shape. BAD.
505 if(!stickPoint.empty())
506 _Instances[s].createLoading(string(), stickPoint);
508 // Create the instance.
509 if (item)
511 if (color != -1) {
512 _Instances[s].createLoading(shapeName, stickPoint, color);
513 } else
514 _Instances[s].createLoading(shapeName, stickPoint, item->MapVariant);
516 else
517 _Instances[s].createLoading(shapeName, stickPoint);
519 // If shapeName is empty, only clear the slot
520 if(shapeName.empty())
522 _Items[slot].release();
523 return;
526 if(!_Instances[s].Loading.empty())
528 _Instances[s].FXItemSheet = item;
530 _Items[slot].initFXs(slot, _Instances[s].Loading);
532 else
533 nlwarning("PL::equip(1):%d: cannot create the instance '%s'.", _Slot, shapeName.c_str());
535 if (!item && (slot != SLOTTYPE::RIGHT_HAND_SLOT) && (slot != SLOTTYPE::LEFT_HAND_SLOT))
536 applyColorSlot(_Instances[s], skin(), 6, _HairColor, _EyesColor);
538 }// equip //
540 //-----------------------------------------------
541 // equip :
542 // Compute the equipmenent worn.
543 //-----------------------------------------------
544 void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, uint index, uint color)
546 // Get the sheet according to the visual slot
547 _Items[slot].Sheet = SheetMngr.getItem(slot, index);
548 if(_Items[slot].Sheet)
550 const CItemSheet *item = _Items[slot].Sheet;
552 std::string shapeName = "";
553 if(_Gender == GSGENDER::male)
555 switch(_PlayerSheet->People)
557 case EGSPD::CPeople::Fyros:
558 shapeName = item->getShapeFyros();
559 break;
560 case EGSPD::CPeople::Matis:
561 shapeName = item->getShapeMatis();
562 break;
563 case EGSPD::CPeople::Tryker:
564 shapeName = item->getShapeTryker();
565 break;
566 case EGSPD::CPeople::Zorai:
567 shapeName = item->getShapeZorai();
568 break;
571 else
573 switch(_PlayerSheet->People)
575 case EGSPD::CPeople::Fyros:
576 shapeName = item->getShapeFyrosFemale();
577 break;
578 case EGSPD::CPeople::Matis:
579 shapeName = item->getShapeMatisFemale();
580 break;
581 case EGSPD::CPeople::Tryker:
582 shapeName = item->getShapeTrykerFemale();
583 break;
584 case EGSPD::CPeople::Zorai:
585 shapeName = item->getShapeZoraiFemale();
586 break;
588 if (shapeName.empty())
589 shapeName = item->getShapeFemale();
591 if (shapeName.empty())
592 shapeName = item->getShape();
595 // use the right type of boots if wearing a caster dress
596 if ((slot == SLOTTYPE::FEET_SLOT) && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS || item->ItemType == ITEM_TYPE::MEDIUM_BOOTS || item->ItemType == ITEM_TYPE::HEAVY_BOOTS))
598 std::string shapeLegs;
600 if (!_Instances[SLOTTYPE::LEGS_SLOT].Loading.empty())
602 shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].LoadingName;
604 else if (!_Instances[SLOTTYPE::LEGS_SLOT].Current.empty())
606 shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].CurrentName;
609 if (!shapeLegs.empty() && shapeLegs.find("_caster01_") != std::string::npos)
611 std::string tmpName = toLowerAscii(shapeName);
613 std::string::size_type posBottes = tmpName.find("_bottes");
615 if (posBottes != std::string::npos)
617 std::string orgType = tmpName.substr(7, posBottes-7); // underwear, caster01, armor00 or armor01
619 tmpName.replace(posBottes+7, 0, "_" + orgType);
620 tmpName.replace(7, orgType.length(), "caster01");
622 if (CPath::exists(tmpName))
624 // use fixed shape name only if file is present
625 shapeName = tmpName;
627 else
629 // temporary hack because Fyros light boots don't respect conventions
630 if (tmpName[0] == 'f' && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS))
632 if (tmpName[5] == 'f')
634 tmpName = "fy_hof_caster01_bottes_civil.shape";
636 else
638 tmpName = "fy_hom_caster01_civil01_bottes.shape";
641 // use fixed shape name only if file is present
642 if (CPath::exists(tmpName))
644 shapeName = tmpName;
646 else
648 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), shapeName.c_str());
651 else
653 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), shapeName.c_str());
660 // If the gender is a female get the right shape else get the default shape.
661 equip(slot, shapeName, item);
663 // Check there is a shape.
664 UInstance pInst = _Instances[slot].createLoadingFromCurrent();
665 if(!pInst.empty())
667 // Set the right texture variation (quality).
668 pInst.selectTextureSet((uint)item->MapVariant);
669 _Instances[slot].TextureSet = item->MapVariant;
671 // If Hair, color is for the head slot.
672 if(slot == SLOTTYPE::HEAD_SLOT && item->Family != ITEMFAMILY::ARMOR)
673 applyColorSlot(_Instances[slot], skin(), 0, color, _EyesColor);
674 else
676 // Set the User Color.
677 if(item->Color == -1)
678 applyColorSlot(_Instances[slot], skin(), color, _HairColor, _EyesColor);
679 // Set the Item Color.
680 else if(item->Color != -2)
681 applyColorSlot(_Instances[slot], skin(), item->Color, _HairColor, _EyesColor);
682 // Else let the default color.
683 else
684 applyColorSlot(_Instances[slot], skin(), 0, _HairColor, _EyesColor);
688 // Default equipment.
689 else
691 nldebug("PL:equip(2):%d: VS '%d' default equipement used.", _Slot, slot);
692 sint idx = SheetMngr.getVSIndex(_PlayerSheet->GenderInfos[_Gender].Items[slot], slot);
693 if(idx != -1)
695 if(SheetMngr.getItem(slot, (uint)idx))
697 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
699 // If the gender is a female get the right shape else get the default shape.
700 equip(slot, _Gender == GSGENDER::female ? itemSheet->getShapeFemale():itemSheet->getShape());
704 }// equip //
706 //-----------------------------------------------
707 // computeAnimSet :
708 // Compute the animation set to use according to weapons, mode and race.
709 //-----------------------------------------------
710 void CPlayerCL::computeAnimSet()
712 // We need a valid Gender to compute the animset.
713 if(_Gender >= 2)
715 nlwarning("PL:computeAnimSet:%d: not a male or a female (gender=%d).", _Slot, _Gender);
716 return;
719 // Now computing the animset.
720 // Do not count weapons if swimming.
721 if(!::computeAnimSet(_CurrentAnimSet[MOVE], _Mode, _PlayerSheet->GenderInfos[_Gender].AnimSetBaseName, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet, !modeWithHiddenItems()))
722 nlwarning("PL:computeAnimSet:%d: pb when computing the animset.", _Slot);
724 }// computeAnimSet //
727 //-----------------------------------------------
728 // updateVisualPropertyVpa :
729 // Update the Visual Property A.
730 // \todo GUIGUI : use gender enum
731 //-----------------------------------------------
732 void CPlayerCL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
734 CInterfaceManager *IM = CInterfaceManager::getInstance ();
736 // Player will now have enough information to display the character.
737 _WaitForAppearance = false;
739 // Get the property.
740 SPropVisualA visualA = *(SPropVisualA *)(&prop);
742 // GENDER
743 _Gender = (GSGENDER::EGender)(visualA.PropertySubData.Sex);
744 if(_Gender!=GSGENDER::male && _Gender!=GSGENDER::female)
746 nlwarning("PL::updateVPVpa:%d: neither a male nor a female -> male selected.", _Slot);
747 _Gender = GSGENDER::male;
750 // update title when gender changed
751 const string replacement = STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw, _Gender == GSGENDER::female);
752 if (!replacement.empty() || !ClientCfg.DebugStringManager)
754 // Get extended name
755 _NameEx = replacement;
756 _Title = replacement;
758 // display the new title in player interface
759 if (_Slot == 0)
761 CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:player:header_opened:player_title"));
762 if (pVT != NULL) pVT->setText(_Title);
765 // rebuild in scene interface
766 buildInSceneInterface();
769 // Setup _CharacterScalePos
770 _CharacterScalePos = _PlayerSheet->GenderInfos[_Gender].CharacterScalePos;
772 // Check if skeleton has changed
773 if (_CacheSkeletonShapeName != _PlayerSheet->GenderInfos[_Gender].Skelfilename)
775 _CacheSkeletonShapeName = _PlayerSheet->GenderInfos[_Gender].Skelfilename;
777 // Clean the playlist.
778 if(_PlayList)
779 _PlayList->resetAllChannels();
781 // We can now build the skeleton so do it now.
782 skeleton(_CacheSkeletonShapeName);
784 // Invalidate instances cache
785 for (uint i = 0; i < _Instances.size(); ++i)
787 _Instances[i].CurrentName.clear();
788 _Instances[i].LoadingName.clear();
790 _Face.CurrentName.clear();
791 _Face.LoadingName.clear();
793 // Check the skeleton.
794 if(skeleton() && !ClientCfg.Light)
796 // To re-link the skeleton to the mount if needed.
797 parent(parent());
798 // Set the skeleton scale.
799 // \todo GUIGUI: put scale too in race_stats.
800 // Setup Lod Character skeleton, if skeleton exist
801 // Get Lod Character Id from the sheet.
802 sint clodId= getLodCharacterId(*Scene, _PlayerSheet->GenderInfos[_Gender].LodCharacterName);
803 if(clodId>=0)
805 // Setup Lod Character shape and distance
806 skeleton()->setLodCharacterShape(clodId);
807 skeleton()->setLodCharacterDistance(_PlayerSheet->GenderInfos[_Gender].LodCharacterDistance);
809 // Compute the
810 computeSomeBoneId();
812 // CHEST
813 equip(SLOTTYPE::CHEST_SLOT, visualA.PropertySubData.JacketModel, visualA.PropertySubData.JacketColor);
814 // LEGS
815 equip(SLOTTYPE::LEGS_SLOT, visualA.PropertySubData.TrouserModel, visualA.PropertySubData.TrouserColor);
816 // ARMS
817 equip(SLOTTYPE::ARMS_SLOT, visualA.PropertySubData.ArmModel, visualA.PropertySubData.ArmColor);
818 // HAT
819 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
820 // OBJECT in the RIGHT HAND
821 _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
822 // Equip the weapon(object/tool).
823 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
825 if(_Gender == GSGENDER::female)
826 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShapeFemale(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
827 else
828 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
830 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
831 if (!itemInstance.empty())
833 // update fxs
834 _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance);
835 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
836 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(0);
837 //_Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualA.PropertySubData.RTrail);
840 else
842 // No Valid item in the right hand.
844 SLOTTYPE::EVisualSlot slot = SLOTTYPE::RIGHT_HAND_SLOT;
845 string rightHandTag = getTag(5);
846 if (!rightHandTag.empty() && rightHandTag != "_")
848 sint idx = SheetMngr.getVSIndex("stake.sitem", slot);
849 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
850 vector<string> tagInfos;
851 splitString(rightHandTag, string("|"), tagInfos);
852 UInstance instance;
854 // Manage cache of items
855 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
857 int itemNameId;
858 fromString(tagInfos[0].substr(1), itemNameId);
859 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
862 if (tagInfos.size() == 2)
864 sint instTexture;
865 fromString(tagInfos[1], instTexture);
866 equip(slot, tagInfos[0], itemSheet);
867 UInstance pInst = _Instances[slot].createLoadingFromCurrent();
868 if(!pInst.empty())
869 pInst.selectTextureSet(instTexture);
870 _Instances[slot].TextureSet = instTexture;
872 else
874 equip(slot, tagInfos[0], itemSheet);
877 else
879 equip(slot, "");
883 // OBJECT in the LEFT HAND
884 _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
885 // Equip the weapon(object/tool).
886 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
888 equip(SLOTTYPE::LEFT_HAND_SLOT, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet);
889 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
890 if (!itemInstance.empty())
892 // update fxs
893 _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance);
894 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(0);
895 //_Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualA.PropertySubData.LTrail);
898 else
900 // No Valid item in the left hand.
901 equip(SLOTTYPE::LEFT_HAND_SLOT, "");
902 SLOTTYPE::EVisualSlot slot = SLOTTYPE::LEFT_HAND_SLOT;
903 string leftHandTag = getTag(6);
904 if (!leftHandTag.empty() && leftHandTag != "_")
906 vector<string> tagInfos;
907 splitString(leftHandTag, string("|"), tagInfos);
908 UInstance instance;
910 // Manage cache of items
911 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
913 int itemNameId;
914 fromString(tagInfos[0].substr(1), itemNameId);
915 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
918 sint idx;
919 if (tagInfos.size() == 3 && tagInfos[2] == ")")
920 idx = SheetMngr.getVSIndex("icbss_pvp.sitem", slot);
921 else
922 idx = SheetMngr.getVSIndex("icfm1pd.sitem", slot);
924 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
926 if (tagInfos.size() >= 2)
928 sint instTexture;
929 fromString(tagInfos[1], instTexture);
930 equip(slot, tagInfos[0], itemSheet);
931 _Instances[slot].selectTextureSet(instTexture);
932 _Instances[slot].TextureSet = instTexture;
934 else
936 equip(slot, tagInfos[0], itemSheet);
939 else
941 equip(slot, "");
944 // Create face
945 // Only create a face when there is no Helmet
946 if(_Items[SLOTTYPE::HEAD_SLOT].Sheet == 0 || _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family != ITEMFAMILY::ARMOR)
948 CItemSheet *faceItem = getItem(_PlayerSheet->GenderInfos[_Gender], SLOTTYPE::FACE_SLOT);
949 if (faceItem)
951 string sFaceName;
953 if(_Gender == GSGENDER::female)
954 sFaceName = faceItem->getShapeFemale();
955 else
956 sFaceName = faceItem->getShape();
958 if (((!_Face.Loading.empty()) && (_Face.LoadingName != sFaceName)) ||
959 ((!_Face.Current.empty()) && (_Face.CurrentName != sFaceName)) ||
960 (_Face.Current.empty()))
962 if (!_Face.Loading.empty())
964 Scene->deleteInstance(_Face.Loading);
965 _Face.Loading = NULL;
966 _Face.LoadingName = sFaceName;
968 _Face.Loading = Scene->createInstance(sFaceName);
969 if (!_Face.Loading.empty())
971 _Face.LoadingName = sFaceName;
972 if(!skeleton()->bindSkin(_Face.Loading))
973 nlwarning("PL::updateVPVpa:%d: Cannot bind the face.", _Slot);
974 _Face.Loading.hide();
975 // set it async for texture
976 _Face.Loading.enableAsyncTextureMode(true);
978 else
979 nlwarning("PL::updateVPVpa:%d: Cannot create the face.", _Slot);
981 _Face.TextureSet = faceItem->MapVariant;
982 applyColorSlot(_Face, skin(), 0, visualA.PropertySubData.HatColor, 0); // Set a default ruflaket color.
984 else
985 nlwarning("PL::updateVPVpa:%d: Face Item '%s' does not exist.", _Slot,
986 _PlayerSheet->GenderInfos[_Gender].Items[SLOTTYPE::FACE_SLOT].c_str());
988 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
990 SLOTTYPE::EVisualSlot slot = SLOTTYPE::HEAD_SLOT;
991 string hatTag = getTag(7);
992 if (!hatTag.empty() && hatTag != "_")
994 sint idx = SheetMngr.getVSIndex("eroukan_h.sitem", slot);
995 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
996 vector<string> tagInfos;
997 splitString(hatTag, string("|"), tagInfos);
998 UInstance instance;
1000 // Manage cache of items
1001 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
1003 int itemNameId;
1004 fromString(tagInfos[0].substr(1), itemNameId);
1005 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
1008 if (tagInfos.size() == 2)
1010 sint instTexture;
1011 fromString(tagInfos[1], instTexture);
1012 equip(slot, tagInfos[0], itemSheet);
1013 _Instances[slot].selectTextureSet(instTexture);
1014 _Instances[slot].TextureSet = instTexture;
1016 else
1018 equip(slot, tagInfos[0], itemSheet);
1024 else
1026 // There is a helmet !
1027 if (!_Face.Loading.empty())
1028 Scene->deleteInstance(_Face.Loading);
1029 _Face.Loading = NULL;
1030 _Face.LoadingName.clear();
1031 if (!_Face.Current.empty())
1032 Scene->deleteInstance(_Face.Current);
1033 _Face.Current = NULL;
1034 _Face.CurrentName.clear();
1036 // Now we have a skeleton, we can update VpB and VpC.
1037 sint64 vB, vC;
1038 string propName;
1039 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPB);
1040 vB = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
1041 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPC);
1042 vC = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
1043 updateVisualPropertyVpb(0, vB);
1044 updateVisualPropertyVpc(0, vC);
1046 // Attach The Light if there is one.
1047 if(!_Light.empty() && _NameBoneId!=-1)
1048 _Skeleton.stickObject(_Light, _NameBoneId);
1050 // Compute the new animation set to use (due to weapons).
1051 computeAnimSet();
1053 // Set the animation to idle.
1054 setAnim(CAnimationStateSheet::Idle);
1056 // No skeleton
1057 else
1058 nlwarning("PL::updateVPVpa:%d: Skeleton not allocated.", _Slot);
1059 }// updateVisualPropertyVpa //
1061 //-----------------------------------------------
1062 // updateVisualPropertyVpb :
1063 // Update the Visual Property B.
1064 //-----------------------------------------------
1065 void CPlayerCL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1067 // Get the property.
1068 SPropVisualB visualB = *(SPropVisualB *)(&prop);
1070 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
1072 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
1073 if (!itemInstance.empty())
1075 // update fxs
1076 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
1077 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualB.PropertySubData.RTrail);
1081 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
1083 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
1084 if (!itemInstance.empty())
1086 // update fxs
1087 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualB.PropertySubData.LTrail);
1091 if(skeleton())
1093 // HANDS
1094 equip(SLOTTYPE::HANDS_SLOT, visualB.PropertySubData.HandsModel, visualB.PropertySubData.HandsColor);
1095 // FEET
1096 equip(SLOTTYPE::FEET_SLOT, visualB.PropertySubData.FeetModel, visualB.PropertySubData.FeetColor);
1098 else
1099 nlinfo("PL::updateVPVpb:%d: Prop Vpb received before prop Vpa.", _Slot);
1100 }// updateVisualPropertyVpb //
1102 //-----------------------------------------------
1103 // updateVisualPropertyVpc :
1104 // Update the Visual Property C.
1105 // \todo GUIGUI : factorize tatoos with character creation
1106 //-----------------------------------------------
1107 void CPlayerCL::updateVisualPropertyVpc(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1109 if(skeleton())
1111 // Get the property.
1112 SPropVisualC visualC = *(SPropVisualC *)(&prop);
1114 // EYES
1115 _EyesColor = visualC.PropertySubData.EyesColor;
1116 UInstance inst;
1118 // must recreate the face asynchronously (because of color change / makeup change)
1119 inst= _Face.createLoadingFromCurrent();
1121 // if exist
1122 if (!inst.empty())
1124 // change eyes color only
1125 applyColorSlot(_Face, _Face.ACSkin, _Face.ACUser, _Face.ACHair, visualC.PropertySubData.EyesColor);
1127 // Tattoo
1128 makeUp(inst, visualC.PropertySubData.Tattoo);
1130 // Morph
1131 static const char *baseName = "visage_00";
1132 float MTmin, MTmax;
1134 const CGenderInfo *pGI = &_PlayerSheet->GenderInfos[_Gender];
1135 if (pGI == NULL)
1136 return;
1138 MTmin = pGI->BlendShapeMin[0];
1139 MTmax = pGI->BlendShapeMax[0];
1140 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1141 inst.setBlendShapeFactor(baseName + toString(0), (float)(visualC.PropertySubData.MorphTarget1) / 7.f * (MTmax-MTmin) + MTmin, true);
1143 MTmin = pGI->BlendShapeMin[1];
1144 MTmax = pGI->BlendShapeMax[1];
1145 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1146 inst.setBlendShapeFactor(baseName + toString(1), (float)(visualC.PropertySubData.MorphTarget2) / 7.f * (MTmax-MTmin) + MTmin, true);
1148 MTmin = pGI->BlendShapeMin[2];
1149 MTmax = pGI->BlendShapeMax[2];
1150 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1151 inst.setBlendShapeFactor(baseName + toString(2), (float)(visualC.PropertySubData.MorphTarget3) / 7.f * (MTmax-MTmin) + MTmin, true);
1153 MTmin = pGI->BlendShapeMin[3];
1154 MTmax = pGI->BlendShapeMax[3];
1155 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1156 inst.setBlendShapeFactor(baseName + toString(3), (float)(visualC.PropertySubData.MorphTarget4) / 7.f * (MTmax-MTmin) + MTmin, true);
1158 MTmin = pGI->BlendShapeMin[4];
1159 MTmax = pGI->BlendShapeMax[4];
1160 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1161 inst.setBlendShapeFactor(baseName + toString(4), (float)(visualC.PropertySubData.MorphTarget5) / 7.f * (MTmax-MTmin) + MTmin, true);
1163 MTmin = pGI->BlendShapeMin[5];
1164 MTmax = pGI->BlendShapeMax[5];
1165 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1166 inst.setBlendShapeFactor(baseName + toString(5), (float)(visualC.PropertySubData.MorphTarget6) / 7.f * (MTmax-MTmin) + MTmin, true);
1168 MTmin = pGI->BlendShapeMin[6];
1169 MTmax = pGI->BlendShapeMax[6];
1170 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1171 inst.setBlendShapeFactor(baseName + toString(6), (float)(visualC.PropertySubData.MorphTarget7) / 7.f * (MTmax-MTmin) + MTmin, true);
1173 MTmin = pGI->BlendShapeMin[7];
1174 MTmax = pGI->BlendShapeMax[7];
1175 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1176 inst.setBlendShapeFactor(baseName + toString(7), (float)(visualC.PropertySubData.MorphTarget8) / 7.f * (MTmax-MTmin) + MTmin, true);
1179 // Set the Gabarit
1180 float characterHeight = (float)((sint8)(visualC.PropertySubData.CharacterHeight)-7)/7.f;
1181 float torsoWidth = (float)((sint8)(visualC.PropertySubData.TorsoWidth)-7)/7.f;
1182 float armsWidth = (float)((sint8)(visualC.PropertySubData.ArmsWidth)-7)/7.f;
1183 float legsWidth = (float)((sint8)(visualC.PropertySubData.LegsWidth)-7)/7.f;
1184 float breastSize = (float)((sint8)(visualC.PropertySubData.BreastSize)-7)/7.f;
1185 float heightScale, baseHeightScale;
1186 // TODO : manage breast size
1187 GabaritSet.applyGabarit(*skeleton(), _Gender, people(), characterHeight, torsoWidth, armsWidth, legsWidth, breastSize, &heightScale);
1188 baseHeightScale = GabaritSet.getRefHeightScale(_Gender, people());
1190 if(baseHeightScale != 0.f)
1191 _CustomScalePos = heightScale/baseHeightScale;
1192 else
1194 _CustomScalePos = 1.f;
1195 nlwarning("PL::updateVPVpc:'%d': baseHeight == 0.", _Slot);
1198 else
1199 nlinfo("PL:updateVPVpc:'%d': Prop Vpc received before prop Vpa.", _Slot);
1200 }// updateVisualPropertyVpc //
1202 //-----------------------------------------------
1203 //-----------------------------------------------
1204 void CPlayerCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1206 CCharacterCL::updateVisualPropertyPvpMode(gameCycle, prop);
1207 // Additionaly, if i am the target, inform interface of the change
1208 if(isTarget())
1210 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1211 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_TARGET_PVP_CHANGE_MODE");
1212 if(pDB)
1214 sint32 val= pDB->getValue32();
1215 pDB->setValue32(val+1);
1221 //-----------------------------------------------
1222 // skin :
1223 // Get The Entity Skin
1224 //-----------------------------------------------
1225 sint CPlayerCL::skin() const // virtual
1227 return _PlayerSheet->Skin;
1228 }// skin //
1230 //-----------------------------------------------
1231 // people :
1232 // Return the People for the entity.
1233 //-----------------------------------------------
1234 EGSPD::CPeople::TPeople CPlayerCL::people() const// virtual
1236 if(_PlayerSheet)
1237 return _PlayerSheet->People;
1238 else
1239 return EGSPD::CPeople::Unknown;
1240 }// people //
1242 //-----------------------------------------------
1243 // people :
1244 // Setup the People for the entity.
1245 //-----------------------------------------------
1246 void CPlayerCL::setPeople(EGSPD::CPeople::TPeople /* people */)
1248 }// people //
1250 //-----------------------------------------------
1251 // drawName :
1252 // Draw the name.
1253 //-----------------------------------------------
1254 void CPlayerCL::drawName(const NLMISC::CMatrix &mat) // virtual
1256 // Draw the name.
1257 if(!getEntityName().empty())
1258 TextContext->render3D(mat, getEntityName());
1259 }// drawName //
1262 //-----------------------------------------------
1263 // getFace :
1264 // Update eyes blink. For the moment, called by updatePos.
1265 //-----------------------------------------------
1266 CEntityCL::SInstanceCL *CPlayerCL::getFace()
1268 // Implemented in CPlayerCL
1269 return &_Face;
1270 }// getFace //
1272 //-----------------------------------------------
1273 // attackRadius :
1274 // Method to return the attack radius of an entity (take the scale into account).
1275 //-----------------------------------------------
1276 double CPlayerCL::attackRadius() const // virtual
1278 return 0.5;
1279 }// attackRadius //
1281 //-----------------------------------------------
1282 // Return the position the attacker should have to combat according to the attack angle.
1283 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
1284 //-----------------------------------------------
1285 CVectorD CPlayerCL::getAttackerPos(double ang, double dist) const
1287 // Compute the local angle
1288 ang = computeShortestAngle(atan2(front().y, front().x), ang);
1289 ang += Pi;
1290 if(ang > Pi)
1291 ang -= 2*Pi;
1293 // Compute the local position.
1294 CVectorD p;
1295 p.x = 0.5 * sin(-ang) + dist*sin(-ang); // or: pos.x = _Sheet->DistToSide*cos(ang) + dist*cos(ang); but 0 should be right side.
1296 p.y = 0.5 * cos(ang) + dist*cos(ang);
1297 p.z = 0.0;
1299 // Compute the world position.
1300 // Create the target matrix.
1301 CVector vj = front();
1302 vj.z = 0;
1303 CVector vk(0,0,1);
1304 CVector vi = vj^vk;
1305 CMatrix bodyBase;
1306 bodyBase.setRot(vi,vj,vk,true);
1307 bodyBase.setPos(pos());
1309 // Get the destination in the world.
1310 return bodyBase * p;
1311 }// getAttackerPos //
1314 ///////////////
1315 // 3D SYSTEM //
1316 ///////////////
1317 //-----------------------------------------------
1318 // updateAsyncTexture
1319 //-----------------------------------------------
1320 float CPlayerCL::updateAsyncTexture()
1322 // Call parent.
1323 float distToCam= CCharacterCL::updateAsyncTexture();
1325 // Check all instance to know if they need to start async load their textures
1326 if(!_Face.Loading.empty())
1328 // dirty?
1329 if(_Face.Loading.isAsyncTextureDirty())
1331 // reset instance state.
1332 _Face.Loading.setAsyncTextureDirty(false);
1333 // must start loading for this isntance
1334 _Face.Loading.startAsyncTextureLoading();
1335 // the entity is now currently loading.
1336 _PlayerCLAsyncTextureLoading= true;
1337 // The LodTexture need to be recomputed
1338 _LodTextureDirty= true;
1343 // Update Async Texture loading of all instances.
1344 if(_PlayerCLAsyncTextureLoading)
1346 bool allLoaded= true;
1347 // update loading for all instances.
1348 if(!_Face.Loading.empty())
1350 // update async texture loading
1351 allLoaded= allLoaded && _Face.Loading.isAsyncTextureReady();
1354 // if all are loaded, then End! don't need to check all instances every frame.
1355 if(allLoaded)
1357 _PlayerCLAsyncTextureLoading= false;
1358 _Face.updateCurrentFromLoading(_Skeleton);
1363 // For LOD texture, must update the "texture distance"
1364 if(!_Face.Current.empty())
1366 // update async texture loading
1367 _Face.Current.setAsyncTextureDistance(distToCam);
1370 return distToCam;
1373 //-----------------------------------------------
1374 // updateLodTexture
1375 //-----------------------------------------------
1376 void CPlayerCL::updateLodTexture()
1378 // if need to recompute, and if Async loading ended
1379 if( _LodTextureDirty && !_PlayerCLAsyncTextureLoading )
1380 // check parent and upadte lod
1381 CCharacterCL::updateLodTexture();
1384 //-----------------------------------------------
1385 // getMaxSpeed :
1386 // Return the basic max speed for the entity in meter per sec
1387 //-----------------------------------------------
1388 double CPlayerCL::getMaxSpeed() const// virtual
1390 return 6.0f;
1391 }// getMaxSpeed //
1395 //---------------------------------------------------
1396 // displayDebug :
1397 // Display Debug Information.
1398 //---------------------------------------------------
1399 void CPlayerCL::displayDebug(float x, float &y, float lineStep) // virtual
1401 CCharacterCL::displayDebug(x, y, lineStep);
1402 }// displayDebug //
1409 //---------------------------------------------------
1410 // readWrite :
1411 // Read/Write Variables from/to the stream.
1412 //---------------------------------------------------
1413 void CPlayerCL::readWrite(NLMISC::IStream &f)
1415 CCharacterCL::readWrite(f);
1417 // PUBLIC
1419 // PROTECTED
1420 // const CPlayerSheet *_Sheet;
1421 // const CRaceStatsSheet *_PlayerSheet;
1422 // NL3D::UInstance _Face;
1423 f.serial(_DefaultChest);
1424 f.serial(_DefaultLegs);
1425 f.serial(_DefaultArms);
1426 f.serial(_DefaultHands);
1427 f.serial(_DefaultFeet);
1428 f.serial(_DefaultHair);
1429 f.serial(_HairColor);
1430 f.serial(_EyesColor);
1431 f.serial(_WaitForAppearance);
1432 f.serial(_PlayerCLAsyncTextureLoading);
1433 f.serial(_LightOn);
1434 // NL3D::UPointLight _Light;
1436 // PRIVATE
1437 }// readWrite //
1439 //---------------------------------------------------
1440 // load :
1441 // To call after a read from a stream to re-initialize the entity.
1442 //---------------------------------------------------
1443 void CPlayerCL::load() // virtual
1445 CInterfaceManager *IM = CInterfaceManager::getInstance ();
1447 // If the entity should be in the world already
1448 if(_First_Pos == false)
1450 // Insert the primitive into the world.
1451 if(_Primitive)
1452 _Primitive->insertInWorldImage(dynamicWI);
1453 // Insert the entity into PACS
1454 pacsPos(pos());
1457 // update
1458 if(!_WaitForAppearance)
1460 // Visual properties A
1461 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
1462 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
1464 }// load //
1466 // *********************************************************************************************
1467 const char *CPlayerCL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const
1469 if (!_PlayerSheet) return CCharacterCL::getBoneNameFromBodyPart(part, side);
1470 return _PlayerSheet->BodyToBone.getBoneName(part, side);
1473 // *********************************************************************************************
1474 const CItemSheet *CPlayerCL::getRightHandItemSheet() const
1476 return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
1479 // *********************************************************************************************
1480 const CItemSheet *CPlayerCL::getLeftHandItemSheet() const
1482 return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
1485 // *********************************************************************************************
1486 const CAttack *CPlayerCL::getAttack(const CAttackIDSheet &id) const
1488 if (!_PlayerSheet) return NULL;
1489 return CCharacterCL::getAttack(id, _PlayerSheet->AttackLists);
1492 // *********************************************************************************************
1493 float CPlayerCL::getScaleRef() const
1495 float fyrosRefScale = GabaritSet.getRefHeightScale(0, EGSPD::CPeople::Fyros);
1496 if (fyrosRefScale == 0) return 1.f;
1497 return _CustomScalePos * (GabaritSet.getRefHeightScale(_Gender, people()) / fyrosRefScale);
1501 // *********************************************************************************************
1502 float CPlayerCL::getNamePosZ() const
1504 if (!_PlayerSheet)
1505 return 0.f;
1507 float namePosZ;
1508 switch (_ModeWanted)
1510 case MBEHAV::DEATH:
1511 case MBEHAV::SIT:
1512 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZLow;
1513 break;
1515 case MBEHAV::MOUNT_NORMAL:
1516 case MBEHAV::MOUNT_SWIM:
1517 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZHigh;
1518 break;
1520 default:
1521 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZNormal;
1522 break;
1525 return namePosZ * _CharacterScalePos * _CustomScalePos;
1528 // ***************************************************************************
1529 void CPlayerCL::doSetVisualSelectionBlink(bool bOnOff, NLMISC::CRGBA emitColor)
1531 // Do it on Face
1532 if(bOnOff)
1533 _Face.setEmissive(emitColor);
1534 else
1535 _Face.restoreEmissive();
1537 // and parent call
1538 CCharacterCL::doSetVisualSelectionBlink(bOnOff, emitColor);
1543 //-----------------------------------------------
1544 // computePrimitive :
1545 // Create (or re-create) a primitive.
1546 //-----------------------------------------------
1547 void CPlayerCL::computePrimitive()
1549 // Initialize the primitive.
1550 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
1551 if(_Primitive)
1552 _Primitive->insertInWorldImage(dynamicWI);
1553 // Set the position.
1554 pacsPos(pos());
1555 }// computePrimitive //