// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . ///////////// // INCLUDE // ///////////// #include "stdpch.h" // Misc #include "nel/misc/time_nl.h" // Client. #include "player_cl.h" #include "ingame_database_manager.h" #include "net_manager.h" #include "time_client.h" #include "entity_animation_manager.h" #include "sheet_manager.h" #include "color_slot_manager.h" #include "debug_client.h" #include "gabarit.h" #include "interface_v3/interface_manager.h" #include "misc.h" #include "pacs_client.h" #include "motion/user_controls.h" #include "client_cfg.h" #include "user_entity.h" #include "faction_war_manager.h" // Client Sheets #include "client_sheets/player_sheet.h" // 3D #include "nel/3d/u_scene.h" #include "nel/3d/u_instance_material.h" #include "nel/3d/u_play_list.h" #include "nel/3d/u_bone.h" #include "nel/3d/u_particle_system_instance.h" #include "nel/3d/u_point_light.h" // game share #include "game_share/player_visual_properties.h" #include "game_share/gender.h" #include "game_share/bot_chat_types.h" /////////// // USING // /////////// using namespace NLMISC; using namespace NL3D; using namespace NLPACS; using namespace std; //////////// // EXTERN // //////////// extern UScene *Scene; extern CEntityAnimationManager *EAM; extern UTextContext *TextContext; extern UCamera MainCam; //----------------------------------------------- // CPlayerCL : // Constructor. //----------------------------------------------- CPlayerCL::CPlayerCL() : CCharacterCL() { Type = Player; // Resize _Instances to the number of visual slots. _Instances.resize(SLOTTYPE::NB_SLOT); // No sheet pointed. _Sheet = 0; _PlayerSheet = 0; // Some default colors. _HairColor = 0; _EyesColor = 0; // Not enough information to display the player. _WaitForAppearance = true; _PlayerCLAsyncTextureLoading= false; // Light Off and not allocated _LightOn = false; }// CPlayerCL // //----------------------------------------------- // ~CPlayerCL : // Destructor. //----------------------------------------------- CPlayerCL::~CPlayerCL() { // No more sheet pointed. _PlayerSheet = NULL; // Remove the light if(!_Light.empty()) { if(Scene) Scene->deletePointLight(_Light); } } //--------------------------------------------------- // getScale : // Return the entity scale. (return 1.0 if there is any problem). //--------------------------------------------------- float CPlayerCL::getScale() const // virtual { // Default Scale. return _CharacterScalePos; }// getScale // //----------------------------------------------- // getGroundFX : // retrieve ground fxs for the entity depending on the ground //----------------------------------------------- const std::vector *CPlayerCL::getGroundFX() const { switch (getGender()) { case 0: return &(_PlayerSheet->GenderInfos[0].GroundFX); case 1: return &(_PlayerSheet->GenderInfos[1].GroundFX); default: break; } return NULL; } //----------------------------------------------- // isNeutral : // Return true if this entity is a neutral entity(pvp or not) //----------------------------------------------- bool CPlayerCL::isNeutral() const { return (!isEnemy() && !isAlly()); } //----------------------------------------------- // isFriend : // Return true if this entity is a user's friend. //----------------------------------------------- bool CPlayerCL::isFriend () const { return isNeutral() || isAlly(); } //----------------------------------------------- // isEnemy : // true if at least enemy in on pvp mode //----------------------------------------------- bool CPlayerCL::isEnemy () const { // Challenge i.e. SOLO FULL PVP if( getPvpMode()&PVP_MODE::PvpChallenge || UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) { return true; } // if one of 2 players is not in pvp they can't be enemies if( UserEntity->getPvpMode() == PVP_MODE::None || getPvpMode() == PVP_MODE::None ) { return false; } // if one of 2 players is safe they can't be enemies if( UserEntity->getPvpMode()&PVP_MODE::PvpSafe || getPvpMode()&PVP_MODE::PvpSafe ) { return false; } // if one of 2 players are in safe zone and not flagged they can't be enemies if ((UserEntity->getPvpMode()&PVP_MODE::PvpZoneSafe && ((UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0)) || (getPvpMode()&PVP_MODE::PvpZoneSafe && ((getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0))) { return false; } // Duel if( getPvpMode()&PVP_MODE::PvpDuel && UserEntity->getPvpMode()&PVP_MODE::PvpDuel ) { return true; // TODO } // Outpost if ( isAnOutpostEnemy() ) { return true; } // Zone Free if( getPvpMode()&PVP_MODE::PvpZoneFree && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree ) { // If not in same Team and not in same League => ennemy if( !isInTeam() && !isInSameLeague() ) return true; } // Zone Guild if( getPvpMode()&PVP_MODE::PvpZoneGuild && UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild ) { // If in same Guild but different Leagues => ennemy if ( isInSameGuild() && oneInLeague() && !isInSameLeague() ) return true; if( !isInTeam() && !isInSameLeague() ) return true; } // Zone Faction if( getPvpMode()&PVP_MODE::PvpZoneFaction && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction ) { if (getPvpClan() != UserEntity->getPvpClan()) return true; } // Free PVP : Ennemis are not in team AND not in league if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { // If in same Guild but different Leagues => ennemy if ( isInSameGuild() && oneInLeague() && !isInSameLeague() ) return true; if (!isInTeam() && !isInSameLeague()) return true; } return false; } // isEnemy // //----------------------------------------------- // isAlly : // true if at least ally in one pvp mode //----------------------------------------------- bool CPlayerCL::isAlly() const { // Challenge i.e. SOLO FULL PVP if( getPvpMode()&PVP_MODE::PvpChallenge || UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) { return false; } // if one of 2 players is not in pvp they can't be allies if( UserEntity->getPvpMode() == PVP_MODE::None || getPvpMode() == PVP_MODE::None ) { return false; } // if one of 2 players is in safe zone and not other they can't be allies if ((UserEntity->getPvpMode()&PVP_MODE::PvpSafe) != (getPvpMode()&PVP_MODE::PvpSafe)) { return false; } // Outpost if ( isAnOutpostAlly() ) { return true; } // Zone Free if( getPvpMode()&PVP_MODE::PvpZoneFree && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree ) { if( isInTeam() || isInSameLeague() ) return true; } // Zone Guild if( getPvpMode()&PVP_MODE::PvpZoneGuild && UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild ) { if( isInTeam() || isInSameLeague() ) return true; if ( isInSameGuild() && !oneInLeague() ) return true; } // Zone Faction if( getPvpMode()&PVP_MODE::PvpZoneFaction && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction ) { if (getPvpClan() == UserEntity->getPvpClan()) return true; } // Free PVP : Allies are in team OR in league if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { if (isInTeam() || isInSameLeague()) return true; if ( isInSameGuild() && !oneInLeague() ) return true; } return false; } // isAlly // //----------------------------------------------- // isNeutralPVP : //----------------------------------------------- bool CPlayerCL::isNeutralPVP() const { // if player is not in pvp they can't be neutral pvp if( getPvpMode() == PVP_MODE::None ) { return false; } if( UserEntity->getPvpMode() == PVP_MODE::None ) { return false; } return (!isEnemy() && !isAlly()); } //----------------------------------------------- // build : // Build the entity from a sheet. //----------------------------------------------- bool CPlayerCL::build(const CEntitySheet *sheet) // virtual { // Cast the sheet in the right type. _PlayerSheet = dynamic_cast(sheet); if(_PlayerSheet==0) { pushDebugStr(NLMISC::toString("Player '%d' sheet is not a '.race_stats' -> BIG PROBLEM.", _Slot)); return false; } else pushInfoStr(NLMISC::toString("Player '%d' sheet is valid.", _Slot)); // Get the DB Entry if(IngameDbMngr.getNodePtr()) { CCDBNodeBranch *nodeRoot = dynamic_cast(IngameDbMngr.getNodePtr()->getNode(0)); if(nodeRoot) { _DBEntry = dynamic_cast(nodeRoot->getNode(_Slot)); if(_DBEntry == 0) pushDebugStr("Cannot get a pointer on the DB entry."); } } // Compute the first automaton. _CurrentAutomaton = automatonType() + "_normal.automaton"; // Initialize the player look. init3d(); // Compute the primitive initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone); // Create the collision entity (used to snap the entity to the ground). computeCollisionEntity(); // Initialize properties of the client. initProperties(); // Entity Created. return true; }// build // //----------------------------------------------- // automatonType : // Return the automaton type of the entity (homin, creature, etc.) //----------------------------------------------- std::string CPlayerCL::automatonType() const // virtual { return _PlayerSheet->Automaton; }// automatonType // //----------------------------------------------- // init3d : // Initialize the graphic for the player. //----------------------------------------------- void CPlayerCL::init3d() { createPlayList(); // Initialize the internal time. _LastFrameTime = ((double)T1) * 0.001; }// init3d // //----------------------------------------------- // initProperties : // Initialize properties of the entity (according to the class). //----------------------------------------------- void CPlayerCL::initProperties() { properties().selectable(true); properties().attackable(false); properties().givable(true); properties().invitable(true); properties().canExchangeItem(true); }// initProperties // //----------------------------------------------- // equip : // Set the equipmenent worn. //----------------------------------------------- void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, const std::string &shapeName, const CItemSheet *item) { // Check slot. if(slot == SLOTTYPE::HIDDEN_SLOT || slot >= SLOTTYPE::NB_SLOT) { nlwarning("CCharacterCL::equip : slot %d is not valid.", (uint)slot); return; } uint s = (uint)slot; // If exactly the same than before -> return if (!_Instances[s].Loading.empty()) { if ((_Instances[s].LoadingName == shapeName) && (_Instances[s].FXItemSheet == item)) return; } else if (!_Instances[s].Current.empty()) { if ((_Instances[s].CurrentName == shapeName) && (_Instances[s].FXItemSheet == item)) return; } // Attach to the skeleton. string stickPoint; if(!_Skeleton.empty()) { switch(slot) { case SLOTTYPE::RIGHT_HAND_SLOT: if( item && item->ItemType != ITEM_TYPE::MAGICIAN_STAFF ) stickPoint = "box_arme"; break; case SLOTTYPE::LEFT_HAND_SLOT: if(_Items[slot].Sheet && _Items[slot].Sheet->getAnimSet()=="s") stickPoint = "Box_bouclier"; else stickPoint = "box_arme_gauche"; break; default: break; } } /* If the object is sticked (ie not a skin), decide to delete the Current instance. Why? because the animation is changed according to the equipped item. Hence, For example, if a sword would be changed for a gun, then the new gun animation would take place, while Keeping the old sword shape. BAD. */ if(!stickPoint.empty()) _Instances[s].createLoading(string(), stickPoint); // Create the instance. if(item) _Instances[s].createLoading(shapeName, stickPoint, item->MapVariant); else _Instances[s].createLoading(shapeName, stickPoint); // If shapeName is empty, only clear the slot if(shapeName.empty()) { _Items[slot].release(); return; } if(!_Instances[s].Loading.empty()) { _Instances[s].FXItemSheet = item; _Items[slot].initFXs(slot, _Instances[s].Loading); } else nlwarning("PL::equip(1):%d: cannot create the instance '%s'.", _Slot, shapeName.c_str()); if (!item && (slot != SLOTTYPE::RIGHT_HAND_SLOT) && (slot != SLOTTYPE::LEFT_HAND_SLOT)) applyColorSlot(_Instances[s], skin(), 6, _HairColor, _EyesColor); }// equip // //----------------------------------------------- // equip : // Compute the equipmenent worn. //----------------------------------------------- void CPlayerCL::equip(SLOTTYPE::EVisualSlot slot, uint index, uint color) { // Get the sheet according to the visual slot _Items[slot].Sheet = SheetMngr.getItem(slot, index); if(_Items[slot].Sheet) { const CItemSheet *item = _Items[slot].Sheet; std::string shapeName = _Gender == GSGENDER::female ? item->getShapeFemale():item->getShape(); // use the right type of boots if wearing a caster dress if ((slot == SLOTTYPE::FEET_SLOT) && (item->ItemType == ITEM_TYPE::LIGHT_BOOTS)) { std::string shapeLegs; if (!_Instances[SLOTTYPE::LEGS_SLOT].Loading.empty()) { shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].LoadingName; } else if (!_Instances[SLOTTYPE::LEGS_SLOT].Current.empty()) { shapeLegs = _Instances[SLOTTYPE::LEGS_SLOT].CurrentName; } if (!shapeLegs.empty() && shapeLegs.find("_caster01_") != std::string::npos) { std::string tmpName = toLower(shapeName); std::string::size_type posBottes = tmpName.find("_bottes"); if (posBottes != std::string::npos) { std::string orgType = tmpName.substr(7, posBottes-7); // underwear, caster01, armor00 or armor01 tmpName.replace(posBottes+7, 0, "_" + orgType); tmpName.replace(7, orgType.length(), "caster01"); // temporary hack because Fyros boots don't respect conventions if (tmpName[0] == 'f') { if (tmpName[5] == 'f') { tmpName = "fy_hof_caster01_bottes_civil.shape"; } else { tmpName = "fy_hom_caster01_civil01_bottes.shape"; } } // use fixed shape name only if file is present if (CPath::exists(tmpName)) { shapeName = tmpName; } else { nlwarning("File %s doesn't exist, use %s", tmpName.c_str(), shapeName.c_str()); } } } } // If the gender is a female get the right shape else get the default shape. equip(slot, shapeName, item); // Check there is a shape. UInstance pInst = _Instances[slot].createLoadingFromCurrent(); if(!pInst.empty()) { // Set the right texture variation (quality). pInst.selectTextureSet((uint)item->MapVariant); _Instances[slot].TextureSet = item->MapVariant; // If Hair, color is for the head slot. if(slot == SLOTTYPE::HEAD_SLOT && item->Family != ITEMFAMILY::ARMOR) applyColorSlot(_Instances[slot], skin(), 0, color, _EyesColor); else { // Set the User Color. if(item->Color == -1) applyColorSlot(_Instances[slot], skin(), color, _HairColor, _EyesColor); // Set the Item Color. else if(item->Color != -2) applyColorSlot(_Instances[slot], skin(), item->Color, _HairColor, _EyesColor); // Else let the default color. else applyColorSlot(_Instances[slot], skin(), 0, _HairColor, _EyesColor); } } } // Default equipment. else { nlwarning("PL:equip(2):%d: VS '%d' default equipement used.", _Slot, slot); sint idx = SheetMngr.getVSIndex(_PlayerSheet->GenderInfos[_Gender].Items[slot], slot); if(idx != -1) { if(SheetMngr.getItem(slot, (uint)idx)) { // If the gender is a female get the right shape. if(_Gender == GSGENDER::female) equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShapeFemale()); // Else get the default shape. else equip(slot, SheetMngr.getItem(slot, (uint)idx)->getShape()); } } } }// equip // //----------------------------------------------- // computeAnimSet : // Compute the animation set to use according to weapons, mode and race. //----------------------------------------------- void CPlayerCL::computeAnimSet() { // We need a valid Gender to compute the animset. if(_Gender >= 2) { nlwarning("PL:computeAnimSet:%d: not a male or a female (gender=%d).", _Slot, _Gender); return; } // Now computing the animset. // Do not count weapons if swimming. if(!::computeAnimSet(_CurrentAnimSet[MOVE], _Mode, _PlayerSheet->GenderInfos[_Gender].AnimSetBaseName, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet, !modeWithHiddenItems())) nlwarning("PL:computeAnimSet:%d: pb when computing the animset.", _Slot); }// computeAnimSet // //----------------------------------------------- // updateVisualPropertyVpa : // Update the Visual Property A. // \todo GUIGUI : use gender enum //----------------------------------------------- void CPlayerCL::updateVisualPropertyVpa(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { CInterfaceManager *IM = CInterfaceManager::getInstance (); // Player will now have enough information to display the character. _WaitForAppearance = false; // Get the property. SPropVisualA visualA = *(SPropVisualA *)(&prop); // GENDER _Gender = (GSGENDER::EGender)(visualA.PropertySubData.Sex); if(_Gender!=GSGENDER::male && _Gender!=GSGENDER::female) { nlwarning("PL::updateVPVpa:%d: neither a male nor a female -> male selected.", _Slot); _Gender = GSGENDER::male; } // update title when gender changed const ucstring replacement(STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw, _Gender == GSGENDER::female)); if (!replacement.empty() || !ClientCfg.DebugStringManager) { // Get extended name _NameEx = replacement; _Title = replacement; // display the new title in player interface if (_Slot == 0) { CViewText *pVT = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface:player:header_opened:player_title")); if (pVT != NULL) pVT->setText(_Title); } // rebuild in scene interface buildInSceneInterface(); } // Setup _CharacterScalePos _CharacterScalePos = _PlayerSheet->GenderInfos[_Gender].CharacterScalePos; // Check if skeleton has changed if (_CacheSkeletonShapeName != _PlayerSheet->GenderInfos[_Gender].Skelfilename) { _CacheSkeletonShapeName = _PlayerSheet->GenderInfos[_Gender].Skelfilename; // Clean the playlist. if(_PlayList) _PlayList->resetAllChannels(); // We can now build the skeleton so do it now. skeleton(_CacheSkeletonShapeName); // Invalidate instances cache for (uint i = 0; i < _Instances.size(); ++i) _Instances[i].CurrentName = _Instances[i].LoadingName = ""; _Face.CurrentName = _Face.LoadingName = ""; } // Check the skeleton. if(skeleton() && !ClientCfg.Light) { // To re-link the skeleton to the mount if needed. parent(parent()); // Set the skeleton scale. // \todo GUIGUI: mettre le scale aussi dans race_stats. // Setup Lod Character skeleton, if skeleton exist // Get Lod Character Id from the sheet. sint clodId= getLodCharacterId(*Scene, _PlayerSheet->GenderInfos[_Gender].LodCharacterName); if(clodId>=0) { // Setup Lod Character shape and distance skeleton()->setLodCharacterShape(clodId); skeleton()->setLodCharacterDistance(_PlayerSheet->GenderInfos[_Gender].LodCharacterDistance); } // Compute the computeSomeBoneId(); // CHEST equip(SLOTTYPE::CHEST_SLOT, visualA.PropertySubData.JacketModel, visualA.PropertySubData.JacketColor); // LEGS equip(SLOTTYPE::LEGS_SLOT, visualA.PropertySubData.TrouserModel, visualA.PropertySubData.TrouserColor); // ARMS equip(SLOTTYPE::ARMS_SLOT, visualA.PropertySubData.ArmModel, visualA.PropertySubData.ArmColor); // HAT equip(SLOTTYPE::HEAD_SLOT, visualA.PropertySubData.HatModel, visualA.PropertySubData.HatColor); // OBJECT in the RIGHT HAND _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, visualA.PropertySubData.WeaponRightHand); // Equip the weapon(object/tool). if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet) { if(_Gender == GSGENDER::female) equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShapeFemale(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet); else equip(SLOTTYPE::RIGHT_HAND_SLOT, _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet); NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current; if (!itemInstance.empty()) { // update fxs _Items[SLOTTYPE::RIGHT_HAND_SLOT].enableAdvantageFX(itemInstance); if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING ) _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(0); //_Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualA.PropertySubData.RTrail); } } else { // No Valid item in the right hand. equip(SLOTTYPE::RIGHT_HAND_SLOT, ""); } // OBJECT in the LEFT HAND _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet = SheetMngr.getItem(SLOTTYPE::LEFT_HAND_SLOT, visualA.PropertySubData.WeaponLeftHand); // Equip the weapon(object/tool). if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet) { equip(SLOTTYPE::LEFT_HAND_SLOT, _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet->getShape(), _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet); NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current; if (!itemInstance.empty()) { // update fxs _Items[SLOTTYPE::LEFT_HAND_SLOT].enableAdvantageFX(itemInstance); _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(0); //_Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualA.PropertySubData.LTrail); } } else { // No Valid item in the left hand. equip(SLOTTYPE::LEFT_HAND_SLOT, ""); } // Create face // Only create a face when there is no Helmet if(_Items[SLOTTYPE::HEAD_SLOT].Sheet == 0 || _Items[SLOTTYPE::HEAD_SLOT].Sheet->Family != ITEMFAMILY::ARMOR) { CItemSheet *faceItem = getItem(_PlayerSheet->GenderInfos[_Gender], SLOTTYPE::FACE_SLOT); if (faceItem) { string sFaceName; if(_Gender == GSGENDER::female) sFaceName = faceItem->getShapeFemale(); else sFaceName = faceItem->getShape(); if (((!_Face.Loading.empty()) && (_Face.LoadingName != sFaceName)) || ((!_Face.Current.empty()) && (_Face.CurrentName != sFaceName)) || (_Face.Current.empty())) { if (!_Face.Loading.empty()) { Scene->deleteInstance(_Face.Loading); _Face.Loading = NULL; _Face.LoadingName = sFaceName; } _Face.Loading = Scene->createInstance(sFaceName); if (!_Face.Loading.empty()) { _Face.LoadingName = sFaceName; if(!skeleton()->bindSkin(_Face.Loading)) nlwarning("PL::updateVPVpa:%d: Cannot bind the face.", _Slot); _Face.Loading.hide(); // set it async for texture _Face.Loading.enableAsyncTextureMode(true); } else nlwarning("PL::updateVPVpa:%d: Cannot create the face.", _Slot); } _Face.TextureSet = faceItem->MapVariant; applyColorSlot(_Face, skin(), 0, visualA.PropertySubData.HatColor, 0); // Set a default ruflaket color. } else nlwarning("PL::updateVPVpa:%d: Face Item '%s' does not exist.", _Slot, _PlayerSheet->GenderInfos[_Gender].Items[SLOTTYPE::FACE_SLOT].c_str()); } else { // There is a helmet ! if (!_Face.Loading.empty()) Scene->deleteInstance(_Face.Loading); _Face.Loading = NULL; _Face.LoadingName = ""; if (!_Face.Current.empty()) Scene->deleteInstance(_Face.Current); _Face.Current = NULL; _Face.CurrentName = ""; } // Now we have a skeleton, we can update VpB and VpC. sint64 vB, vC; string propName; propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPB); vB = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64(); propName = toString("SERVER:Entities:E%d:P%d", _Slot, CLFECOMMON::PROPERTY_VPC); vC = NLGUI::CDBManager::getInstance()->getDbProp(propName)->getValue64(); updateVisualPropertyVpb(0, vB); updateVisualPropertyVpc(0, vC); // Attach The Light if there is one. if(!_Light.empty() && _NameBoneId!=-1) _Skeleton.stickObject(_Light, _NameBoneId); // Compute the new animation set to use (due to weapons). computeAnimSet(); // Set the animation to idle. setAnim(CAnimationStateSheet::Idle); } // No skeleton else nlwarning("PL::updateVPVpa:%d: Skeleton not allocated.", _Slot); }// updateVisualPropertyVpa // //----------------------------------------------- // updateVisualPropertyVpb : // Update the Visual Property B. //----------------------------------------------- void CPlayerCL::updateVisualPropertyVpb(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { // Get the property. SPropVisualB visualB = *(SPropVisualB *)(&prop); if(_Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet) { NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Loading : _Instances[SLOTTYPE::RIGHT_HAND_SLOT].Current; if (!itemInstance.empty()) { // update fxs if ( _CurrentBehaviour.Behaviour != MBEHAV::EXTRACTING ) _Items[SLOTTYPE::RIGHT_HAND_SLOT].setTrailSize(visualB.PropertySubData.RTrail); } } if(_Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet) { NL3D::UInstance itemInstance = (!_Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading.empty()) ? _Instances[SLOTTYPE::LEFT_HAND_SLOT].Loading : _Instances[SLOTTYPE::LEFT_HAND_SLOT].Current; if (!itemInstance.empty()) { // update fxs _Items[SLOTTYPE::LEFT_HAND_SLOT].setTrailSize(2 * (uint) visualB.PropertySubData.LTrail); } } if(skeleton()) { // HANDS equip(SLOTTYPE::HANDS_SLOT, visualB.PropertySubData.HandsModel, visualB.PropertySubData.HandsColor); // FEET equip(SLOTTYPE::FEET_SLOT, visualB.PropertySubData.FeetModel, visualB.PropertySubData.FeetColor); } else nlinfo("PL::updateVPVpb:%d: Prop Vpb received before prop Vpa.", _Slot); }// updateVisualPropertyVpb // //----------------------------------------------- // updateVisualPropertyVpc : // Update the Visual Property C. // \todo GUIGUI : factorize tatoos with character creation //----------------------------------------------- void CPlayerCL::updateVisualPropertyVpc(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { if(skeleton()) { // Get the property. SPropVisualC visualC = *(SPropVisualC *)(&prop); // EYES _EyesColor = visualC.PropertySubData.EyesColor; UInstance inst; // must recreate the face asynchronously (because of color change / makeup change) inst= _Face.createLoadingFromCurrent(); // if exist if (!inst.empty()) { // change eyes color only applyColorSlot(_Face, _Face.ACSkin, _Face.ACUser, _Face.ACHair, visualC.PropertySubData.EyesColor); // Tattoo makeUp(inst, visualC.PropertySubData.Tattoo); // Morph static const char *baseName = "visage_00"; float MTmin, MTmax; const CGenderInfo *pGI = &_PlayerSheet->GenderInfos[_Gender]; if (pGI == NULL) return; MTmin = pGI->BlendShapeMin[0]; MTmax = pGI->BlendShapeMax[0]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(0), (float)(visualC.PropertySubData.MorphTarget1) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[1]; MTmax = pGI->BlendShapeMax[1]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(1), (float)(visualC.PropertySubData.MorphTarget2) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[2]; MTmax = pGI->BlendShapeMax[2]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(2), (float)(visualC.PropertySubData.MorphTarget3) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[3]; MTmax = pGI->BlendShapeMax[3]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(3), (float)(visualC.PropertySubData.MorphTarget4) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[4]; MTmax = pGI->BlendShapeMax[4]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(4), (float)(visualC.PropertySubData.MorphTarget5) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[5]; MTmax = pGI->BlendShapeMax[5]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(5), (float)(visualC.PropertySubData.MorphTarget6) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[6]; MTmax = pGI->BlendShapeMax[6]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(6), (float)(visualC.PropertySubData.MorphTarget7) / 7.f * (MTmax-MTmin) + MTmin, true); MTmin = pGI->BlendShapeMin[7]; MTmax = pGI->BlendShapeMax[7]; if (!ClientCfg.BlendShapePatched) { MTmin = 0.0f; MTmax = 100.0f; } inst.setBlendShapeFactor(baseName + toString(7), (float)(visualC.PropertySubData.MorphTarget8) / 7.f * (MTmax-MTmin) + MTmin, true); } // Set the Gabarit float characterHeight = (float)((sint8)(visualC.PropertySubData.CharacterHeight)-7)/7.f; float torsoWidth = (float)((sint8)(visualC.PropertySubData.TorsoWidth)-7)/7.f; float armsWidth = (float)((sint8)(visualC.PropertySubData.ArmsWidth)-7)/7.f; float legsWidth = (float)((sint8)(visualC.PropertySubData.LegsWidth)-7)/7.f; float breastSize = (float)((sint8)(visualC.PropertySubData.BreastSize)-7)/7.f; float heightScale, baseHeightScale; // TODO : manage breast size GabaritSet.applyGabarit(*skeleton(), _Gender, people(), characterHeight, torsoWidth, armsWidth, legsWidth, breastSize, &heightScale); baseHeightScale = GabaritSet.getRefHeightScale(_Gender, people()); if(baseHeightScale != 0.f) _CustomScalePos = heightScale/baseHeightScale; else { _CustomScalePos = 1.f; nlwarning("PL::updateVPVpc:'%d': baseHeight == 0.", _Slot); } } else nlinfo("PL:updateVPVpc:'%d': Prop Vpc received before prop Vpa.", _Slot); }// updateVisualPropertyVpc // //----------------------------------------------- //----------------------------------------------- void CPlayerCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &gameCycle, const sint64 &prop) { CCharacterCL::updateVisualPropertyPvpMode(gameCycle, prop); // Additionaly, if i am the target, inform interface of the change if(isTarget()) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); CCDBNodeLeaf *pDB= NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:USER:TRACK_TARGET_PVP_CHANGE_MODE"); if(pDB) { sint32 val= pDB->getValue32(); pDB->setValue32(val+1); } } } //----------------------------------------------- // skin : // Get The Entity Skin //----------------------------------------------- sint CPlayerCL::skin() const // virtual { return _PlayerSheet->Skin; }// skin // //----------------------------------------------- // people : // Return the People for the entity. //----------------------------------------------- EGSPD::CPeople::TPeople CPlayerCL::people() const// virtual { if(_PlayerSheet) return _PlayerSheet->People; else return EGSPD::CPeople::Unknown; }// people // //----------------------------------------------- // people : // Setup the People for the entity. //----------------------------------------------- void CPlayerCL::setPeople(EGSPD::CPeople::TPeople /* people */) { }// people // //----------------------------------------------- // drawName : // Draw the name. //----------------------------------------------- void CPlayerCL::drawName(const NLMISC::CMatrix &mat) // virtual { // Draw the name. if(!getEntityName().empty()) TextContext->render3D(mat, getEntityName()); }// drawName // //----------------------------------------------- // getFace : // Update eyes blink. For the moment, called by updatePos. //----------------------------------------------- CEntityCL::SInstanceCL *CPlayerCL::getFace() { // Implemented in CPlayerCL return &_Face; }// getFace // //----------------------------------------------- // attackRadius : // Method to return the attack radius of an entity (take the scale into account). //----------------------------------------------- double CPlayerCL::attackRadius() const // virtual { return 0.5; }// attackRadius // //----------------------------------------------- // Return the position the attacker should have to combat according to the attack angle. // \param ang : 0 = the front, >0 and -Pi = right side. //----------------------------------------------- CVectorD CPlayerCL::getAttackerPos(double ang, double dist) const { // Compute the local angle ang = computeShortestAngle(atan2(front().y, front().x), ang); ang += Pi; if(ang > Pi) ang -= 2*Pi; // Compute the local position. CVectorD p; 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. p.y = 0.5 * cos(ang) + dist*cos(ang); p.z = 0.0; // Compute the world position. // Create the target matrix. CVector vj = front(); vj.z = 0; CVector vk(0,0,1); CVector vi = vj^vk; CMatrix bodyBase; bodyBase.setRot(vi,vj,vk,true); bodyBase.setPos(pos()); // Get the destination in the world. return bodyBase * p; }// getAttackerPos // /////////////// // 3D SYSTEM // /////////////// //----------------------------------------------- // updateAsyncTexture //----------------------------------------------- float CPlayerCL::updateAsyncTexture() { // Call parent. float distToCam= CCharacterCL::updateAsyncTexture(); // Check all instance to know if they need to start async load their textures if(!_Face.Loading.empty()) { // dirty? if(_Face.Loading.isAsyncTextureDirty()) { // reset instance state. _Face.Loading.setAsyncTextureDirty(false); // must start loading for this isntance _Face.Loading.startAsyncTextureLoading(); // the entity is now currently loading. _PlayerCLAsyncTextureLoading= true; // The LodTexture need to be recomputed _LodTextureDirty= true; } } // Update Async Texture loading of all instances. if(_PlayerCLAsyncTextureLoading) { bool allLoaded= true; // update loading for all instances. if(!_Face.Loading.empty()) { // update async texture loading allLoaded= allLoaded && _Face.Loading.isAsyncTextureReady(); } // if all are loaded, then End! don't need to check all instances every frame. if(allLoaded) { _PlayerCLAsyncTextureLoading= false; _Face.updateCurrentFromLoading(_Skeleton); } } // For LOD texture, must update the "texture distance" if(!_Face.Current.empty()) { // update async texture loading _Face.Current.setAsyncTextureDistance(distToCam); } return distToCam; } //----------------------------------------------- // updateLodTexture //----------------------------------------------- void CPlayerCL::updateLodTexture() { // if need to recompute, and if Async loading ended if( _LodTextureDirty && !_PlayerCLAsyncTextureLoading ) // check parent and upadte lod CCharacterCL::updateLodTexture(); } //----------------------------------------------- // getMaxSpeed : // Return the basic max speed for the entity in meter per sec //----------------------------------------------- double CPlayerCL::getMaxSpeed() const// virtual { return 6.0f; }// getMaxSpeed // //--------------------------------------------------- // displayDebug : // Display Debug Information. //--------------------------------------------------- void CPlayerCL::displayDebug(float x, float &y, float lineStep) // virtual { CCharacterCL::displayDebug(x, y, lineStep); }// displayDebug // //--------------------------------------------------- // readWrite : // Read/Write Variables from/to the stream. //--------------------------------------------------- void CPlayerCL::readWrite(class NLMISC::IStream &f) throw(NLMISC::EStream) { CCharacterCL::readWrite(f); // PUBLIC // PROTECTED // const CPlayerSheet *_Sheet; // const CRaceStatsSheet *_PlayerSheet; // NL3D::UInstance _Face; f.serial(_DefaultChest); f.serial(_DefaultLegs); f.serial(_DefaultArms); f.serial(_DefaultHands); f.serial(_DefaultFeet); f.serial(_DefaultHair); f.serial(_HairColor); f.serial(_EyesColor); f.serial(_WaitForAppearance); f.serial(_PlayerCLAsyncTextureLoading); f.serial(_LightOn); // NL3D::UPointLight _Light; // PRIVATE }// readWrite // //--------------------------------------------------- // load : // To call after a read from a stream to re-initialize the entity. //--------------------------------------------------- void CPlayerCL::load() // virtual { CInterfaceManager *IM = CInterfaceManager::getInstance (); // If the entity should be in the world already if(_First_Pos == false) { // Insert the primitive into the world. if(_Primitive) _Primitive->insertInWorldImage(dynamicWI); // Insert the entity into PACS pacsPos(pos()); } // update if(!_WaitForAppearance) { // Visual properties A sint64 prop = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", _Slot)+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64(); updateVisualPropertyVpa(0, prop); // Vpa udapte vpb and vpc too. } }// load // // ********************************************************************************************* const char *CPlayerCL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const { if (!_PlayerSheet) return CCharacterCL::getBoneNameFromBodyPart(part, side); return _PlayerSheet->BodyToBone.getBoneName(part, side); } // ********************************************************************************************* const CItemSheet *CPlayerCL::getRightHandItemSheet() const { return _Items[SLOTTYPE::RIGHT_HAND_SLOT].Sheet; } // ********************************************************************************************* const CItemSheet *CPlayerCL::getLeftHandItemSheet() const { return _Items[SLOTTYPE::LEFT_HAND_SLOT].Sheet; } // ********************************************************************************************* const CAttack *CPlayerCL::getAttack(const CAttackIDSheet &id) const { if (!_PlayerSheet) return NULL; return CCharacterCL::getAttack(id, _PlayerSheet->AttackLists); } // ********************************************************************************************* float CPlayerCL::getScaleRef() const { float fyrosRefScale = GabaritSet.getRefHeightScale(0, EGSPD::CPeople::Fyros); if (fyrosRefScale == 0) return 1.f; return _CustomScalePos * (GabaritSet.getRefHeightScale(_Gender, people()) / fyrosRefScale); } // ********************************************************************************************* float CPlayerCL::getNamePosZ() const { if (!_PlayerSheet) return 0.f; float namePosZ; switch (_ModeWanted) { case MBEHAV::DEATH: case MBEHAV::SIT: namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZLow; break; case MBEHAV::MOUNT_NORMAL: case MBEHAV::MOUNT_SWIM: namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZHigh; break; default: namePosZ = _PlayerSheet->GenderInfos[_Gender].NamePosZNormal; break; } return namePosZ * _CharacterScalePos * _CustomScalePos; } // *************************************************************************** void CPlayerCL::doSetVisualSelectionBlink(bool bOnOff, NLMISC::CRGBA emitColor) { // Do it on Face if(bOnOff) _Face.setEmissive(emitColor); else _Face.restoreEmissive(); // and parent call CCharacterCL::doSetVisualSelectionBlink(bOnOff, emitColor); } //----------------------------------------------- // computePrimitive : // Create (or re-create) a primitive. //----------------------------------------------- void CPlayerCL::computePrimitive() { // Initialize the primitive. initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColPlayer, MaskColNone); if(_Primitive) _Primitive->insertInWorldImage(dynamicWI); // Set the position. pacsPos(pos()); }// computePrimitive //