Added spawnCrystalItem
[ryzomcore.git] / ryzom / client / src / player_cl.cpp
blob7437682798275dde6912b8f44566dd25adf33520
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"
60 #include "interface_v3/lua_ihm_ryzom.h"
63 ///////////
64 // USING //
65 ///////////
66 using namespace NLMISC;
67 using namespace NL3D;
68 using namespace NLPACS;
69 using namespace std;
72 ////////////
73 // EXTERN //
74 ////////////
75 extern UScene *Scene;
76 extern CEntityAnimationManager *EAM;
77 extern UTextContext *TextContext;
78 extern UCamera MainCam;
81 //-----------------------------------------------
82 // CPlayerCL :
83 // Constructor.
84 //-----------------------------------------------
85 CPlayerCL::CPlayerCL()
86 : CCharacterCL()
88 Type = Player;
90 // Resize _Instances to the number of visual slots.
91 _Instances.resize(SLOTTYPE::NB_SLOT);
93 // No sheet pointed.
94 _Sheet = 0;
95 _PlayerSheet = 0;
97 // Some default colors.
98 _HairColor = 0;
99 _EyesColor = 0;
101 // Not enough information to display the player.
102 _WaitForAppearance = true;
104 _PlayerCLAsyncTextureLoading= false;
106 // Light Off and not allocated
107 _LightOn = false;
108 }// CPlayerCL //
111 //-----------------------------------------------
112 // ~CPlayerCL :
113 // Destructor.
114 //-----------------------------------------------
115 CPlayerCL::~CPlayerCL()
117 // No more sheet pointed.
118 _PlayerSheet = NULL;
120 // Remove the light
121 if(!_Light.empty())
123 if(Scene)
124 Scene->deletePointLight(_Light);
128 //---------------------------------------------------
129 // getScale :
130 // Return the entity scale. (return 1.0 if there is any problem).
131 //---------------------------------------------------
132 float CPlayerCL::getScale() const // virtual
134 // Default Scale.
135 return _CharacterScalePos;
136 }// getScale //
140 //-----------------------------------------------
141 // getGroundFX :
142 // retrieve ground fxs for the entity depending on the ground
143 //-----------------------------------------------
144 const std::vector<CGroundFXSheet> *CPlayerCL::getGroundFX() const
146 switch (getGender())
148 case 0: return &(_PlayerSheet->GenderInfos[0].GroundFX);
149 case 1: return &(_PlayerSheet->GenderInfos[1].GroundFX);
150 default: break;
152 return NULL;
156 //-----------------------------------------------
157 // isNeutral :
158 // Return true if this entity is a neutral entity(pvp or not)
159 //-----------------------------------------------
160 bool CPlayerCL::isNeutral() const
162 return (!isEnemy() && !isAlly());
166 //-----------------------------------------------
167 // isFriend :
168 // Return true if this entity is a user's friend.
169 //-----------------------------------------------
170 bool CPlayerCL::isFriend () const
172 return isNeutral() || isAlly();
176 //-----------------------------------------------
177 // isEnemy :
178 // true if at least enemy in on pvp mode
179 //-----------------------------------------------
180 bool CPlayerCL::isEnemy () const
182 // Challenge i.e. SOLO FULL PVP
183 if( getPvpMode()&PVP_MODE::PvpChallenge ||
184 UserEntity->getPvpMode()&PVP_MODE::PvpChallenge )
186 return true;
190 // if one of 2 players is not in pvp they can't be enemies
191 if( UserEntity->getPvpMode() == PVP_MODE::None ||
192 getPvpMode() == PVP_MODE::None )
194 return false;
197 // if one of 2 players is safe they can't be enemies
198 if( UserEntity->getPvpMode()&PVP_MODE::PvpSafe ||
199 getPvpMode()&PVP_MODE::PvpSafe )
201 return false;
204 // if one of 2 players are in safe zone and not flagged they can't be enemies
205 if ((UserEntity->getPvpMode()&PVP_MODE::PvpZoneSafe &&
206 ((UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0))
208 (getPvpMode()&PVP_MODE::PvpZoneSafe &&
209 ((getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0)))
211 return false;
214 // Duel
215 if( getPvpMode()&PVP_MODE::PvpDuel &&
216 UserEntity->getPvpMode()&PVP_MODE::PvpDuel )
218 return true; // TODO
221 // Outpost
222 if ( isAnOutpostEnemy() )
224 return true;
227 // Zone Free
228 if( getPvpMode()&PVP_MODE::PvpZoneFree &&
229 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree )
231 // If not in same Team and not in same League => ennemy
232 if( !isInTeam() && !isInSameLeague() )
233 return true;
236 // Zone Guild
237 if( getPvpMode()&PVP_MODE::PvpZoneGuild &&
238 UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild )
240 // If in same Guild but different Leagues => ennemy
241 if ( isInSameGuild() && oneInLeague() && !isInSameLeague() )
242 return true;
244 if( !isInTeam() && !isInSameLeague() )
245 return true;
248 // Zone Faction
249 if( getPvpMode()&PVP_MODE::PvpZoneFaction &&
250 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction )
252 if (getPvpClan() != UserEntity->getPvpClan())
253 return true;
256 // Free PVP : Ennemis are not in team AND not in league
257 if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) &&
258 (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged))
260 // If in same Guild but different Leagues => ennemy
261 if ( isInSameGuild() && oneInLeague() && !isInSameLeague() )
262 return true;
264 if (!isInTeam() && !isInSameLeague())
265 return true;
268 return false;
270 } // isEnemy //
273 //-----------------------------------------------
274 // isAlly :
275 // true if at least ally in one pvp mode
276 //-----------------------------------------------
277 bool CPlayerCL::isAlly() const
280 // Challenge i.e. SOLO FULL PVP
281 if( getPvpMode()&PVP_MODE::PvpChallenge ||
282 UserEntity->getPvpMode()&PVP_MODE::PvpChallenge )
284 return false;
287 // if one of 2 players is not in pvp they can't be allies
288 if( UserEntity->getPvpMode() == PVP_MODE::None ||
289 getPvpMode() == PVP_MODE::None )
291 return false;
294 // if one of 2 players is in safe zone and not other they can't be allies
295 if ((UserEntity->getPvpMode()&PVP_MODE::PvpSafe) != (getPvpMode()&PVP_MODE::PvpSafe))
297 return false;
300 // Outpost
301 if ( isAnOutpostAlly() )
303 return true;
306 // Zone Free
307 if( getPvpMode()&PVP_MODE::PvpZoneFree &&
308 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree )
310 if( isInTeam() || isInSameLeague() )
311 return true;
314 // Zone Guild
315 if( getPvpMode()&PVP_MODE::PvpZoneGuild &&
316 UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild )
318 if( isInTeam() || isInSameLeague() )
319 return true;
321 if ( isInSameGuild() && !oneInLeague() )
322 return true;
326 // Zone Faction
327 if( getPvpMode()&PVP_MODE::PvpZoneFaction &&
328 UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction )
330 if (getPvpClan() == UserEntity->getPvpClan())
331 return true;
334 // Free PVP : Allies are in team OR in league
335 if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) &&
336 (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged))
338 if (isInTeam() || isInSameLeague())
339 return true;
341 if ( isInSameGuild() && !oneInLeague() )
342 return true;
345 return false;
347 } // isAlly //
350 //-----------------------------------------------
351 // isNeutralPVP :
352 //-----------------------------------------------
353 bool CPlayerCL::isNeutralPVP() const
355 // if player is not in pvp they can't be neutral pvp
356 if( getPvpMode() == PVP_MODE::None )
358 return false;
361 if( UserEntity->getPvpMode() == PVP_MODE::None )
363 return false;
366 return (!isEnemy() && !isAlly());
370 //-----------------------------------------------
371 // build :
372 // Build the entity from a sheet.
373 //-----------------------------------------------
374 bool CPlayerCL::build(const CEntitySheet *sheet) // virtual
376 // Cast the sheet in the right type.
377 _PlayerSheet = dynamic_cast<const CRaceStatsSheet *>(sheet);
378 if(_PlayerSheet==0)
380 pushDebugStr(NLMISC::toString("Player '%d' sheet is not a '.race_stats' -> BIG PROBLEM.", _Slot));
381 return false;
383 else
384 pushInfoStr(NLMISC::toString("Player '%d' sheet is valid.", _Slot));
385 // Get the DB Entry
386 if(IngameDbMngr.getNodePtr())
388 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
389 if(nodeRoot)
391 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
392 if(_DBEntry == 0)
393 pushDebugStr("Cannot get a pointer on the DB entry.");
397 // Compute the first automaton.
398 _CurrentAutomaton = automatonType() + "_normal.automaton";
400 // Initialize the player look.
401 init3d();
402 // Compute the primitive
403 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
404 // Create the collision entity (used to snap the entity to the ground).
405 computeCollisionEntity();
407 // Initialize properties of the client.
408 initProperties();
409 // Entity Created.
410 return true;
411 }// build //
413 //-----------------------------------------------
414 // automatonType :
415 // Return the automaton type of the entity (homin, creature, etc.)
416 //-----------------------------------------------
417 std::string CPlayerCL::automatonType() const // virtual
419 return _PlayerSheet->Automaton;
420 }// automatonType //
423 //-----------------------------------------------
424 // init3d :
425 // Initialize the graphic for the player.
426 //-----------------------------------------------
427 void CPlayerCL::init3d()
429 createPlayList();
430 // Initialize the internal time.
431 _LastFrameTime = ((double)T1) * 0.001;
432 }// init3d //
435 //-----------------------------------------------
436 // initProperties :
437 // Initialize properties of the entity (according to the class).
438 //-----------------------------------------------
439 void CPlayerCL::initProperties()
441 properties().selectable(true);
442 properties().attackable(false);
443 properties().givable(true);
444 properties().invitable(true);
445 properties().canExchangeItem(true);
446 }// initProperties //
449 //-----------------------------------------------
450 // equip :
451 // Set the equipmenent worn.
452 //-----------------------------------------------
453 void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, const std::string &shapeName, const CItemSheet *item, sint color)
455 // Check slot.
456 if(slot == SLOTTYPE::HIDDEN_SLOT || slot >= SLOTTYPE::NB_SLOT)
458 nlwarning("CCharacterCL::equip : slot %d is not valid.", (uint)slot);
459 return;
462 uint s = (uint)slot;
464 // If exactly the same than before -> return
466 if (!_Instances[s].Loading.empty())
468 if ((_Instances[s].LoadingName == shapeName) && (_Instances[s].FXItemSheet == item))
469 return;
471 else if (!_Instances[s].Current.empty())
473 if ((_Instances[s].CurrentName == shapeName) && (_Instances[s].FXItemSheet == item))
474 return;
477 // Attach to the skeleton.
478 string stickPoint;
479 if(!_Skeleton.empty())
481 switch(slot)
483 case SLOTTYPE::RIGHT_HAND_SLOT:
484 if( item && item->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
485 stickPoint = "box_arme";
486 break;
488 case SLOTTYPE::LEFT_HAND_SLOT:
489 if((_Items[slot].Sheet && _Items[slot].Sheet->getAnimSet() == "s") || (item && item->getAnimSet() == "s"))
490 stickPoint = "Box_bouclier";
491 else
492 stickPoint = "box_arme_gauche";
493 break;
495 default:
496 break;
500 /* If the object is sticked (ie not a skin), decide to delete the Current instance. Why? because the animation
501 is changed according to the equipped item.
503 Hence, For example, if a sword would be changed for a gun, then the new gun animation would take place,
504 while Keeping the old sword shape. BAD.
506 if(!stickPoint.empty())
507 _Instances[s].createLoading(string(), stickPoint);
509 // Create the instance.
510 if (color != -1)
511 _Instances[s].createLoading(shapeName, stickPoint, color);
512 else
514 if (item)
515 _Instances[s].createLoading(shapeName, stickPoint, item->MapVariant);
516 else
517 _Instances[s].createLoading(shapeName, stickPoint);
520 // If shapeName is empty, only clear the slot
521 if(shapeName.empty())
523 _Items[slot].release();
524 return;
527 if(!_Instances[s].Loading.empty())
529 _Instances[s].FXItemSheet = item;
531 _Items[slot].initFXs(slot, _Instances[s].Loading);
533 else
534 nlwarning("PL::equip(1):%d: cannot create the instance '%s'.", _Slot, shapeName.c_str());
536 if (!item && (slot != SLOTTYPE::RIGHT_HAND_SLOT) && (slot != SLOTTYPE::LEFT_HAND_SLOT))
537 applyColorSlot(_Instances[s], skin(), 6, _HairColor, _EyesColor);
539 }// equip //
541 //-----------------------------------------------
542 // equip :
543 // Compute the equipmenent worn.
544 //-----------------------------------------------
545 void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, uint index, uint color)
547 // Get the sheet according to the visual slot
548 _Items[slot].Sheet = SheetMngr.getItem(slot, index);
549 if(_Items[slot].Sheet)
551 const CItemSheet *item = _Items[slot].Sheet;
553 std::string shapeName = "";
554 if(_Gender == GSGENDER::male)
556 switch(_PlayerSheet->People)
558 case EGSPD::CPeople::Fyros:
559 shapeName = item->getShapeFyros();
560 break;
561 case EGSPD::CPeople::Matis:
562 shapeName = item->getShapeMatis();
563 break;
564 case EGSPD::CPeople::Tryker:
565 shapeName = item->getShapeTryker();
566 break;
567 case EGSPD::CPeople::Zorai:
568 shapeName = item->getShapeZorai();
569 break;
572 else
574 switch(_PlayerSheet->People)
576 case EGSPD::CPeople::Fyros:
577 shapeName = item->getShapeFyrosFemale();
578 break;
579 case EGSPD::CPeople::Matis:
580 shapeName = item->getShapeMatisFemale();
581 break;
582 case EGSPD::CPeople::Tryker:
583 shapeName = item->getShapeTrykerFemale();
584 break;
585 case EGSPD::CPeople::Zorai:
586 shapeName = item->getShapeZoraiFemale();
587 break;
589 if (shapeName.empty())
590 shapeName = item->getShapeFemale();
592 if (shapeName.empty())
593 shapeName = item->getShape();
596 // use the right type of boots if wearing a caster dress
597 if ((slot == SLOTTYPE::FEET_SLOT) && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS || item->ItemType == ITEM_TYPE::MEDIUM_BOOTS || item->ItemType == ITEM_TYPE::HEAVY_BOOTS))
599 std::string shapeLegs;
601 if (!_Instances[SLOTTYPE::LEGS_SLOT].Loading.empty())
603 shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].LoadingName;
605 else if (!_Instances[SLOTTYPE::LEGS_SLOT].Current.empty())
607 shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].CurrentName;
610 if (!shapeLegs.empty() && shapeLegs.find("_caster01_") != std::string::npos)
612 std::string tmpName = toLowerAscii(shapeName);
614 std::string::size_type posBottes = tmpName.find("_bottes");
616 if (posBottes != std::string::npos)
618 std::string orgType = tmpName.substr(7, posBottes-7); // underwear, caster01, armor00 or armor01
620 tmpName.replace(posBottes+7, 0, "_" + orgType);
621 tmpName.replace(7, orgType.length(), "caster01");
623 if (CPath::exists(tmpName))
625 // use fixed shape name only if file is present
626 shapeName = tmpName;
628 else
630 // temporary hack because Fyros light boots don't respect conventions
631 if (tmpName[0] == 'f' && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS))
633 if (tmpName[5] == 'f')
635 tmpName = "fy_hof_caster01_bottes_civil.shape";
637 else
639 tmpName = "fy_hom_caster01_civil01_bottes.shape";
642 // use fixed shape name only if file is present
643 if (CPath::exists(tmpName))
645 shapeName = tmpName;
647 else
649 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), shapeName.c_str());
652 else
654 nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), shapeName.c_str());
661 // If the gender is a female get the right shape else get the default shape.
662 equip(slot, shapeName, item);
664 // Check there is a shape.
665 UInstance pInst = _Instances[slot].createLoadingFromCurrent();
666 if(!pInst.empty())
668 // Set the right texture variation (quality).
669 pInst.selectTextureSet((uint)item->MapVariant);
670 _Instances[slot].TextureSet = item->MapVariant;
672 // If Hair, color is for the head slot.
673 if(slot == SLOTTYPE::HEAD_SLOT && item->Family != ITEMFAMILY::ARMOR)
674 applyColorSlot(_Instances[slot], skin(), 0, color, _EyesColor);
675 else
677 // Set the User Color.
678 if(item->Color == -1)
679 applyColorSlot(_Instances[slot], skin(), color, _HairColor, _EyesColor);
680 // Set the Item Color.
681 else if(item->Color != -2)
682 applyColorSlot(_Instances[slot], skin(), item->Color, _HairColor, _EyesColor);
683 // Else let the default color.
684 else
685 applyColorSlot(_Instances[slot], skin(), 0, _HairColor, _EyesColor);
689 // Default equipment.
690 else
692 nldebug("PL:equip(2):%d: VS '%d' default equipement used.", _Slot, slot);
693 sint idx = SheetMngr.getVSIndex(_PlayerSheet->GenderInfos[_Gender].Items[slot], slot);
694 if(idx != -1)
696 if(SheetMngr.getItem(slot, (uint)idx))
698 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
700 // If the gender is a female get the right shape else get the default shape.
701 equip(slot, _Gender == GSGENDER::female ? itemSheet->getShapeFemale():itemSheet->getShape());
705 }// equip //
707 //-----------------------------------------------
708 // computeAnimSet :
709 // Compute the animation set to use according to weapons, mode and race.
710 //-----------------------------------------------
711 void CPlayerCL::computeAnimSet(sint32 fakeLeftHand, sint32 fakeRightHand)
713 // We need a valid Gender to compute the animset.
714 if(_Gender >= 2)
716 nlwarning("PL:computeAnimSet:%d: not a male or a female (gender=%d).", _Slot, _Gender);
717 return;
720 // Now computing the animset.
721 // Do not count weapons if swimming.
722 const CItemSheet *leftHand = _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
723 const CItemSheet *rightHand = _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
725 if (fakeLeftHand >= 0)
726 leftHand = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, fakeLeftHand);
727 if (fakeRightHand >= 0)
728 rightHand = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, fakeRightHand);
730 if(!::computeAnimSet(_CurrentAnimSet[MOVE], _Mode, _PlayerSheet->GenderInfos[_Gender].AnimSetBaseName, leftHand, rightHand, !modeWithHiddenItems()))
731 nlwarning("PL:computeAnimSet:%d: pb when computing the animset.", _Slot);
733 }// computeAnimSet //
736 //-----------------------------------------------
737 // updateVisualPropertyVpa :
738 // Update the Visual Property A.
739 // \todo GUIGUI : use gender enum
740 //-----------------------------------------------
741 void CPlayerCL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
743 CInterfaceManager *IM = CInterfaceManager::getInstance ();
745 // Player will now have enough information to display the character.
746 _WaitForAppearance = false;
748 // Get the property.
749 SPropVisualA visualA = *(SPropVisualA *)(&prop);
751 sint32 fakeLeftHand = -1;
752 sint32 fakeRightHand = -1;
754 // GENDER
755 _Gender = (GSGENDER::EGender)(visualA.PropertySubData.Sex);
756 if(_Gender!=GSGENDER::male && _Gender!=GSGENDER::female)
758 nlwarning("PL::updateVPVpa:%d: neither a male nor a female -> male selected.", _Slot);
759 _Gender = GSGENDER::male;
762 // update title when gender changed
763 const string replacement = STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw, _Gender == GSGENDER::female);
764 if (!replacement.empty() || !ClientCfg.DebugStringManager)
766 // Get extended name
767 _NameEx = replacement;
768 _Title = replacement;
770 // display the new title in player interface
771 if (_Slot == 0)
773 CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:player:header_opened:player_title"));
774 if (pVT != NULL) pVT->setText(_Title);
777 // rebuild in scene interface
778 buildInSceneInterface();
781 // Setup _CharacterScalePos
782 _CharacterScalePos = _PlayerSheet->GenderInfos[_Gender].CharacterScalePos;
784 // Check if skeleton has changed
785 if (_CacheSkeletonShapeName != _PlayerSheet->GenderInfos[_Gender].Skelfilename)
787 _CacheSkeletonShapeName = _PlayerSheet->GenderInfos[_Gender].Skelfilename;
789 // Clean the playlist.
790 if(_PlayList)
791 _PlayList->resetAllChannels();
793 // We can now build the skeleton so do it now.
794 skeleton(_CacheSkeletonShapeName);
796 // Invalidate instances cache
797 for (uint i = 0; i < _Instances.size(); ++i)
799 _Instances[i].CurrentName.clear();
800 _Instances[i].LoadingName.clear();
802 _Face.CurrentName.clear();
803 _Face.LoadingName.clear();
805 // Check the skeleton.
806 if(skeleton() && !ClientCfg.Light)
808 string rightHandTag, leftHandTag;
809 // To re-link the skeleton to the mount if needed.
810 parent(parent());
811 // Set the skeleton scale.
812 // \todo GUIGUI: put scale too in race_stats.
813 // Setup Lod Character skeleton, if skeleton exist
814 // Get Lod Character Id from the sheet.
815 sint clodId= getLodCharacterId(*Scene, _PlayerSheet->GenderInfos[_Gender].LodCharacterName);
816 if(clodId>=0)
818 // Setup Lod Character shape and distance
819 skeleton()->setLodCharacterShape(clodId);
820 skeleton()->setLodCharacterDistance(_PlayerSheet->GenderInfos[_Gender].LodCharacterDistance);
822 // Compute the
823 computeSomeBoneId();
825 // CHEST
826 equip(SLOTTYPE::CHEST_SLOT, visualA.PropertySubData.JacketModel, visualA.PropertySubData.JacketColor);
827 // LEGS
828 equip(SLOTTYPE::LEGS_SLOT, visualA.PropertySubData.TrouserModel, visualA.PropertySubData.TrouserColor);
829 // ARMS
830 equip(SLOTTYPE::ARMS_SLOT, visualA.PropertySubData.ArmModel, visualA.PropertySubData.ArmColor);
831 // HAT
832 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
833 // OBJECT in the RIGHT HAND
834 _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
835 // Equip the weapon(object/tool).
836 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
838 if(_Gender == GSGENDER::female)
839 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShapeFemale(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
840 else
841 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
843 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
844 if (!itemInstance.empty())
846 // update fxs
847 _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance);
848 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
849 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(0);
850 //_Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualA.PropertySubData.RTrail);
853 else
855 // No Valid item in the right hand.
856 SLOTTYPE::EVisualSlot slot = SLOTTYPE::RIGHT_HAND_SLOT;
857 rightHandTag = getTag(5);
858 if (!rightHandTag.empty() && rightHandTag != "_")
861 vector<string> tagInfos;
862 splitString(rightHandTag, string("|"), tagInfos);
863 UInstance instance;
865 // Manage cache of items
866 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
868 int itemNameId;
869 fromString(tagInfos[0].substr(1), itemNameId);
870 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
873 if (tagInfos.size() >= 3 && tagInfos[2] == "2H")
874 fakeRightHand = SheetMngr.getVSIndex("ic_candy_stick.sitem", slot);
875 else
876 fakeRightHand = SheetMngr.getVSIndex("stake.sitem", slot);
878 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)fakeRightHand);
880 if (tagInfos.size() >= 2)
882 sint instTexture;
883 fromString(tagInfos[1], instTexture);
884 equip(slot, tagInfos[0], itemSheet, instTexture);
886 else
888 equip(slot, tagInfos[0], itemSheet);
891 else
893 equip(slot, "");
897 // OBJECT in the LEFT HAND
898 _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
899 // Equip the weapon(object/tool).
900 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
902 equip(SLOTTYPE::LEFT_HAND_SLOT, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet);
903 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
904 if (!itemInstance.empty())
906 // update fxs
907 _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance);
908 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(0);
909 //_Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualA.PropertySubData.LTrail);
912 else
914 // No Valid item in the left hand.
915 equip(SLOTTYPE::LEFT_HAND_SLOT, "");
916 SLOTTYPE::EVisualSlot slot = SLOTTYPE::LEFT_HAND_SLOT;
917 leftHandTag = getTag(6);
918 if (!leftHandTag.empty() && leftHandTag != "_")
920 vector<string> tagInfos;
921 splitString(leftHandTag, string("|"), tagInfos);
922 UInstance instance;
924 // Manage cache of items
925 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
927 int itemNameId;
928 fromString(tagInfos[0].substr(1), itemNameId);
929 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
933 if (tagInfos.size() >= 3 && tagInfos[2] == "S")
934 fakeLeftHand = SheetMngr.getVSIndex("icbss_pvp.sitem", slot);
935 else
936 fakeLeftHand = SheetMngr.getVSIndex("icfm1pd.sitem", slot);
938 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)fakeLeftHand);
940 if (tagInfos.size() >= 2)
942 sint instTexture;
943 fromString(tagInfos[1], instTexture);
944 equip(slot, tagInfos[0], itemSheet, instTexture);
946 else
948 equip(slot, tagInfos[0], itemSheet);
951 else
953 equip(slot, "");
956 CLuaManager::getInstance().executeLuaScript(toString("game:updateRpItems('%s', '%s')", leftHandTag.c_str(), rightHandTag.c_str()), 0);
958 // Create face
959 // Only create a face when there is no Helmet
960 if(_Items[SLOTTYPE::HEAD_SLOT].Sheet == 0 || _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family != ITEMFAMILY::ARMOR)
962 CItemSheet *faceItem = getItem(_PlayerSheet->GenderInfos[_Gender], SLOTTYPE::FACE_SLOT);
963 if (faceItem)
965 string sFaceName;
967 if(_Gender == GSGENDER::female)
968 sFaceName = faceItem->getShapeFemale();
969 else
970 sFaceName = faceItem->getShape();
972 if (((!_Face.Loading.empty()) && (_Face.LoadingName != sFaceName)) ||
973 ((!_Face.Current.empty()) && (_Face.CurrentName != sFaceName)) ||
974 (_Face.Current.empty()))
976 if (!_Face.Loading.empty())
978 Scene->deleteInstance(_Face.Loading);
979 _Face.Loading = NULL;
980 _Face.LoadingName = sFaceName;
982 _Face.Loading = Scene->createInstance(sFaceName);
983 if (!_Face.Loading.empty())
985 _Face.LoadingName = sFaceName;
986 if(!skeleton()->bindSkin(_Face.Loading))
987 nlwarning("PL::updateVPVpa:%d: Cannot bind the face.", _Slot);
988 _Face.Loading.hide();
989 // set it async for texture
990 _Face.Loading.enableAsyncTextureMode(true);
992 else
993 nlwarning("PL::updateVPVpa:%d: Cannot create the face.", _Slot);
995 _Face.TextureSet = faceItem->MapVariant;
996 applyColorSlot(_Face, skin(), 0, visualA.PropertySubData.HatColor, 0); // Set a default ruflaket color.
998 else
999 nlwarning("PL::updateVPVpa:%d: Face Item '%s' does not exist.", _Slot,
1000 _PlayerSheet->GenderInfos[_Gender].Items[SLOTTYPE::FACE_SLOT].c_str());
1002 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
1004 SLOTTYPE::EVisualSlot slot = SLOTTYPE::HEAD_SLOT;
1005 string hatTag = getTag(7);
1006 if (!hatTag.empty() && hatTag != "_")
1008 sint idx = SheetMngr.getVSIndex("eroukan_h.sitem", slot);
1009 const CItemSheet *itemSheet = SheetMngr.getItem(slot, (uint)idx);
1010 vector<string> tagInfos;
1011 splitString(hatTag, string("|"), tagInfos);
1012 UInstance instance;
1014 // Manage cache of items
1015 if (!tagInfos[0].empty() && tagInfos[0][0] == '#')
1017 int itemNameId;
1018 fromString(tagInfos[0].substr(1), itemNameId);
1019 tagInfos[0] = SheetMngr.getRpItem(itemNameId);
1022 if (tagInfos.size() == 2)
1024 sint instTexture;
1025 fromString(tagInfos[1], instTexture);
1026 equip(slot, tagInfos[0], itemSheet);
1027 _Instances[slot].selectTextureSet(instTexture);
1028 _Instances[slot].TextureSet = instTexture;
1030 else
1032 equip(slot, tagInfos[0], itemSheet);
1038 else
1040 // There is a helmet !
1041 if (!_Face.Loading.empty())
1042 Scene->deleteInstance(_Face.Loading);
1043 _Face.Loading = NULL;
1044 _Face.LoadingName.clear();
1045 if (!_Face.Current.empty())
1046 Scene->deleteInstance(_Face.Current);
1047 _Face.Current = NULL;
1048 _Face.CurrentName.clear();
1050 // Now we have a skeleton, we can update VpB and VpC.
1051 sint64 vB, vC;
1052 string propName;
1053 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPB);
1054 vB = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
1055 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPC);
1056 vC = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
1057 updateVisualPropertyVpb(0, vB);
1058 updateVisualPropertyVpc(0, vC);
1060 // Attach The Light if there is one.
1061 if(!_Light.empty() && _NameBoneId!=-1)
1062 _Skeleton.stickObject(_Light, _NameBoneId);
1064 // Compute the new animation set to use (due to weapons).
1065 computeAnimSet(fakeLeftHand, fakeRightHand);
1067 // Set the animation to idle.
1068 setAnim(CAnimationStateSheet::Idle);
1070 // No skeleton
1071 else
1072 nlwarning("PL::updateVPVpa:%d: Skeleton not allocated.", _Slot);
1073 }// updateVisualPropertyVpa //
1075 //-----------------------------------------------
1076 // updateVisualPropertyVpb :
1077 // Update the Visual Property B.
1078 //-----------------------------------------------
1079 void CPlayerCL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1081 // Get the property.
1082 SPropVisualB visualB = *(SPropVisualB *)(&prop);
1084 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
1086 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
1087 if (!itemInstance.empty())
1089 // update fxs
1090 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
1091 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualB.PropertySubData.RTrail);
1095 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
1097 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
1098 if (!itemInstance.empty())
1100 // update fxs
1101 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualB.PropertySubData.LTrail);
1105 if(skeleton())
1107 // HANDS
1108 equip(SLOTTYPE::HANDS_SLOT, visualB.PropertySubData.HandsModel, visualB.PropertySubData.HandsColor);
1109 // FEET
1110 equip(SLOTTYPE::FEET_SLOT, visualB.PropertySubData.FeetModel, visualB.PropertySubData.FeetColor);
1112 else
1113 nlinfo("PL::updateVPVpb:%d: Prop Vpb received before prop Vpa.", _Slot);
1114 }// updateVisualPropertyVpb //
1116 //-----------------------------------------------
1117 // updateVisualPropertyVpc :
1118 // Update the Visual Property C.
1119 // \todo GUIGUI : factorize tatoos with character creation
1120 //-----------------------------------------------
1121 void CPlayerCL::updateVisualPropertyVpc(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
1123 if(skeleton())
1125 // Get the property.
1126 SPropVisualC visualC = *(SPropVisualC *)(&prop);
1128 // EYES
1129 _EyesColor = visualC.PropertySubData.EyesColor;
1130 UInstance inst;
1132 // must recreate the face asynchronously (because of color change / makeup change)
1133 inst= _Face.createLoadingFromCurrent();
1135 // if exist
1136 if (!inst.empty())
1138 // change eyes color only
1139 applyColorSlot(_Face, _Face.ACSkin, _Face.ACUser, _Face.ACHair, visualC.PropertySubData.EyesColor);
1141 // Tattoo
1142 makeUp(inst, visualC.PropertySubData.Tattoo);
1144 // Morph
1145 static const char *baseName = "visage_00";
1146 float MTmin, MTmax;
1148 const CGenderInfo *pGI = &_PlayerSheet->GenderInfos[_Gender];
1149 if (pGI == NULL)
1150 return;
1152 MTmin = pGI->BlendShapeMin[0];
1153 MTmax = pGI->BlendShapeMax[0];
1154 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1155 inst.setBlendShapeFactor(baseName + toString(0), (float)(visualC.PropertySubData.MorphTarget1) / 7.f * (MTmax-MTmin) + MTmin, true);
1157 MTmin = pGI->BlendShapeMin[1];
1158 MTmax = pGI->BlendShapeMax[1];
1159 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1160 inst.setBlendShapeFactor(baseName + toString(1), (float)(visualC.PropertySubData.MorphTarget2) / 7.f * (MTmax-MTmin) + MTmin, true);
1162 MTmin = pGI->BlendShapeMin[2];
1163 MTmax = pGI->BlendShapeMax[2];
1164 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1165 inst.setBlendShapeFactor(baseName + toString(2), (float)(visualC.PropertySubData.MorphTarget3) / 7.f * (MTmax-MTmin) + MTmin, true);
1167 MTmin = pGI->BlendShapeMin[3];
1168 MTmax = pGI->BlendShapeMax[3];
1169 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1170 inst.setBlendShapeFactor(baseName + toString(3), (float)(visualC.PropertySubData.MorphTarget4) / 7.f * (MTmax-MTmin) + MTmin, true);
1172 MTmin = pGI->BlendShapeMin[4];
1173 MTmax = pGI->BlendShapeMax[4];
1174 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1175 inst.setBlendShapeFactor(baseName + toString(4), (float)(visualC.PropertySubData.MorphTarget5) / 7.f * (MTmax-MTmin) + MTmin, true);
1177 MTmin = pGI->BlendShapeMin[5];
1178 MTmax = pGI->BlendShapeMax[5];
1179 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1180 inst.setBlendShapeFactor(baseName + toString(5), (float)(visualC.PropertySubData.MorphTarget6) / 7.f * (MTmax-MTmin) + MTmin, true);
1182 MTmin = pGI->BlendShapeMin[6];
1183 MTmax = pGI->BlendShapeMax[6];
1184 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1185 inst.setBlendShapeFactor(baseName + toString(6), (float)(visualC.PropertySubData.MorphTarget7) / 7.f * (MTmax-MTmin) + MTmin, true);
1187 MTmin = pGI->BlendShapeMin[7];
1188 MTmax = pGI->BlendShapeMax[7];
1189 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
1190 inst.setBlendShapeFactor(baseName + toString(7), (float)(visualC.PropertySubData.MorphTarget8) / 7.f * (MTmax-MTmin) + MTmin, true);
1193 // Set the Gabarit
1194 float characterHeight = (float)((sint8)(visualC.PropertySubData.CharacterHeight)-7)/7.f;
1195 float torsoWidth = (float)((sint8)(visualC.PropertySubData.TorsoWidth)-7)/7.f;
1196 float armsWidth = (float)((sint8)(visualC.PropertySubData.ArmsWidth)-7)/7.f;
1197 float legsWidth = (float)((sint8)(visualC.PropertySubData.LegsWidth)-7)/7.f;
1198 float breastSize = (float)((sint8)(visualC.PropertySubData.BreastSize)-7)/7.f;
1199 float heightScale, baseHeightScale;
1200 // TODO : manage breast size
1201 GabaritSet.applyGabarit(*skeleton(), _Gender, people(), characterHeight, torsoWidth, armsWidth, legsWidth, breastSize, &heightScale);
1202 baseHeightScale = GabaritSet.getRefHeightScale(_Gender, people());
1204 if(baseHeightScale != 0.f)
1205 _CustomScalePos = heightScale/baseHeightScale;
1206 else
1208 _CustomScalePos = 1.f;
1209 nlwarning("PL::updateVPVpc:'%d': baseHeight == 0.", _Slot);
1212 else
1213 nlinfo("PL:updateVPVpc:'%d': Prop Vpc received before prop Vpa.", _Slot);
1214 }// updateVisualPropertyVpc //
1216 //-----------------------------------------------
1217 //-----------------------------------------------
1218 void CPlayerCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop)
1220 CCharacterCL::updateVisualPropertyPvpMode(gameCycle, prop);
1221 // Additionaly, if i am the target, inform interface of the change
1222 if(isTarget())
1224 CInterfaceManager *pIM= CInterfaceManager::getInstance();
1225 CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_TARGET_PVP_CHANGE_MODE");
1226 if(pDB)
1228 sint32 val= pDB->getValue32();
1229 pDB->setValue32(val+1);
1235 //-----------------------------------------------
1236 // skin :
1237 // Get The Entity Skin
1238 //-----------------------------------------------
1239 sint CPlayerCL::skin() const // virtual
1241 return _PlayerSheet->Skin;
1242 }// skin //
1244 //-----------------------------------------------
1245 // people :
1246 // Return the People for the entity.
1247 //-----------------------------------------------
1248 EGSPD::CPeople::TPeople CPlayerCL::people() const// virtual
1250 if(_PlayerSheet)
1251 return _PlayerSheet->People;
1252 else
1253 return EGSPD::CPeople::Unknown;
1254 }// people //
1256 //-----------------------------------------------
1257 // people :
1258 // Setup the People for the entity.
1259 //-----------------------------------------------
1260 void CPlayerCL::setPeople(EGSPD::CPeople::TPeople /* people */)
1262 }// people //
1264 //-----------------------------------------------
1265 // drawName :
1266 // Draw the name.
1267 //-----------------------------------------------
1268 void CPlayerCL::drawName(const NLMISC::CMatrix &mat) // virtual
1270 // Draw the name.
1271 if(!getEntityName().empty())
1272 TextContext->render3D(mat, getEntityName());
1273 }// drawName //
1276 //-----------------------------------------------
1277 // getFace :
1278 // Update eyes blink. For the moment, called by updatePos.
1279 //-----------------------------------------------
1280 CEntityCL::SInstanceCL *CPlayerCL::getFace()
1282 // Implemented in CPlayerCL
1283 return &_Face;
1284 }// getFace //
1286 //-----------------------------------------------
1287 // attackRadius :
1288 // Method to return the attack radius of an entity (take the scale into account).
1289 //-----------------------------------------------
1290 double CPlayerCL::attackRadius() const // virtual
1292 return 0.5;
1293 }// attackRadius //
1295 //-----------------------------------------------
1296 // Return the position the attacker should have to combat according to the attack angle.
1297 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
1298 //-----------------------------------------------
1299 CVectorD CPlayerCL::getAttackerPos(double ang, double dist) const
1301 // Compute the local angle
1302 ang = computeShortestAngle(atan2(front().y, front().x), ang);
1303 ang += Pi;
1304 if(ang > Pi)
1305 ang -= 2*Pi;
1307 // Compute the local position.
1308 CVectorD p;
1309 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.
1310 p.y = 0.5 * cos(ang) + dist*cos(ang);
1311 p.z = 0.0;
1313 // Compute the world position.
1314 // Create the target matrix.
1315 CVector vj = front();
1316 vj.z = 0;
1317 CVector vk(0,0,1);
1318 CVector vi = vj^vk;
1319 CMatrix bodyBase;
1320 bodyBase.setRot(vi,vj,vk,true);
1321 bodyBase.setPos(pos());
1323 // Get the destination in the world.
1324 return bodyBase * p;
1325 }// getAttackerPos //
1328 ///////////////
1329 // 3D SYSTEM //
1330 ///////////////
1331 //-----------------------------------------------
1332 // updateAsyncTexture
1333 //-----------------------------------------------
1334 float CPlayerCL::updateAsyncTexture()
1336 // Call parent.
1337 float distToCam= CCharacterCL::updateAsyncTexture();
1339 // Check all instance to know if they need to start async load their textures
1340 if(!_Face.Loading.empty())
1342 // dirty?
1343 if(_Face.Loading.isAsyncTextureDirty())
1345 // reset instance state.
1346 _Face.Loading.setAsyncTextureDirty(false);
1347 // must start loading for this isntance
1348 _Face.Loading.startAsyncTextureLoading();
1349 // the entity is now currently loading.
1350 _PlayerCLAsyncTextureLoading= true;
1351 // The LodTexture need to be recomputed
1352 _LodTextureDirty= true;
1357 // Update Async Texture loading of all instances.
1358 if(_PlayerCLAsyncTextureLoading)
1360 bool allLoaded= true;
1361 // update loading for all instances.
1362 if(!_Face.Loading.empty())
1364 // update async texture loading
1365 allLoaded= allLoaded && _Face.Loading.isAsyncTextureReady();
1368 // if all are loaded, then End! don't need to check all instances every frame.
1369 if(allLoaded)
1371 _PlayerCLAsyncTextureLoading= false;
1372 _Face.updateCurrentFromLoading(_Skeleton);
1377 // For LOD texture, must update the "texture distance"
1378 if(!_Face.Current.empty())
1380 // update async texture loading
1381 _Face.Current.setAsyncTextureDistance(distToCam);
1384 return distToCam;
1387 //-----------------------------------------------
1388 // updateLodTexture
1389 //-----------------------------------------------
1390 void CPlayerCL::updateLodTexture()
1392 // if need to recompute, and if Async loading ended
1393 if( _LodTextureDirty && !_PlayerCLAsyncTextureLoading )
1394 // check parent and upadte lod
1395 CCharacterCL::updateLodTexture();
1398 //-----------------------------------------------
1399 // getMaxSpeed :
1400 // Return the basic max speed for the entity in meter per sec
1401 //-----------------------------------------------
1402 double CPlayerCL::getMaxSpeed() const// virtual
1404 return 6.0f;
1405 }// getMaxSpeed //
1409 //---------------------------------------------------
1410 // displayDebug :
1411 // Display Debug Information.
1412 //---------------------------------------------------
1413 void CPlayerCL::displayDebug(float x, float &y, float lineStep) // virtual
1415 CCharacterCL::displayDebug(x, y, lineStep);
1416 }// displayDebug //
1423 //---------------------------------------------------
1424 // readWrite :
1425 // Read/Write Variables from/to the stream.
1426 //---------------------------------------------------
1427 void CPlayerCL::readWrite(NLMISC::IStream &f)
1429 CCharacterCL::readWrite(f);
1431 // PUBLIC
1433 // PROTECTED
1434 // const CPlayerSheet *_Sheet;
1435 // const CRaceStatsSheet *_PlayerSheet;
1436 // NL3D::UInstance _Face;
1437 f.serial(_DefaultChest);
1438 f.serial(_DefaultLegs);
1439 f.serial(_DefaultArms);
1440 f.serial(_DefaultHands);
1441 f.serial(_DefaultFeet);
1442 f.serial(_DefaultHair);
1443 f.serial(_HairColor);
1444 f.serial(_EyesColor);
1445 f.serial(_WaitForAppearance);
1446 f.serial(_PlayerCLAsyncTextureLoading);
1447 f.serial(_LightOn);
1448 // NL3D::UPointLight _Light;
1450 // PRIVATE
1451 }// readWrite //
1453 //---------------------------------------------------
1454 // load :
1455 // To call after a read from a stream to re-initialize the entity.
1456 //---------------------------------------------------
1457 void CPlayerCL::load() // virtual
1459 CInterfaceManager *IM = CInterfaceManager::getInstance ();
1461 // If the entity should be in the world already
1462 if(_First_Pos == false)
1464 // Insert the primitive into the world.
1465 if(_Primitive)
1466 _Primitive->insertInWorldImage(dynamicWI);
1467 // Insert the entity into PACS
1468 pacsPos(pos());
1471 // update
1472 if(!_WaitForAppearance)
1474 // Visual properties A
1475 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
1476 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
1478 }// load //
1480 // *********************************************************************************************
1481 const char *CPlayerCL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const
1483 if (!_PlayerSheet) return CCharacterCL::getBoneNameFromBodyPart(part, side);
1484 return _PlayerSheet->BodyToBone.getBoneName(part, side);
1487 // *********************************************************************************************
1488 const CItemSheet *CPlayerCL::getRightHandItemSheet() const
1490 return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
1493 // *********************************************************************************************
1494 const CItemSheet *CPlayerCL::getLeftHandItemSheet() const
1496 return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
1499 // *********************************************************************************************
1500 const CAttack *CPlayerCL::getAttack(const CAttackIDSheet &id) const
1502 if (!_PlayerSheet) return NULL;
1503 return CCharacterCL::getAttack(id, _PlayerSheet->AttackLists);
1506 // *********************************************************************************************
1507 float CPlayerCL::getScaleRef() const
1509 float fyrosRefScale = GabaritSet.getRefHeightScale(0, EGSPD::CPeople::Fyros);
1510 if (fyrosRefScale == 0) return 1.f;
1511 return _CustomScalePos * (GabaritSet.getRefHeightScale(_Gender, people()) / fyrosRefScale);
1515 // *********************************************************************************************
1516 float CPlayerCL::getNamePosZ() const
1518 if (!_PlayerSheet)
1519 return 0.f;
1521 float namePosZ;
1522 switch (_ModeWanted)
1524 case MBEHAV::DEATH:
1525 case MBEHAV::SIT:
1526 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZLow;
1527 break;
1529 case MBEHAV::MOUNT_NORMAL:
1530 case MBEHAV::MOUNT_SWIM:
1531 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZHigh;
1532 break;
1534 default:
1535 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZNormal;
1536 break;
1539 return namePosZ * _CharacterScalePos * _CustomScalePos;
1542 // ***************************************************************************
1543 void CPlayerCL::doSetVisualSelectionBlink(bool bOnOff, NLMISC::CRGBA emitColor)
1545 // Do it on Face
1546 if(bOnOff)
1547 _Face.setEmissive(emitColor);
1548 else
1549 _Face.restoreEmissive();
1551 // and parent call
1552 CCharacterCL::doSetVisualSelectionBlink(bOnOff, emitColor);
1557 //-----------------------------------------------
1558 // computePrimitive :
1559 // Create (or re-create) a primitive.
1560 //-----------------------------------------------
1561 void CPlayerCL::computePrimitive()
1563 // Initialize the primitive.
1564 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
1565 if(_Primitive)
1566 _Primitive->insertInWorldImage(dynamicWI);
1567 // Set the position.
1568 pacsPos(pos());
1569 }// computePrimitive //