3897 lines
105 KiB
C++
3897 lines
105 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <string.h>
|
|
#include <algorithm>
|
|
|
|
#include "Entity.h"
|
|
#include <Stream.h>
|
|
#include <IScriptSystem.h>
|
|
|
|
#include <IProcess.h>
|
|
#include <ITimer.h>
|
|
#include <IGame.h>
|
|
#include <ISystem.h>
|
|
#include <IRenderer.h>
|
|
#include "CryHeaders.h"
|
|
#include <ILipSync.h>
|
|
|
|
#include <I3DEngine.h>
|
|
#include <ILog.h>
|
|
#include <IAISystem.h>
|
|
|
|
#include <IAgent.h>
|
|
#include <float.h>
|
|
|
|
//#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)<MAX_FRAME_ID_STEP_PER_FRAME);
|
|
|
|
if (m_bVisible != m_bWasVisible)
|
|
OnVisibilityChange(m_bVisible);
|
|
|
|
// check if entity logic passes selected visibility test
|
|
if (m_eUpdateVisLevel && m_eUpdateVisLevel!=eUT_PhysicsPostStep && m_pEntitySystem->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(velImpact<velImpactCur)
|
|
{
|
|
IPhysicalEntity *pCollider = pWorld->GetPhysicalEntityById(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>((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<Vec3d> 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; i<pLB->m_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<<i)
|
|
m_fCharZOffsetTarget = sensors.pPoints[i].z-fCharZOffsetBase;
|
|
//m_pCryCharInstance[k]->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<<i)
|
|
m_pCryCharInstance[k]->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<float>(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;i<m_nMaxCharNum;i++)
|
|
{
|
|
if (m_pCharPhysPlaceholders[i])
|
|
{
|
|
m_pCharPhysPlaceholders[i]->GetStatus(&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_impulse<sqr(sd.mass*minVel))
|
|
{
|
|
float fScale = m_pEntitySystem->m_pImpulseScale->GetFVal();
|
|
if (fScale>0)
|
|
mod_impulse *= fScale;
|
|
else if (sd.mass<m_pEntitySystem->m_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<IPhysicalEntity*> 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<float>( 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<EntityId> 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<EntityId>::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<Colliders::iterator,bool> 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<IPhysicalEntity*> 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);
|
|
}
|
|
}
|
|
} |