Fix css style order when using external css files
[ryzomcore.git] / ryzom / client / src / player_r2_cl.cpp
blob75effe9bfcba0bb4ab63a38e0bc5502ec6cd27e3
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 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /////////////
24 // INCLUDE //
25 /////////////
26 #include "stdpch.h"
27 // Misc
28 #include "nel/misc/time_nl.h"
29 // Client.
30 #include "player_r2_cl.h"
31 #include "ingame_database_manager.h"
32 #include "net_manager.h"
33 #include "time_client.h"
34 #include "entity_animation_manager.h"
35 #include "sheet_manager.h"
36 #include "color_slot_manager.h"
37 #include "debug_client.h"
38 #include "gabarit.h"
39 #include "interface_v3/interface_manager.h"
40 #include "misc.h"
41 #include "pacs_client.h"
42 #include "motion/user_controls.h"
43 #include "client_cfg.h"
44 #include "user_entity.h"
45 // Client Sheets
46 #include "client_sheets/player_sheet.h"
47 // 3D
48 #include "nel/3d/u_scene.h"
49 #include "nel/3d/u_instance_material.h"
50 #include "nel/3d/u_play_list.h"
51 #include "nel/3d/u_bone.h"
52 #include "nel/3d/u_particle_system_instance.h"
53 #include "nel/3d/u_point_light.h"
54 // game share
55 #include "game_share/player_visual_properties.h"
56 #include "game_share/gender.h"
57 #include "game_share/bot_chat_types.h"
60 ///////////
61 // USING //
62 ///////////
63 using namespace NLMISC;
64 using namespace NL3D;
65 using namespace NLPACS;
66 using namespace std;
69 ////////////
70 // EXTERN //
71 ////////////
72 extern UScene *Scene;
73 extern CEntityAnimationManager *EAM;
74 extern UTextContext *TextContext;
75 extern UCamera MainCam;
78 //-----------------------------------------------
79 // CPlayerR2CL :
80 // Constructor.
81 //-----------------------------------------------
82 CPlayerR2CL::CPlayerR2CL()
83 : CCharacterCL()
85 Type = NPC;
87 // Resize _Instances to the number of visual slots.
88 _Instances.resize(SLOTTYPE::NB_SLOT);
90 // No sheet pointed.
91 _Sheet = 0;
92 //_PlayerSheet = 0;
94 // Some default colors.
95 _HairColor = 0;
96 _EyesColor = 0;
98 // Not enough information to display the player.
99 _WaitForAppearance = true;
101 _PlayerCLAsyncTextureLoading= false;
103 // Light Off and not allocated
104 _LightOn = false;
105 }// CPlayerR2CL //
108 //-----------------------------------------------
109 // ~CPlayerR2CL :
110 // Destructor.
111 //-----------------------------------------------
112 CPlayerR2CL::~CPlayerR2CL()
114 // No more sheet pointed.
115 _Sheet = NULL;
117 // Remove the light
118 if(!_Light.empty())
120 if(Scene)
121 Scene->deletePointLight(_Light);
125 CGenderInfo * CPlayerR2CL::getGenderInfo()
127 EGSPD::CPeople::TPeople ePeople = _Sheet->Race;
128 bool bMale = (_Sheet->Gender == GSGENDER::male);
130 // Read in the race_stats forms the default equipement
131 CSheetId RSid;
132 switch (ePeople)
134 case EGSPD::CPeople::Tryker: RSid = CSheetId("tryker.race_stats"); break;
135 case EGSPD::CPeople::Matis: RSid = CSheetId("matis.race_stats"); break;
136 case EGSPD::CPeople::Zorai: RSid = CSheetId("zorai.race_stats"); break;
137 case EGSPD::CPeople::Fyros:
138 default:
139 RSid = CSheetId("fyros.race_stats"); break;
141 CRaceStatsSheet *pRSS = dynamic_cast<CRaceStatsSheet*>(SheetMngr.get (RSid));
143 if (pRSS == NULL)
145 nlwarning ("cannot find sheet for people:%d male:%d", ePeople, bMale);
146 return NULL;
149 // Choose default stuff is we are male or female
150 CGenderInfo *pGI;
151 if (bMale)
152 pGI = &pRSS->GenderInfos[0];
153 else
154 pGI = &pRSS->GenderInfos[1];
156 return pGI;
161 //---------------------------------------------------
162 // getScale :
163 // Return the entity scale. (return 1.0 if there is any problem).
164 //---------------------------------------------------
165 float CPlayerR2CL::getScale() const // virtual
167 // Default Scale.
168 return _CharacterScalePos;
169 }// getScale //
173 //-----------------------------------------------
174 // getGroundFX :
175 // retrieve ground fxs for the entity depending on the ground
176 //-----------------------------------------------
177 const std::vector<CGroundFXSheet> *CPlayerR2CL::getGroundFX() const
179 switch (getGender())
181 case 0: return &(_PlayerSheet->GenderInfos[0].GroundFX);
182 case 1: return &(_PlayerSheet->GenderInfos[1].GroundFX);
183 default:
184 return NULL;
186 return NULL;
190 //-----------------------------------------------
191 // build :
192 // Build the entity from a sheet.
193 //-----------------------------------------------
194 bool CPlayerR2CL::build(const CEntitySheet *sheet) // virtual
196 // Cast the sheet in the right type.
197 _Sheet = dynamic_cast<const CCharacterSheet *>(sheet);
198 if(_Sheet==0)
200 pushDebugStr(NLMISC::toString("R2 Player '%d' sheet is not a '.creature' -> BIG PROBLEM.", _Slot));
201 return false;
203 else
204 pushInfoStr(NLMISC::toString("R2 Player '%d' sheet is valid.", _Slot));
205 // Get the DB Entry
206 if(IngameDbMngr.getNodePtr())
208 CCDBNodeBranch *nodeRoot = dynamic_cast<CCDBNodeBranch *>(IngameDbMngr.getNodePtr()->getNode(0));
209 if(nodeRoot)
211 _DBEntry = dynamic_cast<CCDBNodeBranch *>(nodeRoot->getNode(_Slot));
212 if(_DBEntry == 0)
213 pushDebugStr("Cannot get a pointer on the DB entry.");
217 // Compute the first automaton.
218 _CurrentAutomaton = automatonType() + "_normal.automaton";
220 // Initialize the player look.
221 init3d();
222 // Compute the primitive
223 initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone);
224 // Create the collision entity (used to snap the entity to the ground).
225 computeCollisionEntity();
227 // Initialize properties of the client.
228 initProperties();
229 // Entity Created.
230 return true;
231 }// build //
234 //-----------------------------------------------
235 // automatonType :
236 // Return the automaton type of the entity (homin, creature, etc.)
237 //-----------------------------------------------
238 std::string CPlayerR2CL::automatonType() const // virtual
240 return _PlayerSheet->Automaton;
241 }// automatonType //*/
244 //-----------------------------------------------
245 // init3d :
246 // Initialize the graphic for the player.
247 //-----------------------------------------------
248 void CPlayerR2CL::init3d()
250 createPlayList();
251 // Initialize the internal time.
252 _LastFrameTime = ((double)T1) * 0.001;
253 }// init3d //
256 //-----------------------------------------------
257 // initProperties :
258 // Initialize properties of the entity (according to the class).
259 //-----------------------------------------------
260 void CPlayerR2CL::initProperties()
262 properties().selectable(true);
263 properties().attackable(false);
264 properties().givable(true);
265 properties().invitable(true);
266 properties().canExchangeItem(true);
267 }// initProperties //
270 //-----------------------------------------------
271 // equip :
272 // Set the equipmenent worn.
273 //-----------------------------------------------
274 void CPlayerR2CL::equip(SLOTTYPE::EVisualSlot slot, const std::string &shapeName, const CItemSheet *item)
276 // Check slot.
277 if(slot == SLOTTYPE::HIDDEN_SLOT || slot >= SLOTTYPE::NB_SLOT)
279 nlwarning("CCharacterCL::equip : slot %d is not valid.", (uint)slot);
280 return;
283 uint s = (uint)slot;
285 // If exactly the same than before -> return
287 if (!_Instances[s].Loading.empty())
289 if ((_Instances[s].LoadingName == shapeName) && (_Instances[s].FXItemSheet == item))
290 return;
292 else if (!_Instances[s].Current.empty())
294 if ((_Instances[s].CurrentName == shapeName) && (_Instances[s].FXItemSheet == item))
295 return;
300 // Attach to the skeleton.
301 string stickPoint;
302 if(!_Skeleton.empty())
304 switch(slot)
306 case SLOTTYPE::RIGHT_HAND_SLOT:
307 if( item && item->ItemType != ITEM_TYPE::MAGICIAN_STAFF )
308 stickPoint = "box_arme";
309 break;
311 case SLOTTYPE::LEFT_HAND_SLOT:
312 if(_Items[slot].Sheet && _Items[slot].Sheet->getAnimSet()=="s")
313 stickPoint = "Box_bouclier";
314 else
315 stickPoint = "box_arme_gauche";
316 break;
318 default:
319 break;
323 /* If the object is sticked (ie not a skin), decide to delete the Current instance. Why? because the animation
324 is changed according to the equipped item.
326 Hence, For example, if a sword would be changed for a gun, then the new gun animation would take place,
327 while Keeping the old sword shape. BAD.
329 if(!stickPoint.empty())
330 _Instances[s].createLoading(string(), stickPoint);
332 // Create the instance.
333 if(item)
334 _Instances[s].createLoading(shapeName, stickPoint, item->MapVariant);
335 else
336 _Instances[s].createLoading(shapeName, stickPoint);
338 // If shapeName is empty, only clear the slot
339 if(shapeName.empty())
341 _Items[slot].release();
342 return;
345 if(!_Instances[s].Loading.empty())
347 _Instances[s].FXItemSheet = item;
349 _Items[slot].initFXs(slot, _Instances[s].Loading);
351 else
352 nlwarning("PL::equip(1):%d: cannot create the instance '%s'.", _Slot, shapeName.c_str());
354 if ((slot != SLOTTYPE::RIGHT_HAND_SLOT) && (slot != SLOTTYPE::LEFT_HAND_SLOT))
355 applyColorSlot(_Instances[s], skin(), 0, _HairColor, _EyesColor);
357 }// equip //
359 //-----------------------------------------------
360 // equip :
361 // Compute the equipmenent worn.
362 //-----------------------------------------------
363 void CPlayerR2CL::equip(SLOTTYPE::EVisualSlot slot, uint index, uint color)
365 // Get the sheet according to the visual slot
366 _Items[slot].Sheet = SheetMngr.getItem(slot, index);
367 if(_Items[slot].Sheet)
369 const CItemSheet *item = _Items[slot].Sheet;
371 // If the gender is a female get the right shape.
372 if(_Gender == GSGENDER::female)
373 equip(slot, item->getShapeFemale(), item);
374 // Else get the default shape.
375 else
376 equip(slot, item->getShape(), item);
378 // Check there is a shape.
379 UInstance pInst = _Instances[slot].createLoadingFromCurrent();
380 if(!pInst.empty())
382 // Set the right texture variation (quality).
383 pInst.selectTextureSet((uint)item->MapVariant);
384 _Instances[slot].TextureSet = item->MapVariant;
386 // If Hair, color is for the head slot.
387 if(slot == SLOTTYPE::HEAD_SLOT && item->Family != ITEMFAMILY::ARMOR)
388 applyColorSlot(_Instances[slot], skin(), 0, color, _EyesColor);
389 else
391 // Set the User Color.
392 if(item->Color == -1)
393 applyColorSlot(_Instances[slot], skin(), color, _HairColor, _EyesColor);
394 // Set the Item Color.
395 else if(item->Color != -2)
396 applyColorSlot(_Instances[slot], skin(), item->Color, _HairColor, _EyesColor);
397 // Else let the default color.
398 else
399 applyColorSlot(_Instances[slot], skin(), 0, _HairColor, _EyesColor);
403 // Default equipment.
404 else
406 nldebug("PL:equip(2):%d: VS '%d' default equipement used.", _Slot, slot);
407 //sint idx = SheetMngr.getVSIndex(_PlayerSheet->GenderInfos[_Gender].Items[slot], slot);
408 sint idx = SheetMngr.getVSIndex(getGenderInfo()->Items[slot], slot);
410 if(idx != -1)
412 if(SheetMngr.getItem(slot, (uint)idx))
414 // If the gender is a female get the right shape.
415 if(_Gender == GSGENDER::female)
416 equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShapeFemale());
417 // Else get the default shape.
418 else
419 equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShape());
423 }// equip //
425 //-----------------------------------------------
426 // updateVisualPropertyVpa :
427 // Update the Visual Property A.
428 // \todo GUIGUI : use gender enum.
429 //-----------------------------------------------
430 void CPlayerR2CL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
432 CInterfaceManager *IM = CInterfaceManager::getInstance ();
434 // Player will now have enough information to display the character.
435 _WaitForAppearance = false;
437 // Get the property.
438 SPropVisualA visualA = *(SPropVisualA *)(&prop);
440 // GENDER
441 _Gender = (GSGENDER::EGender)_Sheet->Gender;
442 if(_Gender!=GSGENDER::male && _Gender!=GSGENDER::female)
444 nlwarning("PL::updateVPVpa:%d: neither a male nor a female -> male selected.", _Slot);
445 _Gender = GSGENDER::male;
448 // update title when gender changed
449 const string replacement(STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw,_Gender == GSGENDER::female));
450 if (!replacement.empty())
452 // Get extended name
453 _NameEx = replacement;
454 _Title = replacement;
456 // rebuild in scene interface
457 buildInSceneInterface();
460 // Setup _CharacterScalePos
461 _CharacterScalePos = _Sheet->CharacterScalePos;
463 // Check if skeleton has changed
464 if (_CacheSkeletonShapeName != _Sheet->getSkelFilename())
466 _CacheSkeletonShapeName = _Sheet->getSkelFilename();
468 // Clean the playlist.
469 if(_PlayList)
470 _PlayList->resetAllChannels();
472 // We can now build the skeleton so do it now.
473 skeleton(_CacheSkeletonShapeName);
475 // Invalidate instances cache
476 for (uint i = 0; i < _Instances.size(); ++i)
478 _Instances[i].CurrentName.clear();
479 _Instances[i].LoadingName.clear();
482 _Face.CurrentName.clear();
483 _Face.LoadingName.clear();
485 // Check the skeleton.
486 if(skeleton() && !ClientCfg.Light)
488 // To re-link the skeleton to the mount if needed.
489 parent(parent());
490 // Set the skeleton scale.
491 // \todo GUIGUI: mettre le scale aussi dans race_stats.
492 // Setup Lod Character skeleton, if skeleton exist
493 // Get Lod Character Id from the sheet.
494 sint clodId= getLodCharacterId(*Scene, _Sheet->getLodCharacterName());
495 if(clodId>=0)
497 // Setup Lod Character shape and distance
498 skeleton()->setLodCharacterShape(clodId);
499 skeleton()->setLodCharacterDistance(_Sheet->LodCharacterDistance);
501 // Compute the
502 computeSomeBoneId();
504 // CHEST
505 equip(SLOTTYPE::CHEST_SLOT, visualA.PropertySubData.JacketModel, visualA.PropertySubData.JacketColor);
506 // LEGS
507 equip(SLOTTYPE::LEGS_SLOT, visualA.PropertySubData.TrouserModel, visualA.PropertySubData.TrouserColor);
508 // ARMS
509 equip(SLOTTYPE::ARMS_SLOT, visualA.PropertySubData.ArmModel, visualA.PropertySubData.ArmColor);
510 // HAT
511 equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor);
512 // OBJECT in the RIGHT HAND
513 bool changeWeapon = false;
514 const CItemSheet * oldRightSheet = _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
515 const CItemSheet * newRightSheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
516 if((oldRightSheet && newRightSheet && oldRightSheet->Id!=newRightSheet->Id) || (!oldRightSheet && newRightSheet))
518 changeWeapon = true;
520 _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand);
521 // Equip the weapon(object/tool).
522 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
524 if(_Gender == GSGENDER::female)
525 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShapeFemale(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
526 else
527 equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet);
529 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
530 if (!itemInstance.empty())
532 // update fxs
533 _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance);
534 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
535 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(0);
536 //_Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualA.PropertySubData.RTrail);
539 else
541 // No Valid item in the right hand.
542 equip(SLOTTYPE::RIGHT_HAND_SLOT, "");
545 // OBJECT in the LEFT HAND
546 const CItemSheet * oldLeftSheet = _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
547 const CItemSheet * newLeftSheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
548 if((oldLeftSheet && newLeftSheet && oldLeftSheet->Id!=newLeftSheet->Id) || (!oldLeftSheet && newLeftSheet))
550 changeWeapon = true;
553 _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand);
554 // Equip the weapon(object/tool).
555 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
557 equip(SLOTTYPE::LEFT_HAND_SLOT, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet);
558 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
559 if (!itemInstance.empty())
561 // update fxs
562 _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance);
563 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(0);
564 //_Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualA.PropertySubData.LTrail);
567 else
569 // No Valid item in the left hand.
570 equip(SLOTTYPE::LEFT_HAND_SLOT, "");
572 // Create face
573 // Only create a face when there is no Helmet
574 if(_Items[SLOTTYPE::HEAD_SLOT].Sheet == 0 || _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family != ITEMFAMILY::ARMOR)
576 CItemSheet *faceItem = getItem(*getGenderInfo(), SLOTTYPE::FACE_SLOT);
577 if (faceItem)
579 string sFaceName;
581 if(_Gender == GSGENDER::female)
582 sFaceName = faceItem->getShapeFemale();
583 else
584 sFaceName = faceItem->getShape();
586 if (((!_Face.Loading.empty()) && (_Face.LoadingName != sFaceName)) ||
587 ((!_Face.Current.empty()) && (_Face.CurrentName != sFaceName)) ||
588 (_Face.Current.empty()))
590 if (!_Face.Loading.empty())
592 Scene->deleteInstance(_Face.Loading);
593 _Face.Loading = NULL;
594 _Face.LoadingName = sFaceName;
596 _Face.Loading = Scene->createInstance(sFaceName);
597 if (!_Face.Loading.empty())
599 _Face.LoadingName = sFaceName;
600 if(!skeleton()->bindSkin(_Face.Loading))
601 nlwarning("PL::updateVPVpa:%d: Cannot bind the face.", _Slot);
602 _Face.Loading.hide();
603 // set it async for texture
604 _Face.Loading.enableAsyncTextureMode(true);
606 else
607 nlwarning("PL::updateVPVpa:%d: Cannot create the face.", _Slot);
609 _Face.TextureSet = faceItem->MapVariant;
610 applyColorSlot(_Face, skin(), 0, visualA.PropertySubData.HatColor, 0); // Set a default ruflaket color.
612 else
613 nlwarning("PL::updateVPVpa:%d: Face Item '%s' does not exist.", _Slot,
614 getGenderInfo()->Items[SLOTTYPE::FACE_SLOT].c_str());
616 else
618 // There is a helmet !
619 if (!_Face.Loading.empty())
620 Scene->deleteInstance(_Face.Loading);
621 _Face.Loading = NULL;
622 _Face.LoadingName.clear();
623 if (!_Face.Current.empty())
624 Scene->deleteInstance(_Face.Current);
625 _Face.Current = NULL;
626 _Face.CurrentName.clear();
628 // Now we have a skeleton, we can update VpB and VpC.
629 sint64 vB, vC;
630 string propName;
631 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPB);
632 vB = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
633 propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPC);
634 vC = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64();
635 updateVisualPropertyVpb(0, vB);
636 updateVisualPropertyVpc(0, vC);
638 // Attach The Light if there is one.
639 if(!_Light.empty() && _NameBoneId!=-1)
640 _Skeleton.stickObject(_Light, _NameBoneId);
642 if(changeWeapon)
644 // Compute the new animation set to use (due to weapons).
645 computeAnimSet();
647 // Set the animation to idle.
648 setAnim(CAnimationStateSheet::Idle);
651 // No skeleton
652 else
653 nlwarning("PL::updateVPVpa:%d: Skeleton not allocated.", _Slot);
654 }// updateVisualPropertyVpa //
656 //-----------------------------------------------
657 // updateVisualPropertyVpb :
658 // Update the Visual Property B.
659 //-----------------------------------------------
660 void CPlayerR2CL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
662 // Get the property.
663 SPropVisualB visualB = *(SPropVisualB *)(&prop);
665 if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet)
667 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current;
668 if (!itemInstance.empty())
670 // update fxs
671 if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING )
672 _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualB.PropertySubData.RTrail);
676 if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet)
678 NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current;
679 if (!itemInstance.empty())
681 // update fxs
682 _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualB.PropertySubData.LTrail);
686 if(skeleton())
688 // HANDS
689 equip(SLOTTYPE::HANDS_SLOT, visualB.PropertySubData.HandsModel, visualB.PropertySubData.HandsColor);
690 // FEET
691 equip(SLOTTYPE::FEET_SLOT, visualB.PropertySubData.FeetModel, visualB.PropertySubData.FeetColor);
693 else
694 nlinfo("PL::updateVPVpb:%d: Prop Vpb received before prop Vpa.", _Slot);
695 }// updateVisualPropertyVpb //
697 //-----------------------------------------------
698 // updateVisualPropertyVpc :
699 // Update the Visual Property C.
700 // \todo GUIGUI : factorize tatoos with character creation
701 //-----------------------------------------------
703 void CPlayerR2CL::updateVisualPropertyVpc(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop)
705 if(skeleton())
707 // Get the property.
708 SPropVisualC visualC = *(SPropVisualC *)(&prop);
710 // EYES
711 _EyesColor = visualC.PropertySubData.EyesColor;
712 UInstance inst;
714 // must recreate the face asynchronously (because of color change / makeup change)
715 inst= _Face.createLoadingFromCurrent();
717 // if exist
718 if (!inst.empty())
720 // change eyes color only
721 applyColorSlot(_Face, _Face.ACSkin, _Face.ACUser, _Face.ACHair, visualC.PropertySubData.EyesColor);
723 // Tattoo
724 makeUp(inst, visualC.PropertySubData.Tattoo);
726 // Morph
727 static const char *baseName = "visage_00";
728 float MTmin, MTmax;
730 CGenderInfo *pGI = getGenderInfo();
731 if (pGI == NULL)
732 return;
734 MTmin = pGI->BlendShapeMin[0];
735 MTmax = pGI->BlendShapeMax[0];
736 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
737 inst.setBlendShapeFactor(baseName + toString(0), (float)(visualC.PropertySubData.MorphTarget1) / 7.f * (MTmax-MTmin) + MTmin, true);
739 MTmin = pGI->BlendShapeMin[1];
740 MTmax = pGI->BlendShapeMax[1];
741 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
742 inst.setBlendShapeFactor(baseName + toString(1), (float)(visualC.PropertySubData.MorphTarget2) / 7.f * (MTmax-MTmin) + MTmin, true);
744 MTmin = pGI->BlendShapeMin[2];
745 MTmax = pGI->BlendShapeMax[2];
746 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
747 inst.setBlendShapeFactor(baseName + toString(2), (float)(visualC.PropertySubData.MorphTarget3) / 7.f * (MTmax-MTmin) + MTmin, true);
749 MTmin = pGI->BlendShapeMin[3];
750 MTmax = pGI->BlendShapeMax[3];
751 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
752 inst.setBlendShapeFactor(baseName + toString(3), (float)(visualC.PropertySubData.MorphTarget4) / 7.f * (MTmax-MTmin) + MTmin, true);
754 MTmin = pGI->BlendShapeMin[4];
755 MTmax = pGI->BlendShapeMax[4];
756 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
757 inst.setBlendShapeFactor(baseName + toString(4), (float)(visualC.PropertySubData.MorphTarget5) / 7.f * (MTmax-MTmin) + MTmin, true);
759 MTmin = pGI->BlendShapeMin[5];
760 MTmax = pGI->BlendShapeMax[5];
761 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
762 inst.setBlendShapeFactor(baseName + toString(5), (float)(visualC.PropertySubData.MorphTarget6) / 7.f * (MTmax-MTmin) + MTmin, true);
764 MTmin = pGI->BlendShapeMin[6];
765 MTmax = pGI->BlendShapeMax[6];
766 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
767 inst.setBlendShapeFactor(baseName + toString(6), (float)(visualC.PropertySubData.MorphTarget7) / 7.f * (MTmax-MTmin) + MTmin, true);
769 MTmin = pGI->BlendShapeMin[7];
770 MTmax = pGI->BlendShapeMax[7];
771 if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; }
772 inst.setBlendShapeFactor(baseName + toString(7), (float)(visualC.PropertySubData.MorphTarget8) / 7.f * (MTmax-MTmin) + MTmin, true);
775 // Set the Gabarit
776 float characterHeight = (float)((sint8)(visualC.PropertySubData.CharacterHeight)-7)/7.f;
777 float torsoWidth = (float)((sint8)(visualC.PropertySubData.TorsoWidth)-7)/7.f;
778 float armsWidth = (float)((sint8)(visualC.PropertySubData.ArmsWidth)-7)/7.f;
779 float legsWidth = (float)((sint8)(visualC.PropertySubData.LegsWidth)-7)/7.f;
780 float breastSize = (float)((sint8)(visualC.PropertySubData.BreastSize)-7)/7.f;
781 float heightScale, baseHeightScale;
782 // TODO : manage breast size
783 GabaritSet.applyGabarit(*skeleton(), _Gender, people(), characterHeight, torsoWidth, armsWidth, legsWidth, breastSize, &heightScale);
784 baseHeightScale = GabaritSet.getRefHeightScale(_Gender, people());
786 if(baseHeightScale != 0.f)
787 _CustomScalePos = heightScale/baseHeightScale;
788 else
790 _CustomScalePos = 1.f;
791 nlwarning("PL::updateVPVpc:'%d': baseHeight == 0.", _Slot);
794 else
795 nlinfo("PL:updateVPVpc:'%d': Prop Vpc received before prop Vpa.", _Slot);
796 }// updateVisualPropertyVpc //
799 //-----------------------------------------------
800 // skin :
801 // Get The Entity Skin
802 //-----------------------------------------------
803 sint CPlayerR2CL::skin() const // virtual
805 return _PlayerSheet->Skin;
806 }// skin //*/
809 //-----------------------------------------------
810 // people :
811 // Return the People for the entity.
812 //-----------------------------------------------
813 EGSPD::CPeople::TPeople CPlayerR2CL::people() const// virtual
815 if(_PlayerSheet)
816 return _PlayerSheet->People;
817 else
818 return EGSPD::CPeople::Unknown;
819 }// people //*/
822 //-----------------------------------------------
823 // people :
824 // Setup the People for the entity.
825 //-----------------------------------------------
826 void CPlayerR2CL::setPeople(EGSPD::CPeople::TPeople people)
828 }// people //*/
830 //-----------------------------------------------
831 // drawName :
832 // Draw the name.
833 //-----------------------------------------------
834 void CPlayerR2CL::drawName(const NLMISC::CMatrix &mat) // virtual
836 // Draw the name.
837 if(!getEntityName().empty())
838 TextContext->render3D(mat, getEntityName());
839 }// drawName //
842 //-----------------------------------------------
843 // getFace :
844 // Update eyes blink. For the moment, called by updatePos.
845 //-----------------------------------------------
846 CEntityCL::SInstanceCL *CPlayerR2CL::getFace()
848 // Implemented in CPlayerR2CL
849 return &_Face;
850 }// getFace //
852 //-----------------------------------------------
853 // attackRadius :
854 // Method to return the attack radius of an entity (take the scale into account).
855 //-----------------------------------------------
856 double CPlayerR2CL::attackRadius() const // virtual
858 return 0.5;
859 }// attackRadius //
861 //-----------------------------------------------
862 // Return the position the attacker should have to combat according to the attack angle.
863 // \param ang : 0 = the front, >0 and <Pi = left side, <0 and >-Pi = right side.
864 //-----------------------------------------------
865 CVectorD CPlayerR2CL::getAttackerPos(double ang, double dist) const
867 // Compute the local angle
868 ang = computeShortestAngle(atan2(front().y, front().x), ang);
869 ang += Pi;
870 if(ang > Pi)
871 ang -= 2*Pi;
873 // Compute the local position.
874 CVectorD p;
875 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.
876 p.y = 0.5 * cos(ang) + dist*cos(ang);
877 p.z = 0.0;
879 // Compute the world position.
880 // Create the target matrix.
881 CVector vj = front();
882 vj.z = 0;
883 CVector vk(0,0,1);
884 CVector vi = vj^vk;
885 CMatrix bodyBase;
886 bodyBase.setRot(vi,vj,vk,true);
887 bodyBase.setPos(pos());
889 // Get the destination in the world.
890 return bodyBase * p;
891 }// getAttackerPos //
894 ///////////////
895 // 3D SYSTEM //
896 ///////////////
897 //-----------------------------------------------
898 // updateAsyncTexture
899 //-----------------------------------------------
900 float CPlayerR2CL::updateAsyncTexture()
902 // Call parent.
903 float distToCam= CCharacterCL::updateAsyncTexture();
905 // Check all instance to know if they need to start async load their textures
906 if(!_Face.Loading.empty())
908 // dirty?
909 if(_Face.Loading.isAsyncTextureDirty())
911 // reset instance state.
912 _Face.Loading.setAsyncTextureDirty(false);
913 // must start loading for this isntance
914 _Face.Loading.startAsyncTextureLoading();
915 // the entity is now currently loading.
916 _PlayerCLAsyncTextureLoading= true;
917 // The LodTexture need to be recomputed
918 _LodTextureDirty= true;
923 // Update Async Texture loading of all instances.
924 if(_PlayerCLAsyncTextureLoading)
926 bool allLoaded= true;
927 // update loading for all instances.
928 if(!_Face.Loading.empty())
930 // update async texture loading
931 allLoaded= allLoaded && _Face.Loading.isAsyncTextureReady();
934 // if all are loaded, then End! don't need to check all instances every frame.
935 if(allLoaded)
937 _PlayerCLAsyncTextureLoading= false;
938 _Face.updateCurrentFromLoading(_Skeleton);
943 // For LOD texture, must update the "texture distance"
944 if(!_Face.Current.empty())
946 // update async texture loading
947 _Face.Current.setAsyncTextureDistance(distToCam);
950 return distToCam;
953 //-----------------------------------------------
954 // updateLodTexture
955 //-----------------------------------------------
956 void CPlayerR2CL::updateLodTexture()
958 // if need to recompute, and if Async loading ended
959 if( _LodTextureDirty && !_PlayerCLAsyncTextureLoading )
960 // check parent and upadte lod
961 CCharacterCL::updateLodTexture();
964 //-----------------------------------------------
965 // getMaxSpeed :
966 // Return the basic max speed for the entity in meter per sec
967 //-----------------------------------------------
968 double CPlayerR2CL::getMaxSpeed() const// virtual
970 return 6.0f;
971 }// getMaxSpeed //
975 //---------------------------------------------------
976 // displayDebug :
977 // Display Debug Information.
978 //---------------------------------------------------
979 void CPlayerR2CL::displayDebug(float x, float &y, float lineStep) // virtual
981 CCharacterCL::displayDebug(x, y, lineStep);
982 }// displayDebug //
989 //---------------------------------------------------
990 // readWrite :
991 // Read/Write Variables from/to the stream.
992 //---------------------------------------------------
993 void CPlayerR2CL::readWrite(NLMISC::IStream &f)
995 CCharacterCL::readWrite(f);
997 // PUBLIC
999 // PROTECTED
1000 // const CPlayerSheet *_Sheet;
1001 // const CRaceStatsSheet *_PlayerSheet;
1002 // NL3D::UInstance _Face;
1003 f.serial(_DefaultChest);
1004 f.serial(_DefaultLegs);
1005 f.serial(_DefaultArms);
1006 f.serial(_DefaultHands);
1007 f.serial(_DefaultFeet);
1008 f.serial(_DefaultHair);
1009 f.serial(_HairColor);
1010 f.serial(_EyesColor);
1011 f.serial(_WaitForAppearance);
1012 f.serial(_PlayerCLAsyncTextureLoading);
1013 f.serial(_LightOn);
1014 // NL3D::UPointLight _Light;
1016 // PRIVATE
1017 }// readWrite //
1019 //---------------------------------------------------
1020 // load :
1021 // To call after a read from a stream to re-initialize the entity.
1022 //---------------------------------------------------
1023 void CPlayerR2CL::load() // virtual
1025 CInterfaceManager *IM = CInterfaceManager::getInstance ();
1027 // If the entity should be in the world already
1028 if(_First_Pos == false)
1030 // Insert the primitive into the world.
1031 if(_Primitive)
1032 _Primitive->insertInWorldImage(dynamicWI);
1033 // Insert the entity into PACS
1034 pacsPos(pos());
1037 // update
1038 if(!_WaitForAppearance)
1040 // Visual properties A
1041 sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64();
1042 updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too.
1044 }// load //
1046 // *********************************************************************************************
1048 const char *CPlayerR2CL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const
1050 if (!_PlayerSheet) return CCharacterCL::getBoneNameFromBodyPart(part, side);
1051 return _PlayerSheet->BodyToBone.getBoneName(part, side);
1054 // *********************************************************************************************
1055 const CItemSheet *CPlayerR2CL::getRightHandItemSheet() const
1057 return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet;
1060 // *********************************************************************************************
1061 const CItemSheet *CPlayerR2CL::getLeftHandItemSheet() const
1063 return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet;
1066 // *********************************************************************************************
1067 /*const CAttack *CPlayerR2CL::getAttack(const CAttackIDSheet &id) const
1069 if (!_PlayerSheet) return NULL;
1070 return CCharacterCL::getAttack(id, _PlayerSheet->AttackLists);
1073 // *********************************************************************************************
1074 float CPlayerR2CL::getScaleRef() const
1076 float fyrosRefScale = GabaritSet.getRefHeightScale(0, EGSPD::CPeople::Fyros);
1077 if (fyrosRefScale == 0) return 1.f;
1078 return _CustomScalePos * (GabaritSet.getRefHeightScale(_Gender, people()) / fyrosRefScale);
1082 // *********************************************************************************************
1084 float CPlayerR2CL::getNamePosZ() const
1086 if (!_PlayerSheet)
1087 return 0.f;
1089 float namePosZ;
1090 switch (_ModeWanted)
1092 case MBEHAV::DEATH:
1093 case MBEHAV::SIT:
1094 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZLow;
1095 break;
1097 case MBEHAV::MOUNT_NORMAL:
1098 case MBEHAV::MOUNT_SWIM:
1099 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZHigh;
1100 break;
1102 default:
1103 namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZNormal;
1104 break;
1107 return namePosZ * _CharacterScalePos * _CustomScalePos;
1110 // ***************************************************************************
1111 void CPlayerR2CL::doSetVisualSelectionBlink(bool bOnOff, NLMISC::CRGBA emitColor)
1113 // Do it on Face
1114 if(bOnOff)
1115 _Face.setEmissive(emitColor);
1116 else
1117 _Face.restoreEmissive();
1119 // and parent call
1120 CCharacterCL::doSetVisualSelectionBlink(bOnOff, emitColor);
1123 // ***************************************************************************
1124 void CPlayerR2CL::makeTransparent(bool t)
1126 CCharacterCL::makeTransparent(t);
1128 uint32 opaMin= getOpacityMin();
1129 uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor));
1131 _Face.makeInstanceTransparent(opacity, (uint8)opaMin);
1132 }// makeTransparent //
1134 // ***************************************************************************
1135 void CPlayerR2CL::setDiffuse(bool onOff, NLMISC::CRGBA diffuse)
1137 CCharacterCL::setDiffuse(onOff, diffuse);
1138 _Face.setDiffuse(onOff, diffuse);