Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / player_r2_cl.cpp
blob4fb1e1afb0b1b3af1a24ce073aebe14a3d349e82
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 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_r2_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 // Client Sheets
47 #include "client_sheets/player_sheet.h"
48 // 3D
49 #include "nel/3d/u_scene.h"
50 #include "nel/3d/u_instance_material.h"
51 #include "nel/3d/u_play_list.h"
52 #include "nel/3d/u_bone.h"
53 #include "nel/3d/u_particle_system_instance.h"
54 #include "nel/3d/u_point_light.h"
55 // game share
56 #include "game_share/player_visual_properties.h"
57 #include "game_share/gender.h"
58 #include "game_share/bot_chat_types.h"
61 ///////////
62 // USING //
63 ///////////
64 using namespace NLMISC;
65 using namespace NL3D;
66 using namespace NLPACS;
67 using namespace std;
70 ////////////
71 // EXTERN //
72 ////////////
73 extern UScene *Scene;
74 extern CEntityAnimationManager *EAM;
75 extern UTextContext *TextContext;
76 extern UCamera MainCam;
79 //-----------------------------------------------
80 // CPlayerR2CL :
81 // Constructor.
82 //-----------------------------------------------
83 CPlayerR2CL::CPlayerR2CL()
84 : CCharacterCL()
86 Type = NPC;
88 // Resize _Instances to the number of visual slots.
89 _Instances.resize(SLOTTYPE::NB_SLOT);
91 // No sheet pointed.
92 _Sheet = 0;
93 //_PlayerSheet = 0;
95 // Some default colors.
96 _HairColor = 0;
97 _EyesColor = 0;
99 // Not enough information to display the player.
100 _WaitForAppearance = true;
102 _PlayerCLAsyncTextureLoading= false;
104 // Light Off and not allocated
105 _LightOn = false;
106 }// CPlayerR2CL //
109 //-----------------------------------------------
110 // ~CPlayerR2CL :
111 // Destructor.
112 //-----------------------------------------------
113 CPlayerR2CL::~CPlayerR2CL()
115 // No more sheet pointed.
116 _Sheet = NULL;
118 // Remove the light
119 if(!_Light.empty())
121 if(Scene)
122 Scene->deletePointLight(_Light);
126 CGenderInfo * CPlayerR2CL::getGenderInfo()
128 EGSPD::CPeople::TPeople ePeople = _Sheet->Race;
129 bool bMale = (_Sheet->Gender == GSGENDER::male);
131 // Read in the race_stats forms the default equipement
132 CSheetId RSid;
133 switch (ePeople)
135 case EGSPD::CPeople::Tryker: RSid = CSheetId("tryker.race_stats"); break;
136 case EGSPD::CPeople::Matis: RSid = CSheetId("matis.race_stats"); break;
137 case EGSPD::CPeople::Zorai: RSid = CSheetId("zorai.race_stats"); break;
138 case EGSPD::CPeople::Fyros:
139 default:
140 RSid = CSheetId("fyros.race_stats"); break;
142 CRaceStatsSheet *pRSS = dynamic_cast<CRaceStatsSheet*>(SheetMngr.get (RSid));
144 if (pRSS == NULL)
146 nlwarning ("cannot find sheet for people:%d male:%d", ePeople, bMale);
147 return NULL;
150 // Choose default stuff is we are male or female
151 CGenderInfo *pGI;
152 if (bMale)
153 pGI = &pRSS->GenderInfos[0];
154 else
155 pGI = &pRSS->GenderInfos[1];
157 return pGI;
162 //---------------------------------------------------
163 // getScale :
164 // Return the entity scale. (return 1.0 if there is any problem).
165 //---------------------------------------------------
166 float CPlayerR2CL::getScale() const // virtual
168 // Default Scale.
169 return _CharacterScalePos;
170 }// getScale //
174 //-----------------------------------------------
175 // getGroundFX :
176 // retrieve ground fxs for the entity depending on the ground
177 //-----------------------------------------------
178 const std::vector<CGroundFXSheet> *CPlayerR2CL::getGroundFX() const
180 switch (getGender())
182 case 0: return &(_PlayerSheet->GenderInfos[0].GroundFX);
183 case 1: return &(_PlayerSheet->GenderInfos[1].GroundFX);
184 default:
185 return NULL;
187 return NULL;
191 //-----------------------------------------------
192 // build :
193 // Build the entity from a sheet.
194 //-----------------------------------------------
195 bool CPlayerR2CL::build(const CEntitySheet *sheet) // virtual
197 // Cast the sheet in the right type.
198 _Sheet = dynamic_cast<const CCharacterSheet *>(sheet);
199 if(_Sheet==0)
201 pushDebugStr(NLMISC::toString("R2 Player '%d' sheet is not a '.creature' -> BIG PROBLEM.", _Slot));
202 return false;
204 else
205 pushInfoStr(NLMISC::toString("R2 Player '%d' sheet is valid.", _Slot));
206 // Get the DB Entry
207 if(IngameDbMngr.getNodePtr())
209 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
210 if(nodeRoot)
212 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
213 if(_DBEntry == 0)
214 pushDebugStr("Cannot get a pointer on the DB entry.");
218 // Compute the first automaton.
219 _CurrentAutomaton = automatonType() + "_normal.automaton";
221 // Initialize the player look.
222 init3d();
223 // Compute the primitive
224 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
225 // Create the collision entity (used to snap the entity to the ground).
226 computeCollisionEntity();
228 // Initialize properties of the client.
229 initProperties();
230 // Entity Created.
231 return true;
232 }// build //
235 //-----------------------------------------------
236 // automatonType :
237 // Return the automaton type of the entity (homin, creature, etc.)
238 //-----------------------------------------------
239 std::string CPlayerR2CL::automatonType() const // virtual
241 return _PlayerSheet->Automaton;
242 }// automatonType //*/
245 //-----------------------------------------------
246 // init3d :
247 // Initialize the graphic for the player.
248 //-----------------------------------------------
249 void CPlayerR2CL::init3d()
251 createPlayList();
252 // Initialize the internal time.
253 _LastFrameTime = ((double)T1) * 0.001;
254 }// init3d //
257 //-----------------------------------------------
258 // initProperties :
259 // Initialize properties of the entity (according to the class).
260 //-----------------------------------------------
261 void CPlayerR2CL::initProperties()
263 properties().selectable(true);
264 properties().attackable(false);
265 properties().givable(true);
266 properties().invitable(true);
267 properties().canExchangeItem(true);
268 }// initProperties //
271 //-----------------------------------------------
272 // equip :
273 // Set the equipmenent worn.
274 //-----------------------------------------------
275 void CPlayerR2CL::equip(SLOTTYPE::EVisualSlot slot, const std::string &shapeName, const CItemSheet *item)
277 // Check slot.
278 if(slot == SLOTTYPE::HIDDEN_SLOT || slot >= SLOTTYPE::NB_SLOT)
280 nlwarning("CCharacterCL::equip : slot %d is not valid.", (uint)slot);
281 return;
284 uint s = (uint)slot;
286 // If exactly the same than before -> return
288 if (!_Instances[s].Loading.empty())
290 if ((_Instances[s].LoadingName == shapeName) && (_Instances[s].FXItemSheet == item))
291 return;
293 else if (!_Instances[s].Current.empty())
295 if ((_Instances[s].CurrentName == shapeName) && (_Instances[s].FXItemSheet == item))
296 return;
301 // Attach to the skeleton.
302 string stickPoint;
303 if(!_Skeleton.empty())
305 switch(slot)
307 case SLOTTYPE::RIGHT_HAND_SLOT:
308 if( item && item->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
309 stickPoint = "box_arme";
310 break;
312 case SLOTTYPE::LEFT_HAND_SLOT:
313 if(_Items[slot].Sheet && _Items[slot].Sheet->getAnimSet()=="s")
314 stickPoint = "Box_bouclier";
315 else
316 stickPoint = "box_arme_gauche";
317 break;
319 default:
320 break;
324 /* If the object is sticked (ie not a skin), decide to delete the Current instance. Why? because the animation
325 is changed according to the equipped item.
327 Hence, For example, if a sword would be changed for a gun, then the new gun animation would take place,
328 while Keeping the old sword shape. BAD.
330 if(!stickPoint.empty())
331 _Instances[s].createLoading(string(), stickPoint);
333 // Create the instance.
334 if(item)
335 _Instances[s].createLoading(shapeName, stickPoint, item->MapVariant);
336 else
337 _Instances[s].createLoading(shapeName, stickPoint);
339 // If shapeName is empty, only clear the slot
340 if(shapeName.empty())
342 _Items[slot].release();
343 return;
346 if(!_Instances[s].Loading.empty())
348 _Instances[s].FXItemSheet = item;
350 _Items[slot].initFXs(slot, _Instances[s].Loading);
352 else
353 nlwarning("PL::equip(1):%d: cannot create the instance '%s'.", _Slot, shapeName.c_str());
355 if ((slot != SLOTTYPE::RIGHT_HAND_SLOT) && (slot != SLOTTYPE::LEFT_HAND_SLOT))
356 applyColorSlot(_Instances[s], skin(), 0, _HairColor, _EyesColor);
358 }// equip //
360 //-----------------------------------------------
361 // equip :
362 // Compute the equipmenent worn.
363 //-----------------------------------------------
364 void CPlayerR2CL::equip(SLOTTYPE::EVisualSlot slot, uint index, uint color)
366 // Get the sheet according to the visual slot
367 _Items[slot].Sheet = SheetMngr.getItem(slot, index);
368 if(_Items[slot].Sheet)
370 const CItemSheet *item = _Items[slot].Sheet;
372 // If the gender is a female get the right shape.
373 if(_Gender == GSGENDER::female)
374 equip(slot, item->getShapeFemale(), item);
375 // Else get the default shape.
376 else
377 equip(slot, item->getShape(), item);
379 // Check there is a shape.
380 UInstance pInst = _Instances[slot].createLoadingFromCurrent();
381 if(!pInst.empty())
383 // Set the right texture variation (quality).
384 pInst.selectTextureSet((uint)item->MapVariant);
385 _Instances[slot].TextureSet = item->MapVariant;
387 // If Hair, color is for the head slot.
388 if(slot == SLOTTYPE::HEAD_SLOT && item->Family != ITEMFAMILY::ARMOR)
389 applyColorSlot(_Instances[slot], skin(), 0, color, _EyesColor);
390 else
392 // Set the User Color.
393 if(item->Color == -1)
394 applyColorSlot(_Instances[slot], skin(), color, _HairColor, _EyesColor);
395 // Set the Item Color.
396 else if(item->Color != -2)
397 applyColorSlot(_Instances[slot], skin(), item->Color, _HairColor, _EyesColor);
398 // Else let the default color.
399 else
400 applyColorSlot(_Instances[slot], skin(), 0, _HairColor, _EyesColor);
404 // Default equipment.
405 else
407 nldebug("PL:equip(2):%d: VS '%d' default equipement used.", _Slot, slot);
408 //sint idx = SheetMngr.getVSIndex(_PlayerSheet->GenderInfos[_Gender].Items[slot], slot);
409 sint idx = SheetMngr.getVSIndex(getGenderInfo()->Items[slot], slot);
411 if(idx != -1)
413 if(SheetMngr.getItem(slot, (uint)idx))
415 // If the gender is a female get the right shape.
416 if(_Gender == GSGENDER::female)
417 equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShapeFemale());
418 // Else get the default shape.
419 else
420 equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShape());
424 }// equip //
426 //-----------------------------------------------
427 // updateVisualPropertyVpa :
428 // Update the Visual Property A.
429 // \todo GUIGUI : use gender enum.
430 //-----------------------------------------------
431 void CPlayerR2CL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
433 CInterfaceManager *IM = CInterfaceManager::getInstance ();
435 // Player will now have enough information to display the character.
436 _WaitForAppearance = false;
438 // Get the property.
439 SPropVisualA visualA = *(SPropVisualA *)(&prop);
441 // GENDER
442 _Gender = (GSGENDER::EGender)_Sheet->Gender;
443 if(_Gender!=GSGENDER::male && _Gender!=GSGENDER::female)
445 nlwarning("PL::updateVPVpa:%d: neither a male nor a female -> male selected.", _Slot);
446 _Gender = GSGENDER::male;
449 // update title when gender changed
450 const string replacement(STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw,_Gender == GSGENDER::female));
451 if (!replacement.empty())
453 // Get extended name
454 _NameEx = replacement;
455 _Title = replacement;
457 // rebuild in scene interface
458 buildInSceneInterface();
461 // Setup _CharacterScalePos
462 _CharacterScalePos = _Sheet->CharacterScalePos;
464 // Check if skeleton has changed
465 if (_CacheSkeletonShapeName != _Sheet->getSkelFilename())
467 _CacheSkeletonShapeName = _Sheet->getSkelFilename();
469 // Clean the playlist.
470 if(_PlayList)
471 _PlayList->resetAllChannels();
473 // We can now build the skeleton so do it now.
474 skeleton(_CacheSkeletonShapeName);
476 // Invalidate instances cache
477 for (uint i = 0; i < _Instances.size(); ++i)
479 _Instances[i].CurrentName.clear();
480 _Instances[i].LoadingName.clear();
483 _Face.CurrentName.clear();
484 _Face.LoadingName.clear();
486 // Check the skeleton.
487 if(skeleton() && !ClientCfg.Light)
489 // To re-link the skeleton to the mount if needed.
490 parent(parent());
491 // Set the skeleton scale.
492 // \todo GUIGUI: mettre le scale aussi dans race_stats.
493 // Setup Lod Character skeleton, if skeleton exist
494 // Get Lod Character Id from the sheet.
495 sint clodId= getLodCharacterId(*Scene, _Sheet->getLodCharacterName());
496 if(clodId>=0)
498 // Setup Lod Character shape and distance
499 skeleton()->setLodCharacterShape(clodId);
500 skeleton()->setLodCharacterDistance(_Sheet->LodCharacterDistance);
502 // Compute the
503 computeSomeBoneId();
505 // CHEST
506 equip(SLOTTYPE::CHEST_SLOT, visualA.PropertySubData.JacketModel, visualA.PropertySubData.JacketColor);
507 // LEGS
508 equip(SLOTTYPE::LEGS_SLOT, visualA.PropertySubData.TrouserModel, visualA.PropertySubData.TrouserColor);
509 // ARMS
510 equip(SLOTTYPE::ARMS_SLOT, visualA.PropertySubData.ArmModel, visualA.PropertySubData.ArmColor);
511 // HAT
512 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
513 // OBJECT in the RIGHT HAND
514 bool changeWeapon = false;
515 const CItemSheet * oldRightSheet = _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
516 const CItemSheet * newRightSheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
517 if((oldRightSheet && newRightSheet && oldRightSheet->Id!=newRightSheet->Id) || (!oldRightSheet && newRightSheet))
519 changeWeapon = true;
521 _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
522 // Equip the weapon(object/tool).
523 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
525 if(_Gender == GSGENDER::female)
526 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShapeFemale(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
527 else
528 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
530 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
531 if (!itemInstance.empty())
533 // update fxs
534 _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance);
535 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
536 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(0);
537 //_Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualA.PropertySubData.RTrail);
540 else
542 // No Valid item in the right hand.
543 equip(SLOTTYPE::RIGHT_HAND_SLOT, "");
546 // OBJECT in the LEFT HAND
547 const CItemSheet * oldLeftSheet = _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
548 const CItemSheet * newLeftSheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
549 if((oldLeftSheet && newLeftSheet && oldLeftSheet->Id!=newLeftSheet->Id) || (!oldLeftSheet && newLeftSheet))
551 changeWeapon = true;
554 _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
555 // Equip the weapon(object/tool).
556 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
558 equip(SLOTTYPE::LEFT_HAND_SLOT, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet);
559 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
560 if (!itemInstance.empty())
562 // update fxs
563 _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance);
564 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(0);
565 //_Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualA.PropertySubData.LTrail);
568 else
570 // No Valid item in the left hand.
571 equip(SLOTTYPE::LEFT_HAND_SLOT, "");
573 // Create face
574 // Only create a face when there is no Helmet
575 if(_Items[SLOTTYPE::HEAD_SLOT].Sheet == 0 || _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family != ITEMFAMILY::ARMOR)
577 CItemSheet *faceItem = getItem(*getGenderInfo(), SLOTTYPE::FACE_SLOT);
578 if (faceItem)
580 string sFaceName;
582 if(_Gender == GSGENDER::female)
583 sFaceName = faceItem->getShapeFemale();
584 else
585 sFaceName = faceItem->getShape();
587 if (((!_Face.Loading.empty()) && (_Face.LoadingName != sFaceName)) ||
588 ((!_Face.Current.empty()) && (_Face.CurrentName != sFaceName)) ||
589 (_Face.Current.empty()))
591 if (!_Face.Loading.empty())
593 Scene->deleteInstance(_Face.Loading);
594 _Face.Loading = NULL;
595 _Face.LoadingName = sFaceName;
597 _Face.Loading = Scene->createInstance(sFaceName);
598 if (!_Face.Loading.empty())
600 _Face.LoadingName = sFaceName;
601 if(!skeleton()->bindSkin(_Face.Loading))
602 nlwarning("PL::updateVPVpa:%d: Cannot bind the face.", _Slot);
603 _Face.Loading.hide();
604 // set it async for texture
605 _Face.Loading.enableAsyncTextureMode(true);
607 else
608 nlwarning("PL::updateVPVpa:%d: Cannot create the face.", _Slot);
610 _Face.TextureSet = faceItem->MapVariant;
611 applyColorSlot(_Face, skin(), 0, visualA.PropertySubData.HatColor, 0); // Set a default ruflaket color.
613 else
614 nlwarning("PL::updateVPVpa:%d: Face Item '%s' does not exist.", _Slot,
615 getGenderInfo()->Items[SLOTTYPE::FACE_SLOT].c_str());
617 else
619 // There is a helmet !
620 if (!_Face.Loading.empty())
621 Scene->deleteInstance(_Face.Loading);
622 _Face.Loading = NULL;
623 _Face.LoadingName.clear();
624 if (!_Face.Current.empty())
625 Scene->deleteInstance(_Face.Current);
626 _Face.Current = NULL;
627 _Face.CurrentName.clear();
629 // Now we have a skeleton, we can update VpB and VpC.
630 sint64 vB, vC;
631 string propName;
632 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPB);
633 vB = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
634 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPC);
635 vC = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
636 updateVisualPropertyVpb(0, vB);
637 updateVisualPropertyVpc(0, vC);
639 // Attach The Light if there is one.
640 if(!_Light.empty() && _NameBoneId!=-1)
641 _Skeleton.stickObject(_Light, _NameBoneId);
643 if(changeWeapon)
645 // Compute the new animation set to use (due to weapons).
646 computeAnimSet();
648 // Set the animation to idle.
649 setAnim(CAnimationStateSheet::Idle);
652 // No skeleton
653 else
654 nlwarning("PL::updateVPVpa:%d: Skeleton not allocated.", _Slot);
655 }// updateVisualPropertyVpa //
657 //-----------------------------------------------
658 // updateVisualPropertyVpb :
659 // Update the Visual Property B.
660 //-----------------------------------------------
661 void CPlayerR2CL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
663 // Get the property.
664 SPropVisualB visualB = *(SPropVisualB *)(&prop);
666 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
668 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
669 if (!itemInstance.empty())
671 // update fxs
672 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
673 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualB.PropertySubData.RTrail);
677 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
679 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
680 if (!itemInstance.empty())
682 // update fxs
683 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualB.PropertySubData.LTrail);
687 if(skeleton())
689 // HANDS
690 equip(SLOTTYPE::HANDS_SLOT, visualB.PropertySubData.HandsModel, visualB.PropertySubData.HandsColor);
691 // FEET
692 equip(SLOTTYPE::FEET_SLOT, visualB.PropertySubData.FeetModel, visualB.PropertySubData.FeetColor);
694 else
695 nlinfo("PL::updateVPVpb:%d: Prop Vpb received before prop Vpa.", _Slot);
696 }// updateVisualPropertyVpb //
698 //-----------------------------------------------
699 // updateVisualPropertyVpc :
700 // Update the Visual Property C.
701 // \todo GUIGUI : factorize tatoos with character creation
702 //-----------------------------------------------
704 void CPlayerR2CL::updateVisualPropertyVpc(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
706 if(skeleton())
708 // Get the property.
709 SPropVisualC visualC = *(SPropVisualC *)(&prop);
711 // EYES
712 _EyesColor = visualC.PropertySubData.EyesColor;
713 UInstance inst;
715 // must recreate the face asynchronously (because of color change / makeup change)
716 inst= _Face.createLoadingFromCurrent();
718 // if exist
719 if (!inst.empty())
721 // change eyes color only
722 applyColorSlot(_Face, _Face.ACSkin, _Face.ACUser, _Face.ACHair, visualC.PropertySubData.EyesColor);
724 // Tattoo
725 makeUp(inst, visualC.PropertySubData.Tattoo);
727 // Morph
728 static const char *baseName = "visage_00";
729 float MTmin, MTmax;
731 CGenderInfo *pGI = getGenderInfo();
732 if (pGI == NULL)
733 return;
735 MTmin = pGI->BlendShapeMin[0];
736 MTmax = pGI->BlendShapeMax[0];
737 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
738 inst.setBlendShapeFactor(baseName + toString(0), (float)(visualC.PropertySubData.MorphTarget1) / 7.f * (MTmax-MTmin) + MTmin, true);
740 MTmin = pGI->BlendShapeMin[1];
741 MTmax = pGI->BlendShapeMax[1];
742 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
743 inst.setBlendShapeFactor(baseName + toString(1), (float)(visualC.PropertySubData.MorphTarget2) / 7.f * (MTmax-MTmin) + MTmin, true);
745 MTmin = pGI->BlendShapeMin[2];
746 MTmax = pGI->BlendShapeMax[2];
747 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
748 inst.setBlendShapeFactor(baseName + toString(2), (float)(visualC.PropertySubData.MorphTarget3) / 7.f * (MTmax-MTmin) + MTmin, true);
750 MTmin = pGI->BlendShapeMin[3];
751 MTmax = pGI->BlendShapeMax[3];
752 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
753 inst.setBlendShapeFactor(baseName + toString(3), (float)(visualC.PropertySubData.MorphTarget4) / 7.f * (MTmax-MTmin) + MTmin, true);
755 MTmin = pGI->BlendShapeMin[4];
756 MTmax = pGI->BlendShapeMax[4];
757 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
758 inst.setBlendShapeFactor(baseName + toString(4), (float)(visualC.PropertySubData.MorphTarget5) / 7.f * (MTmax-MTmin) + MTmin, true);
760 MTmin = pGI->BlendShapeMin[5];
761 MTmax = pGI->BlendShapeMax[5];
762 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
763 inst.setBlendShapeFactor(baseName + toString(5), (float)(visualC.PropertySubData.MorphTarget6) / 7.f * (MTmax-MTmin) + MTmin, true);
765 MTmin = pGI->BlendShapeMin[6];
766 MTmax = pGI->BlendShapeMax[6];
767 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
768 inst.setBlendShapeFactor(baseName + toString(6), (float)(visualC.PropertySubData.MorphTarget7) / 7.f * (MTmax-MTmin) + MTmin, true);
770 MTmin = pGI->BlendShapeMin[7];
771 MTmax = pGI->BlendShapeMax[7];
772 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
773 inst.setBlendShapeFactor(baseName + toString(7), (float)(visualC.PropertySubData.MorphTarget8) / 7.f * (MTmax-MTmin) + MTmin, true);
776 // Set the Gabarit
777 float characterHeight = (float)((sint8)(visualC.PropertySubData.CharacterHeight)-7)/7.f;
778 float torsoWidth = (float)((sint8)(visualC.PropertySubData.TorsoWidth)-7)/7.f;
779 float armsWidth = (float)((sint8)(visualC.PropertySubData.ArmsWidth)-7)/7.f;
780 float legsWidth = (float)((sint8)(visualC.PropertySubData.LegsWidth)-7)/7.f;
781 float breastSize = (float)((sint8)(visualC.PropertySubData.BreastSize)-7)/7.f;
782 float heightScale, baseHeightScale;
783 // TODO : manage breast size
784 GabaritSet.applyGabarit(*skeleton(), _Gender, people(), characterHeight, torsoWidth, armsWidth, legsWidth, breastSize, &heightScale);
785 baseHeightScale = GabaritSet.getRefHeightScale(_Gender, people());
787 if(baseHeightScale != 0.f)
788 _CustomScalePos = heightScale/baseHeightScale;
789 else
791 _CustomScalePos = 1.f;
792 nlwarning("PL::updateVPVpc:'%d': baseHeight == 0.", _Slot);
795 else
796 nlinfo("PL:updateVPVpc:'%d': Prop Vpc received before prop Vpa.", _Slot);
797 }// updateVisualPropertyVpc //
800 //-----------------------------------------------
801 // skin :
802 // Get The Entity Skin
803 //-----------------------------------------------
804 sint CPlayerR2CL::skin() const // virtual
806 return _PlayerSheet->Skin;
807 }// skin //*/
810 //-----------------------------------------------
811 // people :
812 // Return the People for the entity.
813 //-----------------------------------------------
814 EGSPD::CPeople::TPeople CPlayerR2CL::people() const// virtual
816 if(_PlayerSheet)
817 return _PlayerSheet->People;
818 else
819 return EGSPD::CPeople::Unknown;
820 }// people //*/
823 //-----------------------------------------------
824 // people :
825 // Setup the People for the entity.
826 //-----------------------------------------------
827 void CPlayerR2CL::setPeople(EGSPD::CPeople::TPeople people)
829 }// people //*/
831 //-----------------------------------------------
832 // drawName :
833 // Draw the name.
834 //-----------------------------------------------
835 void CPlayerR2CL::drawName(const NLMISC::CMatrix &mat) // virtual
837 // Draw the name.
838 if(!getEntityName().empty())
839 TextContext->render3D(mat, getEntityName());
840 }// drawName //
843 //-----------------------------------------------
844 // getFace :
845 // Update eyes blink. For the moment, called by updatePos.
846 //-----------------------------------------------
847 CEntityCL::SInstanceCL *CPlayerR2CL::getFace()
849 // Implemented in CPlayerR2CL
850 return &_Face;
851 }// getFace //
853 //-----------------------------------------------
854 // attackRadius :
855 // Method to return the attack radius of an entity (take the scale into account).
856 //-----------------------------------------------
857 double CPlayerR2CL::attackRadius() const // virtual
859 return 0.5;
860 }// attackRadius //
862 //-----------------------------------------------
863 // Return the position the attacker should have to combat according to the attack angle.
864 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
865 //-----------------------------------------------
866 CVectorD CPlayerR2CL::getAttackerPos(double ang, double dist) const
868 // Compute the local angle
869 ang = computeShortestAngle(atan2(front().y, front().x), ang);
870 ang += Pi;
871 if(ang > Pi)
872 ang -= 2*Pi;
874 // Compute the local position.
875 CVectorD p;
876 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.
877 p.y = 0.5 * cos(ang) + dist*cos(ang);
878 p.z = 0.0;
880 // Compute the world position.
881 // Create the target matrix.
882 CVector vj = front();
883 vj.z = 0;
884 CVector vk(0,0,1);
885 CVector vi = vj^vk;
886 CMatrix bodyBase;
887 bodyBase.setRot(vi,vj,vk,true);
888 bodyBase.setPos(pos());
890 // Get the destination in the world.
891 return bodyBase * p;
892 }// getAttackerPos //
895 ///////////////
896 // 3D SYSTEM //
897 ///////////////
898 //-----------------------------------------------
899 // updateAsyncTexture
900 //-----------------------------------------------
901 float CPlayerR2CL::updateAsyncTexture()
903 // Call parent.
904 float distToCam= CCharacterCL::updateAsyncTexture();
906 // Check all instance to know if they need to start async load their textures
907 if(!_Face.Loading.empty())
909 // dirty?
910 if(_Face.Loading.isAsyncTextureDirty())
912 // reset instance state.
913 _Face.Loading.setAsyncTextureDirty(false);
914 // must start loading for this isntance
915 _Face.Loading.startAsyncTextureLoading();
916 // the entity is now currently loading.
917 _PlayerCLAsyncTextureLoading= true;
918 // The LodTexture need to be recomputed
919 _LodTextureDirty= true;
924 // Update Async Texture loading of all instances.
925 if(_PlayerCLAsyncTextureLoading)
927 bool allLoaded= true;
928 // update loading for all instances.
929 if(!_Face.Loading.empty())
931 // update async texture loading
932 allLoaded= allLoaded && _Face.Loading.isAsyncTextureReady();
935 // if all are loaded, then End! don't need to check all instances every frame.
936 if(allLoaded)
938 _PlayerCLAsyncTextureLoading= false;
939 _Face.updateCurrentFromLoading(_Skeleton);
944 // For LOD texture, must update the "texture distance"
945 if(!_Face.Current.empty())
947 // update async texture loading
948 _Face.Current.setAsyncTextureDistance(distToCam);
951 return distToCam;
954 //-----------------------------------------------
955 // updateLodTexture
956 //-----------------------------------------------
957 void CPlayerR2CL::updateLodTexture()
959 // if need to recompute, and if Async loading ended
960 if( _LodTextureDirty && !_PlayerCLAsyncTextureLoading )
961 // check parent and upadte lod
962 CCharacterCL::updateLodTexture();
965 //-----------------------------------------------
966 // getMaxSpeed :
967 // Return the basic max speed for the entity in meter per sec
968 //-----------------------------------------------
969 double CPlayerR2CL::getMaxSpeed() const// virtual
971 return 6.0f;
972 }// getMaxSpeed //
976 //---------------------------------------------------
977 // displayDebug :
978 // Display Debug Information.
979 //---------------------------------------------------
980 void CPlayerR2CL::displayDebug(float x, float &y, float lineStep) // virtual
982 CCharacterCL::displayDebug(x, y, lineStep);
983 }// displayDebug //
990 //---------------------------------------------------
991 // readWrite :
992 // Read/Write Variables from/to the stream.
993 //---------------------------------------------------
994 void CPlayerR2CL::readWrite(NLMISC::IStream &f)
996 CCharacterCL::readWrite(f);
998 // PUBLIC
1000 // PROTECTED
1001 // const CPlayerSheet *_Sheet;
1002 // const CRaceStatsSheet *_PlayerSheet;
1003 // NL3D::UInstance _Face;
1004 f.serial(_DefaultChest);
1005 f.serial(_DefaultLegs);
1006 f.serial(_DefaultArms);
1007 f.serial(_DefaultHands);
1008 f.serial(_DefaultFeet);
1009 f.serial(_DefaultHair);
1010 f.serial(_HairColor);
1011 f.serial(_EyesColor);
1012 f.serial(_WaitForAppearance);
1013 f.serial(_PlayerCLAsyncTextureLoading);
1014 f.serial(_LightOn);
1015 // NL3D::UPointLight _Light;
1017 // PRIVATE
1018 }// readWrite //
1020 //---------------------------------------------------
1021 // load :
1022 // To call after a read from a stream to re-initialize the entity.
1023 //---------------------------------------------------
1024 void CPlayerR2CL::load() // virtual
1026 CInterfaceManager *IM = CInterfaceManager::getInstance ();
1028 // If the entity should be in the world already
1029 if(_First_Pos == false)
1031 // Insert the primitive into the world.
1032 if(_Primitive)
1033 _Primitive->insertInWorldImage(dynamicWI);
1034 // Insert the entity into PACS
1035 pacsPos(pos());
1038 // update
1039 if(!_WaitForAppearance)
1041 // Visual properties A
1042 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
1043 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
1045 }// load //
1047 // *********************************************************************************************
1049 const char *CPlayerR2CL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const
1051 if (!_PlayerSheet) return CCharacterCL::getBoneNameFromBodyPart(part, side);
1052 return _PlayerSheet->BodyToBone.getBoneName(part, side);
1055 // *********************************************************************************************
1056 const CItemSheet *CPlayerR2CL::getRightHandItemSheet() const
1058 return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
1061 // *********************************************************************************************
1062 const CItemSheet *CPlayerR2CL::getLeftHandItemSheet() const
1064 return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
1067 // *********************************************************************************************
1068 /*const CAttack *CPlayerR2CL::getAttack(const CAttackIDSheet &id) const
1070 if (!_PlayerSheet) return NULL;
1071 return CCharacterCL::getAttack(id, _PlayerSheet->AttackLists);
1074 // *********************************************************************************************
1075 float CPlayerR2CL::getScaleRef() const
1077 float fyrosRefScale = GabaritSet.getRefHeightScale(0, EGSPD::CPeople::Fyros);
1078 if (fyrosRefScale == 0) return 1.f;
1079 return _CustomScalePos * (GabaritSet.getRefHeightScale(_Gender, people()) / fyrosRefScale);
1083 // *********************************************************************************************
1085 float CPlayerR2CL::getNamePosZ() const
1087 if (!_PlayerSheet)
1088 return 0.f;
1090 float namePosZ;
1091 switch (_ModeWanted)
1093 case MBEHAV::DEATH:
1094 case MBEHAV::SIT:
1095 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZLow;
1096 break;
1098 case MBEHAV::MOUNT_NORMAL:
1099 case MBEHAV::MOUNT_SWIM:
1100 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZHigh;
1101 break;
1103 default:
1104 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZNormal;
1105 break;
1108 return namePosZ * _CharacterScalePos * _CustomScalePos;
1111 // ***************************************************************************
1112 void CPlayerR2CL::doSetVisualSelectionBlink(bool bOnOff, NLMISC::CRGBA emitColor)
1114 // Do it on Face
1115 if(bOnOff)
1116 _Face.setEmissive(emitColor);
1117 else
1118 _Face.restoreEmissive();
1120 // and parent call
1121 CCharacterCL::doSetVisualSelectionBlink(bOnOff, emitColor);
1124 // ***************************************************************************
1125 void CPlayerR2CL::makeTransparent(bool t)
1127 CCharacterCL::makeTransparent(t);
1129 uint32 opaMin= getOpacityMin();
1130 uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor));
1132 _Face.makeInstanceTransparent(opacity, (uint8)opaMin);
1133 }// makeTransparent //
1135 // ***************************************************************************
1136 void CPlayerR2CL::setDiffuse(bool onOff, NLMISC::CRGBA diffuse)
1138 CCharacterCL::setDiffuse(onOff, diffuse);
1139 _Face.setDiffuse(onOff, diffuse);