////////////////////////////////////////////////////////////////////// // // Crytek CryENGINE Source code // // File:Entity.cpp // Description: Basic Entity functions // An entity can be any object (player,car,rocket,items etc.) with behaviours // defined by script // // History: // -Feb 14,2001:Originally created by Marco Corbetta // -: modified by Everyone :) // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "EntitySystem.h" #include #include #include "Entity.h" #include #include #include #include #include #include #include #include "CryHeaders.h" #include #include #include #include #include #include //#include "list2.h" #define POS_EPSILON 0.0001f #define ANGLES_EPSILON 0.0001f #define SCRIPT_SERVER_STATE "Server" #define SCRIPT_CLIENT_STATE "Client" #if defined(_DEBUG) && !defined(LINUX) static char THIS_FILE[] = __FILE__; #define DEBUG_CLIENTBLOCK new( _NORMAL_BLOCK, THIS_FILE, __LINE__) #define new DEBUG_CLIENTBLOCK #endif ////////////////////////////////////////////////////////////////////////// CEntity::EntPartEmitter::~EntPartEmitter() { if (pEmitter) GetISystem()->GetI3DEngine()->DeleteParticleEmitter( pEmitter ); } ////////////////////////////////////////////////////////////////////////// //! This structure must 100% match EScriptStateFunctions enum. static const char* s_ScriptStateFunctions[] = { "OnBeginState", "OnEndState", "OnUpdate", "OnContact", "OnTimer", "OnEvent", "OnDamage", "OnEnterArea", "OnLeaveArea", "OnProceedFadeArea", "OnBind", "OnUnBind", "OnMove", "OnCollide", "OnStopRollSlideContact", // reserved. "", }; ////////////////////////////////////////////////////////////////////////// // checks if the input angles are valid and returns them; // if a component is invalid, then returns 0 in that component inline Vec3d ValidateAngles(Vec3d v) { if (!(-1e+9 < v.x && v.x < 1e+9)) v.x = 0; if (!(-1e+9 < v.y && v.y < 1e+9)) v.y = 0; if (!(-1e+9 < v.z && v.z < 1e+9)) v.z = 0; return v; } ////////////////////////////////////////////////////////////////////// CEntity::CEntity(CEntitySystem *pEntitySystem, ISystem * pISystem,IScriptSystem *pSS):m_pAnimationEventParams(pSS), m_pObjectCollide(pSS),m_vObjPosCollide(pSS),m_vObjVelCollide(pSS),m_pSplashList(pSS) //,m_vNormDirCollide(pSS) { // m_pCurrIndoorSector = (CIndoorSector*)-1; // not defined at begining //bigger slot with a character m_nLastVisibleFrameID = 0; m_bRecalcBBox = true; //[kirill] no slipping entities for MP game - on client it can never wakeup if(GetISystem()->GetIGame()->GetModuleState(EGameMultiplayer)) m_bSleeping = false; else m_bSleeping = true; m_matParentMatrix.SetIdentity(); m_bForceBindCalculation = false; m_nMaxCharNum=0; m_bHidden=false; m_cLastStateID=0; m_bTrackable=false; m_fLastSubMergeFracion=0.0f; m_flags = 0; m_nTimer=-1; //m_nStartTimer=0; m_pLipSync=NULL; m_center(0, 0, 0); m_angles(0, 0, 0); m_physic = m_physPlaceholder = NULL; m_pPhysState = 0; m_iPhysStateSize = 0; m_iPhysType = PHYS_NONE; m_physicEnabled = true; m_fRollTimeout=0.0f; m_fSlideTimeout=0.0f; //m_static = false; for (int k = 0; k < MAX_ANIMATED_MODELS; k++) { m_pCryCharInstance[k] = NULL; m_pCharPhysPlaceholders[k] = 0; } // m_qsplat=NULL; SetName("No entity loaded"); m_pSaveFunc = 0; m_pLoadFunc = 0; m_pLoadRELEASEFunc = 0; m_pLoadPATCH1Func = 0; m_awakeCounter = 0; m_bUpdate = true; m_bIsBound = false; m_pScriptObject = 0; m_pClientState = 0; m_pServerState = 0; //m_vBoxMin(0,0,0); //m_vBoxMax(0,0,0); m_vBoxMin=m_vForceBBoxMin=SetMaxBB(); m_vBoxMax=m_vForceBBoxMax=SetMinBB(); m_fUpdateRadius = 0; m_fRadius = 0; m_fScale = 1; m_registeredInSector = false; // m_pLSource=0; m_bGarbage = false; // m_flags|=ETY_FLAG_NEED_UPDATE; m_flags |= ETY_FLAG_DESTROYABLE; m_pAIObject = 0; m_nID = 0; m_netPresence = true; // m_dirtyFlags = 0; m_pCamera = NULL; //How many times this entity was entity was serialized //m_nWriteNumber=0; //m_nLastNameUpdate=0; m_pEntitySystem = pEntitySystem; m_pScriptSystem =pSS; m_pContainer = NULL; m_pISystem = pISystem; m_bSave = true; m_pHeadBone = 0; //m_nIndoorArea=-1; //not set yet //register the default state RegisterState(""); m_bForceBBox = false; m_vPrevDrawCenter(-1000,-1000,-1000); m_vPrevDrawAngles(-1000,-1000,-1000); m_pDynLight = NULL; m_pEntityRenderState = 0;//m_pISystem->GetI3DEngine()->MakeEntityRenderState(); m_nSteeringWheelSlot=-1; // not found yet m_pOnCollide=NULL; m_pOnStopRollSlideContact=NULL; m_pParticleEmitters = NULL; m_dwRndFlags=0; m_fWaterDensity=1000.0f; m_eUpdateVisLevel = eUT_Always; // memset(m_narrDrawFrames,0,sizeof(m_narrDrawFrames)); m_bHandIK = false; m_fLastCollideTime=0; m_fLastSplashTime = 0; m_bInitialized=false; m_fTimeRolling = m_fTimeNotRolling = 0; m_PrevVertVel = 0.0f; m_vPrevVel.Set(0,0,0); m_pBBox=NULL; m_pColliders = NULL; m_bTrackColliders = false; m_bUpdateSounds = false; m_bUpdateAI = false; m_bUpdateEmitters = false; m_bUpdateScript = false; m_bUpdateContainer = false; m_bUpdateCharacters = false; m_bUpdateCamera = false; m_bUpdateBinds = false; m_bIsADeadBody = 0; m_bEntityLightsOn = 1; m_bVisible = m_bWasVisible = 0; m_idBoundTo = 0; m_bStateClientside=false; m_fScriptUpdateRate = 0; m_fScriptUpdateTimer = 0; m_fCharZOffsetCur = m_fCharZOffsetTarget = 0; m_nFlyingFrames = 100; } ////////////////////////////////////////////////////////////////////// CEntity::~CEntity() { // Destructor of entity must be almost empty. // All release functions must be in CEntity:ShutDown //m_pISystem->GetI3DEngine()->UnRegisterEntity(this); // [marco] make sure the timer gets stopped since is not // part of the entity anymore KillTimer(); // free m_pEntityRenderState member m_pISystem->GetI3DEngine()->FreeEntityRenderState(this); m_pEntityRenderState = 0; } ////////////////////////////////////////////////////////////////////// void CEntity::ShutDown() { std::vector < CEntityObject>::iterator it; ShutDownScript(); SAFE_RELEASE( m_pContainer ); m_bUpdateContainer = false; if (m_pScriptObject) m_pScriptObject->Release(); //if (HaveCamera() && m_lstBindings.empty()) //SetCamera(0); SAFE_RELEASE( m_pCamera ); m_bUpdateCamera = false; if (m_pDynLight) { delete m_pDynLight; m_pDynLight = NULL; } UnregisterInSector(); // todo: remove this line when 3dengine will stop using deleted pointers to entity // prev line should be enough m_pISystem->GetI3DEngine()->UnRegisterInAllSectors(this); for (it = m_objects.begin(); it < m_objects.end(); it++) { if ((* it).object) { m_pISystem->GetI3DEngine()->ReleaseObject((* it).object);// NOTE } /* if ((* it).image) { m_pISystem->GetIRenderer()->RemoveTexture((* it).image); }*/ } std::vector < IStatObj* >::iterator itaux; for (itaux = m_auxObjects.begin(); itaux < m_auxObjects.end(); itaux++) if (*itaux) m_pISystem->GetI3DEngine()->ReleaseObject(*itaux); m_auxObjects.clear(); ReleaseLipSyncInterface(); // we release lipsync before we destroy the character... // unload character for (int k = 0; k < MAX_ANIMATED_MODELS; k++) { if (m_pCryCharInstance[k]) m_pISystem->GetIAnimationSystem()->RemoveCharacter(m_pCryCharInstance[k]); if (m_pCharPhysPlaceholders[k]) m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_pCharPhysPlaceholders[k]); m_pCryCharInstance[k] = NULL; m_pCharPhysPlaceholders[k] = NULL; } if (m_physic) { m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physic); m_physic = NULL; } if (m_physPlaceholder) { m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physPlaceholder); m_physPlaceholder = NULL; } if (m_pBBox) { m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_pBBox); m_pBBox=NULL; } if (m_pPhysState) { delete[] m_pPhysState; m_pPhysState = 0; } // delete m_pLSource; // m_pLSource=0; if (m_pAIObject) { m_pISystem->GetAISystem()->RemoveObject(m_pAIObject); m_pAIObject = 0; m_bUpdateAI = false; } if (!m_lstBindings.empty()) { BINDLISTItor bi; for (bi=m_lstBindings.begin();bi!=m_lstBindings.end();bi++) { m_pEntitySystem->ReleaseMark( (*bi) ); } } m_bUpdateBinds = false; ////////////////////////////////////////////////////////////////////////// // Stop All attached sounds. ////////////////////////////////////////////////////////////////////////// for (SoundsList::iterator sndit = m_lstAttachedSounds.begin(); sndit != m_lstAttachedSounds.end(); ++sndit) { SAttachedSound &snd = *sndit; if (snd.pSound) snd.pSound->Stop(); } m_lstAttachedSounds.clear(); m_bUpdateSounds = false; ////////////////////////////////////////////////////////////////////////// // free part emitters SAFE_DELETE( m_pParticleEmitters ); m_bUpdateEmitters = false; SAFE_RELEASE( m_pLipSync ); SAFE_RELEASE( m_pCamera ); ////////////////////////////////////////////////////////////////////////// // Delete colliders list. ////////////////////////////////////////////////////////////////////////// delete m_pColliders; m_pColliders = NULL; } ////////////////////////////////////////////////////////////////////////// void CEntity::ShutDownScript() { if (m_pClientState) { // Call Client.OnShutDown _SmartScriptObject pClient(m_pScriptSystem, true); if (m_pScriptObject->GetValue(SCRIPT_CLIENT_STATE,pClient)) { HSCRIPTFUNCTION pOnInitFunc = 0; if (pClient->GetValue( SCRIPT_SHUTDOWN,pOnInitFunc )) { m_pScriptSystem->BeginCall(pOnInitFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pOnInitFunc); } } } if (m_pServerState) { // Call Server.OnShutDown _SmartScriptObject pServer(m_pScriptSystem, true); if (m_pScriptObject->GetValue(SCRIPT_SERVER_STATE,pServer)) { HSCRIPTFUNCTION pOnInitFunc = 0; if (pServer->GetValue( SCRIPT_SHUTDOWN,pOnInitFunc )) { m_pScriptSystem->BeginCall(pOnInitFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pOnInitFunc); } } else { // Call OnShutDown HSCRIPTFUNCTION pOnInitFunc = 0; // Default fallback if Server table not exist. if (m_pScriptObject->GetValue(SCRIPT_SHUTDOWN,pOnInitFunc)) { m_pScriptSystem->BeginCall(pOnInitFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pOnInitFunc); } } } if (m_pClientState) { ReleaseStateTable(*m_pClientState); } if (m_pServerState) { ReleaseStateTable(*m_pServerState); } SAFE_DELETE( m_pServerState ); SAFE_DELETE( m_pClientState ); } ////////////////////////////////////////////////////////////////////////// void CEntity::SetContainer(IEntityContainer* pContainer) { m_pContainer = pContainer; if (m_pContainer) m_bUpdateContainer = true; else m_bUpdateContainer = false; } // set the bbox of the entity ////////////////////////////////////////////////////////////////////// void CEntity::SetBBox(const Vec3d &mins, const Vec3d &maxs) { UnregisterInSector(); m_vForceBBoxMin=m_vBoxMin = mins; m_vForceBBoxMax=m_vBoxMax = maxs; if ((m_vBoxMin-m_center).Length() > (m_vBoxMax-m_center).Length()) m_fRadius = (m_vBoxMin-m_center).Length(); else m_fRadius = (m_vBoxMax-m_center).Length(); if (m_bTrackColliders) CreatePhysicsBBox(); m_bForceBBox = true; RegisterInSector(); } ////////////////////////////////////////////////////////////////////////// void CEntity::GetBBox(Vec3d &mins, Vec3d &maxs) { mins = m_vBoxMin + m_center; maxs = m_vBoxMax + m_center; } ////////////////////////////////////////////////////////////////////////// void CEntity::GetRenderBBox(Vec3d &mins, Vec3d &maxs) { mins = m_vBoxMin + m_center; maxs = m_vBoxMax + m_center; // include attached light into bbox if(m_pDynLight) { mins.CheckMin(m_center-Vec3d(m_pDynLight->m_fRadius,m_pDynLight->m_fRadius,m_pDynLight->m_fRadius)); maxs.CheckMax(m_center+Vec3d(m_pDynLight->m_fRadius,m_pDynLight->m_fRadius,m_pDynLight->m_fRadius)); } // include container lights into bbox if(m_pContainer) { float fContainerLightRadius = m_pContainer->GetLightRadius(); mins.CheckMin(m_center-Vec3d(fContainerLightRadius,fContainerLightRadius,fContainerLightRadius)); maxs.CheckMax(m_center+Vec3d(fContainerLightRadius,fContainerLightRadius,fContainerLightRadius)); } // make bbox at least 2 meter size particle system to make lighting calculations more stable if(m_pParticleEmitters && m_pParticleEmitters->size()) { mins-=Vec3d(1,1,1); maxs+=Vec3d(1,1,1); } } ////////////////////////////////////////////////////////////////////////// float CEntity::GetRenderRadius() const { return m_pDynLight ? max(m_fRadius,m_pDynLight->m_fRadius) : m_fRadius ; } // set the position of the entity ////////////////////////////////////////////////////////////////////// void CEntity::SetPos(const Vec3d &pos, bool bWorldOnly /* = true */) { // if flag calc physic is set then force this new position // to the physic system if (!bWorldOnly && (m_bIsBound||m_bForceBindCalculation)) { if (m_realcenter != pos) { // If moved m_awakeCounter = 1; } m_realcenter = pos; return; } //if (pos == m_center) - Kirill // return; MoveTo(pos); } //set entity's scale ////////////////////////////////////////////////////////////////////// void CEntity::SetScale( float scale ) { if(scale<0) scale=0; m_fScale = scale; // Set scale of physical entity if present. if (m_physic) { pe_params_pos params; params.scale = m_fScale; m_physic->SetParams(¶ms); } //CalcWholeBBox(); [PETAR] its now done in update m_bRecalcBBox = true; } void CEntity::SetPhysAngles(const Vec3d &angl) { if (m_physic &&(m_flags & ETY_FLAG_CALC_PHYSICS) && !(m_flags&ETY_FLAG_IGNORE_PHYSICS_UPDATE)) { pe_params_pos pp; //pp.q = quaternionf((vectorf)angl*(gf_PI/180.0f)); pp.q.SetRotationXYZ( (vectorf)angl*(gf_PI/180.0f) ); m_physic->SetParams(&pp); } } void CEntity::SetAngles(const Vec3d &sAngle, bool bNotifyContainer, bool bUpdatePhysics,bool forceInWorld) { /* // we filter out NaNs here Vec3d sAngle = ValidateAngles(pos); */ //sAngle.Snap360(); if (forceInWorld) { m_angles = sAngle; return; } else if (m_bIsBound||m_bForceBindCalculation) { m_realangles = sAngle; CalculateInWorld(); } else { Vec3d diff; diff = sAngle - m_angles; if (!(fabs(diff.x) < ANGLES_EPSILON && fabs(diff.y) < ANGLES_EPSILON && fabs(diff.z) < ANGLES_EPSILON)) { m_angles += diff; if (bUpdatePhysics && m_physic &&(m_flags & ETY_FLAG_CALC_PHYSICS)) { SetPhysAngles( m_angles ); // pe_params_pos pp; // pp.q = quaternionf(m_angles.z*(PI/180.0f),m_angles.y*(PI/180.0f),m_angles.x*(PI/180.0f)); // m_physic->SetParams(&pp); } } } // just in case :) //CalcWholeBBox(); {petar} its done in update now m_bRecalcBBox = true; if (m_pContainer && bNotifyContainer) m_pContainer->OnSetAngles(sAngle); // m_angles); } ////////////////////////////////////////////////////////////////////// const Vec3d & CEntity::GetPos(bool bWorldOnly /* = false */) const { if (!bWorldOnly && (m_bIsBound||m_bForceBindCalculation)) return m_realcenter; else return (m_center); } const Vec3d & CEntity::GetAngles(int realA) const { Vec3d angle; if( realA && (m_bIsBound||m_bForceBindCalculation)) return m_realangles; else return m_angles; /*if( angle->z < 0 ) { angle->z = 360.f + ((int)angle->z%360); } else angle->z = (float) ((int)angle->z%360); return *angle;*/ } // init the entity ////////////////////////////////////////////////////////////////////// bool CEntity::Init(CEntityDesc &ed) { m_nID = ed.id; m_netPresence = ed.netPresence; m_name = ed.name.c_str(); m_fScale = ed.scale; //SetPos(ed.pos); m_center = ed.pos; m_bIsADeadBody = 0; // need this before calling OnInit - it might eb changed there m_angles = ed.angles; _SmartScriptObject pObj(m_pScriptSystem, true); // do not touch the order of the next 3 lines if (m_pContainer) m_pContainer->SetEntity(this); ////////////////////////////////////////////////////////////////////////// // Check if needs to track collider. //[Timur] Only when explicitly set. //m_bTrackColliders = IsStateFunctionImplemented(ScriptState_OnEnterArea) || IsStateFunctionImplemented(ScriptState_OnLeaveArea); ////////////////////////////////////////////////////////////////////////// // Check if need script update function. m_bUpdateScript = IsStateFunctionImplemented(ScriptState_OnUpdate); ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Check if need ResolveCollisions for OnContact script function. m_bUpdateOnContact = IsStateFunctionImplemented(ScriptState_OnContact); ////////////////////////////////////////////////////////////////////////// // Client/Server state init. if (m_pServerState) { // Call Server.OnInit _SmartScriptObject pServer(m_pScriptSystem, true); if (m_pScriptObject->GetValue(SCRIPT_SERVER_STATE,pServer)) { HSCRIPTFUNCTION pOnInitFunc = 0; if (pServer->GetValue( SCRIPT_INIT,pOnInitFunc )) { m_pScriptSystem->BeginCall(pOnInitFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pOnInitFunc); } } else { // Default Fallback. m_pScriptSystem->BeginCall(m_sClassName.c_str(), SCRIPT_INIT); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); } } if (m_pClientState) { // Call Client.OnInit _SmartScriptObject pClient(m_pScriptSystem, true); if (m_pScriptObject->GetValue(SCRIPT_CLIENT_STATE,pClient)) { HSCRIPTFUNCTION pOnInitFunc = 0; if (pClient->GetValue( SCRIPT_INIT,pOnInitFunc )) { m_pScriptSystem->BeginCall(pOnInitFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); } m_pScriptSystem->ReleaseFunc(pOnInitFunc); } } ////////////////////////////////////////////////////////////////////////// if (m_pScriptObject) m_pScriptObject->SetValue("type",m_sClassName.c_str()); ////////////////////////////////////////////////////////////////////////// if (m_pContainer) m_pContainer->Init(); //SetCommonCallbacks(m_pScriptSystem); //if (strcmp("Pig1", m_name.c_str())==0) DEBUG_BREAK; m_bTrackable=false; if (m_pScriptObject) { bool bTrackable = false; // store certain properties so there is no need to get them every frame from script _SmartScriptObject pPropTable(m_pScriptSystem, true); if (m_pScriptObject->GetValue("Properties", pPropTable)) pPropTable->GetValue("bTrackable", bTrackable); m_bTrackable = bTrackable; } //Timur[1/31/2002] /* if (!m_pContactFunc) m_pISystem->GetILog()->LogToFile("[ENTITYWARNING] %s has no contact function, and no contact on it will be called",m_sClassName.c_str()); */ CalcWholeBBox(); // here its ok because its on init m_bInitialized=true; // If anything during init, changed it. if (!m_bTrackColliders) m_awakeCounter = 0; m_bWasAwake = 1; m_fCharZOffsetCur = m_fCharZOffsetTarget = 0; m_nFlyingFrames = 100; return true; } ////////////////////////////////////////////////////////////////////////// void CEntity::SetScriptObject(IScriptObject *pObject) { m_pScriptObject = pObject; if(m_pServerState){ ReleaseStateTable(*m_pServerState); delete m_pServerState; m_pServerState = 0; } if(m_pClientState){ ReleaseStateTable(*m_pClientState); delete m_pClientState; m_pClientState = 0; } if (!m_pScriptObject) return; // Cache script callbacks. HSCRIPTFUNCTION temp=0; m_pScriptObject->GetValue( "OnSave",temp ); m_pSaveFunc.Init(m_pScriptSystem,temp); temp=0; m_pScriptObject->GetValue( "OnLoad",temp ); m_pLoadFunc.Init(m_pScriptSystem,temp); temp=0; m_pScriptObject->GetValue( "OnLoadRELEASE",temp ); m_pLoadRELEASEFunc.Init(m_pScriptSystem,temp); m_pScriptObject->GetValue( "OnLoadPATCH1",temp ); m_pLoadPATCH1Func.Init(m_pScriptSystem,temp); bool bOnClient = m_pEntitySystem->ClientEnabled(); bool bOnServer = m_pEntitySystem->ServerEnabled(); _SmartScriptObject pServerTable(m_pScriptSystem,true); _SmartScriptObject pClientTable(m_pScriptSystem,true); // Get Server table if exist. bool bServerTable = m_pScriptObject->GetValue( SCRIPT_SERVER_STATE,pServerTable ); // Get Client table if exist. bool bClientTable = m_pScriptObject->GetValue( SCRIPT_CLIENT_STATE,pClientTable ); // Analyze script object for Client/Server states. if (bOnClient && bClientTable) { // Client state exist only on client and only if have Client table. m_pClientState = new SScriptState; InitializeStateTable( pClientTable,*m_pClientState ); } // If Neither Client neither Server states exist, fallback to single server state. // This provided for backward compatability support and for classes that dont care about Client/Server. if (bOnServer || !m_pClientState) { // Server state always exist on server (have it Server table or not). m_pServerState = new SScriptState; if (bServerTable) InitializeStateTable( pServerTable,*m_pServerState ); else InitializeStateTable( m_pScriptObject,*m_pServerState ); } } /* it sets common callback functions for those entities without a script, by calling a common script function for materials / sounds etc. Client side only. @param pScriptSystem pointer to script system */ ////////////////////////////////////////////////////////////////////////// void CEntity::SetCommonCallbacks(IScriptSystem *pScriptSystem) { m_pOnCollide.Init(pScriptSystem,pScriptSystem->GetFunctionPtr("CommonCallbacks", "OnCollide")); m_pOnStopRollSlideContact.Init(pScriptSystem,pScriptSystem->GetFunctionPtr("CommonCallbacks", "OnStopRollSlideContact")); } ////////////////////////////////////////////////////////////////////////// void CEntity::SetName(const char*name) { m_name = name; } ////////////////////////////////////////////////////////////////////////// Vec3d CEntity::GetSoundPos() { Vec3d vPos; IEntityContainer *pICnt=GetContainer(); if (pICnt) vPos=pICnt->CalcSoundPos(); else vPos=GetPos(); return vPos; } ////////////////////////////////////////////////////////////////////////// void CEntity::PlaySound(ISound *pSound, float fSoundScale, Vec3d &Offset) { m_bUpdateSounds = true; if (m_lstAttachedSounds.size()<15) { bool bWasPlaying = pSound->IsPlaying(); pSound->SetPosition(GetSoundPos()); pSound->Play(fSoundScale); // If this sound already playing don`t add it again for playback. if (!bWasPlaying && pSound->IsPlaying()) { m_lstAttachedSounds.push_back(SAttachedSound(pSound, Offset)); } } else { m_pISystem->GetILog()->Log("\001 warning trying to attach more than 15 sounds to %s",m_name.c_str()); int ddd=1; for(SoundsList::iterator sItr = m_lstAttachedSounds.begin();sItr!=m_lstAttachedSounds.end(); ++sItr,++ddd) { ISound *pDbg = (*sItr).pSound; m_pISystem->GetILog()->Log("\005 %d. %s",ddd,pDbg->GetName()); } // let's clear it, to restore from an erroneous situation m_lstAttachedSounds.clear(); } } ////////////////////////////////////////////////////////////////////////// void CEntity::MoveTo(const Vec3d &pos, bool moveObjects, bool bUpdatePhysics) { // move physics if (bUpdatePhysics && m_physic &&(m_flags & ETY_FLAG_CALC_PHYSICS)) { pe_params_pos temp; temp.pos = vectorf(pos.x, pos.y, pos.z); m_physic->SetParams(&temp); } if (fabs(pos.x-m_center.x) < POS_EPSILON && fabs(pos.y-m_center.y) < POS_EPSILON && fabs(pos.z-m_center.z) < POS_EPSILON) return; // assign the new position m_center = pos; m_awakeCounter = 1; // Awake object for one frame at least if sleeping. if (moveObjects) { // recalc the bbox again //CalcWholeBBox(); [Petar] moved in Update m_bRecalcBBox = true; } // Must be already initialized to call OnMove callback. if (m_bInitialized) { CallStateFunction( ScriptState_OnMove ); } } ////////////////////////////////////////////////////////////////////////// void CEntity::Reset() { // Not garbage anymore. m_bGarbage = false; // Go to sleep m_bSleeping = true; m_awakeCounter = 0; m_fRollTimeout=0.0f; m_fSlideTimeout=0.0f; m_fLastSubMergeFracion=0.0f; // Call script OnReset. if (m_pScriptObject) { HSCRIPTFUNCTION pOnResetFunc = 0; if (m_pScriptObject->GetValue( SCRIPT_ONRESET,pOnResetFunc )) { m_pScriptSystem->BeginCall(pOnResetFunc); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pOnResetFunc); } } if (m_pPhysState) { delete[] m_pPhysState; m_pPhysState = 0; } //[Timur] Its enough to make self:AwakePhysics(0) at end of OnReset in script. // /* //put physics to sleep IPhysicalEntity *phys = GetPhysics(); if(phys) { pe_action_awake aa; aa.bAwake=0; phys->Action(&aa); } */ m_PrevVertVel = 0.0f; m_vPrevVel.Set(0,0,0); // This is for testing only (at least now): to find out if anybody starts animations after the character dies for (int i =0; i < MAX_ANIMATED_MODELS; ++i) if (m_pCryCharInstance[i]) { m_pCryCharInstance[i]->EnableStartAnimation(true); m_pCryCharInstance[i]->StopAllMorphs(); } // Remove all colliders. if (m_pColliders) { delete m_pColliders; m_pColliders = NULL; } } ////////////////////////////////////////////////////////////////////////// void CEntity::SetDestroyable( bool b ) { if (b) m_flags |= ETY_FLAG_DESTROYABLE; else m_flags &= ~ETY_FLAG_DESTROYABLE; } ////////////////////////////////////////////////////////////////////////// bool CEntity::IsDestroyable() const { return (m_flags & ETY_FLAG_DESTROYABLE) == ETY_FLAG_DESTROYABLE; } // update the entity-this is called every frame and // updtae a bunch of various stuff ////////////////////////////////////////////////////////////////////// void CEntity::Update( SEntityUpdateContext &ctx ) { ENTITY_PROFILER // Decrease awake counter. if (m_awakeCounter > 0) m_awakeCounter--; /* //[Timur] This is not optimal to do for every entity every frame. // [Sergiy] this is a safety check to avoid invalid numbers that come from outside // and result in "invisible character" bug (because the entity can't calculate the bbox correctly // and can't be registered in a sector m_angles = ValidateAngles(m_angles); */ if (m_bIsBound || m_bForceBindCalculation) { // Must be before potential visibilty check, otherwise child entity position will be wrong. // this entity is bound or is forced to be calculated as bound CalculateInWorld(); //[Timur] This is needed for binded Dynamic lights, or they are not passing CheckUpdateVisLevel check. if (m_bRecalcBBox) { // Reregister entity in sectors. UnregisterInSector(); RegisterInSector(); } } // [PETAR] commented out this temp var - not really needed anymore // bool bEntityVisible = (ctx.nFrameID == m_nLastVisibleFrameID); // m_bWasVisible = bEntityVisible; m_bWasVisible = m_bVisible; m_bVisible = (m_eUpdateVisLevel==eUT_Unconditional) ? true : ((ctx.nFrameID - m_nLastVisibleFrameID)m_pVisCheckForUpdate->GetIVal()) { if (!CheckUpdateVisLevel( ctx,m_eUpdateVisLevel )) { return; } } // Calculate how many entities where actually updated. ctx.numUpdatedEntities++; if (m_bUpdateSounds) UpdateSounds( ctx ); // should be called before any rendering if (m_bVisible) { ctx.numVisibleEntities++; if (m_pLipSync) UpdateLipSync( ctx ); if (m_bEntityHasLights && m_bEntityLightsOn) { ProcessEntityLightSources(); } } if (m_bUpdateEmitters) { UpdateParticleEmitters( ctx ); } ////////////////////////////////////////////////////////////////////////// if (m_bUpdateOnContact) { //[Timur] @TODO remove this when will be possible. ResolveCollision(); } ////////////////////////////////////////////////////////////////////////// if (m_physic) { UpdatePhysics(ctx); }; /* ////////////////////////////////////////////////////////////////////////// // Update timer. ////////////////////////////////////////////////////////////////////////// if(m_nTimer>0) { m_awakeCounter = 2; // As long as there is timer, must stay awake. if ((ctx.fCurrTime*1000) - m_nStartTimer >= m_nTimer) { int t = m_nTimer; KillTimer(); OnTimer(t); } } */ // Update`s script function if present. if (m_bUpdateScript) { if (m_pEntitySystem->m_pUpdateScript->GetIVal()) { m_fScriptUpdateTimer -= ctx.fFrameTime; if (m_fScriptUpdateTimer <= 0) { m_fScriptUpdateTimer = m_fScriptUpdateRate; ENTITY_PROFILER_NAME( "CEntity:Update:Script" ) ////////////////////////////////////////////////////////////////////////// // Script Update. if (ctx.pScriptUpdateParams) CallStateFunction( ScriptState_OnUpdate,ctx.fFrameTime,ctx.pScriptUpdateParams ); else CallStateFunction( ScriptState_OnUpdate,ctx.fFrameTime ); // ////////////////////////////////////////////////////////////////////////// } } } if (m_bUpdateAI) UpdateAIObject(m_bVisible); // update container if (m_bUpdateContainer) { if (m_pContainer && m_pEntitySystem->m_pUpdateContainer->GetIVal()) { ENTITY_PROFILER_NAME( "CEntity:Update:Container" ) m_pContainer->Update(); } } ////////////////////////////////////////////////////////////////////////// // Update Characters. if (m_bUpdateCharacters || m_physPlaceholder) { UpdateCharacters( ctx ); } // update camera if (m_bUpdateCamera) { if (m_pCamera && m_pEntitySystem->m_pUpdateCamera->GetIVal()) { m_pCamera->Update(); } } if (m_bRecalcBBox) { CalcWholeBBox(); m_bRecalcBBox = false; } if (m_bUpdateCharacters || m_physPlaceholder) { UpdatePhysPlaceholders( ctx ); } if (m_bTrackColliders) CheckColliders(); return; } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdateCharacters( SEntityUpdateContext &ctx ) { if(!m_pEntitySystem->m_pUpdateBonePositions->GetIVal()) return; bool bProcess=m_bVisible; IGame *pGame = GetISystem()->GetIGame(); // ensure update to collision detection in multiplayer // (only done on the server so the client decal might be produced // wrong if you don't look at the target but this way we can save computation time) if(pGame && pGame->GetModuleState(EGameMultiplayer) && pGame->GetModuleState(EGameServer)) bProcess = true; if(m_eUpdateVisLevel == eUT_Physics) bProcess = true; if(bProcess) { ENTITY_PROFILER for(int k = 0; k < m_nMaxCharNum; k++) { if(m_pCryCharInstance[k] && (m_pCryCharInstance[k]->GetFlags() & CS_FLAG_UPDATE)) { m_pCryCharInstance[k]->Update(m_center,m_fRadius); // recalc bbox if animated if(m_pCryCharInstance[k]->IsCharacterActive()) //CalcWholeBBox(); look further down for actual call m_bRecalcBBox = true; } } } else { for(int k = 0; k < m_nMaxCharNum; k++) { if(m_pCryCharInstance[k] && (m_pCryCharInstance[k]->GetFlags() & CS_FLAG_UPDATE)) m_pCryCharInstance[k]->Update( m_center,m_fRadius, ICryCharInstance::flagDontUpdateBones); } } if(bProcess) { UpdateCharacterPhysicsAndIK( ctx ); } else if(m_physic) { pe_params_sensors ps; ps.nSensors = 0; m_physic->SetParams(&ps); } } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdateLipSync( SEntityUpdateContext &ctx ) { if (!m_pLipSync) return; ENTITY_PROFILER // if we're too far away we dont do facial-animation bool bAnimate=false; IRenderer *pRenderer=m_pISystem->GetIRenderer(); if (pRenderer) { //[Timur] This code have no place here, it is hack and must be removed (to inside lip sync for example) // we project the top and bottom to screen-space and check if the character is visible enough to update the face... Vec3d Min, Max; GetBBox(Min, Max); Vec3d Center=(Max-Min)*0.5f+Min; Vec3d TopCenter(Center.x, Center.y, Max.z); Vec3d BotCenter(Center.x, Center.y, Min.z); Vec3d TopCenterProj, BotCenterProj; pRenderer->ProjectToScreen(TopCenter.x, TopCenter.y, TopCenter.z, &TopCenterProj.x, &TopCenterProj.y, &TopCenterProj.z); pRenderer->ProjectToScreen(BotCenter.x, BotCenter.y, BotCenter.z, &BotCenterProj.x, &BotCenterProj.y, &BotCenterProj.z); if (fabs(TopCenterProj.y-BotCenterProj.y)>50.0f) // lets animate the face if the character is higher than 50 pixels bAnimate=true; } // if (!bAnimate) // TRACE("Skipping lipsync for entity %s", m_name.c_str()); m_pLipSync->Update(bAnimate); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnCollide(float fDeltaTime) { //m_pISystem->GetILog()->LogToConsole("diff=%0.2f",m_pISystem->GetITimer()->GetCurrTime()-m_fLastCollideTime); int bAwake = m_physic ? m_physic->GetStatus(&pe_status_awake()) : 0; float fFreq = m_physic && (m_physic->GetType()==PE_RIGID || m_physic->GetType()==PE_WHEELEDVEHICLE) && (bAwake+m_bWasAwake) ? 0.01f : 0.3f; float fFrameTime = m_pISystem->GetITimer()->GetCurrTime()-m_fLastCollideTime; if (!m_physic || fFrameTime<=fFreq && bAwake==m_bWasAwake) // avoid to create a rolling sound for impact, and avoid to call the // script function every frame return; m_fLastCollideTime = m_pISystem->GetITimer()->GetCurrTime(); coll_history_item contacts[32]; int nColls; float velImpact=0,velSlide2=0,velRoll2=0,velImpactCur,velSlide2Cur,velRoll2Cur; bool bCallOnCollide = false; IEntity *pColliderEntity=NULL; pe_status_collisions sc; pe_status_dynamics sd; //some lazy values to get only the latest collider from physics sc.age = 0.3f; sc.bClearHistory = 1; sc.pHistory = contacts; sc.len = sizeof(contacts)/sizeof(contacts[0]); m_physic->GetStatus(&sd); float velVertDelta = (sd.v - m_vPrevVel).len(); m_vPrevVel = sd.v; // m_PrevVertVel = sd.v.z; // float velVertDelta = sd.v.z - m_PrevVertVel; // m_PrevVertVel = sd.v.z; Vec3 vVel(0,0,0); float fVelSz=0; if ((bAwake+m_bWasAwake) && (nColls = m_physic->GetStatus(&sc))) { IPhysicalWorld *pWorld = m_pISystem->GetIPhysicalWorld(); for(nColls--;nColls>=0;nColls--) { // finding max contact velocity float fTmp=contacts[nColls].v[0].len2(); if(fTmp>fVelSz) { fVelSz = fTmp; vVel = contacts[nColls].v[0]; } Vec3d vrel = contacts[nColls].v[1]-contacts[nColls].v[0], r = contacts[nColls].pt-sd.centerOfMass; if (sd.w.len2()>0.01f) r -= sd.w*((r*sd.w)/sd.w.len2()); velImpactCur = fabs(vrel*contacts[nColls].n); velSlide2Cur = (vrel-contacts[nColls].n*velImpactCur).len2(); velRoll2Cur = (sd.w^r).len2(); if(velImpactGetPhysicalEntityById(contacts[nColls].idCollider); if(pCollider) pColliderEntity = (IEntity *)pCollider->GetForeignData(); velImpact = velImpactCur; } // velImpact = max(velImpact,velImpactCur); velSlide2 = max(velSlide2,velSlide2Cur); //m_pISystem->GetILog()->LogToConsole("test=%0.4f", (r*contacts[nColls].n)/r.len()); if (sqr(r*contacts[nColls].n)>r.len2()*sqr(0.97f)) velRoll2 = max(velRoll2,velRoll2Cur); } if (velRoll2<0.1f) { if ((m_fTimeNotRolling+=fFrameTime)>0.15f) m_fTimeRolling = 0; } else { m_fTimeRolling += fFrameTime; m_fTimeNotRolling = 0; } if (m_fTimeRolling<0.2f) velRoll2 = 0; //if (velImpact>2.0f) // m_pISystem->GetILog()->LogToConsole("impact %.2f",velImpact); //if (velSlide2>1.0f) // m_pISystem->GetILog()->LogToConsole("slide %.2f",cry_sqrtf(velSlide2)); //m_pISystem->GetILog()->LogToConsole(velRoll2>0 ? "rolling":m_fTimeRolling>0 ? "potentially rolling":"not rolling"); // if (contacts[0].pCollider==NULL) // return; //* float fSpeed=(contacts[0].v[0]-contacts[0].v[1]).len(); //m_pISystem->GetILog()->LogToConsole("Speed=%0.2f",fSpeed); //if (fSpeed<1.5f) // return; //do not bother if velocity is too small //*/ bCallOnCollide = true; m_pObjectCollide->BeginSetGetChain(); m_pObjectCollide->SetValueChain("fSpeed",fSpeed); m_pObjectCollide->SetValueChain("matId",contacts[0].idmat[1]); //pObject->SetValueChain("matId",tCollider.idmat[0]); // collision position m_vObjPosCollide->BeginSetGetChain(); m_vObjPosCollide->SetValueChain("x",contacts[0].pt.x); m_vObjPosCollide->SetValueChain("y",contacts[0].pt.y); m_vObjPosCollide->SetValueChain("z",contacts[0].pt.z); m_vObjPosCollide->EndSetGetChain(); m_pObjectCollide->SetValueChain("vPos",m_vObjPosCollide); // collision velocity m_vObjVelCollide->BeginSetGetChain(); m_vObjVelCollide->SetValueChain("x",vVel.x); m_vObjVelCollide->SetValueChain("y",vVel.y); m_vObjVelCollide->SetValueChain("z",vVel.z); m_vObjVelCollide->EndSetGetChain(); m_pObjectCollide->SetValueChain("vVel",m_vObjVelCollide); m_pObjectCollide->SetValueChain("impactVert",velVertDelta); // vertical impact contact m_pObjectCollide->SetToNullChain("impact"); m_pObjectCollide->SetToNullChain("roll"); m_pObjectCollide->SetToNullChain("slide"); float velImpactThresh = 1.5f; if (velRoll2>0.1f) { m_fRollTimeout=0.5f; // adjust if needed m_pObjectCollide->SetValueChain("roll",cry_sqrtf(velRoll2)); // roll contact velImpactThresh = 3.5f; } else if (velSlide2>0.1f) { m_fSlideTimeout=0.5f; // adjust if needed m_pObjectCollide->SetValueChain("slide",cry_sqrtf(velSlide2)); // slide contact } if (velImpact>velImpactThresh) m_pObjectCollide->SetValueChain("impact",velImpact); // impact contact if(pColliderEntity) m_pObjectCollide->SetValueChain("collider",pColliderEntity->GetScriptObject()); else m_pObjectCollide->SetToNullChain("collider"); /* vNormDir->BeginSetGetChain(); vNormDir->SetValueChain("x",contacts[0].n.x); vNormDir->SetValueChain("y",contacts[0].n.y); vNormDir->SetValueChain("z",contacts[0].n.z); vNormDir->EndSetGetChain(); pObject->SetValueChain("vNorm",vNormDir); */ } m_bWasAwake = bAwake; int nCircles = 0; //_SmartScriptObject pSplashList(m_pScriptSystem); IScriptObject *psoSplashes[32],*psoCenters[32]; if (m_fLastSplashTime>m_pEntitySystem->m_pSplashTimeout->GetFVal() && sd.waterResistance>m_pEntitySystem->m_pSplashThreshold->GetFVal()) // now test if the object hit the water hard enough { pe_status_pos sp; sp.flags = 0; geom_world_data gwd; geom_contact *pContacts; IPhysicalWorld *pWorld = m_pISystem->GetIPhysicalWorld(); int iCont,iCircle; Vec2 *pCenters; float *pRadii; Vec3 v; primitives::box boxWater; pe_params_bbox pbb; m_physic->GetParams(&pbb); boxWater.Basis.SetIdentity(); boxWater.center = (pbb.BBox[0]+pbb.BBox[1])*0.5f; boxWater.size = pbb.BBox[1]-pbb.BBox[0]; boxWater.center.z += m_pISystem->GetI3DEngine()->GetWaterLevel(this,&v) - (boxWater.center.z-boxWater.size.z); IGeometry *pWaterSurface = pWorld->GetGeomManager()->CreatePrimitive(primitives::box::type, &boxWater); m_pSplashList->Clear(); for(sp.ipart=m_physic->GetStatus(&pe_status_nparts())-1; sp.ipart>=0; sp.ipart--) { m_physic->GetStatus(&sp); gwd.offset = sp.pos; gwd.R = matrix3x3f(sp.q); gwd.scale = sp.scale; for(iCont=sp.pGeomProxy->Intersect(pWaterSurface, &gwd,0,0, pContacts)-1; iCont>=0; iCont--) for(iCircle = pWorld->GetPhysUtils()->CoverPolygonWithCircles(strided_pointer((Vec2*)pContacts[iCont].ptborder,sizeof(Vec3)), pContacts[iCont].nborderpt,pContacts[iCont].bBorderConsecutive, (const Vec2&)pContacts[iCont].center, pCenters,pRadii, 0.5f)-1; iCircle>=0; iCircle--) { psoCenters[nCircles] = m_pScriptSystem->CreateObject(); psoCenters[nCircles]->BeginSetGetChain(); psoCenters[nCircles]->SetValueChain("x",pCenters[iCircle].x); psoCenters[nCircles]->SetValueChain("y",pCenters[iCircle].y); psoCenters[nCircles]->SetValueChain("z",boxWater.center.z-boxWater.size.z); psoCenters[nCircles]->EndSetGetChain(); psoSplashes[nCircles] = m_pScriptSystem->CreateObject(); psoSplashes[nCircles]->BeginSetGetChain(); psoSplashes[nCircles]->SetValueChain("center",psoCenters[nCircles]); psoSplashes[nCircles]->SetValueChain("radius",pRadii[iCircle]); psoSplashes[nCircles]->SetValueChain("intensity",sd.waterResistance); psoSplashes[nCircles]->EndSetGetChain(); m_pSplashList->SetAt(nCircles+1, psoSplashes[nCircles]); if (++nCircles==6) goto CirclesNoMore; } } CirclesNoMore: pWorld->GetGeomManager()->DestroyGeometry(pWaterSurface); } if (nCircles) { if (!bCallOnCollide) { m_pObjectCollide->BeginSetGetChain(); m_pObjectCollide->SetToNullChain("impact"); m_pObjectCollide->SetToNullChain("roll"); m_pObjectCollide->SetToNullChain("slide"); } bCallOnCollide = true; m_pObjectCollide->SetValueChain("num_splashes", nCircles); m_pObjectCollide->SetValueChain("splashes", m_pSplashList); m_fLastSplashTime = 0; } else if (bCallOnCollide) { m_pObjectCollide->SetToNullChain("num_splashes"); m_pObjectCollide->SetToNullChain("splashes"); } m_fLastSplashTime += fFrameTime; bool bSplash; if ((sd.waterResistance>10.0f) && (sd.submergedFraction>0.0f) && (m_fLastSubMergeFracion<=0.0f)) { if (!bCallOnCollide) m_pObjectCollide->BeginSetGetChain(); bCallOnCollide=true; m_fLastSubMergeFracion=sd.submergedFraction; bSplash=true; }else { if (bCallOnCollide) m_pObjectCollide->SetToNullChain("waterresistance"); if (sd.submergedFraction==0.0f) m_fLastSubMergeFracion=0.0f; bSplash=false; } if (bCallOnCollide) { if (bSplash) m_pObjectCollide->SetValueChain("waterresistance", sd.waterResistance); m_pObjectCollide->EndSetGetChain(); if (m_pOnCollide) { m_pScriptSystem->BeginCall(m_pOnCollide); m_pScriptSystem->PushFuncParam(false); m_pScriptSystem->PushFuncParam(m_pObjectCollide); m_pScriptSystem->EndCall(); } else { IScriptObject *pScriptObject = m_pObjectCollide; CallStateFunction( ScriptState_OnCollide,pScriptObject ); } } for(nCircles--;nCircles>=0;nCircles--) psoSplashes[nCircles]->Release(),psoCenters[nCircles]->Release(); } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdateSounds( SEntityUpdateContext &ctx ) { if (!m_lstAttachedSounds.empty()) { ENTITY_PROFILER SoundsListItor itor = m_lstAttachedSounds.begin(); Vec3d vPos= GetSoundPos(); while(itor!=m_lstAttachedSounds.end()) { SAttachedSound &Sound=(*itor); #if !defined(LINUX64) if((Sound.pSound!=NULL) && (Sound.pSound->IsPlaying() || Sound.pSound->IsPlayingVirtual())) #else if((Sound.pSound!=0) && (Sound.pSound->IsPlaying() || Sound.pSound->IsPlayingVirtual())) #endif { Sound.pSound->SetPosition(vPos+Sound.Offset); ++itor; } else { itor=m_lstAttachedSounds.erase(itor); } } } m_bUpdateSounds = !m_lstAttachedSounds.empty(); } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdatePhysics( SEntityUpdateContext &ctx ) { ENTITY_PROFILER if (!m_pEntitySystem->m_pUpdatePhysics->GetIVal()) return; float fDeltaTime = ctx.fFrameTime; if (m_physic && (m_flags & ETY_FLAG_CALC_PHYSICS) && !(m_flags&ETY_FLAG_IGNORE_PHYSICS_UPDATE)) { // get the new position from the physics system pe_status_pos pos; quaternionf qmaster, q[32]; m_physic->GetStatus(&pos); qmaster = pos.q; Vec3d new_pos = pos.pos, new_angles; // get new angles from physics //CHANGED_BY_IVO (NOTE: order of angles is flipped!!!!) //pos.q.get_Euler_angles_xyz(new_angles.z, new_angles.y, new_angles.x); //EULER_IVO //Vec3 TempAng; new_angles=pos.q.GetEulerAngles_XYZ(TempAng); new_angles=Ang3::GetAnglesXYZ(matrix3x3f(pos.q)); new_angles *= 180.0f/gf_PI; MoveTo(new_pos, new_pos.x!=m_center.x || new_pos.y!=m_center.y || new_pos.z!=m_center.z, false); if (m_physic->GetType() != PE_LIVING) { //SetAngles(new_angles, true, false); RotateTo(new_angles, false); // m_angles = new_angles; } if (m_physic->GetType()==PE_SOFT && GetEntityStatObj(0)) { SetBBox(pos.BBox[0],pos.BBox[1]); pe_params_softbody psb; m_physic->GetParams(&psb); pe_action_awake aa; aa.bAwake = m_bVisible; if (m_bVisible) { pe_status_softvtx ssv; CLeafBuffer *pLB = GetEntityStatObj(0)->GetLeafBuffer(); if (pLB && pLB->m_arrVtxMap && m_physic->GetStatus(&ssv)) { strided_pointer pVtx,pNormals,pBinormals,pTangents; bool bHasTangents = false; pVtx.data = (Vec3*)pLB->GetPosPtr(pVtx.iStride); if (g_VertFormatNormalOffsets[pLB->m_nVertexFormat]!=-1) pNormals.data = (Vec3*)pLB->GetNormalPtr(pNormals.iStride); else { pNormals.data = (Vec3*)pLB->GetTNormalPtr(pNormals.iStride); pBinormals.data = (Vec3*)pLB->GetBinormalPtr(pBinormals.iStride); pTangents.data = (Vec3*)pLB->GetTangentPtr(pTangents.iStride); bHasTangents = true; } for(int i=0; im_SecVertCount; i++) { pVtx[i] = ssv.pVtx[pLB->m_arrVtxMap[i]]; pNormals[i] = ssv.pNormals[pLB->m_arrVtxMap[i]]; if (bHasTangents) { pBinormals[i] = GetOrthogonal(pNormals[i]).normalized(); pTangents[i] = pNormals[i]^pBinormals[i]; } } pLB->InvalidateVideoBuffer(); } } if ((m_bVisible^m_bWasVisible) && (!m_bVisible || psb.wind*psb.airResistance>0)) m_physic->Action(&aa); } int k = 0; pos.flags = status_local; bool bOnCollideImplemented = false; if (m_pServerState && m_pServerState->pFunction[ScriptState_OnCollide]) bOnCollideImplemented=true; else if (m_pClientState && m_pClientState->pFunction[ScriptState_OnCollide]) bOnCollideImplemented=true; // call the OnCollide function only if the oncollide is implemented OR // if there is not script object AND the entity is a rigid body entity, in // which case it calls the common colliding behaviour (if necessary) if ((bOnCollideImplemented) || (m_pOnCollide)) OnCollide(fDeltaTime); for (unsigned int j = 0; j < m_objects.size(); j++) { pos.partid = j; if (m_physic->GetStatus(&pos)) { m_objects[j].pos =(Vec3d)pos.pos; q[j] = pos.q; //CHANGED_BY_IVO (NOTE: order of angles is flipped!!!!) //pos.q.get_Euler_angles_xyz(m_objects[j].angles.z, m_objects[j].angles.y, m_objects[j].angles.x); //EULER_IVO //Vec3 TempAng; m_objects[j].angles=pos.q.GetEulerAngles_XYZ(TempAng); m_objects[j].angles = Ang3::GetAnglesXYZ(matrix3x3f(pos.q)); m_objects[j].angles *= 180.0f/gf_PI; } if (m_objects[j].flags & ETY_OBJ_IS_A_LINK) { float len0, len; matrix3x3in4x4Tf &mtx(*(matrix3x3in4x4Tf*)&m_objects[j].mtx); matrix3x3f R0,R1; m_objects[j].mtx.SetIdentity(); Vec3d link_start, link_end, link_offset; // calculate current link start, link end in entity coordinates link_start = q[m_objects[j].ipart0]*m_objects[j].link_start0 + m_objects[m_objects[j].ipart0].pos; link_end = q[m_objects[j].ipart1]*m_objects[j].link_end0 + m_objects[m_objects[j].ipart1].pos; len0 =(m_objects[j].link_end0 - m_objects[j].link_start0).Length(); len =(link_end - link_start).Length(); mtx = matrix3x3in4x4Tf(qmaster); m_objects[j].mtx.SetRow(3,m_center); // initialize object matrix to entity world matrix link_offset = m_objects[j].mtx.TransformPointOLD(link_start); // build (rotate to previous orientation)*(scale along z axis) matrix R1 = matrix3x3f(GetRotationV0V1(vectorf(0,0,1), (link_end-link_start)/len )); R1.SetColumn(2,R1.GetColumn(2)*(len/len0)); // build (rotate so that link axis becomes z axis) matrix R0 = matrix3x3f( GetRotationV0V1(vectorf(m_objects[j].link_end0-m_objects[j].link_start0)/len0, vectorf(0, 0, 1))); mtx *= (R1*R0); // offset matrix by difference between current and required staring points link_offset -= m_objects[j].mtx.TransformPointOLD(m_objects[j].link_start0); m_objects[j].mtx.SetRow(3,m_objects[j].mtx.GetRow(3)+link_offset); m_objects[j].flags |= ETY_OBJ_USE_MATRIX; } else m_objects[j].flags &= ~ETY_OBJ_USE_MATRIX; } // Set water level to any physic entity (Ignore for soft invisible entity). if (m_bVisible || m_physic->GetType()!=PE_SOFT) { // set water level pe_params_buoyancy pb; //pb.waterDensity = m_fWaterDensity; pb.waterPlane.n.Set(0,0,1); Vec3d vWaterFlowSpeed(0,0,0); //float fWaterLevel = m_pISystem->GetI3DEngine()->GetWaterLevel(&GetPos(), &vWaterFlowSpeed); float fWaterLevel = m_pISystem->GetI3DEngine()->GetWaterLevel(this, &vWaterFlowSpeed); pb.waterPlane.origin.Set(0,0,fWaterLevel); pb.waterFlow = vWaterFlowSpeed; m_physic->SetParams(&pb); } } if (m_fRollTimeout>0) { m_fRollTimeout -= fDeltaTime; if (m_fRollTimeout<=0.0f) { if (m_pOnStopRollSlideContact) { m_pScriptSystem->BeginCall(m_pOnStopRollSlideContact); m_pScriptSystem->PushFuncParam(false); m_pScriptSystem->PushFuncParam("roll"); m_pScriptSystem->EndCall(); }else { CallStateFunction(ScriptState_OnStopRollSlideContact, "roll"); } } } if (m_fSlideTimeout>0) { m_fSlideTimeout -= fDeltaTime; if (m_fSlideTimeout<=0.0f) { if (m_pOnStopRollSlideContact) { m_pScriptSystem->BeginCall(m_pOnStopRollSlideContact); m_pScriptSystem->PushFuncParam(false); m_pScriptSystem->PushFuncParam("slide"); m_pScriptSystem->EndCall(); }else { CallStateFunction(ScriptState_OnStopRollSlideContact, "slide"); } } } } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdateCharacterPhysicsAndIK( SEntityUpdateContext &ctx ) { ENTITY_PROFILER // for (int k = 0; k < m_nMaxCharNum; k++) if (m_pCryCharInstance[k] ) for (int k = 0; k < m_nMaxCharNum; k++) if (m_pCryCharInstance[k] && (m_pCryCharInstance[k]->GetFlags() & CS_FLAG_UPDATE)) { m_pCryCharInstance[k]->SetOffset(vectorf(0,0,0)); if (m_physic && m_pEntitySystem->m_pUpdatePhysics->GetIVal()) { pe_status_living livstat; m_physic->GetStatus(&livstat); pe_status_sensors sensors; if(m_bHandIK) { m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_LEFT_ARM, m_vHandIKTarget, ik_arm, 0, Vec3d(1,0,0) ); m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_RIGHT_ARM, m_vHandIKTarget, ik_arm, 0, Vec3d(1,0,0) ); m_bHandIK = false; // has to be reset before next update } else { m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_LEFT_ARM); m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_RIGHT_ARM); } if (!m_bIsBound && m_pEntitySystem->m_pCharacterIK->GetIVal() && m_physic->GetType()==PE_LIVING && !(livstat.bFlying && livstat.timeFlying>0.2f) && m_physic->GetStatus(&sensors) && sensors.flags) { pe_player_dimensions livdim; m_physic->GetParams(&livdim); float fCharZOffsetBase = 0, fOffsetSpeed = m_pEntitySystem->m_pCharZOffsetSpeed->GetFVal(); if (livstat.timeSinceStanceChange>3.0f) // don't offset character during stance changes, since it's done by animation blending fCharZOffsetBase = livstat.camOffset.z-livdim.heightEye; m_fCharZOffsetTarget = 0; //m_pCryCharInstance[k]->SetOffset(livstat.camOffset-vectorf(0,0,livdim.heightEye)); Vec3 feet[2]; int i, ikflags = ik_leg; //if (livstat.vel.len2()>0.01f) ikflags |= ik_avoid_stretching; for(i=0; i<2; i++) if (sensors.pPoints[i].z > livdim.heightCollider*0.7f) sensors.pPoints[i].z = livdim.heightCollider*0.7f; m_nFlyingFrames = m_nFlyingFrames+livstat.bFlying & -livstat.bFlying; if (m_nFlyingFrames<4 && livstat.vel.len2()<16 && (livstat.vel.len2()<0.01f || !livstat.bOnStairs)) for(i=0; i<2; i++) if (sensors.pPoints[i].z - fCharZOffsetBase/*m_pCryCharInstance[k]->GetOffset().z*/ < -0.02f && sensors.flags&1<SetOffset(m_pCryCharInstance[k]->GetOffset()+ //vectorf(0, 0, sensors.pPoints[i].z - m_pCryCharInstance[k]->GetOffset().z));// + 0.02f)); if (fabs_tpl(m_fCharZOffsetCur-m_fCharZOffsetTarget) < fOffsetSpeed*ctx.fFrameTime) m_fCharZOffsetCur = m_fCharZOffsetTarget; else m_fCharZOffsetCur += fOffsetSpeed*sgnnz(m_fCharZOffsetTarget-m_fCharZOffsetCur)*ctx.fFrameTime; m_pCryCharInstance[k]->SetOffset(Vec3(0,0,fCharZOffsetBase+m_fCharZOffsetCur)); for(i=0; i<2; i++) { feet[i].Set(IK_NOT_USED, IK_NOT_USED, sensors.pPoints[i].z - m_pCryCharInstance[k]->GetOffset().z); if (feet[i].z > livdim.heightCollider*0.5f) feet[i].z = livdim.heightCollider*0.5f; if (sensors.flags&1<SetLimbIKGoal(LIMB_LEFT_LEG + i, feet[i], ikflags, 0, sensors.pNormals[i]); else m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_LEFT_LEG + i); } } else { m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_LEFT_LEG); m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_RIGHT_LEG); } } else { m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_LEFT_LEG); m_pCryCharInstance[k]->SetLimbIKGoal(LIMB_RIGHT_LEG); } #ifdef DEBUG_BONES_SYNC if (m_pEntitySystem->m_bServer) #endif m_pCryCharInstance[k]->UpdatePhysics(m_fScale); m_pCryCharInstance[k]->SynchronizeWithPhysicalEntity(m_physic, m_center, GetRotationXYZ(m_angles*(gf_PI/180.0f))); pe_params_sensors sensors; if (!m_bIsBound && m_physic && m_pEntitySystem->m_pCharacterIK->GetIVal() && m_physic->GetType()==PE_LIVING) { Vec3 feet[2], offset = m_pCryCharInstance[k]->GetOffset(); // generally should be transformed into local coordinates offset.z = max(offset.z,0.0f); sensors.nSensors = 2; sensors.pOrigins = feet; feet[0] = m_pCryCharInstance[k]->GetLimbEndPos(LIMB_LEFT_LEG); feet[1] = m_pCryCharInstance[k]->GetLimbEndPos(LIMB_RIGHT_LEG) + offset; if (!(feet[0].x*feet[0].x>=0) || !(feet[1].x*feet[1].x>=0)) // temporary validity check sensors.nSensors = 0; feet[0].z -= 0.4f; feet[1].z -= 0.4f; m_physic->SetParams(&sensors); } else if (m_physic && m_physic->GetType()==PE_LIVING) { sensors.nSensors = 0; m_physic->SetParams(&sensors); } // recalc bbox if animated if (m_pCryCharInstance[k]->IsCharacterActive()) //CalcWholeBBox(); [Petar] moved to Update m_bRecalcBBox = true; } } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdatePhysPlaceholders( SEntityUpdateContext &ctx ) { ENTITY_PROFILER if (m_vBoxMin.x>=m_vBoxMax.x || m_vBoxMin.y>=m_vBoxMax.y || m_vBoxMin.z>=m_vBoxMax.z) return; pe_params_bbox pbb; pe_status_placeholder spc; pbb.BBox[0] = m_vBoxMin+m_center; pbb.BBox[1] = m_vBoxMax+m_center; if (m_bUpdateCharacters) { for(int i=0;iGetStatus(&spc); if (!spc.pFullEntity) m_pCharPhysPlaceholders[i]->SetParams(&pbb); } } } if (m_physPlaceholder && !m_physic) m_physPlaceholder->SetParams(&pbb); } void CEntity::UpdateAIObject( bool bEntityVisible ) { // reflect changes in position or orientation to the AI object ENTITY_PROFILER if (!m_pAIObject) return; if (!m_pEntitySystem->m_pUpdateAI->GetIVal()) return; if ( m_pHeadBone ) // if (m_pHeadBone && (m_pCryCharInstance[0]->GetFlags() & CS_FLAG_DRAW_MODEL)) { Vec3d pos; Vec3d angles = m_angles; pos = m_pHeadBone->GetBonePosition(); // // if bound - in vehicle - force eyeheight to make it more noticable // if(IsBound()) // m_pAIObject->SetEyeHeight(pos.z+1); // else m_pAIObject->SetEyeHeight(pos.z); bool bWorldSpace = false; if (m_pAIObject->GetProxy()) bWorldSpace = m_pAIObject->GetProxy()->CustomUpdate(pos,angles); if (!bWorldSpace) { angles.x = 0; // angles.Set(0,0,0); Matrix44 mat; mat.SetIdentity(); //mat.RotateMatrix_fix(angles); mat=Matrix44::CreateRotationZYX(-gf_DEGTORAD*angles)*mat; //NOTE: angles in radians and negated pos = mat.TransformPointOLD(pos); m_pAIObject->SetPos(pos+m_center,false); m_pAIObject->SetAngles(m_angles); } else { m_pAIObject->SetPos(pos,false); m_pAIObject->SetAngles(m_angles); } } else { m_pAIObject->SetPos(m_center, m_pAIObject->GetType()!=AIOBJECT_SNDSUPRESSOR && m_pAIObject->GetType()!=AIOBJECT_VEHICLE ); m_pAIObject->SetAngles(m_angles); } if (bEntityVisible) m_pAIObject->Event(AIEVENT_WAKEUP,0); } IEntityCamera* CEntity::GetCamera() const { if (m_pCamera) { return m_pCamera; } /*if (m_bind) { return m_bind->GetCamera(); }*/ return 0; } void CEntity::SetThirdPersonCameraMode(const Vec3d ¢er, const Vec3d &angles, int mode, float frameTime, float range, int dangleAmmount, IPhysicalEntity *physic) { if (m_pCamera) { m_pCamera->SetThirdPersonMode(center, angles, mode, frameTime, range, dangleAmmount, physic); } } void CEntity::SetPhysics(IPhysicalEntity* physic) { if (m_physic) { // destroy previous physics object. m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physic); // Check if we need to unregister this entity from sector. if (m_registeredInSector && CheckFlags(ETY_FLAG_NOT_REGISTER_IN_SECTORS)) { UnregisterInSector(); } } m_physic = physic; if (m_physic) { // enable physics calculations. SetFlags(ETY_FLAG_CALC_PHYSICS); } } void CEntity::DestroyPhysics() { if (m_physic) { m_physicEnabled = false; ClearFlags(ETY_FLAG_CALC_PHYSICS); m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physic); m_physic=NULL; } if (m_physPlaceholder) { m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physPlaceholder); m_physPlaceholder=NULL; } if (m_pPhysState) { delete[] m_pPhysState; m_pPhysState = 0; } m_iPhysType = PHYS_NONE; } int CEntity::CreatePhysicalEntityCallback(int iForeignFlags) { if (iForeignFlags & 1<<15) { int iPos = iForeignFlags>>12 & 7; PhysicalizeCharacter(iPos, m_charPhysData[iPos].mass,m_charPhysData[iPos].surface_idx,m_charPhysData[iPos].stiffness_scale, true); SendScriptEvent(ScriptEvent_PhysicalizeOnDemand,0); } else switch (m_iPhysType) { case PHYS_RIGID: { CreateRigidBody(m_PhysData.RigidBody.type,m_PhysData.RigidBody.density,m_PhysData.RigidBody.mass, m_PhysData.RigidBody.surface_idx,0,m_PhysData.RigidBody.slot,true); SendScriptEvent(ScriptEvent_PhysicalizeOnDemand,0); if (m_pPhysState && m_physic) { CStream stm(m_iPhysStateSize,m_pPhysState); m_physic->SetStateFromSnapshot(stm); m_physic->PostSetStateFromSnapshot(); delete[] m_pPhysState; m_pPhysState = 0; } } break; case PHYS_STATIC: CreateStaticEntity(0,m_PhysData.Static.surface_idx,m_PhysData.Static.slot,true); } SetUpdateVisLevel(m_eUpdateVisLevel); return 1; } int CEntity::DestroyPhysicalEntityCallback(IPhysicalEntity *pent) { pe_params_foreign_data pfd; pent->GetParams(&pfd); if (pfd.iForeignFlags & 1<<15) m_pCryCharInstance[pfd.iForeignFlags>>12 & 7]->DestroyCharacterPhysics(); else { if (m_iPhysType==PHYS_RIGID) { static CStream stm; stm.SetSize(0); m_physic->GetStateSnapshot(stm); m_iPhysStateSize = ((stm.GetSize()-1)>>3)+1; if (m_pPhysState) { delete[] m_pPhysState; m_pPhysState = 0; } if (m_iPhysStateSize) memcpy(m_pPhysState = new unsigned char[m_iPhysStateSize],stm.GetPtr(),m_iPhysStateSize); ClearFlags(ETY_FLAG_CALC_PHYSICS); } m_physic = 0; } return 1; } void CEntity::EnablePhysics(bool enable) { if (!enable && m_physPlaceholder) { m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physPlaceholder); m_physPlaceholder = 0; m_physic = 0; m_physicEnabled = false; if (m_pPhysState) { delete[] m_pPhysState; m_pPhysState = 0; } return; } else if (enable && !m_physic && m_iPhysType!=PHYS_NONE) { switch (m_iPhysType) { case PHYS_RIGID: CreateRigidBody(m_PhysData.RigidBody.type,m_PhysData.RigidBody.density,m_PhysData.RigidBody.mass, m_PhysData.RigidBody.surface_idx,0,m_PhysData.RigidBody.slot); break; case PHYS_STATIC: CreateStaticEntity(0,m_PhysData.Static.surface_idx,m_PhysData.Static.slot); } m_physicEnabled = true; } else { if (!m_physic) return; if (enable) { m_physicEnabled = true; SetFlags(ETY_FLAG_CALC_PHYSICS); m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physic, 2); } else { m_physicEnabled = false; ClearFlags(ETY_FLAG_CALC_PHYSICS); m_pISystem->GetIPhysicalWorld()->DestroyPhysicalEntity(m_physic, 1); } } } void CEntity::AddImpulse(int ipart, Vec3d pos, Vec3d impulse,bool bPos,float fAuxScale) { #ifndef _ISNOTFARCRY IXGame *pXGame = (IXGame*) GetISystem()->GetIGame(); #endif IPhysicalEntity *physic = GetPhysics(); if (physic && (!m_bIsADeadBody #ifndef GERMAN_GORE_CHECK || (m_pEntitySystem->m_pHitDeadBodies->GetIVal() #ifndef _ISNOTFARCRY && pXGame->GoreOn() #endif ) #endif )) { Vec3d mod_impulse = impulse; if (!(physic->GetStatus(&pe_status_nparts())>5 && physic->GetType()==PE_ARTICULATED)) { // don't scale impulse for complex articulated entities pe_status_dynamics sd; float minVel = m_pEntitySystem->m_pMinImpulseVel->GetFVal(); if (minVel>0 && physic->GetStatus(&sd) && mod_impulse*mod_impulsem_pImpulseScale->GetFVal(); if (fScale>0) mod_impulse *= fScale; else if (sd.massm_pMaxImpulseAdjMass->GetFVal()) mod_impulse = mod_impulse.normalized()*(minVel*sd.mass); } } pe_action_impulse ai; ai.partid = ipart; if(bPos) ai.point = pos; ai.impulse = mod_impulse; physic->Action(&ai); } if (m_bVisible && (!m_physic || m_physic->GetType()!=PE_LIVING #ifndef GERMAN_GORE_CHECK || (m_pEntitySystem->m_pHitCharacters->GetIVal() #ifndef _ISNOTFARCRY && pXGame->GoreOn() #endif ) #endif )) if (bPos) for (int i = 0; i < MAX_ANIMATED_MODELS; i++) if (m_pCryCharInstance[i]) m_pCryCharInstance[i]->AddImpact(ipart, pos, impulse*fAuxScale); } void CEntity::SetRegisterInSectors(bool needToRegister) { if (needToRegister) { if (!m_pEntityRenderState) InitEntityRenderState(); ClearFlags(ETY_FLAG_NOT_REGISTER_IN_SECTORS); if (!m_registeredInSector) { RegisterInSector(); } } else { SetFlags(ETY_FLAG_NOT_REGISTER_IN_SECTORS); if (m_registeredInSector) { UnregisterInSector(); } } } void CEntity::GetEntityDesc(CEntityDesc &desc) const { desc.id = m_nID; desc.ClassId = m_ClassId; desc.name = m_name.c_str(); desc.netPresence = m_netPresence; desc.angles=GetAngles(); desc.pos=GetPos(); desc.scale = m_fScale; if(m_pContainer) m_pContainer->GetEntityDesc(desc); } ////////////////////////////////////////////////////////////////////////// void CEntity::GetHelperPosition(const char *helper, Vec3d &vPos, bool objectspace) { if (m_objects.empty()) return; std::vector < CEntityObject>::iterator oi; bool bDone = false; for (oi = m_objects.begin(); !bDone && oi != m_objects.end(); oi++) { CEntityObject eo =(*oi); if (!eo.object) continue; vPos=eo.object->GetHelperPos(helper); bDone = !IsEquivalent(vPos,Vec3d(0, 0, 0)); } // the character might also contain a helper we'd like if (!bDone) { ICryCharInstance *character = GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); if (character) vPos = character->GetHelperPos(helper); bDone = !IsEquivalent(vPos,Vec3d(0, 0, 0)); } // found... if (bDone && !objectspace) { //OPTIMISED_BY_IVO Matrix44 mtx=Matrix34::CreateRotationXYZ( Deg2Rad(m_angles),m_center); mtx=GetTransposed44(mtx); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 vPos = mtx.TransformPointOLD(vPos); } } bool CEntity::IsObjectLoaded(unsigned int slot) { if (slot < m_objects.size()) { if (m_objects[slot].object != 0) return true; } return false; } bool CEntity::SetEntityObject(unsigned int slot, const CEntityObject &object) { if (slot < m_objects.size()) { if (m_objects[slot].object != object.object) { // Release old object. m_pISystem->GetI3DEngine()->ReleaseObject(m_objects[slot].object); } /* if (m_objects[slot].image != object.image) { m_pISystem->GetIRenderer()->RemoveTexture(m_objects[slot].image); }*/ m_objects[slot] = object; return true; } return false; } bool CEntity::GetEntityObject(unsigned int slot, CEntityObject &object) { if (slot < m_objects.size()) { object = m_objects[slot]; return true; } return false; } void CEntity::RegisterInSector() { // register this entity for rendering if (!m_registeredInSector && GetRadius() && !IsHidden() && m_pEntityRenderState /*&& IsEntityHasSomethingToRender()*/) { m_pISystem->GetI3DEngine()->RegisterEntity(this); m_registeredInSector = true; } } void CEntity::UnregisterInSector() { // unregister this entity from the list of sectors if (m_registeredInSector) { m_pISystem->GetI3DEngine()->UnRegisterEntity(this); m_registeredInSector = false; } } void CEntity::ForceRegisterInSectors() { if (!CheckFlags(ETY_FLAG_NOT_REGISTER_IN_SECTORS)) { UnregisterInSector(); RegisterInSector(); } } // void CEntity::SinkRebind(IEntitySystemSink *pSink) { for( BINDLIST::iterator iCurBind=m_lstBindings.begin(); iCurBind!=m_lstBindings.end(); ++iCurBind) { CEntity *pChild =(CEntity *) m_pEntitySystem->GetEntity((*iCurBind)); if(pChild) pSink->OnBind(GetId(),(*iCurBind),pChild->m_cBind); } } // bSetPos -- needed to be able to set position before OnBind is called (it can set some other position) // void CEntity::Bind(EntityId id,unsigned char cBind, const bool bClientOnly, const bool bSetPos ) { // safe upcast since we know what the entity system holds CEntity *pEntity =(CEntity *) m_pEntitySystem->GetEntity(id); if (!pEntity) return; // only add if it is not there already //if (std::find(m_lstBindings.begin(), m_lstBindings.end(), pEntity) == m_lstBindings.end()) if (std::find(m_lstBindings.begin(), m_lstBindings.end(), id) == m_lstBindings.end()) { m_bUpdateBinds = 1; m_lstBindings.push_back(id); pEntity->m_bIsBound = 1; pEntity->m_bForceBindCalculation = 0; // [kirill] makes problem for network // so just set position after you bind something if(bSetPos) pEntity->m_realcenter = pEntity->m_center; pEntity->m_realangles = pEntity->m_angles; pEntity->m_idBoundTo = GetId(); pEntity->m_cBind = cBind; if(!bClientOnly) // to prevent circular loop between client and server m_pEntitySystem->OnBind(GetId(),id,cBind); OnBind( pEntity, cBind); } } void CEntity::Unbind(EntityId id,unsigned char cBind, const bool bClientOnly) { CEntity *pEntity =(CEntity *) m_pEntitySystem->GetEntity(id); //if (std::find(m_lstBindings.begin(), m_lstBindings.end(), pEntity) != m_lstBindings.end()) if (std::find(m_lstBindings.begin(), m_lstBindings.end(), id) != m_lstBindings.end()) { m_lstBindings.remove(id); if (pEntity) { pEntity->m_bIsBound = false; if(!bClientOnly) // to prevent circular loop between client and server m_pEntitySystem->OnUnbind(GetId(),id,cBind); OnUnBind( pEntity, cBind); } } m_bUpdateBinds = !m_lstBindings.empty(); } ////////////////////////////////////////////////////////////////////////// void CEntity::ResolveCollision() { ENTITY_PROFILER if (!m_pEntitySystem->m_pUpdateCollision->GetIVal()) return; // resolve collisions IPhysicalWorld *pWorld = m_pISystem->GetIPhysicalWorld(); IPhysicalEntity **ppColliders; int cnt = 0; Vec3d mins, maxs; GetBBox(mins, maxs); if (mins.x > maxs.x) { // Bad bounding box. return; } cnt = pWorld->GetEntitiesInBox(mins, maxs, ppColliders, 14 ); if (cnt > 0) { static std::vector s_colliders; s_colliders.resize(cnt); memcpy( &s_colliders[0],ppColliders,cnt*sizeof(IPhysicalEntity*) ); // execute on collide for all of the entities for (int i = 0; i < cnt; i++) { CEntity *pEntity = (CEntity *)s_colliders[i]->GetForeignData(); //IStatObj *pStatObj = (IStatObj *) ppColliders[i]->GetForeignData(1); if (pEntity) { if (pEntity->IsGarbage()) continue; if (pEntity->GetId() == m_nID) continue; if (!m_pEntitySystem->m_pUpdateCollisionScript->GetIVal()) continue; CallStateFunction( ScriptState_OnContact,pEntity->GetScriptObject() ); } } } } ////////////////////////////////////////////////////////////////////////// IStatObj *CEntity::GetIStatObj(unsigned int pos) { CEntityObject object; if (pos < m_objects.size()) { object = m_objects[pos]; return object.object; } return (NULL); } void CEntity::SendScriptEvent(enum EScriptEventId Event, IScriptObject *pParamters, bool *pRet) { // Server side always first. bool bClientReturn=true; if (m_pServerState && m_pServerState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pServerState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); if (pParamters) m_pScriptSystem->PushFuncParam(pParamters); else m_pScriptSystem->PushFuncParam(false); if (pRet){ m_pScriptSystem->EndCall(*pRet); } else m_pScriptSystem->EndCall(); // Only use return value if we ain't got a server event bClientReturn=false; } // Client side always second. if (m_pClientState && m_pClientState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pClientState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); if (pParamters) m_pScriptSystem->PushFuncParam(pParamters); else m_pScriptSystem->PushFuncParam(false); // Only use return value if we ain't got a server event if (pRet && bClientReturn) m_pScriptSystem->EndCall(*pRet); else m_pScriptSystem->EndCall(); } } void CEntity::SendScriptEvent(enum EScriptEventId Event, const char *str, bool *pRet ) { // Server side always first. bool bClientReturn=true; if (m_pServerState && m_pServerState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pServerState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); if (str) m_pScriptSystem->PushFuncParam( str ); else m_pScriptSystem->PushFuncParam( "" ); if (pRet) m_pScriptSystem->EndCall(*pRet); else m_pScriptSystem->EndCall(); bool bClientReturn=false; } // Client side always second. if (m_pClientState && m_pClientState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pClientState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); if (str) m_pScriptSystem->PushFuncParam( str ); else m_pScriptSystem->PushFuncParam(false); if (pRet && bClientReturn) m_pScriptSystem->EndCall(*pRet); else m_pScriptSystem->EndCall(); } } void CEntity::SendScriptEvent(enum EScriptEventId Event, int nParam, bool *pRet ) { // Server side always first. bool bClientReturn=true; if (m_pServerState && m_pServerState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pServerState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); m_pScriptSystem->PushFuncParam(nParam); if (pRet) m_pScriptSystem->EndCall(*pRet); else m_pScriptSystem->EndCall(); bClientReturn = false; } // Client side always second. if (m_pClientState && m_pClientState->pFunction[ScriptState_OnEvent]) { m_pScriptSystem->BeginCall(m_pClientState->pFunction[ScriptState_OnEvent]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam((int)Event); m_pScriptSystem->PushFuncParam(nParam); if (pRet&&bClientReturn) m_pScriptSystem->EndCall(*pRet); else m_pScriptSystem->EndCall(); } } void CEntity::RotateTo(const Vec3d &angles, bool bUpdatePhysics) { // Vec3d diff; // diff = angles - m_angles; Ang3 diff = angles; diff.Snap360(); diff -= m_angles; if (fabs(diff.x) < ANGLES_EPSILON && fabs(diff.y) < ANGLES_EPSILON && fabs(diff.z) < ANGLES_EPSILON) return; m_angles += diff; m_angles.Snap360(); // SetAngles( angles ); // SetAngles(m_angles + diff); if (bUpdatePhysics && m_physic &&(m_flags & ETY_FLAG_CALC_PHYSICS)) { SetPhysAngles( m_angles ); // pe_params_pos pp; // pp.q = quaternionf(m_angles.z*(PI/180.0f),m_angles.y*(PI/180.0f),m_angles.x*(PI/180.0f)); // m_physic->SetParams(&pp); } } IAIObject * CEntity::GetAI() { if (m_pAIObject) return m_pAIObject; return 0; } void CEntity::LoadBreakableObject(const char *pFileName) { std::vector < CEntityObject>::iterator it; for (it = m_objects.begin(); it < m_objects.end(); it++) { if ((* it).object) m_pISystem->GetI3DEngine()->ReleaseObject((* it).object); } m_objects.clear(); { // load unbreaked (initial) version IStatObj * cobj; cobj = m_pISystem->GetI3DEngine()->MakeObject(pFileName,"unbreaked"); if (!cobj) m_pISystem->GetILog()->Log("CEntity::LoadBreakableObject: Could not load unbreaked version of object %s", pFileName); else { CEntityObject ceo; ceo.object = cobj; ceo.pos = Vec3d(0,0,0); m_objects.push_back(ceo); } } // load broken version { IStatObj * cobj; cobj = m_pISystem->GetI3DEngine()->MakeObject(pFileName,"broken"); if (!cobj) m_pISystem->GetILog()->Log("CEntity::LoadBreakableObject: Could not load unbreaked version of object %s", pFileName); else { CEntityObject ceo; ceo.object = cobj; ceo.pos = Vec3d(0,0,0); m_objects.push_back(ceo); } } // load pieces of object char geomname[50]; int i=1; sprintf(geomname,"piece%02d",i); IStatObj * cobj; while (cobj = m_pISystem->GetI3DEngine()->MakeObject(pFileName,geomname)) { if (cobj->IsDefaultObject()) break; CEntityObject ceo; ceo.object = cobj; ceo.pos = Vec3d(0,0,0); m_objects.push_back(ceo); i++; sprintf(geomname,"piece%02d",i); } if (i==1) m_pISystem->GetILog()->Log("CEntity::LoadBreakableObject: Could not load pieces of breakable object %s", pFileName); m_pISystem->GetI3DEngine()->FreeEntityRenderState(this); InitEntityRenderState(); //UnregisterInSector(); // leave this call... it will cause the geometry to be properly registered CalcWholeBBox(); // RegisterInSector(); } void CEntity::OnStartAnimation(const char *sAnimation) { ENTITY_PROFILER //char sTemp[200]; //sprintf(sTemp,"OnStartAnimation(%s)\n",sAnimation); //::OutputDebugString(sTemp); SendScriptEvent( ScriptEvent_StartAnimation,sAnimation ); } void CEntity::OnAnimationEvent(const char *sAnimation,AnimSinkEventData UserData) { ENTITY_PROFILER USER_DATA udUserData=(USER_DATA)UserData.p; //_SmartScriptObject pObject(m_pScriptSystem); m_pAnimationEventParams->SetValue("animation",sAnimation); // if(udUserData>0) m_pAnimationEventParams->SetToNull("number"); m_pAnimationEventParams->SetToNull("userdata"); if (UserData.n == svtUserData) { m_pAnimationEventParams->SetValue("userdata",udUserData); } else if (UserData.n == svtNumber) { m_pAnimationEventParams->SetValue("number", (int) udUserData); } // add other types as necessary /* if(udUserData!=USER_DATA(-1)) m_pAnimationEventParams->SetValue("userdata",udUserData); else m_pAnimationEventParams->SetToNull("userdata"); */ SendScriptEvent( ScriptEvent_AnimationKey,m_pAnimationEventParams ); } void CEntity::OnEndAnimation(const char *sAnimation) { ENTITY_PROFILER //char sTemp[200]; //sprintf(sTemp,"OnEndAnimation(%s)\n",sAnimation); //OutputDebugString(sTemp); SendScriptEvent( ScriptEvent_EndAnimation,sAnimation ); } ////////////////////////////////////////////////////////////////////////// // Calls Event_##sEvent function from script. void CEntity::CallEventHandler( const char *sEvent ) { if (m_pScriptObject) { char funcName[1024]; strcpy( funcName,"Event_" ); strcat( funcName,sEvent ); HSCRIPTFUNCTION pEventFunc = 0; if (m_pScriptObject->GetValue( funcName,pEventFunc )) { m_pScriptSystem->BeginCall(pEventFunc); // Pass itself as a sender. m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); m_pScriptSystem->ReleaseFunc(pEventFunc); } } } ////////////////////////////////////////////////////////////////////////// const char* CEntity::GetState() { return m_sStateName.c_str(); } ////////////////////////////////////////////////////////////////////////// int CEntity::GetStateIdx() { EntityStateMapItor itor; itor=m_mapStates.find(m_sStateName); if(itor!=m_mapStates.end()) return itor->second; return 0; } ////////////////////////////////////////////////////////////////////////// bool CEntity::IsInState( int nState ) { EntityStateMapItor itor=m_mapStates.begin(); while(itor!=m_mapStates.end()) { if(itor->second==nState) { return true; } ++itor; } return false; } ////////////////////////////////////////////////////////////////////////// bool CEntity::GotoState( int nState ) { EntityStateMapItor itor=m_mapStates.begin(); while(itor!=m_mapStates.end()) { if(itor->second==nState) { GotoState(itor->first.c_str()); return true; } ++itor; } return false; } ////////////////////////////////////////////////////////////////////////// bool CEntity::GotoState( const char *sState ) { if (!m_pScriptObject) return false; // State name is case sensetive. if (m_sStateName==sState) { // Entity is already in this state, so ignore this call. return true; } _SmartScriptObject pServerTable(m_pScriptSystem,true); _SmartScriptObject pClientTable(m_pScriptSystem,true); // Get Server table if exist. bool bServerTable = m_pScriptObject->GetValue( SCRIPT_SERVER_STATE,pServerTable ); // Get Client table if exist. bool bClientTable = m_pScriptObject->GetValue( SCRIPT_CLIENT_STATE,pClientTable ); SScriptState newServerState; SScriptState newClientState; // If state name is empty assume we want to reference main entity script table. if (sState[0] != 0) { // Non empty state name // Find if state with givven name exist. bool bStateFound = false; // Check for table in Server part. if (m_pServerState && bServerTable) { _SmartScriptObject pStateTable(m_pScriptSystem,true); if (pServerTable->GetValue( sState,pStateTable )) { // If state table found initialize it InitializeStateTable( pStateTable,newServerState ); bStateFound = true; } } // Check for table in Client part. if (m_pClientState && bClientTable) { _SmartScriptObject pStateTable(m_pScriptSystem,true); if (pClientTable->GetValue( sState,pStateTable )) { // If state table found initialize it InitializeStateTable( pStateTable,newClientState ); bStateFound = true; } } if (!bStateFound) { _SmartScriptObject pStateTable(m_pScriptSystem,true); if (m_pScriptObject->GetValue( sState,pStateTable )) { // If state table found in entity table initialize it for both client and server states. bStateFound = true; if (m_pServerState) InitializeStateTable( pStateTable,newServerState ); // Initialize Client State table only if no server state also exist. if (m_pClientState && !m_pServerState) InitializeStateTable( pStateTable,newClientState ); } } if (!bStateFound) { m_pISystem->GetILog()->Log("[ENTITYWARNING] GotoState of name='%s' called with unknown State: %s",GetName(),sState ); return false; } } else { if (m_pServerState) { if (bServerTable) // If Have Server table initialize state to this Table. InitializeStateTable( pServerTable,newServerState ); else // If Server table not exist use Entity mail table. InitializeStateTable( m_pScriptObject,newServerState ); } if (m_pClientState && bClientTable) { InitializeStateTable( pClientTable,newClientState ); } } // Call End state event. CallStateFunction( ScriptState_OnEndState ); // If state changed kill old timer. KillTimer(); // Goto to new state. SScriptState newState; m_sStateName = sState; // Copy states. if (m_pServerState) { ReleaseStateTable(*m_pServerState); *m_pServerState = newServerState; } if (m_pClientState){ ReleaseStateTable(*m_pClientState); *m_pClientState = newClientState; } // Call BeginState event. CallStateFunction( ScriptState_OnBeginState ); ////////////////////////////////////////////////////////////////////////// // Repeat check if update script function is implemented. m_bUpdateScript = IsStateFunctionImplemented(ScriptState_OnUpdate); ////////////////////////////////////////////////////////////////////////// // Check if need ResolveCollisions for OnContact script function. m_bUpdateOnContact = IsStateFunctionImplemented(ScriptState_OnContact); ////////////////////////////////////////////////////////////////////////// return true; } ////////////////////////////////////////////////////////////////////////// void CEntity::ReleaseStateTable(SScriptState &scriptState ) { // Query state table for supported functions. for (int stateFunc = 0; stateFunc < sizeof(scriptState.pFunction)/sizeof(scriptState.pFunction[0]); stateFunc++) { assert( stateFunc < sizeof(s_ScriptStateFunctions)/sizeof(s_ScriptStateFunctions[0]) ); if(scriptState.pFunction[stateFunc]) m_pScriptSystem->ReleaseFunc(scriptState.pFunction[stateFunc]); } } ////////////////////////////////////////////////////////////////////////// void CEntity::InitializeStateTable( IScriptObject *pStateTable,SScriptState &scriptState ) { // Query state table for supported functions. for (int stateFunc = 0; stateFunc < sizeof(scriptState.pFunction)/sizeof(scriptState.pFunction[0]); stateFunc++) { assert( stateFunc < sizeof(s_ScriptStateFunctions)/sizeof(s_ScriptStateFunctions[0]) ); scriptState.pFunction[stateFunc] = 0; pStateTable->GetValue( s_ScriptStateFunctions[stateFunc],scriptState.pFunction[stateFunc] ); } } ////////////////////////////////////////////////////////////////////////// bool CEntity::IsInState( const char *sState ) { // State name is case sensetive. #if defined(LINUX) if (strcasecmp(sState,m_sStateName.c_str()) == 0) #else if (strcmp(sState,m_sStateName.c_str()) == 0) #endif { return true; } return false; } ////////////////////////////////////////////////////////////////////////// void CEntity::OnTimer( int nTimerId ) { ENTITY_PROFILER if (m_pEntitySystem->m_pDebugTimer->GetIVal()) { CryLog( "OnTimer: %d ms for Entity: %s (%s)",nTimerId,m_name.c_str(),m_sClassName.c_str() ); } if (m_pEntitySystem->m_pUpdateTimer->GetIVal()) CallStateFunction( ScriptState_OnTimer,nTimerId ); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnDamage( IScriptObject *pObj ) { if(m_bGarbage) return; // // store hit parameters // to use by CPlayer::StartDeathAnimation() // CScriptObjectVector deathDir(m_pScriptSystem,true); if(!pObj->GetValue("weapon_death_anim_id", m_DeathType)) m_DeathType = 100; pObj->GetValue("dir", m_DeathDirection); if(pObj->GetValue("ipart", m_DeathZone)) m_DeathZone = GetBoneHitZone(m_DeathZone); else m_DeathZone = 100; CallStateFunction( ScriptState_OnDamage,pObj ); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnEnterArea( IEntity* entity, const int areaID ) { CallStateFunction( ScriptState_OnEnterArea,entity->GetScriptObject(),areaID ); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnLeaveArea( IEntity* entity, const int areaID ) { CallStateFunction( ScriptState_OnLeaveArea,entity->GetScriptObject(),areaID ); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnProceedFadeArea( IEntity* entity, const int areaID, const float fadeCoeff ) { // Server side always first. CallStateFunction( ScriptState_OnProceedFadeArea,entity->GetScriptObject(),areaID,fadeCoeff ); } ////////////////////////////////////////////////////////////////////////// void CEntity::OnBind( IEntity* entity, const char par ) { // don't call it on server - client only if (m_pClientState && m_pClientState->pFunction[ScriptState_OnBind]) { m_pScriptSystem->BeginCall(m_pClientState->pFunction[ScriptState_OnBind]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam(entity->GetScriptObject()); m_pScriptSystem->PushFuncParam(par); m_pScriptSystem->EndCall(); } } ////////////////////////////////////////////////////////////////////////// void CEntity::OnUnBind( IEntity* entity, const char par ) { // don't call it on server - client only if (m_pClientState && m_pClientState->pFunction[ScriptState_OnUnBind]) { m_pScriptSystem->BeginCall(m_pClientState->pFunction[ScriptState_OnUnBind]); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->PushFuncParam(entity->GetScriptObject()); m_pScriptSystem->PushFuncParam(par); m_pScriptSystem->EndCall(); } } ////////////////////////////////////////////////////////////////////////// void CEntity::SetTimer(int msec) { // Remove old timer. if (m_nTimer > 0) m_pEntitySystem->RemoveTimerEvent( m_nID ); //m_nStartTimer = (int)(m_pISystem->GetITimer()->GetCurrTime()*1000); m_nTimer = msec; //m_awakeCounter = 2; // As long as there is timer, must stay awake. if (m_nTimer > 0) { SEntityTimerEvent event; event.timerId = msec; event.entityId = m_nID; m_pEntitySystem->AddTimerEvent( msec,event ); } } ////////////////////////////////////////////////////////////////////////// void CEntity::KillTimer() { //if (m_nTimer > 0) m_pEntitySystem->RemoveTimerEvent( m_nID ); m_nTimer = -1; } ////////////////////////////////////////////////////////////////////////// void CEntity::RegisterState(const char *sState) { EntityStateMapItor itor=m_mapStates.find(sState); if(itor!=m_mapStates.end()) return; m_mapStates.insert(EntityStateMapItor::value_type(sState,m_cLastStateID)); m_cLastStateID++; } void CEntity::ApplyForceToEnvironment(const float radius, const float force) { m_pISystem->GetI3DEngine()->ApplyForceToEnvironment( m_center, radius, force); } // //------------------------------------------------------------------ //!determens on each side if entity direction is //!1-front 2-back 3-left 4- right int CEntity::GetSide(const Vec3d& direction) { bool forward; // Get our current orientation angles Vec3d playerDir = GetAngles(); // Convert to a direction vector playerDir=ConvertToRadAngles(playerDir); // don't need vertical component to determin left/right direction playerDir.z = 0; playerDir = GetNormalized(playerDir); // Create a working copy of the direction vector Vec3d playerRight = playerDir; // Create a vector pointing up Vec3d up(0,0,1); // Create a vector perpendicular to both the direction vector and the up vector // This vector will point to the players right playerRight = playerRight.Cross(up); // Generate the Cos of the angle between the player facing // direction and the current player velocity float dotDir = playerDir.Dot( direction ); // Determine if the velocity vector and the facing vector differ by less than 90 degrees if (dotDir <= 0.0) // We have determined that the player's general direction // matches direction (within 90 degrees) forward = true; else forward = false; if(forward) return 1; else return 2; } // //------------------------------------------------------------------ void CEntity::AttachToBone(EntityId id,const char* boneName) { // safe upcast since we know what the entity system holds CEntity *pEntity =(CEntity *) m_pEntitySystem->GetEntity(id); ICryCharInstance *character = GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); if (character) { // m_pSelectedInfo->pBindInfo = character->AttachObjectToBone( pEntity->GetIStatObj(0),boneName, false ); } } // //------------------------------------------------------------------ BoneBindHandle CEntity::AttachObjectToBone(int slot,const char* boneName,bool bMultipleAttachments, bool bUseZOffset ) { ICryCharInstance *character = GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); if (character) { if (!bMultipleAttachments) { // Old way. // m_pSelectedInfo->pBindInfo = return character->AttachObjectToBone( GetIStatObj(slot),boneName, false, bUseZOffset ? ICryCharInstance::FLAGS_ATTACH_ZOFFSET : 0 ); } else { // New way. int boneid = character->GetModel()->GetBoneByName(boneName); if (boneid >= 0) { return character->AttachToBone( GetIStatObj(slot),boneid, bUseZOffset ? ICryCharInstance::FLAGS_ATTACH_ZOFFSET : 0); } } } return -1; } // //------------------------------------------------------------------ void CEntity::DetachObjectToBone( const char* boneName,BoneBindHandle objectBindHandle/* =-1 */ ) { ICryCharInstance *character = GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); if (character) { if (objectBindHandle == -1) { // Old way. // m_pSelectedInfo->pBindInfo = character->AttachObjectToBone( NULL, boneName, false ); } else { character->Detach( objectBindHandle ); } } } // returns true if the entity changed in any way trough moving or animation bool CEntity::HasChanged(void) { if (m_pCryCharInstance[0]) if (m_pCryCharInstance[0]->IsCharacterActive()) return true; return IsMoving(); } void CEntity::CalculateInWorld(void) { // rotate the point offset Vec3d vBpos = m_matParentMatrix.TransformPointOLD(m_realcenter); MoveTo(vBpos); Matrix44 tempMat = GetTransposed44(m_matParentMatrix); tempMat.NoScale(); //M2Q_CHANGED_BY_IVO //CryQuat cxquat(m_matParentMatrix); //CryQuat cxquat = CovertMatToQuat( GetTransposed44(m_matParentMatrix) ); CryQuat cxquat = Quat( tempMat ); CryQuat rxquat; rxquat.SetRotationXYZ(DEG2RAD(m_realangles)); CryQuat result = cxquat*rxquat; Vec3d finalangles = Ang3::GetAnglesXYZ(Matrix33(result)); RotateTo(RAD2DEG(finalangles)); } void CEntity::ForceBindCalculation(bool bEnable) { m_bForceBindCalculation = bEnable; //m_realcenter = m_center; //m_realangles = m_angles; //@FIXME: Real scale.. } void CEntity::SetParentLocale(const Matrix44 & matParent) { // If matrices differ, entity moved so update it. if (memcmp(&m_matParentMatrix,&matParent,sizeof(matParent)) != 0) m_awakeCounter = 1; // Awake for at least one frame. m_matParentMatrix = matParent; } // //-------------------------------------------------------------------------------------- bool CEntity::InitLight( const char* sImg, const char* sShader, bool bUseAsCube, float fAnimSpeed, int nLightStyle, float fCoronaScale ) { if (m_pDynLight) delete m_pDynLight; m_pDynLight = new CDLight(); if(sImg && sImg[0]) { m_pDynLight->m_fAnimSpeed = fAnimSpeed; int nFlags2 = FT2_FORCECUBEMAP; if (bUseAsCube) nFlags2 |= FT2_REPLICATETOALLSIDES; if (fAnimSpeed) nFlags2 |= FT2_CHECKFORALLSEQUENCES; m_pDynLight->m_pLightImage = m_pISystem->GetIRenderer()->EF_LoadTexture(sImg, 0, nFlags2, eTT_Cubemap); if(!m_pDynLight->m_pLightImage || !m_pDynLight->m_pLightImage->IsTextureLoaded()) CryWarning(VALIDATOR_MODULE_ENTITYSYSTEM,VALIDATOR_WARNING,"Light projector texture not found: %s", sImg); m_pDynLight->m_Flags = DLF_PROJECT; } else m_pDynLight->m_Flags = DLF_POINT; m_pDynLight->m_nLightStyle = nLightStyle; m_pDynLight->m_fCoronaScale = fCoronaScale; if(sShader && sShader[0]) m_pDynLight->m_pShader = m_pISystem->GetIRenderer()->EF_LoadShader((char*)sShader, eSH_World); InitEntityRenderState(); return true; } // //-------------------------------------------------------------------------------------- CDLight *CEntity::GetLight( ) { return m_pDynLight; } //---------------------------------------------------------------------------------------------------- // int CEntity::GetBoneHitZone( int boneIdx ) const { ICryCharInstance *pIChar = GetCharInterface()->GetCharacter(0); if (!pIChar) return 0; int hitZone = pIChar->GetDamageTableValue(boneIdx); #ifdef _DEBUG const char *szBoneName = pIChar->GetModel()->GetBoneName(boneIdx); #endif // TRACE( "%s hit", name ); return pIChar->GetDamageTableValue(boneIdx); } //---------------------------------------------------------------------------------------------------- // hit parameters // set by OnDamage() // used by CPlayer::StartDeathAnimation() void CEntity::GetHitParms( int &deathType, int &deathDir, int &deathZone ) const { deathType = m_DeathType; deathDir = m_DeathDirection; deathZone = m_DeathZone; } // //-------------------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// void CEntity::Hide( bool bHide) { if (m_bHidden != bHide)// && m_physic != NULL) { SetRndFlags(ERF_HIDDEN,bHide); bool bEnablePhysics = !bHide; if (m_physicEnabled != bEnablePhysics) EnablePhysics( bEnablePhysics ); m_bHidden = bHide; if(!bHide) ForceRegisterInSectors(); if (!bHide) CryLogComment( "Entity %s Unhidden",m_name.c_str() ); else CryLogComment( "Entity %s Hidden",m_name.c_str() ); } m_bHidden = bHide; } void CEntity::UpdateHierarchy( SEntityUpdateContext &ctx ) { if (!m_lstBindings.empty()) { ENTITY_PROFILER //[kirill] // when we update child - it can unbind itself - so m_lstBindings changes - let's keep a copy static std::vector binds; binds.resize(0); //this does not work on AMD64 // binds.insert( binds.end(),m_lstBindings.begin(),m_lstBindings.end() ); BINDLISTItor bi; for (bi = m_lstBindings.begin(); bi != m_lstBindings.end(); ++bi) binds.push_back((*bi)); for(std::vector::iterator bndItr=binds.begin();bndItr!=binds.end();++bndItr ) { CEntity *pEntity =(CEntity *)m_pEntitySystem->GetEntity((*bndItr)); if (pEntity) { //OPTIMISED_BY_IVO Matrix44 mat=Matrix34::CreateRotationXYZ( Deg2Rad(m_angles),m_center); mat=GetTransposed44(mat); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 //pEntity->SetParentLocale(m_center, m_angles); pEntity->SetParentLocale(mat); bool bNeedUpdate = (pEntity->m_bUpdate && !pEntity->m_bSleeping) || (pEntity->m_awakeCounter > 0); if (bNeedUpdate) { pEntity->Update(ctx); } // If have childs. if (pEntity->m_bUpdateBinds) { // If not updated. pEntity->UpdateHierarchy( ctx ); } } else { // this entity was deleted. Lets release its ID so it can be reused // m_pEntitySystem->ReleaseMark((*bi)); GetISystem()->GetILog()->LogToFile( "UpdateHierarchy <%s> has dead bind id >> %d",GetName(), (*bndItr) ); } } } /* if (!m_lstBindings.empty()) { ENTITY_PROFILER BINDLISTItor bi,next; //[kirill] // when we update child - it can unbind itself - so m_lstBindings changes for (bi = m_lstBindings.begin(); bi != m_lstBindings.end() && !m_lstBindings.empty(); bi = next) { next = bi; ++next; CEntity *pEntity =(CEntity *)m_pEntitySystem->GetEntity((*bi)); if (pEntity) { //Matrix44 mat; //mat.Identity(); //mat=GetTranslationMat(m_center)*mat; //mat=GetRotationZYX44(-gf_DEGTORAD*m_angles)*mat; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO Matrix44 mat=Matrix34::GetRotationXYZ34( Deg2Rad(m_angles),m_center); mat=GetTransposed44(mat); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 //pEntity->SetParentLocale(m_center, m_angles); pEntity->SetParentLocale(mat); bool bNeedUpdate = (pEntity->m_bUpdate && !pEntity->m_bSleeping) || (pEntity->m_awakeCounter > 0); if (bNeedUpdate) { pEntity->Update(ctx); } // If have childs. if (pEntity->m_bUpdateBinds) { // If not updated. pEntity->UpdateHierarchy( ctx ); } } else { // this entity was deleted. Lets release its ID so it can be reused // m_pEntitySystem->ReleaseMark((*bi)); GetISystem()->GetILog()->LogToFile( "UpdateHierarchy <%s> has dead bind id >> %d",GetName(), (*bi) ); // m_lstBindings.erase(bi); m_lstBindings.remove((*bi)); } } } */ } // // gets radius from physics bounding box float CEntity::GetRadiusPhys() const { IPhysicalEntity *physic = GetPhysics(); if (physic) { Vec3d min, max; pe_params_bbox pbb; physic->GetParams(&pbb); min = pbb.BBox[0]-m_center; max = pbb.BBox[1]-m_center; //pe_status_pos status_pos; //m_physic->GetStatus(&status_pos); //min = status_pos.BBox[0]; //max = status_pos.BBox[1]; if (min.Length() > max.Length()) return min.Length(); return max.Length(); } return 0.0f; } //! // void CEntity::ActivatePhysics( bool activate ) { if(!m_physic) return; pe_player_dynamics pd; pd.bActive = activate; m_physic->SetParams(&pd); // phisics switched } void CEntity::CheckBBox(void) { if (m_bRecalcBBox) { CalcWholeBBox(); m_bRecalcBBox = false; } } #define sizeof_std_string(m_sClassName) ((m_sClassName.length()<16)?sizeof(string):m_sClassName.length()+1+sizeof(string)) void CEntity::GetMemoryStatistics(ICrySizer *pSizer) { size_t nSize = sizeof(CEntity) + sizeof_std_string(m_sClassName) + sizeof_std_string(m_name) + sizeof_std_string(m_sStateName); if (!pSizer->AddObject(this,nSize)) return; pSizer->Add(*m_pCamera); pSizer->Add(*m_pDynLight); pSizer->Add(*m_pClientState); pSizer->Add(*m_pServerState); if (m_pPhysState && m_iPhysStateSize>0) pSizer->AddObject(m_pPhysState,m_iPhysStateSize); } void CEntity::SetHandsIKTarget( const Vec3d* target ) { if(target) { m_vHandIKTarget = *target; m_vHandIKTarget -= m_center; /* Matrix44 mat; Matrix44 matInv; mat.Identity(); // mat.SetRotationXYZ44(-gf_DEGTORAD*GetAngles()); // matInv = mat.GetInverted44( mat ); // m_vHandIKTarget = matInv.TransformVector(m_vHandIKTarget); mat.SetRotationZ44(gf_DEGTORAD*(GetAngles().z)); m_vHandIKTarget = mat.TransformVector(m_vHandIKTarget); */ Matrix44 mat = Matrix33::CreateRotationZ( DEG2RAD(GetAngles().z) ); m_vHandIKTarget = mat.TransformVectorOLD(m_vHandIKTarget); m_bHandIK = true; } else m_bHandIK = false; } ////////////////////////////////////////////////////////////////////////// bool CEntity::IsStateFunctionImplemented( EScriptStateFunctions function ) { bool bRes = false; if (m_pServerState && m_pServerState->pFunction[function]) bRes = true; else if (m_pClientState && m_pClientState->pFunction[function]) bRes = true; return bRes; } ////////////////////////////////////////////////////////////////////////// void CEntity::CallStateFunction( EScriptStateFunctions function ) { HSCRIPTFUNCTION funcServer = 0; HSCRIPTFUNCTION funcClient = 0; if (m_pServerState) funcServer = m_pServerState->pFunction[function]; if (m_pClientState) funcClient = m_pClientState->pFunction[function]; if (funcClient) { m_pScriptSystem->BeginCall(funcClient); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); } if (funcServer) { m_pScriptSystem->BeginCall(funcServer); m_pScriptSystem->PushFuncParam(m_pScriptObject); m_pScriptSystem->EndCall(); } } ////////////////////////////////////////////////////////////////////////// void CEntity::MarkAsGarbage() { m_bGarbage = true; if (m_registeredInSector) UnregisterInSector(); } ////////////////////////////////////////////////////////////////////////// void CEntity::SetGarbageFlag( bool bGarbage ) { m_bGarbage = bGarbage; if (m_bGarbage && m_registeredInSector) UnregisterInSector(); } ////////////////////////////////////////////////////////////////////////// void CEntity::AddCollider( EntityId id ) { if (!m_bTrackColliders) return; if (!m_pColliders) m_pColliders = new Colliders; // Try to insert new colliding entity to our colliders set. std::pair result = m_pColliders->insert(id); if (result.second == true) { // New collider was successfully added. IEntity *pEntity = m_pEntitySystem->GetEntity(id); if (pEntity) { //GetISystem()->GetILog()->Log( "Add Collider %s",pEntity->GetName() ); OnEnterArea( pEntity,0 ); } } } ////////////////////////////////////////////////////////////////////////// void CEntity::RemoveCollider( EntityId id ) { if (!m_pColliders) return; // Try to remove collider from set, then compare size of set before and after erase. int prevSize = m_pColliders->size(); m_pColliders->erase(id); if (m_pColliders->size() != prevSize) { // If anything was erased. IEntity *pEntity = m_pEntitySystem->GetEntity(id); if (pEntity) { //GetISystem()->GetILog()->Log( "Remove Collider %s",pEntity->GetName() ); OnLeaveArea( pEntity,0 ); } } } ////////////////////////////////////////////////////////////////////////// void CEntity::CheckColliders() { ENTITY_PROFILER if (!m_bTrackColliders) return; Vec3d mins, maxs; GetBBox(mins,maxs); if (maxs.x<=mins.x || maxs.y<=mins.y || maxs.z<=mins.z) return; // something wrong // resolve collisions IPhysicalWorld *pWorld = m_pISystem->GetIPhysicalWorld(); IPhysicalEntity **ppColliders; int nCount = 0; // Prepare temporary set of colliders. // Entities which will be reported as colliders will be erased from this set. // So that all the entities which are left in the set are not colliders anymore. Colliders tempSet; if (m_pColliders) tempSet = *m_pColliders; if (nCount = pWorld->GetEntitiesInBox(mins,maxs, ppColliders, 14 )) { static std::vector s_colliders; s_colliders.resize(nCount); memcpy( &s_colliders[0],ppColliders,nCount*sizeof(IPhysicalEntity*) ); // Check if colliding entities are in list. for (int i = 0; i < nCount; i++) { CEntity *pEntity = (CEntity *)s_colliders[i]->GetForeignData(0); if (!pEntity) continue; int id = pEntity->GetId(); int prevSize = tempSet.size(); tempSet.erase( id ); if (tempSet.size() == prevSize) { // pEntity is a new entity not in list. AddCollider( id ); } } } // If any entity left in this set, then they are not colliders anymore. if (!tempSet.empty()) { for (Colliders::iterator it = tempSet.begin(); it != tempSet.end(); ++it) { RemoveCollider( *it ); } } } ////////////////////////////////////////////////////////////////////////// void CEntity::OnPhysicsBBoxOverlap(IEntity *pOther) { //#ifdef USE_PHYSICS_EVENT_CALLBACK if (!m_bTrackColliders) return; // Awake entity for at least 5 frames. m_awakeCounter = 5; //#endif } ////////////////////////////////////////////////////////////////////////// void CEntity::OnPhysicsStateChange( int nNewSymClass,int nPrevSymClass ) { // If its update depends on physics, physics state defines if this entity is to be updated. if (m_eUpdateVisLevel == eUT_Physics || m_eUpdateVisLevel == eUT_PhysicsVisible) { // Awake entity for few frames. m_awakeCounter = 4; if (nNewSymClass == SC_ACTIVE_RIGID) { //m_pISystem->GetILog()->Log("Phys AWAKE" ); SetNeedUpdate( true ); } else if (nNewSymClass == SC_SLEEPING_RIGID) { // Entity must go to sleep. //m_pISystem->GetILog()->Log("Phys SLEEP" ); SetNeedUpdate( false ); CallStateFunction(ScriptState_OnStopRollSlideContact, "roll"); CallStateFunction(ScriptState_OnStopRollSlideContact, "slide"); } } } ////////////////////////////////////////////////////////////////////////// // creates the physical bbox for callback void CEntity::CreatePhysicsBBox() { //#ifdef USE_PHYSICS_EVENT_CALLBACK //m_vBoxMin = mins; //m_vBoxMax = maxs; if (!m_bTrackColliders) return; if (!m_pBBox) { ////////////////////////////////////////////////////////////////////////// // Create a physics bbox to get the callback from physics when a move event happens. m_pBBox = m_pISystem->GetIPhysicalWorld()->CreatePhysicalEntity(PE_STATIC); pe_params_pos parSim; parSim.iSimClass = 6; m_pBBox->SetParams(&parSim); pe_params_foreign_data foreignData; m_pBBox->GetParams(&foreignData); foreignData.pForeignData = (IEntity*)this; foreignData.iForeignFlags |= ent_rigid | ent_living; m_pBBox->SetParams(&foreignData); } if (m_pBBox) { Vec3 min,max; GetBBox(min,max); // Check for invalid bounding box. if (GetLengthSquared(max-min) < 10000*10000) { pe_params_bbox parBBox; parBBox.BBox[0] = min; parBBox.BBox[1] = max; m_pBBox->SetParams(&parBBox); } } //#endif } ////////////////////////////////////////////////////////////////////////// void CEntity::TrackColliders( bool bEnable ) { if (bEnable && bEnable != m_bTrackColliders) { m_bTrackColliders = bEnable; CreatePhysicsBBox(); m_awakeCounter = 5; // Awake entity for few updates. } m_bTrackColliders = bEnable; } void CEntity::Remove( ) { m_pEntitySystem->RemoveEntity( GetId(), false ); } ////////////////////////////////////////////////////////////////////////// void CEntity::SetPhysicsState( const char *sPhysicsState ) { if (m_physic) { m_physic->SetStateFromSnapshotTxt( sPhysicsState,strlen(sPhysicsState) ); // Update entity few times to get physics data to character. m_awakeCounter = 5; } } ////////////////////////////////////////////////////////////////////////// void CEntity::InvalidateBBox() { m_awakeCounter = 1; m_bRecalcBBox = true; } ////////////////////////////////////////////////////////////////////////// void CEntity::SetUpdateVisLevel(EEntityUpdateVisLevel nUpdateVisLevel) { m_eUpdateVisLevel = nUpdateVisLevel; if (m_physic) { pe_params_flags pf; if (m_eUpdateVisLevel==eUT_PhysicsPostStep) pf.flagsOR = pef_custom_poststep; else pf.flagsAND = ~pef_custom_poststep; m_physic->SetParams(&pf); } } ////////////////////////////////////////////////////////////////////////// void CEntity::OnVisibilityChange( bool bVisible ) { if (!bVisible) { // Turn off cloth update. if (m_physic && (m_flags & ETY_FLAG_CALC_PHYSICS) && !(m_flags&ETY_FLAG_IGNORE_PHYSICS_UPDATE) && m_physic->GetType()==PE_SOFT) { pe_action_awake aa; aa.bAwake = false; m_physic->Action(&aa); } } }