// 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" // Sheets #include "client_sheets/item_sheet.h" // Misc #include "nel/misc/vectord.h" #include "nel/misc/vector_2f.h" #include "nel/misc/bsphere.h" // Interface 3D #include "nel/3d/u_driver.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_material.h" #include "nel/3d/u_visual_collision_manager.h" #include "nel/3d/u_play_list.h" #include "nel/3d/u_animation_set.h" #include "nel/3d/u_bone.h" #include "nel/3d/u_track.h" #include "nel/3d/u_instance_material.h" #include "nel/3d/material.h" // for advanced material usage // Pacs Interface #include "nel/pacs/u_global_position.h" // Client #include "entity_cl.h" #include "entity_animation_manager.h" #include "pacs_client.h" #include "ig_client.h" #include "debug_client.h" #include "time_client.h" #include "entities.h" #include "ingame_database_manager.h" #include "debug_client.h" #include "misc.h" #include "client_cfg.h" #include "interface_v3/action_handler.h" #include "interface_v3/interface_manager.h" #include "interface_v3/group_container.h" #include "interface_v3/guild_manager.h" #include "interface_v3/skill_manager.h" #include "user_entity.h" #include "interface_v3/people_interraction.h" #include "view.h" #include "color_slot_manager.h" #include "string_manager_client.h" #include "interface_v3/bar_manager.h" #include "continent_manager.h" #include "connection.h" #include "weather.h" #include "npc_icon.h" // Game share #include "game_share/mission_desc.h" #include "game_share/inventories.h" #include "game_share/animal_type.h" #include "interface_v3/group_in_scene.h" // #include "r2/editor.h" /////////// // USING // /////////// using namespace NLMISC; using namespace NL3D; using namespace NLPACS; using namespace std; using namespace CLFECOMMON; //////////// // EXTERN // //////////// extern UDriver *Driver; extern UScene *Scene; extern UVisualCollisionManager *CollisionManager; extern CEntityAnimationManager *EAM; extern UCamera MainCam; extern UTextContext *TextContext; extern CLFECOMMON::TCLEntityId SlotUnderCursor; extern CContinentManager ContinentMngr; extern bool ShowInterface; //////////// // GLOBAL // //////////// #define RYZOM_EPSILON_SQRT_POSITION (1.f*1.f) NLMISC::CRGBA CEntityCL::_EntitiesColor[TypeCount]; NLMISC::CRGBA CEntityCL::_DeadColor; NLMISC::CRGBA CEntityCL::_TargetColor; NLMISC::CRGBA CEntityCL::_GroupColor; NLMISC::CRGBA CEntityCL::_GuildColor; NLMISC::CRGBA CEntityCL::_UserMountColor; NLMISC::CRGBA CEntityCL::_UserPackAnimalColor; NLMISC::CRGBA CEntityCL::_PvpEnemyColor; NLMISC::CRGBA CEntityCL::_PvpNeutralColor; NLMISC::CRGBA CEntityCL::_PvpAllyInTeamColor; NLMISC::CRGBA CEntityCL::_PvpAllyInGuildColor; NLMISC::CRGBA CEntityCL::_PvpAllyColor; NLMISC::CRGBA CEntityCL::_GMTitleColor[CHARACTER_TITLE::EndGmTitle - CHARACTER_TITLE::BeginGmTitle + 1]; uint8 CEntityCL::_InvalidGMTitleCode = 0xFF; NLMISC::CRefPtr CEntityCL::_OpacityMinNodeLeaf; // Context help extern void contextHelp (const std::string &help); ///////////// // METHODS // ///////////// //----------------------------------------------- // showStaticFXs // //----------------------------------------------- void CEntityCL::SInstanceCL::showStaticFXs() { for(std::vector::iterator it = StaticFXs.begin(); it != StaticFXs.end(); ++it) { if (!it->empty()) { (*it).show(); } } } // showStaticFXs // //----------------------------------------------- // hideStaticFXs // //----------------------------------------------- void CEntityCL::SInstanceCL::hideStaticFXs() { for(std::vector::iterator it = StaticFXs.begin(); it != StaticFXs.end(); ++it) { if (!it->empty()) { (*it).hide(); } } } // hideStaticFXs // //--------------------------------------------------- //--------------------------------------------------- bool CEntityCL::SInstanceCL::createLoading(const string &strShapeName, const string &strStickPoint, sint texture, bool clearIfFail) { // create the new instance UInstance newInst; if(!strShapeName.empty()) { newInst= Scene->createInstance(strShapeName); // if fails to create and not clearIfFail, return if(newInst.empty() && !clearIfFail) return false; } // Remove the old loading instance. if(!Loading.empty()) { Scene->deleteInstance(Loading); LoadingName = ""; } // if the new instance is NULL, then clean ALL if( newInst.empty() ) { if(!Current.empty()) { releaseStaticFXs(); Scene->deleteInstance(Current); CurrentName = ""; } } // else setup into loading else { Loading = newInst; LoadingName = strShapeName; TextureSet = texture; StickPoint = strStickPoint; Loading.hide(); // Select the texture. if(texture != -1) { // Set the right texture variation. Loading.selectTextureSet((uint) texture); } else { Loading.selectTextureSet(computeCurrSeason()); } // Set Scale (relatively from scale exported by artists) Loading.setRelativeScale(_Scale); // Set async loading Texture Mode (faster loading) Loading.enableAsyncTextureMode(true); } // ok only if instance created or if shapeName empty return !newInst.empty() || strShapeName.empty(); } //--------------------------------------------------- //--------------------------------------------------- void CEntityCL::SInstanceCL::setColors(sint skin, sint user, sint hair, sint eyes) { ApplyColor = true; ACSkin = skin; ACUser = user; ACHair = hair; ACEyes = eyes; } //--------------------------------------------------- //--------------------------------------------------- void CEntityCL::SInstanceCL::setScale(const CVector &scale) { // Bkup for new created loading instances _Scale= scale; // Set Scale to any created ones (relatively from scale exported by artists) if(!Current.empty()) Current.setRelativeScale(_Scale); if(!Loading.empty()) Loading.setRelativeScale(_Scale); } //--------------------------------------------------- //--------------------------------------------------- NL3D::UInstance CEntityCL::SInstanceCL::createLoadingFromCurrent() { if (!Loading.empty()) return Loading; if (Current.empty()) return NULL; createLoading(CurrentName, StickPoint, TextureSet); if (ApplyColor) { CColorSlotManager::TIntCouple array[4]; // Skin array[0].first = (uint)0; array[0].second = (uint)ACSkin; // User Color array[1].first = (uint)1; array[1].second = (uint)ACUser; // Hair Color array[2].first = (uint)2; array[2].second = (uint)ACHair; // Eyes Color array[3].first = (uint)3; array[3].second = (uint)ACEyes; if (!Loading.empty()) ColorSlotManager.setInstanceSlot(Loading, array, 4); } // TODO : there is probably a bug for FX here : if we want to create a loading instance from the // current instance and there are some FXs binded to the current instance we have to : // 1 - recreate them onto the loading instance // or // 2 - unbind them from the current instance and rebind them to the loading instance return Loading; } //--------------------------------------------------- void CEntityCL::SInstanceCL::releaseStaticFXs() { if (Scene) { for(std::vector::iterator it = StaticFXs.begin(); it != StaticFXs.end(); ++it) { if (!it->empty()) { Scene->deleteInstance(*it); } } } StaticFXs.clear(); } //--------------------------------------------------- bool CEntityCL::isAsyncLoading() const { for(uint k = 0; k < _Instances.size(); ++k) { if (!_Instances[k].Loading.empty()) return true; } return false; } void CEntityCL::SInstanceCL::selectTextureSet(uint8 value, bool async) { if (!Loading.empty()) { Loading.selectTextureSet(value); } else if (!Current.empty()) { Current.enableAsyncTextureMode(async); Current.selectTextureSet(value); if (async) { // NB: async case for this function not tested yet! Current.startAsyncTextureLoading(); } } } //--------------------------------------------------- void CEntityCL::SInstanceCL::updateCurrentFromLoading(NL3D::USkeleton Skeleton) { if(Loading.empty()) return; bool bShow = true; if (!Current.empty()) { // copy temp info bShow = (Current.getVisibility() == UInstance::Show); } // Delete current instances if (!Current.empty()) { Scene->deleteInstance(Current); CurrentName = ""; } // Assign loading to current Current = Loading; CurrentName = LoadingName; Loading = NULL; LoadingName = ""; // If there is a skeleton, bind the skin to the skeleton. if(!Skeleton.empty()) { // No Stick Point, try to bind. if(StickPoint.empty()) { if(!Skeleton.bindSkin(Current)) nlwarning("Cannot bind the skin %s to the skeleton in the slot.", CurrentName.c_str()); } // Try to Stick Object. else { sint stickID = Skeleton.getBoneIdByName(StickPoint); if(stickID != -1) Skeleton.stickObject(Current, stickID); } } // Show current instance if(bShow) { if(KeepHiddenWhenLoaded == false) { Current.show(); } else KeepHiddenWhenLoaded = false; } releaseStaticFXs(); // Add static fxs if (FXItemSheet && Scene) { uint numStaticFX = FXItemSheet->FX.getNumStaticFX(); StaticFXs.reserve(numStaticFX); for(uint k = 0; k < numStaticFX; ++k) { std::string boneName = FXItemSheet->FX.getStaticFXBone(k); std::string name = FXItemSheet->FX.getStaticFXName(k); if (!boneName.empty() && !name.empty()) { sint boneID = Skeleton.getBoneIdByName(boneName); if (boneID != -1) { UInstance instance = Scene->createInstance(name); if (!instance.empty()) { instance.setTransformMode(UTransform::DirectMatrix); CMatrix mat; mat.setPos(FXItemSheet->FX.getStaticFXOffset(k)); instance.setMatrix(mat); Skeleton.stickObject(instance, boneID); StaticFXs.push_back(instance); } else { nlwarning("Can't create static fx %s sticked on bone %s", name.c_str(), boneName.c_str()); } } else { nlwarning("Can't find bone %s for static fx %s", boneName.c_str(), name.c_str()); } } } } } //--------------------------------------------------- void CEntityCL::SInstanceCL::setEmissive(NLMISC::CRGBA emit) { // Do it on both Loading and Current, to avoid any problem UInstance insts[2]; insts[0]= Current; insts[1]= Loading; for(uint i=0;i<2;i++) { // test if instance is valid UInstance inst= insts[i]; if(inst.empty()) continue; UShape shape= inst.getShape(); if(shape.empty()) continue; uint numMats= shape.getNumMaterials(); if(numMats==0) continue; if(numMats!=inst.getNumMaterials()) continue; // For all materials for(uint j=0;jdeleteEntity(_CollisionEntity); _CollisionEntity = 0; } // If there still is a scene -> delete pointers. if(Scene) { // Remove skeleton. if(!_Skeleton.empty()) { Scene->deleteSkeleton(_Skeleton); _Skeleton = 0; } // Remove Instance. if(!_Instance.empty()) { Scene->deleteInstance(_Instance); _Instance = 0; } // Delete Instances. for(uint i = 0; i < _Instances.size(); ++i) { if(!_Instances[i].Loading.empty()) { Scene->deleteInstance(_Instances[i].Loading); } if(!_Instances[i].Current.empty()) { Scene->deleteInstance(_Instances[i].Current); } } } // No more scene -> reset pointers. else { _Skeleton = 0; _Instance = 0; } // Remove the collision entity. removeCollisionEntity(); // Remove the primitive. removePrimitive(); // Release the animation playlist if(_PlayList) { EAM->deletePlayList(_PlayList); _PlayList = 0; } if (!_StateFX.empty() && Scene) { Scene->deleteInstance(_StateFX); _StateFXName = ""; } if (!_SelectionFX.empty() && Scene) { Scene->deleteInstance(_SelectionFX); } if (!_MouseOverFX.empty() && Scene) { Scene->deleteInstance(_MouseOverFX); } // Free the link with the parent parent(CLFECOMMON::INVALID_SLOT); // Remove Link with children TChildren::iterator it = _Children.begin(); while(it != _Children.end()) { // Remove the child from the list if found. if(skeleton() && (*it) && (*it)->skeleton()) skeleton()->detachSkeletonSon(*((*it)->skeleton())); // Next Child it++; } // Remove the list _Children.clear(); }// ~CEntityCL // //----------------------------------------------- // init : // Initialize the Object with this function for all constructors. //----------------------------------------------- void CEntityCL::init() { // No parent. _Parent = CLFECOMMON::INVALID_SLOT; // No entry for the moment. _DBEntry = 0; // _Name = NULL; // Entity Up. up(CVector(0.f, 0.f, 1.f)); // Orientation of the entity. front(CVector(0.f, 1.f, 0.f)); // Current direction for the entity. _DirMatrix.identity(); dir(CVector(0.f, 1.f, 0.f)); // Position of the entity. _Position = CVectorD(0.f, 0.f, 0.f); // Initialize the position limiter _PositionLimiter = CVectorD(0.0, 0.0, 0.0); // Entity is not flyer at the beginning. _Flyer = false; // Initialize the mode. _Mode = MBEHAV::UNKNOWN_MODE; _TheoreticalMode = MBEHAV::UNKNOWN_MODE; // Initialize the behaviour. _CurrentBehaviour = MBEHAV::IDLE; // No skeleton at the beginning. _Skeleton = 0; // No Instance at the beginning. _Instance = 0; // No primitive at the beginning. _Primitive = 0; // No collision Entity at the beginning. _CollisionEntity = 0; // No PlayList at the beginning. _PlayList = 0; _FacePlayList = 0; // AABBox CAABBox aabbox; CVector min = CVector(-0.5f, -0.5f, 0); CVector max = CVector( 0.5f, 0.5f, 2); aabbox.setMinMax(min, max); box(aabbox); // Default Clip Sphere _ClipRadius= aabbox.getRadius(); _ClipDeltaZ= aabbox.getCenter().z; // 'true' as long as the entity has not received any position. _First_Pos = true; // No position managed for the time so the next one will be the first one. _FirstPosManaged = true; // No DataSetId initiliazed _DataSetId= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX; _NPCAlias = 0; // The entity is not in any slot for the time. _Slot = CLFECOMMON::INVALID_SLOT; // The entity has no target for the time. _TargetSlot = CLFECOMMON::INVALID_SLOT; _TargetSlotNoLag = CLFECOMMON::INVALID_SLOT; // init _LogicInfo3D _LogicInfo3D.Self= this; // Async texture _AsyncTextureLoading= false; _LodTextureDirty= false; // Angle to have with the target when in combat mode with. _TargetAngle = 0.0; // Must do a setGlobalPosition _SetGlobalPositionDone = false; _SnapToGroundDone = false; // Entity are not displayed at the beginning. _Displayable = true; // _Clipped = false; if (ClientCfg.Local) { _Title = "Newbie"; _HasReservedTitle = false; _EntityName = "Name"; } _NameId = 0; _PermanentStatutIcon = string(""); // Not a mission target by default _MissionTarget = false; // The entity has not moved for the time. _HasMoved = false; _TranspFactor = 0.0f; _IsInTeam= false; _IsUserMount= false; _IsUserPackAnimal= false; // GroundTypeCache _GroundTypeCachePos= CVectorD::Null; _GroundTypeCache= 0; _StateFX = NULL; _StateFXName = ""; _SelectionFX = NULL; _MouseOverFX = NULL; _SomeInstanceCastShadowMap= false; _ShadowMapZDirClamp= ClientCfg.ShadowZDirClampLandscape; _ShadowMapMaxDepth= ClientCfg.ShadowMaxDepthLandscape; _ShadowMapPropertyLastUpdate= 0; #ifdef TMP_DEBUG_GUIGUI _TheoreticalPosition = CVectorD(0.f, 0.f, 0.f); _TheoreticalOrientation = -10.0f; // Init value to know if it has been changed. #endif // TMP_DEBUG_GUIGUI // Bot Objects flags _DisplayInRadar= true; _DisplayOSDName= true; _DisplayOSDBars= true; _DisplayOSDForceOver= false; _Traversable= true; _CanTurn = true; _ForbidClipping = false; _VisualSelectionTime= 0; _VisualSelectionBlinked= false; _InSceneInterfaceEnabled = true; }// init // //----------------------------------------------- // initialize : // Method to call to initialize all members of the right class. //----------------------------------------------- void CEntityCL::initialize() { // Initialize the primitive. initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColNone, MaskColNone); // Create the collision entity (used to snap the entity to the ground). computeCollisionEntity(); // Initialize properties of the client. initProperties(); }// initialize // //----------------------------------------------- // initPrimitive : // //----------------------------------------------- bool CEntityCL::initPrimitive(float radius, float height, float length, float width, UMovePrimitive::TReaction reactionType, UMovePrimitive::TTrigger triggerType, UMovePrimitive::TCollisionMask occlusionMask, UMovePrimitive::TCollisionMask collisionMask, float clipRadius, float clipHeight) { // Check primitive, there should not be a primitive because it could have been created with another PACS and crash(continent changed). if(_Primitive) { nlwarning("ENT:initPrimitive:%d: There is already a primitive -> _Primitive = 0.", _Slot); _Primitive = 0; } // **** Create the primitive bool primitiveOk= false; if(PACS) { _Primitive = PACS->addCollisionablePrimitive(dynamicWI, 1); if(_Primitive) { _Primitive->setReactionType(reactionType); _Primitive->setTriggerType(triggerType); _Primitive->setAbsorbtion(0); // Set the collision if the radius is > 0 if(radius > 0.0f) { _Primitive->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder); _Primitive->setRadius( std::min(radius, (float)(RYZOM_ENTITY_SIZE_MAX/2)) ); _Primitive->setHeight(height+ClientCfg.PrimitiveHeightAddition); CVector min = CVector(-radius, -radius, 0.0f); CVector max = CVector( radius, radius, height); _Aabbox.setMinMax(min, max); _Primitive->setOcclusionMask(occlusionMask); // This is an npc. } // Maybe be a Box else if(length > 0.0f) { _Primitive->setPrimitiveType(UMovePrimitive::_2DOrientedBox); _Primitive->setSize(std::min(length, (float)(RYZOM_ENTITY_SIZE_MAX/2)), std::min(width, (float)(RYZOM_ENTITY_SIZE_MAX/2))); _Primitive->setHeight(height+ClientCfg.PrimitiveHeightAddition); if(length > width) { CVector min = CVector(-length, -length, 0.0f); CVector max = CVector( length, length, height); _Aabbox.setMinMax(min, max); } else { CVector min = CVector(-width, -width, 0.0f); CVector max = CVector( width, width, height); _Aabbox.setMinMax(min, max); } _Primitive->setOcclusionMask(occlusionMask); // This is an npc. } // Non-collisionnable entity else { _Primitive->setOcclusionMask(MaskColNone); // This is an npc. // still have some _Aabbox for ???? use width for radius float maxRad= max(width, 0.5f); CVector min = CVector(-maxRad, -maxRad, 0.0f); CVector max = CVector( maxRad, maxRad, height); _Aabbox.setMinMax(min, max); } _Primitive->setCollisionMask(collisionMask); _Primitive->setObstacle(true); _Primitive->UserData = UserDataEntity + (((uint64)this)<<16); primitiveOk= true; } else nlwarning("CEntityCL::initPrimitive:%d: Cannot create the _Primitive.", _Slot); } else nlwarning("CEntityCL::initPrimitive:%d: PACS not allocated -> _Primitive not created.", _Slot); // **** setup the 3D Clip Sphere. // if clip radius not specified, deduce from collision if(clipRadius<=0.f) { // Cylinder Colision? if(radius > 0.0f) { clipRadius= radius; } // Box Colision? else if(length > 0.0f) { clipRadius= max(width, length); } // Non-collisionnable entity else { // Backward compatibility: use width for radius clipRadius= width; } // at least 0.5f clip radius clipRadius= max(clipRadius, 0.5f); } // if clip height not specified if(clipHeight<=0.f) { // at least 0.5f clip height, with height from collision clipHeight= max(height, 0.5f); } // Mount the sphere around the Clip cylinder (over estimate) CAABBox clipBox; clipBox.setMinMax(CVector(-clipRadius, -clipRadius, 0.0f), CVector( clipRadius, clipRadius, clipHeight)); // Compute the local Sphere info _ClipRadius= clipBox.getRadius(); _ClipDeltaZ= clipBox.getCenter().z; // NB different from radius, since radius>center.z return primitiveOk; }// initPrimitive // //----------------------------------------------- // initProperties : // Initialize properties of the entity (according to the class). //----------------------------------------------- void CEntityCL::initProperties() { properties().selectable(true); }// initProperties // //----------------------------------------------- // updateVisualProperty : // Update a visual property from the database. // \param gameCycle : when this was sent. // \param prop : the property to udapte. //----------------------------------------------- void CEntityCL::updateVisualProperty(const NLMISC::TGameCycle &gameCycle, const uint &prop, const NLMISC::TGameCycle &predictedInterval) { CCDBNodeBranch *nodePtr = IngameDbMngr.getNodePtr(); if (nodePtr) { CCDBNodeBranch *nodeRoot = dynamic_cast(nodePtr->getNode(0)); if(!nodeRoot) { nlwarning("CEntityCL::updateVisualProperty : There is no entry in the DB for entities (current slot %d).", _Slot); return; } CCDBNodeBranch *nodGrp = dynamic_cast(nodeRoot->getNode(_Slot)); if(nodGrp == 0) { nlwarning("CEntityCL::updateVisualProperty : Cannot find the entity '%d' in the database.", _Slot); return; } // Get The property ptr. CCDBNodeLeaf *nodeProp = dynamic_cast(nodGrp->getNode(prop)); if(nodeProp == 0) { nlwarning("CEntityCL::updateVisualProperty : Cannot find the property '%d' for the slot %d.", prop, _Slot); return; } switch(prop) { case PROPERTY_POSITION: updateVisualPropertyPos(gameCycle, nodeProp->getValue64(), predictedInterval); break; case PROPERTY_ORIENTATION: updateVisualPropertyOrient(gameCycle, nodeProp->getValue64()); break; case PROPERTY_BEHAVIOUR: updateVisualPropertyBehaviour(gameCycle, nodeProp->getValue64()); break; case PROPERTY_NAME_STRING_ID: updateVisualPropertyName(gameCycle, nodeProp->getValue64()); break; case PROPERTY_TARGET_ID: updateVisualPropertyTarget(gameCycle, nodeProp->getValue64()); break; case PROPERTY_MODE: updateVisualPropertyMode(gameCycle, nodeProp->getValue64()); break; case PROPERTY_VPA: updateVisualPropertyVpa(gameCycle, nodeProp->getValue64()); break; case PROPERTY_VPB: updateVisualPropertyVpb(gameCycle, nodeProp->getValue64()); break; case PROPERTY_VPC: updateVisualPropertyVpc(gameCycle, nodeProp->getValue64()); break; case PROPERTY_ENTITY_MOUNTED_ID: updateVisualPropertyEntityMounted(gameCycle, nodeProp->getValue64()); break; case PROPERTY_RIDER_ENTITY_ID: updateVisualPropertyRiderEntity(gameCycle, nodeProp->getValue64()); break; case PROPERTY_TARGET_LIST_0: case PROPERTY_TARGET_LIST_1: case PROPERTY_TARGET_LIST_2: case PROPERTY_TARGET_LIST_3: updateVisualPropertyTargetList(gameCycle, nodeProp->getValue64(), prop - PROPERTY_TARGET_LIST_0); break; case PROPERTY_VISUAL_FX: updateVisualPropertyVisualFX(gameCycle, nodeProp->getValue64()); break; // Property to update the contextual menu, and some important status case PROPERTY_CONTEXTUAL: updateVisualPropertyContextual(gameCycle, nodeProp->getValue64()); break; case PROPERTY_BARS: updateVisualPropertyBars(gameCycle, nodeProp->getValue64()); break; case PROPERTY_GUILD_SYMBOL: updateVisualPropertyGuildSymbol(gameCycle, nodeProp->getValue64()); break; case PROPERTY_GUILD_NAME_ID: updateVisualPropertyGuildNameID(gameCycle, nodeProp->getValue64()); break; case PROPERTY_EVENT_FACTION_ID: updateVisualPropertyEventFactionID(gameCycle, nodeProp->getValue64()); break; case PROPERTY_PVP_MODE: updateVisualPropertyPvpMode(gameCycle, nodeProp->getValue64()); break; case PROPERTY_PVP_CLAN: updateVisualPropertyPvpClan(gameCycle, nodeProp->getValue64()); break; case PROPERTY_OWNER_PEOPLE: updateVisualPropertyOwnerPeople(gameCycle, nodeProp->getValue64()); break; case PROPERTY_OUTPOST_INFOS: updateVisualPropertyOutpostInfos(gameCycle, nodeProp->getValue64()); break; // case PROPERTY_STATUS: // updateVisualPropertyStatus(gameCycle, nodeProp->getValue64()); // break; default: nlwarning("CEntityCL::updateVisualProperty : Unknown Property '%d' for the entity in the slot '%d'.", prop, _Slot); break; } } }// updateVisualProperty // //----------------------------------------------- // updateVisualPropertyContextual //----------------------------------------------- void CEntityCL::updateVisualPropertyContextual(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { _Properties.selectable( CProperties( (uint16) prop ).selectable() ); _Properties.talkableTo( CProperties( (uint16) prop ).talkableTo() ); _Properties.attackable( CProperties( (uint16) prop ).attackable() ); _Properties.mountable( CProperties( (uint16) prop ).mountable() ); _Properties.lootable( CProperties( (uint16) prop ).lootable() ); _Properties.harvestable( CProperties( (uint16) prop ).harvestable() ); _Properties.afk( CProperties( (uint16) prop ).afk() ); } //----------------------------------------------- // skeleton : // Set the skeleton for the entity or an empty string to remove a skeleton. // \param filename : file with the skeleton to apply to the entity or empty string to remove a skeleton. //----------------------------------------------- USkeleton *CEntityCL::skeleton(const string &filename) { // Check there is a scene. if(!Scene) { pushDebugStr("No scene allocated -> Cannot create the skeleton."); _Skeleton = 0; return skeleton(); } // Remove the old skeleton. if(!_Skeleton.empty()) { Scene->deleteSkeleton(_Skeleton); } // If the filename is not empty -> changes the skeleton. if(filename.empty()) { pushDebugStr("skeleton filename is empty -> Cannot create the skeleton."); return skeleton(); } // Create the skeleton. _Skeleton = Scene->createSkeleton(filename); if(!_Skeleton.empty()) { // Set default Transform mode -> RotQuat. _Skeleton.setTransformMode(UTransformable::RotQuat); // Initialize the playlist a bit. if(_PlayList) { // Register the skeleton to the playlist. _PlayList->registerTransform(_Skeleton); // \todo GUIGUI : is this location right for this ? // Animation should not move alone. uint posTmp = EAM->getAnimationSet()->getChannelIdByName("pos"); if(posTmp != UAnimationSet::NotFound) _PlayList->enableChannel(posTmp, false); else pushDebugStr("Channel 'pos' not found."); // Animation should not rotate alone. uint rotquatTmp = EAM->getAnimationSet()->getChannelIdByName("rotquat"); if(rotquatTmp != UAnimationSet::NotFound) _PlayList->enableChannel(rotquatTmp, false); else pushDebugStr("Channel 'rotquat' not found."); } // else pushDebugStr("Playlist no allocated -> skeleton not registered."); // Attach Light Information request to the skeleton _Skeleton.setLogicInfo(&_LogicInfo3D); // Enable user clipping _Skeleton.setUserClipping (true); // Update Shadow updateCastShadowMap(); } else pushDebugStr(toString("Cannot create the Skeleton '%s', the file probably not exist.", filename.c_str())); // Return the skeleton pointer. return skeleton(); }// skeleton // //----------------------------------------------- // addInstance : // Add an instance to the list of instance composing the entity. // \param shapeName : shape filename. // \param stickPoint : Name of the bone to stick on. // \param texture : texture to use (in multi texture) or -1 for default texture. // \param instIdx : if not CEntityCL::BadIndex, the instance will replace the one at this index. // \return uint32 : index of the instance created, or CEntityCL::BadIndex. //----------------------------------------------- uint32 CEntityCL::addInstance(const string &shapeName, const std::string &stickPoint, sint texture, uint32 instIdx) { // Future Index for the instance. uint32 idx; // Replace Instance if(instIdx < _Instances.size()) // And so instIdx != CEntityCL::BadIndex { // Index given from parameters so do not change. idx = instIdx; } else { if(instIdx != CEntityCL::BadIndex) nlwarning("CEntityCL::addInst:%d: given index '%d' is not valid.", _Slot, instIdx); idx = instIdx = CEntityCL::BadIndex; } // If the shape is empty, just clean and leave. if(shapeName.empty()) { if(instIdx == CEntityCL::BadIndex) nlwarning("CEntityCL::addInst:%d: shape empty but given index was invalid, so nothing to clean and cannot add an empty shape.", _Slot); // clean the instance if(instIdx<_Instances.size()) _Instances[instIdx].createLoading(string(), stickPoint, texture); return idx; } // else create else { // Create new instance slot? if(instIdx == CEntityCL::BadIndex) { idx= _Instances.size(); _Instances.push_back(SInstanceCL()); } // load, but dont replace instance if fails (yoyo: I keep the same behavior than before ) if( !_Instances[idx].createLoading(shapeName, stickPoint, texture, false) ) { // If fails to load.... nlwarning("CEntityCL::addInstance : Cannot create the instance '%s' for the slot %d.", shapeName.c_str(), _Slot); // if the index was just being created, erase array entry if(instIdx == CEntityCL::BadIndex) { _Instances.pop_back(); idx= CEntityCL::BadIndex; } } } // Return the instance index. return idx; }// addInstance // //--------------------------------------------------- // show : // Show or Hide the entity. // \param s : if 'true' = entity visible, else invisible. //--------------------------------------------------- void CEntityCL::show(bool s) { if(!_Skeleton.empty()) { if(s) _Skeleton.show(); else _Skeleton.hide(); } else if(!_Instance.empty()) { if(s) _Instance.show(); else _Instance.hide(); } else { if (s) { for(std::vector::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (!it->Current.empty()) it->Current.show(); } } else { for(std::vector::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (!it->Current.empty()) it->Current.hide(); } } } }// show // //----------------------------------------------- // hideSkin : // hide the entity Skin (all entity instances). // todo GUIGUI : for video, it shouldn't be a problem, but we should improve with the current which could not be ready yet while loading not done //----------------------------------------------- void CEntityCL::hideSkin() { const uint nbInst = _Instances.size(); for(uint i = 0; i problem so do not changed the vector. if(vect == CVector::Null) { nlwarning("CEntityCL::up : attempt to set the vector up with a vector Null. Up not changed !"); return; } // Compute the up vector _Up = vect; _Up.normalize(); }// up // //--------------------------------------------------- // front : // Change the entity front vector. // \param vect : new vector to use for the front. // \param compute : adjust the param 'vect' to be valid or leave the old front unchanged if impossible. // \param check : warning if the param 'vect' is not valid to be the front (vector Null) even with compute=true. // \param forceTurn : set front even if the entity cannot turn // \return bool : 'true' if the front has been filled, else 'false'. //--------------------------------------------------- bool CEntityCL::front(const CVector &vect, bool compute, bool check, bool forceTurn) { if (!forceTurn && !_CanTurn) return false; return setVect(_Front, vect, compute, check); }// front // //--------------------------------------------------- // dir : // Change the entity direction vector. // \param vect : new vector to use for the direction. // \param compute : adjust the param 'vect' to be valid or leave the old direction unchanged if impossible. // \param check : warning if the param 'vect' is not valid to be the direction (vector Null) even with compute=true. // \return bool : 'true' if the direction has been filled, else 'false'. //--------------------------------------------------- bool CEntityCL::dir(const CVector &vect, bool compute, bool check) { if(setVect(_Dir, vect, compute, check)) { // Compute the direction Matrix CVector vi = _Dir^up(); CVector vk = vi^_Dir; _DirMatrix.setRot(vi, _Dir, vk, true); return true; } return false; }// dir // //--------------------------------------------------- // posBox : // Change the box position. // \param pos contains the new box position. //--------------------------------------------------- void CEntityCL::posBox(const CVector &pos) { CVector halfSize = _Aabbox.getHalfSize(); halfSize.x = 0; halfSize.y = 0; _Aabbox.setCenter(pos+halfSize); }// posBox // //--------------------------------------------------- // box : // Set the box. // \param box : an axis aligned bounding box to apply to the entity. //--------------------------------------------------- void CEntityCL::box(const CAABBox &box) { // Copy the Box. _Aabbox = box; // position the box around the entity. posBox(pos()); }// box // //----------------------------------------------- // lastFramePACSPos : // Return the last frame PACS position. //----------------------------------------------- bool CEntityCL::lastFramePACSPos(NLPACS::UGlobalPosition &result) const { if(_Primitive) { result = _LastFramePACSPos; if(result.InstanceId != -1) return true; else return false; } else return false; }// lastFramePACSPos // //----------------------------------------------- // currentPACSPos : // Return the current PACS position. //----------------------------------------------- bool CEntityCL::currentPACSPos(NLPACS::UGlobalPosition &result) const { if(_Primitive) { _Primitive->getGlobalPosition(result, dynamicWI); if(result.InstanceId != -1) return true; else return false; } else return false; }// currentPACSPos // //--------------------------------------------------- // pacsPos : // Change the PACS position and the entity position too. // \todo GUIGUI : do we have to get the position from pacs or from the parameter ? //--------------------------------------------------- void CEntityCL::pacsPos(const CVectorD &vect, const UGlobalPosition &globPos) { // Set then entity position pos(vect); if(!_Primitive) return; if(!GR) { nlwarning("CEntityCL::pacsPos : Global Retriever not allocated."); return; } // Use the precise PACS position if possible if( globPos.InstanceId != -1 /*&& ContinentMngr.getCurrentContinentSelectName() == "newbieland"*/ ) { _Primitive->setGlobalPosition(globPos, dynamicWI); } // NB: else use the 3D position. => the Z may be different than the Z wanted! (according to the pacs pos found) else { _Primitive->setGlobalPosition(vect, dynamicWI, UGlobalPosition::Unspecified); } // Get the global position UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); _FinalPacsPos = GR->getDoubleGlobalPosition(gPos); // Check the new position is valid if (gPos.InstanceId != -1) { _SetGlobalPositionDone = true; _LastRetrievedPosition = vect; _LastRetrievedPacsPosition = gPos; } // If the entity do not fly, update Z value (X,Y should be the same or close and to avoid a pb with a too small move, just keep them) if(!flyer()) pos().z = _FinalPacsPos.z; }// pacsPos // //--------------------------------------------------- // pacsMove : // Move the PACS position and the entity position too. //--------------------------------------------------- void CEntityCL::pacsMove(const CVectorD &vect) { // Entity has not moved. _HasMoved = false; // If there is a PACS primitive -> changes the PACS position. if(!_Primitive) { // Set then entity position pos(vect); return; } if(!GR) { // Set then entity position pos(vect); nlwarning("CEntityCL::pacsMove : Global Retriever not allocated."); return; } if ( _SetGlobalPositionDone ) { // Change the position of the entity in PACS and apply the new position to the entity. CVectorD deltaPos ((vect.x - _FinalPacsPos.x)/ DT, (vect.y - _FinalPacsPos.y)/ DT, 0); if ((fabs (deltaPos.x) > 0.05) || (fabs (deltaPos.y) > 0.05)) { _HasMoved = true; _Primitive->move (deltaPos, dynamicWI); } } else { _Primitive->setGlobalPosition(vect, dynamicWI); // Check the new position is valid UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); if (gPos.InstanceId != -1) { _SetGlobalPositionDone = true; _LastRetrievedPosition = vect; _LastRetrievedPacsPosition = gPos; } } // Set the entity position pos(vect); }// pacsMove // //----------------------------------------------- // updateDisplay : // Update the PACS position after the evalCollision. The entity position is set too. This is fast. // If the entity position is too far from its PACS position, setGlobalPosition is called. // After this call, the position.z is valid. //----------------------------------------------- void CEntityCL::pacsFinalizeMove() // virtual { if(_Primitive == 0) { return; } // Get the global position _FinalPacsPos = _Primitive->getFinalPosition (dynamicWI); // Get the global position UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); if (gPos.InstanceId != -1) { if(ClientCfg.UsePACSForAll && (_Mode == MBEHAV::COMBAT || _Mode == MBEHAV::COMBAT_FLOAT)) { pos(_FinalPacsPos); } else { // Delta between real and evaluated position CVectorD deltaPos = pos(); deltaPos -= _FinalPacsPos; // Too far from real value ? if ( ((deltaPos.x*deltaPos.x+deltaPos.y*deltaPos.y) > (double)RYZOM_EPSILON_SQRT_POSITION) ) { pacsPos (pos ()); } // If the entity do not fly, update Z value (X,Y should be the same or close and to avoid a pb with a too small move, just keep them) if(!flyer()) pos().z = _FinalPacsPos.z; } } else { _SetGlobalPositionDone = false; _SnapToGroundDone = false; } }// pacsFinalizeMove // //--------------------------------------------------- // updateDisplay : // Get the entity position and set all visual stuff with it. // \todo GUIGUI : put this method 'virtual' to have a different code for the user (no playlist). // \todo GUIGUI : manage the parent better. //--------------------------------------------------- void CEntityCL::updateDisplay(CEntityCL * /* parent */) { }// updateDisplay // //----------------------------------------------- // setCluster : // Set the cluster system for the current entity and all of its chidren. //----------------------------------------------- void CEntityCL::setClusterSystem(UInstanceGroup *cluster) { // Place the skeleton in the right cluster. if(!_Skeleton.empty()) _Skeleton.setClusterSystem(cluster); else if(!_Instance.empty()) _Instance.setClusterSystem(cluster); else { for(std::vector::iterator it = _Instances.begin(); it != _Instances.end(); ++it) { if (!it->Current.empty()) { it->Current.setClusterSystem(cluster); } } } // Update the children display. std::list::iterator it = _Children.begin(); while(it != _Children.end()) { // Update the display for the child (*it)->setClusterSystem(cluster); // Next Child. ++it; } }// setCluster // //***************************************************************************************************** NL3D::UInstanceGroup *CEntityCL::getClusterSystem() { if (!_Skeleton.empty()) return _Skeleton.getClusterSystem(); if (!_Instance.empty()) return _Instance.getClusterSystem(); return NULL; } //----------------------------------------------- // updateCluster : // Choose the right cluster according to the entity position. //----------------------------------------------- void CEntityCL::updateCluster() { // Check there is a primitive. if(_Primitive) { // Get the global position. UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); // Set the new cluster. setClusterSystem(getCluster(gPos)); } }// updateCluster // //--------------------------------------------------- // snapToGround : // Snap entity to the ground. //--------------------------------------------------- void CEntityCL::snapToGround() { // While PACS not done, do not try to snap. if(_SetGlobalPositionDone == false) return; // If the entity is a flyer no snap to ground will be done. if(!flyer()) { CVector vect; bool needSnap = true; // Check there is a primitive. if(_Primitive && GR) { // Get the right pos from PACS. UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); // Get the final position from pacs and after the snap if on landscape. vect = GR->getGlobalPosition(gPos); // if on interior, no need to snap if(GR->isInterior(gPos)) needSnap = false; // if on water, snap on water else { float waterHeight = 0.0f; if(GR->isWaterPosition(gPos, waterHeight)) { if ( isUser() || isPlayer() || isNPC()) { float waterOffset = ClientCfg.WaterOffset; switch(people()) { case EGSPD::CPeople::Unknown : break; case EGSPD::CPeople::Fyros : waterOffset = ClientCfg.FyrosWaterOffset; break; case EGSPD::CPeople::Matis : waterOffset = ClientCfg.MatisWaterOffset; break; case EGSPD::CPeople::Tryker : waterOffset = ClientCfg.TrykerWaterOffset; break; case EGSPD::CPeople::Zorai : waterOffset = ClientCfg.ZoraiWaterOffset; break; } vect.z = waterHeight + waterOffset; } else // creature { vect.z = waterHeight + ClientCfg.WaterOffsetCreature; } needSnap= false; } } } // No primitive, vect = current pos. else vect = pos(); // Snap to the ground. if(needSnap && _CollisionEntity) { // for Snap on landscape, use always current 3d position, for snap cache performance. vect = pos(); // Have i to force the snap to ground ? if (!_SnapToGroundDone) { CVector vectNull = CVector::Null; _CollisionEntity->snapToGround(vectNull); } _SnapToGroundDone = _CollisionEntity->snapToGround(vect); } // Change the entity position. pos().z = vect.z; } // Set the box position. posBox(pos()); }// snapToGround // //--------------------------------------------------- // chooseRandom : // //--------------------------------------------------- string chooseRandom( const vector& sounds, uint32& previousIndex ) { uint32 randomIndex = rand()%sounds.size(); if( randomIndex == previousIndex ) { randomIndex = (randomIndex+1)%sounds.size(); } previousIndex = randomIndex; return sounds[randomIndex]; }// chooseRandom // //--------------------------------------------------- // CEntityLogicInfo3D::getStaticLightSetup() callBack //--------------------------------------------------- void CEntityLogicInfo3D::getStaticLightSetup(NLMISC::CRGBA sunAmbient, std::vector &pointLightList, uint8 &sunContribution, NLMISC::CRGBA &localAmbient) { // Is the instance in a InstanceGroup??? UInstanceGroup *igUnderInstance= NULL; const UMovePrimitive *movePrim= Self->getPrimitive(); if(Self->skeleton() && movePrim) igUnderInstance= Self->skeleton()->getClusterSystem(); // if it is, retrieve static light from the IG. if(igUnderInstance) { // get the logic position of the entity. UGlobalPosition gPos; movePrim->getGlobalPosition(gPos, dynamicWI); // retrieve collision identifier from Pacs. uint localRetrieverId= GR->getLocalRetrieverId(gPos); // get static light info from ig igUnderInstance->getStaticLightSetup(sunAmbient, localRetrieverId, gPos.LocalPosition.Surface, gPos.LocalPosition.Estimation, pointLightList, sunContribution, localAmbient); } // else retrieve from landscape else { // Use VisualCollisionEntity to retrieve info from landscape. UVisualCollisionEntity *colEnt= Self->getCollisionEntity(); if(colEnt) { colEnt->getStaticLightSetup(sunAmbient, Self->pos(), pointLightList, sunContribution, localAmbient); } } } //----------------------------------------------- // computePrimitive : // Create (or re-create) a primitive. //----------------------------------------------- void CEntityCL::computePrimitive() { // Initialize the primitive. initPrimitive(0.5f, 2.0f, 0.0f, 0.0f, UMovePrimitive::DoNothing, UMovePrimitive::NotATrigger, MaskColNone, MaskColNone); // Set the position. pacsPos(pos()); }// computePrimitive // //--------------------------------------------------- // removePrimitive : // remove the entity primitive from PACS //--------------------------------------------------- void CEntityCL::removePrimitive() { // If there is a Primitive -> remove it. if(_Primitive) { // Remove the primitive if PACS allocated if(PACS) PACS->removePrimitive(_Primitive); // Primitive removed _Primitive = 0; } }// removePrimitive // //----------------------------------------------- // computeCollisionEntity : // Create the collision entity. //----------------------------------------------- void CEntityCL::computeCollisionEntity() { // Check for an old collision entity not removed. if(_CollisionEntity) { nlwarning("CEntityCL::computeCollisionEntity: There is already a collision entity in the slot %d.", _Slot); _CollisionEntity = 0; } // Is there a collision manager. if(CollisionManager) { // Create the collision entity. _CollisionEntity = CollisionManager->createEntity(); if(_CollisionEntity == 0) nlwarning("CEntityCL::computeCollisionEntity : Cannot create the _CollisionEntity for the slot %d.", _Slot); else _CollisionEntity->setSnapToRenderedTesselation(false); } }// computeCollisionEntity // //----------------------------------------------- // removeCollisionEntity : // Remove the collision entity. //----------------------------------------------- void CEntityCL::removeCollisionEntity() { // If there is a collision entity -> remove it. if(_CollisionEntity) { // Remove it if there is a collision manager. if(CollisionManager) CollisionManager->deleteEntity(_CollisionEntity); // Collision Entity Removed. _CollisionEntity = 0; } }// removeCollisionEntity // //----------------------------------------------- // attackRadius : // Method to return the attack radius of an entity //----------------------------------------------- double CEntityCL::attackRadius() const { return 0.5; }// attackRadius // //----------------------------------------------- //----------------------------------------------- CVectorD CEntityCL::getAttackerPos(double /* ang */, double /* dist */) const { return pos(); } /////////////// // 3D SYSTEM // /////////////// //----------------------------------------------- // updateAsyncTexture //----------------------------------------------- float CEntityCL::updateAsyncTexture() { H_AUTO ( RZ_Client_Entity_CL_Update_Async_Texture ) uint i; // Check all instance to know if they need to start async load their textures for (i = 0; i < _Instances.size(); i++) { if (!_Instances[i].Loading.empty()) { UParticleSystemInstance psi; psi.cast (_Instances[i].Loading); // dirty? if (_Instances[i].Loading.isAsyncTextureDirty() || !psi.empty()) { // reset instance state. _Instances[i].Loading.setAsyncTextureDirty(false); // must start loading for this isntance _Instances[i].Loading.startAsyncTextureLoading(); // the entity is now currently loading. _AsyncTextureLoading= true; // The LodTexture need to be recomputed _LodTextureDirty= true; } } } // Update Async Texture loading of all instances. if (_AsyncTextureLoading) { bool allLoaded= true; // update loading for all instances. for (i = 0; i < _Instances.size(); i++) { if(!_Instances[i].Loading.empty()) { // update async texture loading allLoaded= allLoaded && _Instances[i].Loading.isAsyncTextureReady(); } } // if all are loaded, then End! don't need to check all instances every frame. if(allLoaded) { _AsyncTextureLoading= false; // Transfert InstancesLoading to Instances for(i = 0; i < _Instances.size(); i++) { _Instances[i].updateCurrentFromLoading(_Skeleton); if(_Skeleton.empty() && !_Instances[i].Current.empty()) { _Instances[i].Current.setPos(pos()); // force update right now (else the object would be at 0, 0, 0 for first frame) } } // additionally, if their is no skeleton (bot objects), may enable cast shadow here updateCastShadowMap(); } } // compute distance to camera. // For lesser update, take the refine position (else in 3th person view, rotation will cause lot of update) // \todo GUIGUI : make sure pos() will return the world position all the time even when a child. float distToCam = (View.refinePos() - pos()).norm(); // For LOD texture, must update the "texture distance" for(i = 0; i < _Instances.size(); i++) { if(!_Instances[i].Current.empty()) { // update async texture loading _Instances[i].Current.setAsyncTextureDistance(distToCam); } } return distToCam; } //----------------------------------------------- // updateLodTexture //----------------------------------------------- void CEntityCL::updateLodTexture() { H_AUTO ( RZ_Client_Entity_CL_Update_Lod_Texture ) // if need to recompute, and if Async loading ended if( _LodTextureDirty && !_AsyncTextureLoading ) { // clean _LodTextureDirty= false; // compute if(!_Skeleton.empty()) _Skeleton.computeLodTexture(); } } //----------------------------------------------- // addChild : // Add a new child pointer. //----------------------------------------------- void CEntityCL::addChild(CEntityCL *c) { if(c == 0) { nlwarning("ENT:addChild:%d: Try to add a child with a Null Pointer.", _Slot); return; } delChild(c); _Children.push_back(c); if(skeleton() && c->skeleton()) { const string stickBone = "stick_1"; sint idBones = skeleton()->getBoneIdByName(stickBone); if(idBones != -1) { // Child position is relative to the parent so set its position to 0 0 0. c->skeleton()->setPos(CVector::Null); skeleton()->stickObjectEx(*(c->skeleton()), idBones, true); } else nlwarning("ENT:addChild:%d: the Bone '%s' does not exist", _Slot, stickBone.c_str()); } }// addChild // //----------------------------------------------- // delChild : // Remove a new child pointer. //----------------------------------------------- void CEntityCL::delChild(CEntityCL *c) { if(c == 0) { nlwarning("ENT:delChild:%d: Try to remove a child with a Null Pointer.", _Slot); return; } TChildren::iterator it = _Children.begin(); while(it != _Children.end()) { // Remove the child from the list if found. if((*it) == c) { if(skeleton() && c->skeleton()) skeleton()->detachSkeletonSon(*(c->skeleton())); _Children.erase(it); break; } // Next Child it++; } }// delChild // //----------------------------------------------- // parent : // Set the new parent //----------------------------------------------- void CEntityCL::parent(CLFECOMMON::TCLEntityId p) { // Remove the entity from the old parent. CEntityCL *parent = EntitiesMngr.entity(_Parent); if(parent) parent->delChild(this); // Initialize the new parent. _Parent = p; // Remove the entity from the old parent. parent = EntitiesMngr.entity(_Parent); if(parent) parent->addChild(this); }// parent // bool CEntityCL::clipped (const std::vector &clippingPlanes, const CVector &camPos) { // If the entity still have no position, and still have no mode, entity should not be displayed. if(mode() == MBEHAV::UNKNOWN_MODE || _First_Pos) return true; // If the entity is not displayable, count it as clipped. if(!_Displayable) return true; // Use Clip position (sphere clip) CVector clipPos = _Aabbox.getCenter(); clipPos.z+= _ClipDeltaZ - _Aabbox.getHalfSize().z; // _ClipDeltaZ is relative to pos on ground if (!_ForbidClipping) { // Speed Clip: clip just the sphere. // if out of only plane, entirely out. const uint count = clippingPlanes.size (); uint i; for(i=0;i_ClipRadius) return true; } } // Character clip float sqrdist = (camPos - clipPos).sqrnorm(); return (sqrdist > (ClientCfg.CharacterFarClip*ClientCfg.CharacterFarClip)); } //--------------------------------------------------- // setName : // Set the name of the entity. Handle replacement tag if any // to insert NPC task translated. //--------------------------------------------------- void CEntityCL::setEntityName(const ucstring &name) { _EntityName = name; } //--------------------------------------------------- // displayDebug : // Display Debug Informations. //--------------------------------------------------- void CEntityCL::displayDebug(float x, float &y, float lineStep) // virtual { // Type TextContext->printfAt(x, y, "Type: %d", Type); y += lineStep; // Slot TextContext->printfAt(x, y, "Slot: %d", _Slot); y += lineStep; // Outpost TextContext->printfAt(x, y, "Outpost id:%d side:%s",this->getOutpostId(),OUTPOSTENUMS::toString(this->getOutpostSide()).c_str() ); y += lineStep; // Name if(!getEntityName().empty()) TextContext->printAt(x, y, getEntityName()); else TextContext->printfAt(x, y, "Name not received"); y += lineStep; // Target TextContext->printfAt(x, y, "Target: %d", _TargetSlot); y += lineStep; // DataSet Id TextContext->printfAt(x, y, "DataSet Id: %u", _DataSetId); y += lineStep; // Sheet Id TextContext->printfAt(x, y, "Sheet: %d(%s)", _SheetId.asInt(), _SheetId.toString().c_str()); y += lineStep; // NPC Alias TextContext->printfAt(x, y, "NPCAlias: %u", _NPCAlias); y += lineStep; // Position #ifndef TMP_DEBUG_GUIGUI TextContext->printfAt(x, y, "Pos: %f %f %f", pos().x, pos().y, pos().z); #else TextContext->printfAt(x, y, "Pos: %f %f %f (Theoretical : %f %f %f)", pos().x, pos().y, pos().z, _TheoreticalPosition.x, _TheoreticalPosition.y, _TheoreticalPosition.z); #endif y += lineStep; // Display the Target Mode. TextContext->printfAt(x, y, "Mode: %d(%s) (Theoretical : %d(%s))", (sint)mode(), MBEHAV::modeToString(mode()).c_str(), (sint)_TheoreticalMode, MBEHAV::modeToString(_TheoreticalMode).c_str()); y += lineStep; // Display the Target Behaviour. TextContext->printfAt(x, y, "Behaviour: %d(%s)", (sint)behaviour(), MBEHAV::behaviourToString(behaviour()).c_str()); y += lineStep; // Front #ifndef TMP_DEBUG_GUIGUI TextContext->printfAt(x, y, "%f(%f,%f,%f) front", frontYaw(), front().x, front().y, front().z); #else TextContext->printfAt(x, y, "%f(%f,%f,%f) front (Theoretical : %f)", frontYaw(), front().x, front().y, front().z, _TheoreticalOrientation); #endif y += lineStep; // Direction TextContext->printfAt(x, y, "%f(%f,%f,%f) dir", atan2(dir().y, dir().x), dir().x, dir().y, dir().z); y += lineStep; // Last Retrieved pacs TextContext->printfAt(x, y, "Last PACS: (%.4f,%.4f,%.4f) (%d,%d,%.4f,%.4f,%.4f)", _LastRetrievedPosition.x, _LastRetrievedPosition.y, _LastRetrievedPosition.z, _LastRetrievedPacsPosition.InstanceId, _LastRetrievedPacsPosition.LocalPosition.Surface, _LastRetrievedPacsPosition.LocalPosition.Estimation.x, _LastRetrievedPacsPosition.LocalPosition.Estimation.y, _LastRetrievedPacsPosition.LocalPosition.Estimation.z); y += lineStep; // Current pacs UGlobalPosition gp; if (_Primitive) _Primitive->getGlobalPosition(gp, dynamicWI); float waterHeight = 0.0f; bool water = (GR ? GR->isWaterPosition(gp, waterHeight) : false); TextContext->printfAt(x, y, "Current PACS: (%d,%d,%.4f,%.4f,%.4f) %s %.1f", gp.InstanceId, gp.LocalPosition.Surface, gp.LocalPosition.Estimation.x, gp.LocalPosition.Estimation.y, gp.LocalPosition.Estimation.z, (water ? "WATER" : "LANDSCAPE"), waterHeight); y += lineStep; }// displayDebug // //----------------------------------------------- //----------------------------------------------- void CEntityCL::displayDebugPropertyStages(float /* x */, float &y, float /* lineStep */) { } //----------------------------------------------- // serial : // Serialize entity. //----------------------------------------------- void CEntityCL::serial(class NLMISC::IStream &f) throw(NLMISC::EStream) { readWrite(f); if(f.isReading()) load(); }// serial // //----------------------------------------------- // readWrite : // Read/Write Variables from/to the stream. //----------------------------------------------- void CEntityCL::readWrite(class NLMISC::IStream &f) throw(NLMISC::EStream) // virtual { f.serialVersion(4); // PUBLIC // PROTECTED f.serial(_DataSetId); f.serial(_SheetId); // NL3D::UPlayList *_PlayList; // NL3D::UPlayList *_FacePlayList; // NL3D::UVisualCollisionEntity *_CollisionEntity; // NL3D::USkeleton *_Skeleton; f.serial(_Slot); f.serial(_TargetSlot); f.serial(_Position); f.serialEnum(_Mode); f.serial(_CurrentBehaviour); f.serial(_Properties); f.serial(_EntityName); f.serial(_PermanentStatutIcon); // f.serial(_NameEx); // NLPACS::UMovePrimitive *_Primitive; // CEntityLogicInfo3D _LogicInfo3D; bool asyncTextureLoading = _AsyncTextureLoading; f.serial(asyncTextureLoading); _AsyncTextureLoading = asyncTextureLoading; bool lodTextureDirty = _LodTextureDirty; f.serial(lodTextureDirty); _LodTextureDirty = lodTextureDirty; f.serial(_Aabbox); f.serial(_Parent); // TChildren _Children; // UInstance _Instance; // std::vector _Instances; f.serial(_Front); f.serial(_Up); f.serial(_Dir); f.serial(_DirMatrix); f.serial(_LastFramePos); bool firstPos = _First_Pos; f.serial(firstPos); _First_Pos = firstPos; bool firstPosManaged = _FirstPosManaged; f.serial(firstPosManaged); _FirstPosManaged = firstPosManaged; bool flyier = _Flyer; f.serial(flyier); _Flyer = flyier; f.serial(_TargetAngle); // PRIVATE }// readWrite // //----------------------------------------------- // load : // To call after a read from a stream to re-initialize the entity. //----------------------------------------------- void CEntityCL::load() // virtual { }// load // //----------------------------------------------- // onStringAvailable : // Callback when the name is arrived. //----------------------------------------------- void CEntityCL::onStringAvailable(uint /* stringId */, const ucstring &value) { _EntityName = value; // remove the shard name if possible _EntityName= removeShardFromName(_EntityName); // New title ucstring newtitle; _HasReservedTitle = false; // check if there is any replacement tag in the string ucstring::size_type p1 = _EntityName.find('$'); if (p1 != ucstring::npos) { // we found a replacement point begin tag ucstring::size_type p2 = _EntityName.find('$', p1+1); if (p2 != ucstring::npos) { // ok, we have the second replacement point! // extract the replacement id ucstring id = _EntityName.substr(p1+1, p2-p1-1); // retrieve the translated string _TitleRaw = id.toString(); // ucstring replacement = CI18N::get(strNewTitle); bool womenTitle = false; CCharacterCL * c = dynamic_cast(this); if(c) { if( c->getGender() == GSGENDER::female ) womenTitle = true; } const ucstring replacement(STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw,womenTitle)); if (!replacement.empty() || !ClientCfg.DebugStringManager) { // build the final name _EntityName = _EntityName.substr(0, p1); // + _Name.substr(p2+1) // Get extended name _NameEx = replacement; newtitle = _NameEx; } CHARACTER_TITLE::ECharacterTitle titleEnum = CHARACTER_TITLE::toCharacterTitle( _TitleRaw ); if ( titleEnum >= CHARACTER_TITLE::BeginGmTitle && titleEnum <= CHARACTER_TITLE::EndGmTitle ) { _GMTitle = titleEnum - CHARACTER_TITLE::BeginGmTitle; _HasReservedTitle = true; } else { _GMTitle = _InvalidGMTitleCode; if ( titleEnum == CHARACTER_TITLE::FBT ) _HasReservedTitle = true; } } } // Is the first title or a new title ? if ( !_Title.empty() && (_Slot==0) ) { // Context help contextHelp ("title"); } _Title = newtitle; // Update interface with new title if (_Slot == 0) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CViewText *pVT = dynamic_cast(pIM->getElementFromId("ui:interface:player:header_opened:player_title")); if (pVT != NULL) pVT->setText(_Title); CGroupContainer *pGC = dynamic_cast(pIM->getElementFromId("ui:interface:player")); if (pGC != NULL) pGC->setUCTitle(_EntityName); CSkillManager *pSM = CSkillManager::getInstance(); pSM->setPlayerTitle(_TitleRaw); } // Must rebuild the in scene interface 'cause name has changed buildInSceneInterface (); }// onStringAvailable // //----------------------------------------------- // getTitleFromName //----------------------------------------------- string CEntityCL::getTitleFromName(const ucstring &name) { ucstring::size_type p1 = name.find('$'); if (p1 != ucstring::npos) { ucstring::size_type p2 = name.find('$', p1 + 1); if (p2 != ucstring::npos) return name.toString().substr(p1+1, p2-p1-1); } return ""; }// getTitleFromName // //----------------------------------------------- // removeTitleFromName //----------------------------------------------- ucstring CEntityCL::removeTitleFromName(const ucstring &name) { ucstring::size_type p1 = name.find('$'); if (p1 == ucstring::npos) { return name; } else { ucstring::size_type p2 = name.find('$', p1 + 1); if (p2 != ucstring::npos) { return name.substr(0, p1) + name.substr(p2 + 1); } else { return name.substr(0, p1); } } }// removeTitleFromName // //----------------------------------------------- // removeShardFromName //----------------------------------------------- ucstring CEntityCL::removeShardFromName(const ucstring &name) { // The string must contains a '(' and a ')' ucstring::size_type p0= name.find('('); ucstring::size_type p1= name.find(')'); if(p0==ucstring::npos || p1==ucstring::npos || p1<=p0) return name; // if it is the same as the shard name of the user, remove it if(ucstrnicmp(name, p0+1, p1-p0-1, PlayerSelectedHomeShardName)==0) return name.substr(0,p0) + name.substr(p1+1); // else don't modify else return name; } //----------------------------------------------- // removeTitleAndShardFromName //----------------------------------------------- ucstring CEntityCL::removeTitleAndShardFromName(const ucstring &name) { return removeTitleFromName(removeShardFromName(name)); } //----------------------------------------------- class CUpdateEntitiesColor : public IActionHandler { public: virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CEntityCL::_EntitiesColor[CEntityCL::User] = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:USER")->getValueRGBA(); CEntityCL::_EntitiesColor[CEntityCL::Player] = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PLAYER")->getValueRGBA(); CEntityCL::_EntitiesColor[CEntityCL::NPC] = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:NPC")->getValueRGBA(); CEntityCL::_EntitiesColor[CEntityCL::Fauna] = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:FAUNA")->getValueRGBA(); CEntityCL::_EntitiesColor[CEntityCL::ForageSource] = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:SOURCE")->getValueRGBA(); CEntityCL::_DeadColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:DEAD")->getValueRGBA(); CEntityCL::_TargetColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:TARGET")->getValueRGBA(); CEntityCL::_GroupColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:GROUP")->getValueRGBA(); CEntityCL::_GuildColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:GUILD")->getValueRGBA(); CEntityCL::_UserMountColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:MOUNT")->getValueRGBA(); CEntityCL::_UserPackAnimalColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:BEAST")->getValueRGBA(); CEntityCL::_PvpEnemyColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPENEMY")->getValueRGBA(); CEntityCL::_PvpAllyColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLY")->getValueRGBA(); CEntityCL::_PvpAllyInTeamColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLYINTEAM")->getValueRGBA(); CEntityCL::_PvpAllyInGuildColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLYINGUILD")->getValueRGBA(); CEntityCL::_PvpNeutralColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPNEUTRAL")->getValueRGBA(); // don't save these colors in .icfg because players can't change them CEntityCL::_GMTitleColor[ CHARACTER_TITLE::SGM - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:SGM")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::GM - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:GM")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::VG - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:VG")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::SG - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:SG")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::G - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:G")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::CM - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:CM")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::EM - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:EM")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::EG - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:EG")->getValueRGBA(); CEntityCL::_GMTitleColor[ CHARACTER_TITLE::OBSERVER - CHARACTER_TITLE::BeginGmTitle ] = pIM->getDbProp("UI:INTERFACE:ENTITY:COLORS:OBSERVER")->getValueRGBA(); } }; REGISTER_ACTION_HANDLER (CUpdateEntitiesColor, "update_entities_color"); //----------------------------------------------- bool CEntityCL::isTarget () const { return UserEntity && UserEntity->selection() == _Slot; } //----------------------------------------------- bool CEntityCL::isInGuild () const { if (Type != Player && Type != User) return false; const uint32 guildNameId = this->getGuildNameID(); if (guildNameId != 0 && UserEntity && guildNameId == UserEntity->getGuildNameID()) return true; return false; } //----------------------------------------------- NLMISC::CRGBA CEntityCL::getColor () const { // target if (isTarget()) return _TargetColor; // dead if (isReallyDead()) return _DeadColor; // mount if (isUserMount()) return _UserMountColor; // pack animal if (isUserPackAnimal()) return _UserPackAnimalColor; // GM if ( ( Type == Player || Type == User ) && _GMTitle != _InvalidGMTitleCode ) { return _GMTitleColor[_GMTitle]; } if (Type == Player || Type == NPC) { // enemy if( Type == NPC ) { if (isAnOutpostEnemy()) { return _PvpEnemyColor; } } else { if (isEnemy()) { return _PvpEnemyColor; } } // neutral pvp if (isNeutralPVP()) { return _PvpNeutralColor; } // ally if (isAlly()) { if (isInTeam()) return _PvpAllyInTeamColor; if(isInGuild()) return _PvpAllyInGuildColor; return _PvpAllyColor; } // neutral if (isInTeam()) return _GroupColor; if (isInGuild()) return _GuildColor; } return _EntitiesColor[Type]; } //--------------------------------------------------- // indoor : // Return true if the current position is an indoor position. //--------------------------------------------------- bool CEntityCL::indoor() const { if(_Primitive && GR) { UGlobalPosition gPos; _Primitive->getGlobalPosition(gPos, dynamicWI); return GR->isInterior(gPos); } // Return false if any problem return false; }// indoor // //----------------------------------------------- // pos : //----------------------------------------------- void CEntityCL::pos(const NLMISC::CVectorD &vect) { CVectorD checkDist = _PositionLimiter-vect; checkDist.z = 0.0; if(checkDist != CVectorD::Null) { if(checkDist.norm() > ClientCfg.PositionLimiterRadius) { checkDist.normalize(); _PositionLimiter = vect + checkDist*ClientCfg.PositionLimiterRadius/2.0; } } _Position = vect; }// pos // //----------------------------------------------- void CEntityCL::updateMissionTarget() { // Update the mission target flag _MissionTarget = false; if (_NameId) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); uint i,j; for (i=0; igetDbProp("SERVER:MISSIONS:"+toString(i)+":TARGET"+toString(j)+":TITLE", false); if (prop) { _MissionTarget = _NameId == (uint32)prop->getValue32(); if (_MissionTarget) return; } } } } //----------------------------------------------- uint CEntityCL::getGroundType() const { // default: 1 meter const float srqCacheDistLimit= 1; // If the user is not too far from cached pos, return same value if((_GroundTypeCachePos-pos()).sqrnorm() < srqCacheDistLimit) { return _GroundTypeCache; } uint gt; if (indoor()) { if(GR) { UGlobalPosition gPos; getPrimitive()->getGlobalPosition(gPos, dynamicWI); gt= GR->getMaterial(gPos); } else gt= 0; } else { // outside NL3D::CSurfaceInfo si; _CollisionEntity->getSurfaceInfo(pos(), si); gt= si.UserSurfaceData; } // store in cache _GroundTypeCachePos= pos(); _GroundTypeCache= gt; return gt; } //--------------------------------------------------- void CEntityCL::makeTransparent(bool t) { if (t == true) _TranspFactor += ((float)(DT)) / ((float)RZ_TIME_TO_BECOME_TRANSPARENT_IN_SECOND); else _TranspFactor -= ((float)(DT)) / ((float)RZ_TIME_TO_BECOME_TRANSPARENT_IN_SECOND); if (_TranspFactor < 0.0) _TranspFactor = 0.0; if (_TranspFactor > 1.0) _TranspFactor = 1.0; uint32 opaMin= getOpacityMin(); uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor)); for (uint32 i = 0; i < _Instances.size(); ++i) { _Instances[i].makeInstanceTransparent(opacity, (uint8)opaMin); } }// makeTransparent // //--------------------------------------------------- void CEntityCL::makeTransparent(float factor) { clamp(factor, 0.f, 1.f); _TranspFactor = 1.f - factor; uint32 opaMin= getOpacityMin(); uint8 opacity = (uint8)(opaMin + (255-opaMin) * (1.0 - _TranspFactor)); for (uint32 i = 0; i < _Instances.size(); ++i) { _Instances[i].makeInstanceTransparent(opacity, (uint8)opaMin); } } // *************************************************************************** void CEntityCL::setDiffuse(bool onOff, NLMISC::CRGBA diffuse) { for (uint32 i = 0; i < _Instances.size(); ++i) { _Instances[i].setDiffuse(onOff, diffuse); } } // *************************************************************************** CCDBNodeLeaf *CEntityCL::getOpacityDBNode() { if (!_OpacityMinNodeLeaf) { CInterfaceManager *pIM= CInterfaceManager::getInstance(); _OpacityMinNodeLeaf = pIM->getDbProp("UI:SAVE:USER_CHAR_OPA_MIN", false); } return _OpacityMinNodeLeaf; } // *************************************************************************** uint32 CEntityCL::getOpacityMin() { CCDBNodeLeaf *pNL = getOpacityDBNode(); if(pNL) { uint32 val= pNL->getValue32(); clamp(val, (uint32)0, (uint32)255); return val; } else // this is an error case... return 128; } // *************************************************************************** void CEntityCL::setOpacityMin(uint32 value) { CCDBNodeLeaf *pNL = getOpacityDBNode(); if(pNL) { clamp(value, (uint32)0, (uint32)255); pNL->setValue32(value); } } // *************************************************************************** /* * Return true if the in-scene interface must be shown */ bool CEntityCL::mustShowInsceneInterface( bool enabledInSheet ) const { return ( (enabledInSheet /*&& !CNPCIconCache::getInstance().getNPCIcon(this).getTextureMain().empty()*/) && (_InSceneInterfaceEnabled) && ( ClientCfg.Names || isUser () || ((getDisplayOSDForceOver() || ClientCfg.ShowNameUnderCursor) && slot()==SlotUnderCursor) || (ClientCfg.ShowNameSelected && UserEntity && slot()==UserEntity->selection()) || (isInTeam() && !isUser ()) || (UserEntity && ((UserEntity->pos()-pos()).sqrnorm() < ClientCfg.ShowNameBelowDistanceSqr) && !isUser ()) ) ); } //----------------------------------------------- const char *CEntityCL::getBoneNameFromBodyPart(BODY::TBodyPart part, BODY::TSide side) const { BODY::TBodyPart hominPart = BODY::getMatchingHominBodyPart(part); switch(hominPart) { case BODY::HHead: return "Bip01 Head"; case BODY::HChest: return "Bip01 Spine2"; case BODY::HArms: return side == BODY::Left ? "Bip01 L UpperArm" : "Bip01 R UpperArm"; case BODY::HHands: return side == BODY::Left ? "Bip01 L Hand" : "Bip01 R Hand"; case BODY::HLegs: return side == BODY::Left ? "Bip01 L Calf" : "Bip01 R Calf"; case BODY::HFeet: return side == BODY::Left ? "Bip01 L Foot" : "Bip01 R Foot"; } return NULL; } //----------------------------------------------- // idx2Inst : // Return a pointer on an instance structure. //----------------------------------------------- CEntityCL::SInstanceCL *CEntityCL::idx2Inst(uint idx) { if(idx < _Instances.size()) // CEntityCL::BadIndex is the max so idx < ... mean idx != CEntityCL::BadIndex too return &_Instances[idx]; return NULL; }// idx2Inst // //----------------------------------------------- void CEntityCL::updateIsInTeam () { CInterfaceManager *pIM= CInterfaceManager::getInstance(); _IsInTeam= false; // if UId not set, false if(dataSetId()==CLFECOMMON::INVALID_CLIENT_DATASET_INDEX) return; for (uint i=0; igetDbProp(toString(TEAM_DB_PATH ":%d:UID", i), false); CCDBNodeLeaf *presentProp = pIM->getDbProp(toString(TEAM_DB_PATH ":%d:NAME", i), false); // If same Entity uid than the one in the Database, ok the entity is in the Player TEAM!! if (uidProp && uidProp->getValue32() == (sint32)dataSetId() && presentProp && presentProp->getValueBool() ) { _IsInTeam= true; return; } } } //----------------------------------------------- void CEntityCL::updateIsUserAnimal () { CInterfaceManager *pIM= CInterfaceManager::getInstance(); _IsUserMount= false; _IsUserPackAnimal= false; // if UId not set, false if(dataSetId()==CLFECOMMON::INVALID_CLIENT_DATASET_INDEX) return; // Am i a pack animal? for (uint i=0; igetDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:UID", i), false); CCDBNodeLeaf *statusProp = pIM->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS", i), false); CCDBNodeLeaf *typeProp = pIM->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:TYPE", i), false); // I must have the same Id, and the animal entry must be ok. if(uidProp && statusProp && typeProp && uidProp->getValue32() == (sint32)dataSetId() && ANIMAL_STATUS::isSpawned((ANIMAL_STATUS::EAnimalStatus)(statusProp->getValue32()) )) { switch(typeProp->getValue16()) { case ANIMAL_TYPE::Mount: _IsUserMount = true; break; default: case ANIMAL_TYPE::Packer: _IsUserPackAnimal = true; break; } return; } } } //----------------------------------------------- ANIMAL_STATUS::EAnimalStatus CEntityCL::getPackAnimalStatus() const { CInterfaceManager *pIM= CInterfaceManager::getInstance(); if(! (isUserPackAnimal() || isUserMount())) return 0; // Am i a pack animal? for (uint i=0; igetDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:UID", i), false); CCDBNodeLeaf *statusProp = pIM->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS", i), false); // I must have the same Id, and the animal entry must be ok. if(uidProp && statusProp && uidProp->getValue32() == (sint32)dataSetId()) return (ANIMAL_STATUS::EAnimalStatus)(statusProp->getValue32()); } return 0; } //----------------------------------------------- bool CEntityCL::getPackAnimalIndexInDB(sint &dbIndex) const { CInterfaceManager *pIM= CInterfaceManager::getInstance(); if(! (isUserPackAnimal() || isUserMount())) return false; // Am i a pack animal? for (uint i=0; igetDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:UID", i), false); CCDBNodeLeaf *statusProp = pIM->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:STATUS", i), false); // I must have the same Id, and the animal entry must be ok. if(uidProp && statusProp && uidProp->getValue32() == (sint32)dataSetId()) { dbIndex= i; return true; } } return false; } //----------------------------------------------- void CEntityCL::dataSetId(CLFECOMMON::TClientDataSetIndex dataSet) { _DataSetId = dataSet; // additionaly, on a UID change, must check the IsInTeam and IsAniml flags updateIsInTeam(); updateIsUserAnimal(); // and update Bar Manager, only if correctly init if(_Slot!=CLFECOMMON::INVALID_SLOT && _DataSetId!=CLFECOMMON::INVALID_CLIENT_DATASET_INDEX) CBarManager::getInstance()->addEntity(_Slot, _DataSetId); } // *************************************************************************** void CEntityCL::updateVisible(const NLMISC::TTime &/* time */, CEntityCL * /* target */) { // update some shadowmap properties updateShadowMapProperties(); // For Skeleton Spawn Animation, must update some pos if(!_Skeleton.empty()) { _Skeleton.setSSSWOPos(pos()); _Skeleton.setSSSWODir(front()); } if (!R2::isEditionCurrent()) { // NB : if editor is enabled, then highlighting is managed in another fashion (there may be multiple-semihighlights) // Selection Highlight if(_VisualSelectionTime != 0) { bool blinkOn = false; sint64 t= T1 -_VisualSelectionTime; // hardcoded blink animation if(t>150 && t<300) blinkOn= true; if(t>450) _VisualSelectionTime = 0; setVisualSelectionBlink(blinkOn, CRGBA::White); } else { // If i am the target if(isTarget()) // set semi-highlight setVisualSelectionBlink(true, CRGBA(100,100,100)); // else If i am under cursor else if(ShowInterface && isUnderCursor()) // highlight setVisualSelectionBlink(true, CRGBA::White); else // disable hightlight setVisualSelectionBlink(false, CRGBA::White); } } } // *************************************************************************** void CEntityCL::updateSomeClipped(const NLMISC::TTime &/* time */, CEntityCL * /* target */) { // update some shadowmap properties updateShadowMapProperties(); } // *************************************************************************** void CEntityCL::updateClipped(const NLMISC::TTime &/* currentTimeInMs */, CEntityCL * /* target */) { // hide only if I am not the target if (! isTarget()) { if (!_SelectionFX.empty() && Scene) { Scene->deleteInstance(_SelectionFX); _SelectionFX = NULL; } if (!_MouseOverFX.empty() && Scene) { Scene->deleteInstance(_MouseOverFX); _MouseOverFX = NULL; } } } // *************************************************************************** void CEntityCL::updateVisiblePostPos(const NLMISC::TTime &/* currentTimeInMs */, CEntityCL * /* target */) { if (R2::isEditionCurrent()) return; // selection managed by r2 editor in edition mode // No-op if I am not the current UserEntity Target if(isTarget()) { // activate selection fx if (_SelectionFX.empty()) { // Keep a static instance of the selection FX NL3D::UInstance instance = Scene->createInstance(ClientCfg.SelectionFX); if (!instance.empty()) { _SelectionFX.cast (instance); if (_SelectionFX.empty()) { // shape found, but not a particle system Scene->deleteInstance(instance); } } } } else { // No selection FX if (!_SelectionFX.empty() && Scene) { Scene->deleteInstance(_SelectionFX); } } // Mouse over SFX, only if the entity is selectable if (ShowInterface && !isTarget() && isUnderCursor() && properties().selectable()) { // activate selection fx if (_MouseOverFX.empty()) { // Keep a static instance of the selection FX NL3D::UInstance instance = Scene->createInstance(ClientCfg.MouseOverFX); if (!instance.empty()) { _MouseOverFX.cast (instance); if (_MouseOverFX.empty()) { // shape found, but not a particle system Scene->deleteInstance(instance); } } } } else { // No selection FX if (!_MouseOverFX.empty() && Scene) { Scene->deleteInstance(_MouseOverFX); } } if (!_StateFX.empty()) { // Build a matrix for the fx NLMISC::CMatrix mat; mat.identity(); // Scale const CVector &boxes = _SelectBox.getHalfSize (); // take mean of XY and Z float halfwidth = std::max(boxes.x, boxes.y); halfwidth = (halfwidth + boxes.z)/2; mat.setScale (halfwidth); // Pos. Avoid Flick in XY, but take precise Z CVector position; position = pos().asVector(); position.z= _SelectBox.getMin().z; mat.setPos(position); _StateFX.setTransformMode(NL3D::UTransformable::DirectMatrix); _StateFX.setMatrix(mat); if (skeleton()) _StateFX.setClusterSystem(skeleton()->getClusterSystem()); } else if (!_SelectionFX.empty() || !_MouseOverFX.empty()) { // Build a matrix for the fx NLMISC::CMatrix mat; mat.identity(); // Scale const CVector &boxes = _SelectBox.getHalfSize (); // take mean of XY and Z float halfwidth = std::max(boxes.x, boxes.y); halfwidth = (halfwidth + boxes.z)/2; mat.setScale (halfwidth * 2 * ClientCfg.SelectionFXSize); // Pos. Avoid Flick in XY, but take precise Z CVector position; position = pos().asVector(); position.z= _SelectBox.getCenter().z; mat.setPos(position); if (!_SelectionFX.empty()) { _SelectionFX.setTransformMode(NL3D::UTransformable::DirectMatrix); _SelectionFX.setMatrix(mat); if (skeleton()) _SelectionFX.setClusterSystem(skeleton()->getClusterSystem()); // Colorize the selection depending of the level of the creature { CRGBA col = CRGBA(0,0,0); uint8 nForce = CInterfaceManager::getInstance()->getDbProp("SERVER:TARGET:FORCE_RATIO")->getValue8(); _SelectionFX.setUserParam(0, 0.1f*nForce + 0.1f); } } if (!_MouseOverFX.empty()) { _MouseOverFX.setTransformMode(NL3D::UTransformable::DirectMatrix); _MouseOverFX.setMatrix(mat); if (skeleton()) _MouseOverFX.setClusterSystem(skeleton()->getClusterSystem()); } } } // *************************************************************************** void CEntityCL::buildInSceneInterface () { } // *************************************************************************** void CEntityCL::doSetVisualSelectionBlink(bool bOnOff, CRGBA emitColor) { // blink all instances for(uint i = 0; i < instances().size(); ++i) { SInstanceCL &inst = instances()[i]; if(bOnOff) inst.setEmissive(emitColor); else inst.restoreEmissive(); } // also blink skeleton in CLod form if(!_Skeleton.empty()) { if(bOnOff) _Skeleton.setLodEmit(emitColor); else _Skeleton.setLodEmit(CRGBA::Black); } } // *************************************************************************** void CEntityCL::visualSelectionStart() { _VisualSelectionTime= T1; } // *************************************************************************** void CEntityCL::visualSelectionStop() { _VisualSelectionTime= 0; } // *************************************************************************** bool CEntityCL::canCastShadowMap() const { return ClientCfg.Shadows; } // *************************************************************************** void CEntityCL::updateCastShadowMap() { bool shadowOn= canCastShadowMap(); // if the entity has a skeleton if(skeleton()) { // then shadow will be done through the skeleton skeleton()->enableCastShadowMap(shadowOn); // disable shadows on instances if(_SomeInstanceCastShadowMap) { for(uint i = 0; i < _Instances.size(); i++) { if(!_Instances[i].Current.empty()) _Instances[i].Current.enableCastShadowMap(false); } _SomeInstanceCastShadowMap= false; } } else { // enable/disable cast shadow on instances instead (eg: on bot objects) // NB: must do it at each updateCastShadowMap(), cause don't know if an instance has been added or not for(uint i = 0; i < _Instances.size(); i++) { if(!_Instances[i].Current.empty()) { _Instances[i].Current.enableCastShadowMap(shadowOn); } } _SomeInstanceCastShadowMap= shadowOn; } // if shadow enabled, update the shadow properties if(shadowOn) { if(skeleton()) { skeleton()->setShadowMapDirectionZThreshold(_ShadowMapZDirClamp); skeleton()->setShadowMapMaxDepth(_ShadowMapMaxDepth); } else { for(uint i = 0; i < _Instances.size(); i++) { if(!_Instances[i].Current.empty()) { _Instances[i].Current.setShadowMapDirectionZThreshold(_ShadowMapZDirClamp); _Instances[i].Current.setShadowMapMaxDepth(_ShadowMapMaxDepth); } } } } } // *************************************************************************** void CEntityCL::updateShadowMapProperties() { /* Choose the z clamp direction wether or not the player is on "interior" stuff. In "interior" stuff, the ZClamp direction is lesser, to avoid some problems of "cast shadow behind the walls" Also choose the MaxDepth of shadow map wether or not the player is on "interior" stuff. In "interior" stuff, the MaxDepth is lesser to, to avoid some problems with the "bud": when the player go up stairs and when in the "bud", the shadow still appears on landscape */ float zDirClampWanted= ClientCfg.ShadowZDirClampLandscape; float maxDepth= ClientCfg.ShadowMaxDepthLandscape; if(indoor()) { zDirClampWanted= ClientCfg.ShadowZDirClampInterior; maxDepth= ClientCfg.ShadowMaxDepthInterior; } // smooth over time if(_ShadowMapZDirClamp!=zDirClampWanted || _ShadowMapMaxDepth!=maxDepth) { // if some time passed since last update sint64 dt= T1-_ShadowMapPropertyLastUpdate; if(dt!=0) { // update _ShadowMapZDirClamp if(_ShadowMapZDirClampzDirClampWanted) _ShadowMapZDirClamp= zDirClampWanted; } else if(_ShadowMapZDirClamp>zDirClampWanted) { _ShadowMapZDirClamp-= ClientCfg.ShadowZDirClampSmoothSpeed * 0.001f * dt; if(_ShadowMapZDirClampmaxDepth) _ShadowMapMaxDepth=maxDepth; } else if(_ShadowMapMaxDepth>maxDepth) { _ShadowMapMaxDepth-= ClientCfg.ShadowMaxDepthSmoothSpeed * 0.001f * dt; if(_ShadowMapMaxDepthdeleteInstance(_SelectionFX); } } // *************************************************************************** EGSPD::CPeople::TPeople CEntityCL::people() const { return EGSPD::CPeople::Unknown; } // *************************************************************************** void CEntityCL::setPeople(EGSPD::CPeople::TPeople /* people */) { } // *************************************************************************** void CEntityCL::forceEvalAnim() { if (getLastClip()) { // find highest father in the hierarchy CEntityCL *parentEntity = this; for(;;) { CEntityCL *nextParent = EntitiesMngr.entity(parentEntity->parent()); if (!nextParent) break; parentEntity = nextParent; } // Snap the parent entity to the ground. { H_AUTO ( RZ_Client_Entity_CL_Update_Snap_To_Ground ) parentEntity->snapToGround(); } // Animate the parent entity (and also child entities) { H_AUTO ( RZ_Client_Entity_CL_Update_Display ) parentEntity->updateDisplay(); } } } // *************************************************************************** const NLMISC::CAABBox &CEntityCL::localSelectBox() { // recompute the selection box? if(_LastLocalSelectBoxComputeTime(_Instance).getScale(scale); } else if (!_Instances.empty()) { if (!_Instances[0].Current.empty()) { const_cast(_Instances[0].Current).getScale(scale); } else if (!_Instances[0].Loading.empty()) { const_cast(_Instances[0].Loading).getScale(scale); } } } //---------------------------------------------------------------------- bool CEntityCL::isAnOutpostEnemy() const { if ( getOutpostId() != 0 ) { if( UserEntity->getOutpostId() == getOutpostId() ) { if( UserEntity->getOutpostSide() != getOutpostSide() ) { // same outpost but different side return true; } } } return false; } //---------------------------------------------------------------------- bool CEntityCL::isAnOutpostAlly() const { if ( getOutpostId() != 0 ) { if( UserEntity->getOutpostId() == getOutpostId() ) { if( UserEntity->getOutpostSide() == getOutpostSide() ) { // same outpost and same side return true; } } } return false; } //---------------------------------------------------------------------- CVector CEntityCL::dirToTarget() const { CVector dir2Target; CEntityCL * target = EntitiesMngr.entity( targetSlot() ); if( target ) { dir2Target = target->pos() - pos(); dir2Target.z = 0; dir2Target.normalize(); } else { dir2Target = dir(); } return dir2Target; } //---------------------------------------------------------------------- void CEntityCL::setStateFx(const std::string &fxName) { if (fxName != _StateFXName) { if (!_StateFX.empty() && Scene) { Scene->deleteInstance(_StateFX); } NL3D::UInstance instance = Scene->createInstance(fxName); if (!instance.empty()) { _StateFX.cast (instance); if (_StateFX.empty()) { // shape found, but not a particle system Scene->deleteInstance(instance); } else { _StateFX.setScale(0.5, 0.5, 0.5); _StateFXName = fxName; } } } } //---------------------------------------------------------------------- void CEntityCL::removeStateFx() { if (!_StateFX.empty() && Scene) { Scene->deleteInstance(_StateFX); _StateFXName = ""; } }