////////////////////////////////////////////////////////////////////// // // Game Source Code // // File: XEntityPlayer.cpp // Description: Entity player class. // // History: // - August 16, 2001: Created by Alberto Demichelis // ////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "XPlayer.h" #include "WeaponClass.h" #include "XVehicle.h" #include "WeaponSystemEx.h" #include "ScriptObjectStream.h" #include #include #include // remove this //#include #include // <> look above #include "I3DEngine.h" #include // to use _isnan() #include //! Minimal time before player can be alive again. #define PLAYER_RESPAWN_TIME 1.0f //! Minimal time before player can be respawned. #define PLAYER_DEATH_TIME 1.0f //! Minimal time before player can change weapon. #define PLAYER_WEAPON_CHANGE_TIME 0.1f /* static int64 g_nValidateHeapCounter = 0; // central place to turn on/off the heap validation inline void ValidateHeap() { #if defined(WIN64) && defined(_DEBUG) // on AMD64, heap validation is extremely slow if (g_nValidateHeapCounter > 1000*1000 || !((++g_nValidateHeapCounter)&0xFF)) #endif assert(IsHeapValid()); } */ //------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------- bool CheckIfNAN( const Vec3d& vPos ) { if (_isnan(vPos.x) || _isnan(vPos.y) || _isnan(vPos.z)) { GameWarning( "NotANumber tried to be set for position of CPlayer" ); return true; } return false; } ////////////////////////////////////////////////////////////////////////////////////////////// //! CPlayer implemenation. /////////////////////////////////////////////// CPlayer::CPlayer(CXGame *pGame) : m_ssoHitPosVec(pGame->GetScriptSystem()), m_ssoHitDirVec(pGame->GetScriptSystem()), m_ssoHitNormVec(pGame->GetScriptSystem()), m_vColor(1,0,1), m_vShake(0,0,0), m_pDynLight( NULL ), m_vCharacterAngles( 0,0,0 ), m_JumpStage(0), m_JumpAniLenght(0), m_bInertiaOverride(false), m_bIsFish(false), m_AutoCenter(0), m_fNoChangeangleTime(0.0f), m_fProcessTime(0.0f), m_fCameraTime(0.0f), m_vEyeAnglesBaseOffset(0,0,0), m_bLastDeltaEyeVehicle( false ), m_vSafeAngAtMountedWeapon(0,0,0), m_MntWeaponStandTime(0.0f), m_vProneEnvNormal(0,0,0), m_vCurEntAngle(0,0,0), m_bGrenadeAnimation(false) { m_pLastUsedCharacter = NULL; m_weaponPositionState = WEAPON_POS_UNDEFINED; m_vEyePos.Set(0,0,0); m_bWeaponJustFired = false; m_pLightTarget = 0; float tm=.3f; m_pBoneHead = NULL; m_pBoneNeck = NULL; m_pBoneSpine = NULL; m_pBoneSpine1 = NULL; m_pBoneSpine2 = NULL; m_pGame = pGame; m_pTimer = pGame->GetSystem()->GetITimer(); m_pScriptSystem = pGame->GetScriptSystem(); m_p3DEngine = pGame->GetSystem()->GetI3DEngine(); m_pPhysicalWorld=pGame->GetSystem()->GetIPhysicalWorld(); m_stmScriptStream.Create(m_pScriptSystem); m_nSelectedWeaponID = -1; m_PrevWeaponID = -1; ZeroStruct( m_stats ); m_stats.weapon = -1; m_stats.inVehicleState = PVS_OUT; // m_stats.inVehicleActive = false; ZeroStruct( m_walkParams ); // Init Walk params. m_walkParams.runRoll = 0.2f; m_walkParams.runPitch = 0.5f; m_walkParams.curLength = 0.0f; m_walkParams.stepLength = 3.5f; m_walkParams.flyCoeff = 15.0f; m_walkParams.prevF = 0.0f; m_walkParams.weaponCycle = 0.006f; m_walkParams.weaponCycleSpeed = 8; m_walkParams.swayAmp=0.0f; m_walkParams.swayFreq=0.0f; m_walkParams.swayOfs=0.0f; m_walkParams.shakeDegree=0.0f; m_walkParams.leanAmount = 0.0f; m_walkParams.leanStart = 0.0f; m_walkParams.leanEnd = 0.0f; m_walkParams.leanFactor = 0.0f; m_walkParams.fWaterPitchPhase=0.0f; m_walkParams.fWaterRollPhase=0.0f; m_walkParams.fLeanTarget=0; m_walkParams.fCurrLean=0; m_pVehicle = NULL; //@FIXME Give player some life m_stats.health = 100; m_stats.armor = 0; m_stats.maxHealth = 100; m_stats.maxArmor = 100; m_stats.legHealth = 100; m_stats.maxLegHealth = 100; m_stats.armHealth = 100; m_stats.maxArmHealth = 100; m_stats.dmgFireAccuracy = 100; m_stats.dmgSpeedCoeff = 100; m_stats.FiringType = m_stats.LastFiringType = eNotFiring; m_stats.cancelFireFlag = false; m_stats.weapon = 0; m_stats.score=0; m_stats.deaths=0; m_stats.firing_grenade = false; m_stats.firemode = 0; m_stats.canfire = true; m_stats.crosshairOnScreen = true; m_stats.fSpeedScale = 1.0f; m_stats.climbing = false; m_stats.holding_breath=false; m_stats.concentration=false; m_stats.firing=false; m_stats.reloading=false; m_stats.weapon_busy=0; m_stats.grenadetype = 1; m_stats.numofgrenades = 0; m_stats.drawfpweapon=true; m_stats.aiming=false; m_stats.fire_rate_busy=false; m_stats.fVel=1.0f; m_stats.melee_distance = 0.0f; m_stats.has_flashlight = false; m_stats.has_binoculars = false; m_stats.fInWater = 0.0f; m_stats.swimTime = 0.0f; m_staminaClient = m_stats.stamina = 100; // m_breathClient = m_stats.breath = 100; SetDimNormal(); SetDimCrouch(); SetDimProne(); SetDimStealth(); m_currDimensions = eDimNone; ZeroStruct( m_AngleLimitBase ); m_AngleLimitVFlag = false; m_AngleLimitHFlag = false; m_fGravityOverride=1E10; m_pScriptObject=NULL; m_pRedirected = NULL; m_nStanding = -1; m_nMode = -1; m_nStrafe = -1; m_nForward = -1; m_bFirstPerson = false; m_bFirstPersonLoaded = true; m_bLightOn = false; m_LegAngle = 0; m_LegAngleDesired = 0; m_LegDeltaAngle = 0; m_LegAngleIdleTime = 0; m_LegADeltaLimit = 110; m_LegADeltaLimitForce = 120; m_LegAIdleTimeLimit = 2; m_LegAngleVelMoving = 350; m_LegAngleVelTimed = 320; m_LegAngleVel = m_LegAngleVelMoving; m_FlyTime = 0; m_NotRunTime = 0; m_RunSpeed = 4; m_WalkSpeed = 3; m_CrouchSpeed = 2; m_ProneSpeed = 1; m_SwimSpeed = 3.5f; m_Sprinting = false; m_CameraMode=PCM_OUTVEHICLE; m_bCameraTransitionOnlyZ = false; // SetAnimationRefSpeed(); SetAnimationRefSpeedRun( ); SetAnimationRefSpeedWalk( ); SetAnimationRefSpeedXWalk( ); SetAnimationRefSpeedCrouch( ); m_JumpForce = 4.0f; m_LeanDegree = 9.0f; m_Dynamics.gravity = 9.81f; m_Dynamics.swimming_gravity = 2.0f; m_Dynamics.inertia = 10.0f; m_Dynamics.swimming_inertia = 1.0f; m_Dynamics.air_control = 0.1f; m_CurStance = eNone; m_PrevStance = m_CurStance; m_aimLook = true; m_bIsAI = false; //m_PrevAniName = "NoAni"; m_AnimationSystemEnabled = 1; m_bAlwaysRun = true; m_bSwimming=false; m_bEyesInWater=false; // not in water at the beginning m_stats.underwater = 0.0f; // standing jump m_JumpDist[0] = 3.0f; m_JumpHeight[0] = 1.3f; // walk jump m_JumpDist[1] = 3.0f; m_JumpHeight[1] = 1.3f; // run jump m_JumpDist[2] = 4.0f; m_JumpHeight[2] = 1.3f; m_fRecoilXDelta=0; m_fRecoilZDelta=0; m_fRecoilXUp=0; m_fRecoilZUp=0; m_fRecoilX=0; m_fRecoilZ=0; m_fAccuracy=0; m_fAccuracyMod=0; memset(m_vWeaponSlots,0,sizeof(m_vWeaponSlots)); m_stats.lock_weapon=false; m_fHeatBodyFadingSpeed=m_fHeatWeaponsFadingSpeed=-1; // switch immediately m_fHeatBodyDesiredValue=m_fHeatWeaponsDesiredValue=-1; // not set m_fHeatBodyCurrValue=m_fHeatWeaponsCurrValue=0; m_LastUsed = m_vBlindingList.end(); m_RunSpeedScale = 3.63f; m_CrouchSpeedScale = .8f; m_ProneSpeedScale = .5f; m_XRunSpeedScale = 1.5f; // stealthth m_XWalkSpeedScale = .81f; m_RRunSpeedScale = 3.63f; // relaxed m_RWalkSpeedScale = .81f; m_stats.bIsBlinded = false; m_stats.curBlindingValue = 0.0f; m_pMountedWeapon = NULL; m_stats.bIsLimping = false; m_pPrevDmgCollider = 0; m_timeDmgCollision = 0; m_prevCollDamage = 0; m_sPrevAniName = ""; m_sPrevAniNameLayer1 = ""; m_sPrevAniNameLayer2 = ""; m_fShootAniLength = 0.0f; SetBlendTime("aidle_jump_air", .2f); m_fDeathTimer = -1; m_stats.onLadder = false; m_RunningTime = m_RunningTimeClient = 0; m_WalkingTime = m_WalkingTimeClient = 0; m_NotRunTime = m_NotRunTimeClient = 0; m_JTWalk = .2f; m_JTRun = .4f; m_fLastCamUpdateTime = 0; m_vDeltaCamAngles.Set(0,0,0); m_vDeltaCamPos.Set(0,0,0); m_vPrevCamAngles.Set(0,0,0); m_vPrevCamPos.Set(0,0,0); m_vWeaponAngles.Set(0,0,0); m_AniSpeedXRun[0] = -1.0f; m_AniSpeedXRun[1] = -1.0f; m_AniSpeedXRun[2] = -1.0f; m_sVehicleEyeHelper.clear(); m_bWaitForFireRelease = false; m_vLastMotionDir.Set(0,0,0); m_input_accel = 0;//default is no input acceleration. m_input_stop_accel = 0; m_input_accel_indoor = 0;//default is no input acceleration. m_input_stop_accel_indoor = 0; m_bWriteOccured = false; m_bStayCrouch = false; m_SynchedRandomSeed.SetParent(this); m_vLadderPosition.Set(0,0,0); m_vLadderAngles.Set(0,0,0); m_vHeadAngles.Set(0,0,0); m_AreaUser.SetGame( pGame ); m_AreaUser.SetEntity( GetEntity() ); m_fLastProneTime = 0; m_pLastAiming=NULL; } /////////////////////////////////////////////// CPlayer::~CPlayer() { SwitchFlashLight( false ); ListOfPlayers::iterator self = std::find(m_pGame->m_DeadPlayers.begin(), m_pGame->m_DeadPlayers.end(), this); if(self!=m_pGame->m_DeadPlayers.end()) m_pGame->m_DeadPlayers.erase(self); m_pGame->m_XAreaMgr.ExitAllAreas( m_AreaUser ); // remove shared weapon GetEntity()->GetCharInterface()->SetCharacter(1, 0); // if(m_pSelectedWeapon){ // m_pSelectedWeapon->GetEntity()->DrawCharacter(0,0); // } SetEntity(0); if(m_pScriptObject) m_pScriptObject->Release(); m_pScriptObject=NULL; m_mapPlayerWeapons.clear(); if (m_pDynLight) { delete m_pDynLight; m_pDynLight=NULL; } } /////////////////////////////////////////////// /*! Initializes the player-container. @return true if succeeded, false otherwise */ bool CPlayer::Init() { //@FIXME probably should be created in script. m_pGame->m_pLog->Log("XEntityPlayer %d initialised\n", m_pEntity->GetId() ); m_pEntity->GetScriptObject()->SetValue("type", "Player"); IAIObject *pObject = m_pEntity->GetAI(); if (pObject) { IPuppet *pPuppet; if (pObject->CanBeConvertedTo(AIOBJECT_PUPPET, (void **) &pPuppet)) m_bIsAI = true; } if (!m_bIsAI) // if(m_pEntity->GetAI()->GetType() == AIOBJECT_PLAYER) m_pEntity->SetNeedUpdate(true); // else // m_pEntity->SetDestroyable( true ); /* // if (!m_bIsAI) if(m_pEntity->GetAI()->GetType() == AIOBJECT_PLAYER) { m_pEntity->SetNeedUpdate(true); m_pGame->p_speed_run->Set(m_RunSpeed); m_pGame->p_speed_walk->Set(m_WalkSpeed); m_pGame->p_speed_crouch->Set(m_CrouchSpeed); m_pGame->p_speed_prone->Set(m_ProneSpeed); } */ // create the camera // <> create only for client which do the rendering ICryCharInstance* pCharacter = m_pEntity->GetCharInterface()->GetCharacter(0); if (pCharacter) { pCharacter->EnableLastIdleAnimationRestart(0,true); if (m_bIsAI) pCharacter->EnableLastIdleAnimationRestart(3,true); } ////////////////////////////////////////////////////////////////////////// // Initialize Player Camera Fov. ////////////////////////////////////////////////////////////////////////// IEntityCamera *pEntCamera = m_pGame->GetSystem()->GetIEntitySystem()->CreateEntityCamera(); m_pEntity->SetCamera(pEntCamera); int rw = m_pGame->GetSystem()->GetIRenderer()->GetWidth(); int rh = m_pGame->GetSystem()->GetIRenderer()->GetHeight(); float fFOV = DEG2RAD(90.0f); // Initialize with 90 degree fov. pEntCamera->SetFov(fFOV,rw,rh); pEntCamera->GetCamera().Init(rw,rh,fFOV); pEntCamera->GetCamera().Update(); ////////////////////////////////////////////////////////////////////////// Vec3d default_offset; default_offset(0,m_pGame->cl_ThirdPersonRange->GetFVal(),4); m_pEntity->GetCamera()->SetCameraOffset(default_offset); // [kirill] need this to use only z component of entity angles (same as for drawing) // when calculating BBox GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_ZROTATE ); GoStand(); return true; } /*!Called by the entity when the angles are changed. This notification is used to force the orientation of the player. */ void CPlayer::OnSetAngles( const Vec3d &ang ) { // say to Vladimir if you want to comment this code again // if(m_pGame->m_pClient && !m_pVehicle) //[kirill] need it in cars for autocentering if(m_pGame->m_pClient) { // if(m_pGame->m_pClient->GetPlayerId()==m_pEntity->GetId() || m_pGame->m_pClient->m_bLocalHost) if(m_pGame->m_pClient->GetPlayerId()==m_pEntity->GetId()) m_pGame->m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(ang); } } // set a certain shader for the current selected weapon ////////////////////////////////////////////////////////////////////////// void CPlayer::SetHeatVisionValues(int dwFlags,const char *szName,float fValue,float fFadingValue) { if (dwFlags & BITMASK_WEAPON) { m_fHeatWeaponsDesiredValue=fValue; m_fHeatWeaponsFadingSpeed=fFadingValue; } if (dwFlags & BITMASK_PLAYER) { m_fHeatBodyDesiredValue=fValue; m_fHeatBodyFadingSpeed=fFadingValue; } } /*! Initializes the weapon-information stored in the player-container. */ void CPlayer::InitWeapons() { // ValidateHeap(); IEntitySystem *pEntitySystem = (IEntitySystem *) m_pGame->GetSystem()->GetIEntitySystem(); SetWeapon(-1); m_mapPlayerWeapons.clear(); for (int i=0;iGetWeaponSystemEx()->GetNumWeaponClasses(); for (unsigned int i = 0; i < weaponCount; ++i) { CWeaponClass *pWC = GetGame()->GetWeaponSystemEx()->GetWeaponClass(i); assert(pWC); WeaponInfo wi; wi.fireTime = m_pTimer->GetCurrTime(); wi.fireLastShot = 0; wi.reloading = false; wi.iFireMode = 0; assert(m_mapPlayerWeapons.count(pWC->GetID())==0); // otherwise we produce memory leaks wi.owns = false; m_mapPlayerWeapons[pWC->GetID()] = wi; } } void CPlayer::SelectFirstWeapon() { if (!m_pGame->IsServer()) return; int slot=0; while(slotpa_leg_velmoving->GetFVal(); m_LegAngleVelTimed = m_pGame->pa_leg_velidle->GetFVal(); m_LegAIdleTimeLimit = m_pGame->pa_leg_idletime->GetFVal(); if( !m_bRotateHead ) { if( m_pEntity->GetCurrentAnimation(0, 3)>=0 || m_pEntity->IsBound() || m_stats.onLadder // no rotatehead on ladders // || m_stats.landing || m_stats.flying // no rotatehead while jumping ) m_LegNeedsForceAngle = true; m_LegRotation = false; return; } Vec3d angles = GetActualAngles(); if(m_LegNeedsForceAngle || m_pMountedWeapon) // some animation was played - need to reset all rotations { m_LegAngle = angles.z; m_LegAngleDesired = m_LegAngle; m_LegDeltaAngle = 0.0f; m_LegNeedsForceAngle = false; } m_LegRotation = true; float legDeltaAngle = m_LegAngleVel*m_pTimer->GetFrameTime(); float legDiff = Ffabs(Snap_s180(m_LegAngleDesired - m_LegAngle)); if(legDiff160) { if(Ffabs(Snap_s180(m_LegAngle+legDeltaAngle-angles.z))0?m_LegAngleVel*m_pTimer->GetFrameTime():-m_LegAngleVel*m_pTimer->GetFrameTime(); m_LegAngle = Snap_s180(m_LegAngle); } if( m_stats.moving ) { m_LegDeltaAngle = Snap_s180(m_LegAngle - angles.z); /* if(Ffabs(m_LegDeltaAngle) > m_LegADeltaLimitForce) { if(m_LegDeltaAngle<0) { m_LegAngleDesired = Snap180(angles.z-m_LegADeltaLimit); m_LegAngle = m_LegAngleDesired; } else { m_LegAngleDesired = Snap180(angles.z+m_LegADeltaLimit); m_LegAngle = m_LegAngleDesired; } } */ return; } if( m_bRotateHead && IsAlive()) { float prevDelta = m_LegDeltaAngle; m_LegDeltaAngle = Snap_s180(m_LegAngle - angles.z); if(Ffabs(m_LegDeltaAngle) > m_LegADeltaLimit) { if(m_LegDeltaAngle<0) { m_LegAngleDesired = Snap_s180(angles.z-m_LegADeltaLimit); } else { m_LegAngleDesired = Snap_s180(angles.z+m_LegADeltaLimit); } m_LegAngleVel = m_LegAngleVelMoving; if(Ffabs(m_LegDeltaAngle) > m_LegADeltaLimit*2 && !m_bIsAI) // to prevent player's from twisting legs/upper_body m_LegAngleVel *= 100; } if(Ffabs(m_LegDeltaAngle) > m_LegADeltaLimit-5 ) { m_LegAngleIdleTime += m_pTimer->GetFrameTime(); if( m_LegAngleIdleTime>m_LegAIdleTimeLimit ) { m_LegAngleDesired = angles.z; m_LegAngleVel = m_LegAngleVelTimed; } } else m_LegAngleIdleTime = 0; /* // if less than 5 degrees - don't autorotate. if(Ffabs(prevDelta)< 5 ) prevDelta = 0; if(m_LegDeltaAngle!=0 && Ffabs(m_LegDeltaAngle - prevDelta)<1.0f) { m_LegAngleIdleTime += m_pTimer->GetFrameTime(); if( m_LegAngleIdleTime>m_LegAIdleTimeLimit ) { m_LegAngleDesired = angles.z; m_LegAngleVel = m_LegAngleVelTimed; } } else m_LegAngleIdleTime = 0; */ } else { m_LegAngle = angles.z; m_LegAngleDesired = m_LegAngle; m_LegDeltaAngle = 0.0f; } } /////////////////////////////////////////////// /*! Updates the player-container. Should be called every frame. */ void CPlayer::UpdateDead( SPlayerUpdateContext &ctx ) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); if (m_bIsAI && m_fDeathTimer>=0.0f ) { m_fDeathTimer -= m_pTimer->GetFrameTime(); if(m_fDeathTimer <= 0.0f) { m_fDeathTimer = 0.0f; int nRendererFrameID = m_pGame->GetSystem()->GetIRenderer()->GetFrameID(); if( nRendererFrameID - m_pEntity->GetDrawFrame() > 60*2 ) { m_pEntity->Remove(); return; } // restart the timer m_fDeathTimer=m_pGame->p_deathtime->GetFVal(); } } // don't check/update areas for AI's if( !m_bIsAI ) { // m_pGame->m_XAreaMgr.UpdatePlayer( this ); ////////////////////////////////////////////////////////////////////////////// //UPDATE THE CAMERA SetEyePosDead(); UpdateCamera(); } // UpdateDrawAngles( ); if (m_bFirstPerson) { m_pEntity->SetRegisterInSectors(false); UpdateFirstPersonView(); } else { m_pEntity->SetRegisterInSectors(true); UpdateThirdPersonView(); } } ////////////////////////////////////////////////////////////////////////// void CPlayer::AutoAiming() { const char *pszBoneName="Bip01 Spine1\0"; //const char *pszBoneName=NULL; Vec3 Center; Vec3 bestPoint3D; float fBestDist=99999; IEntity *pBest=NULL; IEntityItPtr It=m_pGame->GetSystem()->GetIEntitySystem()->GetEntityInFrustrumIterator( true ); CCamera Cam=m_pGame->GetSystem()->GetViewCamera(); IEntity *pEnt; //ray_hit RayHit; IEntity *pLocal=m_pGame->GetMyPlayer(); Vec3 m_vWpnPos=pLocal->GetPos(); while (pEnt=It->Next()) { if (pEnt==m_pEntity) continue; if (!pEnt->IsTrackable()) continue; CPlayer *pPlayer; if( pEnt->GetContainer() && pEnt->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**)&pPlayer) && !pPlayer->IsAlive() ) continue; IPhysicalEntity *pPE=pEnt->GetPhysics(); if (!pPE) continue; if (pszBoneName) // if we want a bone instead of bbox-center lets do so... { IEntityCharacter *pIChar=pEnt->GetCharInterface(); if (pIChar) { ICryCharInstance *cmodel=pIChar->GetCharacter(0); if (cmodel) { ICryBone *pBone = cmodel->GetBoneByName(pszBoneName); if (pBone) { Center=pBone->GetBonePosition(); Matrix44 m; m.SetIdentity(); m=GetTranslationMat(pEnt->GetPos())*m; m=Matrix44::CreateRotationZYX(-pEnt->GetAngles()*gf_DEGTORAD)*m; //NOTE: angles in radians and negated Center=m.TransformPointOLD(Center); } } } else { Center=pEnt->GetPos(); } } Vec3 diff(Center-m_vWpnPos); float length2=GetLengthSquared(diff); if(length2>15*15 || length2<1.5*1.5) continue; // pick the closest float fDist=(float)sqrt(length2); if (fDistGetSystem()->GetITimer()->GetCurrTime(); //ICVar *pCvar=m_pGame->GetSystem()->GetIConsole()->GetCVar("fixed_time_step"); if (!pBest) { m_pLastAiming=NULL; return; } if (m_pLastAiming) { CPlayer *pPlayer; if(m_pLastAiming->GetContainer() && m_pLastAiming->GetContainer()->QueryContainerInterface(CIT_IPLAYER,(void**)&pPlayer) && !pPlayer->IsAlive() ) m_pLastAiming=NULL; } if (m_pLastAiming && m_pLastAiming!=pBest) { if (fCurrTime-m_fLastTime<2.0f) return; m_fLastTime=fCurrTime; } else m_fLastTime=m_pGame->GetSystem()->GetITimer()->GetCurrTime(); m_pLastAiming=pBest; bestPoint3D.z-=1.5f; Vec3 diff(bestPoint3D-m_vWpnPos); Vec3 vCurrAng=pLocal->GetAngles(); Vec3 vNewAng=ConvertVectorToCameraAngles(diff); //vCurrAng.z+=1.0f; pLocal->SetAngles(vNewAng); } /////////////////////////////////////////////// /*! Updates the player-container. Should be called every frame. */ void CPlayer::Update() { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); bool bMyPlayer = IsMyPlayer(); bool bPlayerVisible = bMyPlayer || IsVisible(); SPlayerUpdateContext ctx; ctx.bPlayerVisible = bPlayerVisible; if (bMyPlayer) { // force physics proxy being updated in first person view, if somebody has a better idea // I'm open to suggestions m_pEntity->SetNeedUpdate(true); } if( !IsAlive() ) { UpdateDead(ctx); // [marco] even after the player dies, he can slide and fall into water - // this call will create the splash sounds UpdateSwimState(false); return; } // fixme - remove this after artists done with animation params tweaking if(m_pGame->pa_forcerelax->GetIVal()) { GoRelaxed(); m_pEntity->StartAnimation(0, NULL, 1, .15f); // to stop all the fire animations } // fixme end if(m_pGame->p_limp->GetIVal() == 0) // no limping m_stats.bIsLimping = false; else if(m_pGame->p_limp->GetIVal() == 1) // limp if wounded { if(m_stats.health<50&&(!m_stats.bIsLimping)) m_stats.bIsLimping = true; } else if(m_pGame->p_limp->GetIVal() == 2) // limp always m_stats.bIsLimping = true; if(bMyPlayer && m_pGame->m_bHideLocalPlayer) { FRAME_PROFILER( "CPlayerUpdate::UpdateCamera",GetISystem(),PROFILE_GAME ); m_AreaUser.SetEntity( GetEntity()); m_pGame->m_XAreaMgr.UpdatePlayer( m_AreaUser ); UpdateCamera(); ///m_pEntity->DrawCharacter(0, ETY_DRAW_NONE); //m_bFirstPerson = false; //UpdateThirdPersonView(); return; } //if (bMyPlayer) // AutoAiming(); m_bRotateHead = m_pEntity->GetCurrentAnimation(0, 3)<0 && m_pGame->p_RotateHead->GetIVal()!=0 && IsAlive() && !m_pEntity->IsBound() && !m_stats.onLadder // no rotatehead on ladders && !m_bIsFish // it's not a fish // && m_pEntity->WasVisible() //Removed to fix BUG6238 (you always hear Valeries footsteps when she is behind you) ; //if (!m_bIsAI) [Anton] moved to UpdatePhysics // UpdateStamina(); // ladder stuff // if(m_stats.onLadder) // { // SetGravityOverride(0.0f); // } // update and store player's current velocity IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); pe_status_living &status = ctx.status; if (!physEnt || !physEnt->GetStatus(&status)) { GameWarning( "Bad Phyics Entity for Player %s",m_pEntity->GetName() ); return; } Vec3d vel = (Vec3d)status.vel; if (status.pGroundCollider) vel -= status.velGround; m_stats.fVel = vel.Length(); //fixme - remove this after done with lights tweaking if(m_pGame->pl_force->GetIVal() && m_bIsAI && IsAlive()) SwitchFlashLight(true); //fixme - this has to be done once - when model is loaded UpdateBonesPtrs(); // Only updates when player have flash light on. ProceedFLight(); UpdateLightBlinding(); //UpdateCollisionDamage(); [Anton] moved to UpdatePhysics CounterUpdateAll( m_pTimer->GetFrameTime() ); //--------------------------------------------------------------- //Removed to fix BUG6238 (you always hear Valeries footsteps when she is behind you) //As long as the character is NOT visible, the angles were not updated and that is the reason // why the "aidle" animation doesn't start! // if (bPlayerVisible) UpdateRotateHead(); if (bPlayerVisible && !m_pVehicle) { FRAME_PROFILER( "CPlayerUpdate::RotateHead",GetISystem(),PROFILE_GAME ); if( m_CurStance != eProne ) { //@FIXME remove this pe_params_pos pp; // if (m_pVehicle) // { // Vec3d angles = GetActualAngles(); // pp.q = GetRotationAA(angles.z*(gf_PI/180.0f),vectorf(0,0,1)); // } if (m_bRotateHead) pp.q = GetRotationAA(m_LegAngle*(gf_PI/180.0f),vectorf(0,0,1)); else pp.q = GetRotationAA(m_pEntity->GetAngles().z*(gf_PI/180.0f),vectorf(0,0,1)); pp.bRecalcBounds = 0; m_pEntity->GetPhysics()->SetParams(&pp); } } //work in progress //do this to prevent sliding when prone if( m_CurStance == eProne ) { // if( !m_stats.flying && !CanProne() ) if( !CanProne() ) GoStand(); else { m_EnvTangent = CalcTangentOnEnviroment( m_pEntity->GetAngles() ); m_pEntity->SetPhysAngles( m_EnvTangent ); // set angels for phisics (body is flat on surfece tangent space) } } if (!m_bIsAI) UpdateLean(); if (bPlayerVisible) UpdateBonesRotation(); SetEyePos(); if (physEnt && physEnt->GetType()==PE_LIVING) { FRAME_PROFILER( "CPlayerUpdate::PostStep",GetISystem(),PROFILE_GAME ); pe_params_flags pf; pf.flagsOR = pef_custom_poststep; if (m_bIsAI) pf.flagsOR |= lef_loosen_stuck_checks; physEnt->SetParams(&pf); } // // in water checks, swimming states updates bool bPrevSweem = m_bSwimming; UpdateSwimState(true); if( m_bSwimming ) m_stats.swimTime +=m_pTimer->GetFrameTime(); else m_stats.swimTime = 0.0f; // update stances if( !m_pVehicle ) { if( m_bSwimming && m_CurStance!=eSwim ) GoSwim(); else if(bPrevSweem != m_bSwimming && !m_bSwimming) { if(!RestorePrevStence()) if(!GoCrouch()) GoProne(); } } // if(!m_bIsAI) // don't check/update areas for AI's if(bMyPlayer) // areas are only used for the local player (they will not trigger things on the server) { FRAME_PROFILER( "CPlayerUpdate::XAreaMgrUpdatePlayer",GetISystem(),PROFILE_GAME ); m_AreaUser.SetEntity( GetEntity()); m_pGame->m_XAreaMgr.UpdatePlayer( m_AreaUser ); // used for camera transitions e.g. when entering a vehicle UpdateAutoCenter(); // update the camera UpdateCamera(); } if (bPlayerVisible) UpdateDrawAngles(); if (!m_bIsAI) { // Only for local player. if (m_bFirstPerson) { m_pEntity->SetRegisterInSectors(false); UpdateFirstPersonView(); } else { m_pEntity->SetRegisterInSectors(true); UpdateThirdPersonView(); } } ////////////////////////////////////////////////////////////////////////////// //UPDATE THE WEAPON if(m_stats.health>0) { if(m_stats.weapon_busy>0) m_stats.weapon_busy-=m_pTimer->GetFrameTime(); UpdateMelee(); UpdateWeapon(); } if(m_CurStance == eProne || m_bSwimming) // disable IK in prone mode and when swimming { FRAME_PROFILER( "CPlayerUpdate::DisableIK",GetISystem(),PROFILE_GAME ); pe_params_sensors ps; ps.nSensors = 0; physEnt->SetParams(&ps); } ////////////////////////////////////////////////////////////////////////////// //UPDATE THE ANIMATION // layer 0 is used for movement/stance animations (lower body) // layer 1 is used for aiming/shooting animations // layer 2 is used for jump/land animations UpdateCharacterAnimations( ctx ); UpdateFireAnimations(); // UpdateJumpAnimations(); } void CPlayer::UpdatePhysics(float fDeltaTime) { m_fLastDeltaTime = fDeltaTime; IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); float MIN_WALK_SPEED = m_WalkSpeed*.15f; pe_status_living sl; physEnt->GetStatus(&sl); if (!m_bIsAI) UpdateStamina(fDeltaTime); UpdateCollisionDamage(); //keep count of the last groudheight if (!sl.bFlying) { m_fLastGroundHeight = m_pEntity->GetPos(true).z; m_bHasJumped = false; } // when on ladder - no gravity // when using mounted weapon - no gravity if(m_stats.onLadder || m_pMountedWeapon ) { SetGravityOverride(0.0f); } // [Anton] - moved from ProcessMovements, since it should be called for remote clients in multiplayer // float kwater; // int bSwimming = IsSwimming( kwater ); pe_player_dynamics movedyn; movedyn.kInertia = m_Dynamics.inertia*(1-m_stats.fKWater) + m_Dynamics.swimming_inertia*m_stats.fKWater; movedyn.kAirControl = m_Dynamics.air_control*(1-m_stats.fKWater); movedyn.bSwimming = m_bSwimming; if (m_fGravityOverride!=1E10) { movedyn.gravity = m_fGravityOverride; if (!m_fGravityOverride) { movedyn.kAirControl=1.0f; movedyn.bSwimming=true; } m_fGravityOverride = 1E10; } else { float fgravity = m_Dynamics.gravity; //if we are jumping and there is a special gravity for jump use it. if (sl.bFlying && m_bHasJumped && m_Dynamics.jump_gravity!=1E10) { //use jump gravity only if we are over the jump starting pos. if (m_pEntity->GetPos(true).z > m_fLastGroundHeight) fgravity = m_Dynamics.jump_gravity; } //we land, in any case use standard gravity. /*if (!sl.bFlying) { fgravity = m_Dynamics.gravity; m_bHasJumped = false; }*/ //GetISystem()->GetILog()->Log("%f",fgravity); movedyn.gravity = fgravity*(1-m_stats.fKWater)*m_pGame->p_gravity_modifier->GetFVal()+m_Dynamics.swimming_gravity*m_stats.fKWater; } // 0 gravity behaves like swimming (up and down movement is possible) if(movedyn.gravity==0.0f) { movedyn.bSwimming=true; // allow not only movements in ground plane movedyn.kAirControl=m_Dynamics.air_control; // water should not affect control } Vec3 vAngles = ConvertToRad(m_pEntity->GetAngles()), vDir(-cry_sinf(vAngles[YAW])*cry_sinf(vAngles[PITCH]),cry_cosf(vAngles[YAW])*cry_sinf(vAngles[PITCH]),-cry_cosf(vAngles[PITCH])); // make underwater movement smoother if (m_stats.fKWater>=1.0f && sl.velRequested.len2()>0 && sqr(sl.velRequested*vDir)>sl.velRequested.len2()*0.5f && m_stats.swimTime>1.0f) movedyn.kAirControl = 1.0f; if(m_bInertiaOverride) { if(movedyn.bSwimming) movedyn.kAirControl = 1.0f; else movedyn.kInertia = 0; // the bigger value - the faster we can change speed (0 is special case, not warking in water) } physEnt->SetParams(&movedyn); // if(movedyn.bSwimming && m_bSwimming) // GoSwim(); // else if(m_bSwimming && !movedyn.bSwimming) // RestorePrevStence(); // m_bSwimming = movedyn.bSwimming && (m_stats.fInWater>0.0f); m_stats.moving = false; m_stats.running = false; // Calculate our current speed Vec3 vel = sl.vel; if (sl.pGroundCollider) vel -= sl.velGround; float speed = vel.Length(); m_walkParams.speed = speed; m_walkParams.vertSpeed = vel.z; // Calculate projected current speed vel.z = 0.0f; float speed2d = vel.Length(); if ((speed2d > .0f && m_NotRunTime>1.5f) || (speed2d > MIN_WALK_SPEED) ) { // Have the player continue moving m_stats.moving = true; m_NotRunTime = 0; } //else if (speed>0) m_NotRunTime += fDeltaTime; else m_NotRunTime = 0; if (m_stats.moving && (speed2d-m_WalkSpeed>0.1f || ((m_stats.flying||m_stats.landing)&&m_RunningTime>.5f)) ) // if jumping in air - slows down, not to change snimation { // Have the player start/continue running m_stats.running = true; } if( m_stats.running ) m_RunningTime += fDeltaTime; else m_RunningTime = 0; if( m_stats.moving ) m_WalkingTime += fDeltaTime; else m_WalkingTime = 0; m_pGame->ConstrainToSandbox(GetEntity()); } /////////////////////////////////////////////// /*! Sets and loads the player-model. @param model filename of the player-model */ void CPlayer::SetPlayerModel( const string &model ) { //@FIXME: enable this later. //if (m_pEntity->GetCharInterface()->LoadCharacter(PLAYER_MODEL_IDX,model.c_str())) // m_strModel = model; m_strModel = model; if (m_nSelectedWeaponID == -1) SelectWeapon(m_stats.weapon); UpdateBonesPtrs(); } /////////////////////////////////////////////// /*! Retrieves a weapon-info-structure for a certain weapon @param nWeaponIndex weapon-id to retrieve from (negative value will return info of current weapon) @return WeaponInfo if the function succeeds, NULL otherwise */ WeaponInfo & CPlayer::GetWeaponInfo(int nWeaponIndex /* = -1 */) { if(nWeaponIndex == -1) nWeaponIndex = m_nSelectedWeaponID; PlayerWeaponsItor wi = m_mapPlayerWeapons.find(nWeaponIndex); if (wi != m_mapPlayerWeapons.end()) { return wi->second; } // this should NEVER EVER be reached assert(false); return m_mapPlayerWeapons.begin()->second; } void CPlayer::GetCurrentWeaponParams(WeaponParams& wp) { WeaponInfo wi = GetWeaponInfo(); GetSelectedWeapon()->GetModeParams(wi.iFireMode, wp); } /*! Executes the processing commands @param ProcessingCmd structure of commands to process */ void CPlayer::ProcessCmd(unsigned int nPing,CXEntityProcessingCmd &ProcessingCmd) { ProcessMovements(ProcessingCmd); // client in MP processes fire event differently // here the server relays the fire event to the client, but due to // heavier client-side simulation of firing (see XClient.cpp) we // keep the state of our client-side calculation bool bClientFire = false; bool bPrevFiring = ProcessingCmd.CheckAction(ACTION_FIRE0); bool bPrevFireGrenade = ProcessingCmd.CheckAction(ACTION_FIRE_GRENADE); if (m_pGame->IsMultiplayer() && m_pGame->IsClient() && !m_pGame->IsServer() && IsMyPlayer()) { bClientFire = true; ProcessingCmd.RemoveAction(ACTION_FIRE0); if (m_stats.firing) ProcessingCmd.AddAction(ACTION_FIRE0); ProcessingCmd.RemoveAction(ACTION_FIRE_GRENADE); if (m_stats.firing_grenade) ProcessingCmd.AddAction(ACTION_FIRE_GRENADE); } ProcessWeapons(ProcessingCmd); if (bClientFire) { ProcessingCmd.RemoveAction(ACTION_FIRE0); if (bPrevFiring) ProcessingCmd.AddAction(ACTION_FIRE0); ProcessingCmd.RemoveAction(ACTION_FIRE_GRENADE); if (bPrevFireGrenade) ProcessingCmd.AddAction(ACTION_FIRE_GRENADE); } } // //claps angl to be within min-max range. Check fro special case when min>max -- for example min=350 max=40 inline float ClampAngle180( float min, float max, float angl ) { if( angl>0 && anglmax ) return max; return angl; } /////////////////////////////////////////////// /*! Updates the orientation of the player @param ProcessingCmd structure of commands to process */ void CPlayer::ProcessAngles(CXEntityProcessingCmd &ProcessingCmd) { if (IsMyPlayer() && !IsAlive()) return; FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); Vec3d Angles=ProcessingCmd.GetDeltaAngles(); if (IsMyPlayer() && m_pVehicle && !m_bFirstPerson && !m_pMountedWeapon) { // [marco] if this is the localplayer, and we are driving the vehicle, // and we are in third person mode, AND we are not at mounted weapon // then do not allow the player // to mess around with the camera - he can switch // to first person mode to shoot from inside the vehicle // Angles.Set(0,0,180); } if(m_AngleLimitVFlag) //check vertical limits { Angles.x = ClampAngle( Snap_s360(m_AngleLimitBase.x + m_MinVAngle), Snap_s360(m_AngleLimitBase.x + m_MaxVAngle), Snap_s360(Angles.x)); Angles.x = Snap_s180(Angles.x); } // //probably not the best way. If we will have angle restriction (like mounted weapon) in prone - have to chage it if( m_CurStance == eProne ) m_AngleLimitBase = m_EnvTangent; if(m_AngleLimitHFlag) { //check horizontal limits bool bClamped = false; Angles.z = Snap_s180(Angles.z); Angles.z = ClampAngle( Snap_s360(m_AngleLimitBase.z + m_MinHAngle), Snap_s360(m_AngleLimitBase.z + m_MaxHAngle), Snap_s360(Angles.z),bClamped); if (m_bIsAI && bClamped) m_pEntity->GetAI()->SetSignal(1,"RETURN_TO_NORMAL"); Angles.z = Snap_s180(Angles.z); } // if at the mounted weapon - check for collisions coz player position is forced bu the weapon if( m_pMountedWeapon ) { Vec3d pos2check = m_pEntity->GetPos(); if(!CanStand( pos2check ) ) { // this position is bad (collides with something) - restore prev pos/angles // float diff = Snap_s360(m_vSafeAngAtMountedWeapon.z) - Snap_s360(Angles.z); float diff = Snap_s180(Snap_s180(m_vSafeAngAtMountedWeapon.z) - Snap_s180(Angles.z)); Angles.z += diff*.31f; //GetISystem()->GetILog()->Log(">>> %.2f ", m_vSafeAngAtMountedWeapon.z); } else if(CanStand( pos2check ) ) { // this position is good (no intersetion/collisions) - remember it m_vSafeAngAtMountedWeapon = m_pEntity->GetAngles(); //GetISystem()->GetILog()->Log("<<GetILog()->Log("RECOIL: %f %f %f", m_fRecoilXUp, m_fRecoilZUp, m_fRecoilXDelta); if((m_fRecoilXUp==0 && (m_fRecoilZUp==0)) && (m_fRecoilXDelta!=0) )//blend back recoil { //TRACE("RETURNING m_fRecoilXDelta=%f",m_fRecoilXDelta); float multiplier=m_stats.firing?m_pGame->w_recoil_speed_down*0.2f:m_pGame->w_recoil_speed_down; float m=min(1,m_pTimer->GetFrameTime()*multiplier); float xdiff=m_fRecoilXDelta>m_fRecoilX*m?m_fRecoilX*m:m_fRecoilXDelta; //float zdiff=m_fRecoilZDelta>0?min(m_fRecoilZDelta,m_fRecoilZ*m):-m_fRecoilZDelta; xdiff *= stanceRecoilModifier; if(m_fRecoilXDelta-xdiff<=0){ xdiff=m_fRecoilXDelta; } vA.x+=xdiff; m_fRecoilXDelta-=xdiff; //TRACE("m_fRecoilXDelta=%f xdiff=%f m_fRecoilZDelta=%f \n",m_fRecoilXDelta,xdiff,m_fRecoilZDelta); ProcessingCmd.SetDeltaAngles(vA); //TRACE("RETURNING ENDm_fRecoilXDelta=%f",m_fRecoilXDelta); } //APPLY RECOIL if((m_fRecoilXUp!=0 || m_fRecoilZUp!=0) )//apply recoil { //GetISystem()->GetILog()->Log("APPLYING m_fRecoilXDelta=%f",m_fRecoilXDelta); float deltatime=m_pTimer->GetFrameTime()*m_pGame->w_recoil_speed_up; //GetISystem()->GetILog()->Log("deltatime=%f",deltatime); float dx=m_fRecoilXUp>0?min(m_fRecoilXUp,m_fRecoilX*deltatime):max(m_fRecoilXUp,m_fRecoilX*deltatime); float dz=m_fRecoilZUp>0?(float)min(m_fRecoilZUp,m_fRecoilZ*deltatime):(float)max(m_fRecoilZUp,-fabs(m_fRecoilZ*deltatime)); if(m_fRecoilXDelta+dx>=m_pGame->w_recoil_max_degree){ //GetISystem()->GetILog()->Log("w_recoil_max_degree=%f",m_pGame->w_recoil_max_degree); dx=m_fRecoilXUp; } //GetISystem()->GetILog()->Log("m_fRecoilXDelta=%f dx=%f dz=%f m_fRecoilXUp=%f m_fRecoilZUp=%f\n",m_fRecoilXDelta,dx,dz,m_fRecoilXUp,m_fRecoilZUp); m_fRecoilXUp-=dx; if(m_fRecoilXUp<0)m_fRecoilXUp=0; m_fRecoilZUp-=dz; dx *= stanceRecoilModifier; dz *= stanceRecoilModifier; vA.x-=dx; if(!m_pGame->w_recoil_vertical_only) { vA.z-=dz; } m_fRecoilXDelta+=dx; ProcessingCmd.SetDeltaAngles(vA); //TRACE("APPLYING END m_fRecoilXDelta=%f",m_fRecoilXDelta); } ////////////////////////////////////////////////////////////////////////// float fWaterPitch=m_pGame->p_waterbob_pitch->GetFVal(); float fWaterPitchSpeed=m_pGame->p_waterbob_pitchspeed->GetFVal(); float fWaterRoll=m_pGame->p_waterbob_roll->GetFVal(); float fWaterRollSpeed=m_pGame->p_waterbob_rollspeed->GetFVal(); float fWaterDepthWobble=m_pGame->p_waterbob_mindepth->GetFVal(); if (m_bEyesInWater) // wobble camera if in water { float fWobbleScale=(m_stats.fInWater-fWaterDepthWobble); if (fWobbleScale<0.0f) fWobbleScale=0.0f; if (fWobbleScale>1.0f) fWobbleScale=1.0f; float fFrameTime=m_pTimer->GetFrameTime(); m_walkParams.fWaterPitchPhase+=fWaterPitchSpeed*fFrameTime; if (m_walkParams.fWaterPitchPhase>=1.0f) m_walkParams.fWaterPitchPhase-=1.0f; m_walkParams.fWaterRollPhase+=fWaterRollSpeed*fFrameTime; if (m_walkParams.fWaterRollPhase>=1.0f) m_walkParams.fWaterRollPhase-=1.0f; //Vec3d cangles = Angles; vA.x += fWobbleScale*fWaterPitch*cry_sinf(m_walkParams.fWaterPitchPhase*gf_PI*2.0f); vA.y = (fWaterRoll *cry_sinf(m_walkParams.fWaterRollPhase *gf_PI*2.0f))*fWobbleScale; }else { //Vec3d cangles = vA; vA.y=0.0f; //ProcessingCmd.SetDeltaAngles(vA); m_walkParams.fWaterPitchPhase=0.0f; m_walkParams.fWaterRollPhase=0.0f; } ProcessingCmd.SetDeltaAngles(vA); ////////////////////////////////////////////////////////////////////////// } /// if (!m_pRedirected) { ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //if (!m_pVehicle) m_pEntity->SetAngles( Angles,false, false ); m_vShake=Vec3d(0,0,0); // // so here goes wapon sway if(m_Dynamics.gravity!=0.0f) // only if there is gravity { // add some swaying to the angles m_vShake.x+=(cry_cosf(m_walkParams.swayOfs*15.9f+0.3f)*m_walkParams.swayAmp*0.1f+cry_sinf(m_walkParams.swayOfs*5.9f+0.0f)*m_walkParams.swayAmp*0.45f+cry_cosf(m_walkParams.swayOfs*0.95f+0.5f)*m_walkParams.swayAmp)*0.33333f; m_vShake.z+=(cry_cosf(m_walkParams.swayOfs*14.9f+0.7f)*m_walkParams.swayAmp*0.12f+cry_sinf(m_walkParams.swayOfs*5.05f+0.6f)*m_walkParams.swayAmp*0.5f+cry_cosf(m_walkParams.swayOfs*1.0f+0.2f)*m_walkParams.swayAmp)*0.33333f; m_walkParams.swayOfs+=m_walkParams.swayFreq*m_pTimer->GetFrameTime(); } // // after explosion camera shacking - not when in vehicle // when proning - aligne on terrain if (IsAlive() && !m_pEntity->IsBound() ) { // add shaking if (IsMyPlayer()) { if((fabsf(m_walkParams.shakeDegree)>0.1f) && (m_walkParams.shakeTime>0.0f)) { m_walkParams.shakeOffset += m_walkParams.shakeFreq * m_pTimer->GetFrameTime(); m_walkParams.shakeElapsedTime += m_pTimer->GetFrameTime(); if (m_walkParams.shakeElapsedTime>m_walkParams.shakeTime) { m_walkParams.shakeDegree=0.0f; }else { float fDeg = m_walkParams.shakeDegree * ( 1 - ( m_walkParams.shakeElapsedTime / m_walkParams.shakeTime ) ); float fSinOfs = cry_sinf(m_walkParams.shakeOffset * 6.283185307179586476925286766559f); // m_vShake.x += m_walkParams.shakeAxis.x * fSinOfs * fDeg; // m_vShake.y += m_walkParams.shakeAxis.y * fSinOfs * fDeg; // m_vShake.z += m_walkParams.shakeAxis.z * fSinOfs * fDeg; // make it shake on camera roll axis // create the matrix here Matrix44 matWorld; matWorld.SetIdentity(); matWorld=Matrix44::CreateRotationZYX(-gf_DEGTORAD*m_vEyeAngles)*matWorld; //NOTE: angles in radians and negated CryQuat cxquat = Quat( GetTransposed44(matWorld) ); CryQuat rxquat; Vec3d shake( 0, fSinOfs * fDeg, 0 ); rxquat.SetRotationXYZ(DEG2RAD(shake)); CryQuat result = cxquat*rxquat; Vec3d finalangles = RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(result))); m_vShake += m_vEyeAngles - finalangles; } } } } } else { ProcessingCmd.SetDeltaAngles(Angles); // m_pEntity->SetAngles( Angles );//,false, false ); if (m_pRedirected) // [marco] Kirill does this check make sense - // this should never happens but it crashes with the helicopter m_pRedirected->SetAngles(Angles); // ProcessingCmd.Reset(); } } //#define UNDERWATER_SPEED 0.19f/8.0f /////////////////////////////////////////////// /*! Updates the position and stats of the player @param ProcessingCmd structure of commands to process */ void CPlayer::ProcessMovements(CXEntityProcessingCmd &cmd, bool bScheduled) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); IPhysicalEntity *pPhysEnt = (IPhysicalEntity *)m_pEntity->GetPhysics(); if(!pPhysEnt) return; //[kirill] need this to process autocentering // no autocentering when moving mouse or fiering m_bMouseMoved = cmd.CheckAction(ACTION_TURNLR) || cmd.CheckAction(ACTION_TURNUD) || cmd.CheckAction(ACTION_FIRE0); PhysicsVars *physvars = m_pGame->GetSystem()->GetIPhysicalWorld()->GetPhysVars(); // [Anton] setting dynprops for the physics is moved to ::Update pe_player_dynamics pd; pPhysEnt->GetParams(&pd); bool bSuperDesignerMode = pd.gravity==0 && pd.kInertia==0; bool bNoGravity = !bSuperDesignerMode && (physvars->bFlyMode || IsSwimming() || m_stats.onLadder); //return; /* this blick would be needed only when designers tweak player's speeds //FixME fix me FIXME!!! //if(IsMyPlayer()) { // m_RunSprintDecoyScale = m_pGame->p_sprint_decoy->GetFVal(); // m_RunSprintRestoreScaleRun = m_pGame->p_sprint_restore_run->GetFVal(); // m_RunSprintRestoreScaleIdle = m_pGame->p_sprint_restore_idle->GetFVal(); // m_RunSprintScale = m_pGame->p_sprint_scale->GetFVal(); m_RunSpeed = m_pGame->p_speed_run->GetFVal(); m_WalkSpeed = m_pGame->p_speed_walk->GetFVal(); m_CrouchSpeed = m_pGame->p_speed_crouch->GetFVal(); m_ProneSpeed = m_pGame->p_speed_prone->GetFVal(); m_JumpForce = m_pGame->p_jump_force->GetFVal(); m_LeanDegree = m_pGame->p_lean->GetFVal(); m_JTWalk = m_pGame->p_jump_walk_time->GetFVal(); m_JTRun = m_pGame->p_jump_run_time->GetFVal(); } */ m_StealthSpeed = (m_CrouchSpeed + m_WalkSpeed)*.5f; IPhysicalWorld *pW=pPhysEnt->GetWorld(); float fTimeDelta=0; CXClient *pClient=m_pGame->GetClient(); if(!pClient || pClient->GetPlayerId()!=m_pEntity->GetId()) fTimeDelta=cmd.GetServerPhysDelta(); if (!m_pVehicle && !m_pMountedWeapon && fTimeDelta>0) { pPhysEnt->StepBack(fTimeDelta); m_NotRunTime = m_NotRunTimeClient; m_RunningTime = m_RunningTimeClient; m_WalkingTime = m_WalkingTimeClient; m_stats.stamina = m_staminaClient; // m_stats.breath = m_breathClient; } if (!IsAlive()) return; if (cmd.CheckAction(ACTION_FLASHLIGHT)) { if (m_pVehicle && m_stats.inVehicleState == PVS_DRIVER) m_pVehicle->SwitchLights(5); // switch car's lights else if (m_stats.has_flashlight) SwitchFlashLight(!m_bLightOn); // cmd.RemoveAction(ACTION_FLASHLIGHT); } // when in vehicle - use crouch key to toggle camera autocentering if ((cmd.CheckAction(ACTION_MOVEMODE) || cmd.CheckAction(ACTION_MOVEMODE_TOGGLE)) && m_pVehicle) { StartAutoCenter( false ); cmd.RemoveAction(ACTION_MOVEMODE); cmd.CheckAction(ACTION_MOVEMODE_TOGGLE); } // use button has been removed m_stats.melee_attack=false; if(cmd.CheckAction(ACTION_USE)) { bool bUsed=false; m_pEntity->SendScriptEvent(ScriptEvent_Use,0,&bUsed); if (bUsed==false) { m_stats.use_pressed=true; } } else { m_stats.use_pressed=false; } if (cmd.CheckAction(ACTION_CHANGE_VIEW)) { //if (m_pGame->IsDevModeEnable()) { m_pGame->SetViewMode(m_bFirstPerson); } cmd.RemoveAction(ACTION_CHANGE_VIEW); } if ( m_pVehicle ) { //if (!m_pGame->IsMultiplayer() || !m_pGame->UseFixedStep() || bScheduled) ProcessVehicleMovements(cmd); // so that we dont need any reference to vechile in this file // can't sprint in vehicle m_Sprinting = false; return; } // if using mounted weapon - can't move if(m_pMountedWeapon) { // can't sprint when useing mounted weapon m_Sprinting = false; return; } if (m_nSelectedWeaponID != -1) { WeaponParams wp; GetCurrentWeaponParams(wp); m_stats.holding_breath=false; if (wp.bAllowHoldBreath && cmd.CheckAction(ACTION_HOLDBREATH) && m_stats.stamina>m_StaminaTable.BreathDecoyAim*.5f) { m_stats.holding_breath = true; } } //lets remove the action flag every frame if in crouching mode. if(m_pGame->IsServer()) { //crouch toggle if (cmd.CheckAction(ACTION_MOVEMODE_TOGGLE)) { //apply the toggle crouch only if we are outside a vehicle if (!m_pVehicle) m_bStayCrouch = (m_bStayCrouch)?false:true; cmd.RemoveAction(ACTION_MOVEMODE_TOGGLE); } bool bGoCrouch = cmd.CheckAction(ACTION_MOVEMODE) || m_bStayCrouch; bool bGoProne = cmd.CheckAction(ACTION_MOVEMODE2); if (m_stats.crouch && !m_bSwimming && !m_stats.onLadder) { //if it was crouching lets remove it otherwise the "onhold" will not work if (!bGoCrouch) RestorePrevStence(); // GoStand(); } if (bGoCrouch || bGoProne) { if (!m_stats.onLadder) { if (m_stats.crouch) { if (bGoProne) { if (m_PrevStance == eProne) GoStand(false); else GoProne( ); } } else if (m_CurStance == eProne) { if (bGoProne) // RestorePrevStence(); GoStand(false); else GoCrouch( ); } else if (m_CurStance == eSwim) { if (bGoProne) // RestorePrevStence(); GoStand(false); } else { if (bGoProne) GoProne( ); else GoCrouch( ); } } cmd.RemoveAction(ACTION_MOVEMODE); cmd.RemoveAction(ACTION_MOVEMODE2); } } int bJump = 0; if ( ( cmd.CheckAction(ACTION_WALK)^m_bAlwaysRun)&& ( cmd.CheckAction(ACTION_MOVE_RIGHT)||cmd.CheckAction(ACTION_MOVE_LEFT) || cmd.CheckAction(ACTION_MOVE_FORWARD)||cmd.CheckAction(ACTION_MOVE_BACKWARD) ) && !m_stats.bForceWalk ) { if (!m_Running) { // cannot run if in crouch mode if (!m_stats.crouch&&!m_stats.prone&&!m_bSwimming) { GoStand(); m_Running = true; } } } else { if( m_Running ) { RestorePrevStence(); } m_Running = false; } if (m_Running) { if ( cmd.CheckAction(ACTION_RUNSPRINT) && m_stats.stamina>m_StaminaTable.DecoyRun*.5f || cmd.CheckAction(ACTION_RUNSPRINT) && m_Sprinting && m_stats.stamina>1) m_Sprinting = true; else m_Sprinting = false; m_stats.running = true; } else { m_stats.running = false; m_Sprinting = false; } m_walkParams.fLeanTarget=0; // calculate leaning (if not proning and not at mounted weapon) if (cmd.CheckAction(ACTION_LEANLEFT) && (!m_stats.lock_weapon) && (m_CurStance != eProne)) { if(IsMyPlayer()) { if (m_walkParams.leanEnd!=1.0f) { m_walkParams.leanStart=m_walkParams.leanAmount; m_walkParams.leanEnd=1.0f; m_walkParams.leanFactor=0.0f; m_walkParams.leanDegree=m_LeanDegree; } m_walkParams.fLeanTarget=.5; m_walkParams.leanSpeed=1.5f; } }else if (cmd.CheckAction(ACTION_LEANRIGHT) && (!m_stats.lock_weapon) && (m_CurStance != eProne)) { if(IsMyPlayer()) { if (m_walkParams.leanEnd!=-1.0f) { m_walkParams.leanStart=m_walkParams.leanAmount; m_walkParams.leanEnd=-1.0f; m_walkParams.leanFactor=0.0f; m_walkParams.leanDegree=m_LeanDegree; } m_walkParams.fLeanTarget=-1; m_walkParams.leanSpeed=1.5f; } }else if (cmd.CheckAction(ACTION_MOVE_LEFT) ) { if( !m_stats.onLadder ) { if (m_walkParams.leanEnd!=0.1f) { m_walkParams.leanStart=m_walkParams.leanAmount; m_walkParams.leanEnd=0.1f; m_walkParams.leanFactor=0.0f; m_walkParams.leanDegree=m_LeanDegree; } if ((m_walkParams.leanAmount>=0.0f) && (m_walkParams.leanAmount<=0.1f)) m_walkParams.leanSpeed=0.1f; m_walkParams.fLeanTarget=0.05f; if(m_walkParams.fCurrLean==0) m_walkParams.leanSpeed=0.2f; } }else if (cmd.CheckAction(ACTION_MOVE_RIGHT) ) { if( !m_stats.onLadder ) { if (m_walkParams.leanEnd!=-0.1f) { m_walkParams.leanStart=m_walkParams.leanAmount; m_walkParams.leanEnd=-0.1f; m_walkParams.leanFactor=0.0f; m_walkParams.leanDegree=m_LeanDegree; } if ((m_walkParams.leanAmount<=0.0f) && (m_walkParams.leanAmount>=-0.1f)) m_walkParams.leanSpeed=0.1f; m_walkParams.fLeanTarget=-0.05f; if(m_walkParams.fCurrLean==0) m_walkParams.leanSpeed=0.2f; } }else if (m_walkParams.leanEnd!=0.0f) { m_walkParams.leanStart=m_walkParams.leanAmount; m_walkParams.leanEnd=0.0f; m_walkParams.leanFactor=0.0f; } ////////////////// //convert in the format used by physics Vec3d tempangle = cmd.GetDeltaAngles(); Vec3d dirangle = tempangle; dirangle=ConvertToRad(dirangle); float m_pcos = cry_cosf(dirangle[YAW]);//*inputspeed; float m_psin = cry_sinf(dirangle[YAW]);//*inputspeed; float m_pcos2 = cry_cosf(dirangle[PITCH]); float m_psin2 = cry_sinf(dirangle[PITCH]); m_walkParams.dir.x = -m_psin; m_walkParams.dir.y = m_pcos; m_walkParams.dir.z = 0.0f; Vec3d speedxyz(0.0f, 0.0f, 0.0f); pe_status_dynamics dynStatus; pPhysEnt->GetStatus((pe_status*)&dynStatus); float fWaterMoveScale = m_SwimSpeed*0.5f*m_stats.fKWater + (1.0f-m_stats.fKWater); if (cmd.CheckAction(ACTION_MOVE_FORWARD)) { if(m_stats.onLadder) // when on ladder - move mostly UP/DOWN { speedxyz[0] += -m_psin*.3f; speedxyz[1] += m_pcos*.3f; } else { speedxyz[0] += -m_psin; speedxyz[1] += m_pcos; } if (bNoGravity) { speedxyz[0] *= m_psin2; speedxyz[1] *= m_psin2; // //if swimming up - make sure not to go too fast to prevent jumping out of water if(m_bSwimming && m_pcos2<0) { if( (m_stats.fInWater < m_PlayerDimSwim.heightEye && // m_walkParams.vertSpeed > .02f && !m_stats.onLadder) || dynStatus.v.z<-7.0f) speedxyz[2] = -m_pcos2*fWaterMoveScale*m_stats.fKWater*.02f; // if close to surface - don't go up too fast - not to jump out of water // or big vertical vel - falling down else speedxyz[2] = -m_pcos2*fWaterMoveScale; // deep enoght - can go up } else speedxyz[2] = -m_pcos2;//*inputspeed; } else if(bSuperDesignerMode) { speedxyz[2] = -m_pcos2;//*inputspeed; } } if (cmd.CheckAction(ACTION_MOVE_BACKWARD)) { m_stats.back_pressed = true; //FIXME: would be nice if backward key detach us from the ladder when we approach the ground (instead use the jump button), but for this //more info about the ladder are needed, like the center position, to know if we are going up or down. // if(m_stats.onLadder /*&& (m_fLastGroundHeight+1.0 GetPos(true).z)*/)// when on ladder - move mostly UP/DOWN // { // speedxyz[0] += -m_psin*.3f; // speedxyz[1] -= m_pcos*.3f; // } // else { speedxyz[0] -= -m_psin; speedxyz[1] -= m_pcos; } if ( bNoGravity ) { speedxyz[0] *= m_psin2; speedxyz[1] *= m_psin2; // //if swimming up - make sure not to go too fast to prevent jumping out of water if(m_bSwimming && m_pcos2>0) { if( (m_stats.fInWater < m_PlayerDimSwim.heightEye && // m_walkParams.vertSpeed > .02f && !m_stats.onLadder) || dynStatus.v.z<-7.0f) speedxyz[2] = m_pcos2*fWaterMoveScale*m_stats.fKWater*.02f; // if close to surface - don't go up too fast - not to jump out of water else speedxyz[2] = m_pcos2*fWaterMoveScale; // deep enoght - can go up } else speedxyz[2] = m_pcos2;//*inputspeed; } else if(bSuperDesignerMode) { speedxyz[2] = m_pcos2;//*inputspeed; } } else m_stats.back_pressed = false; bool bStrafe = false; if (cmd.CheckAction(ACTION_MOVE_LEFT)) //&& !m_stats.onLadder) { /*if (m_stats.onLadder) { speedxyz[0] -= m_pcos*0.1f; speedxyz[1] -= m_psin*0.1f; } else*/ { speedxyz[0] -= m_pcos; speedxyz[1] -= m_psin; } bStrafe = true; } if (cmd.CheckAction(ACTION_MOVE_RIGHT)) //&& !m_stats.onLadder) { /*if (m_stats.onLadder) { speedxyz[0] += m_pcos*0.1f; speedxyz[1] += m_psin*0.1f; } else*/ { speedxyz[0] += m_pcos; speedxyz[1] += m_psin; } bStrafe = true; } if (GetLengthSquared(speedxyz)>0) { float fSpeedScale = m_stats.fSpeedScale*m_stats.dmgSpeedCoeff/100.0f; float inputspeed; if(m_stats.back_pressed && !m_stats.onLadder) { switch(m_CurStance) { case eStealth: inputspeed = m_StealthSpeedBack; break; case eCrouch: inputspeed = m_CrouchSpeedBack; break; case eProne: inputspeed = m_ProneSpeedBack; break; case eSwim: inputspeed = m_SwimSpeedBack; break; default: inputspeed = m_WalkSpeedBack; break; } if(m_Sprinting) inputspeed = m_RunSpeedBack*m_StaminaTable.RunSprintScale; else if(m_stats.running) inputspeed = m_RunSpeedBack; } else if(!bStrafe || m_stats.onLadder) { switch(m_CurStance) { case eStealth: inputspeed = m_StealthSpeed; break; case eCrouch: inputspeed = m_CrouchSpeed; break; case eProne: inputspeed = m_ProneSpeed; break; case eSwim: inputspeed = m_SwimSpeed; break; default: inputspeed = m_WalkSpeed; break; } if(m_Sprinting) inputspeed = m_RunSpeed*m_StaminaTable.RunSprintScale; else if(m_stats.running) inputspeed = m_RunSpeed; } else { switch(m_CurStance) { case eStealth: inputspeed = m_StealthSpeedStrafe; break; case eCrouch: inputspeed = m_CrouchSpeedStrafe; break; case eProne: inputspeed = m_ProneSpeedStrafe; break; case eSwim: inputspeed = m_SwimSpeedStrafe; break; default: inputspeed = m_WalkSpeedStrafe; break; } if(m_Sprinting) inputspeed = m_RunSpeedStrafe*m_StaminaTable.RunSprintScale; else if(m_stats.running) inputspeed = m_RunSpeedStrafe; } if(bSuperDesignerMode) { if (m_Running) inputspeed = m_pGame->p_speed_run->GetFVal(); else inputspeed = m_pGame->p_speed_walk->GetFVal(); } inputspeed *= fSpeedScale; speedxyz.Normalize(); speedxyz*=inputspeed; } //Vec3 ppos=m_pEntity->GetPos(true); // if player wants to jump AND proning - stand up // otherwice if not crouching AND has enough stamina for jump // AND not in air right now OR in water deep enough (go up then) // AND not in landing animation // do jump if ((cmd.CheckAction(ACTION_JUMP))) { //if player is jumping when on ladder throw him back, to detach from the ladder. if (m_stats.onLadder) { speedxyz[0] -= -m_psin; speedxyz[1] -= m_pcos; } else if(m_CurStance == eProne) { GoStand(false); cmd.RemoveAction(ACTION_JUMP); } else if (m_CurStance != eCrouch // && m_CurStance != eProne && ((!m_stats.flying) || IsSwimming()) && ((m_stats.stamina>m_StaminaTable.DecoyJump&&!m_stats.flying) || IsSwimming()) && m_JumpAniLenght<=0 ) { m_JumpHeight[0] = m_pGame->p_jump_walk_h->GetFVal(); m_JumpHeight[1] = m_pGame->p_jump_walk_h->GetFVal(); m_JumpHeight[2] = m_pGame->p_jump_run_h->GetFVal(); m_JumpDist[0] = m_pGame->p_jump_walk_d->GetFVal(); m_JumpDist[1] = m_pGame->p_jump_walk_d->GetFVal(); m_JumpDist[2] = m_pGame->p_jump_run_d->GetFVal(); float jumpSpeedH, jumpSpeedV; bJump = 1; Vec3d horSpeed = speedxyz; if(!IsSwimming()) { if( m_RunningTime >= m_JTRun ) // if runs long enough - long jump CalcJumpSpeed( m_JumpDist[2], m_JumpHeight[2], jumpSpeedH, jumpSpeedV ); else if( m_WalkingTime >= m_JTWalk ) // if runs long enough - long jump CalcJumpSpeed( m_JumpDist[1], m_JumpHeight[1], jumpSpeedH, jumpSpeedV ); else CalcJumpSpeed( m_JumpDist[0], m_JumpHeight[0], jumpSpeedH, jumpSpeedV ); horSpeed.z = 0; horSpeed.Normalize(); speedxyz[0] = horSpeed.x*jumpSpeedH; speedxyz[1] = horSpeed.y*jumpSpeedH; speedxyz[2] = jumpSpeedV; // decrease stamina - jumping is not for free! if((m_stats.stamina-=m_StaminaTable.DecoyJump)<0) m_stats.stamina = 0; m_bHasJumped = true; } else { float depth = m_p3DEngine->GetWaterLevel(m_pEntity) - m_p3DEngine->GetTerrainElevation(m_pEntity->GetPos().x, m_pEntity->GetPos().y); // if not deep and pressed jupm key - stand up, if can't stand (too deep) - push player up if( depth<1.5f ) { GoStand(false); cmd.RemoveAction(ACTION_JUMP); } else { // if pressing jump in water - just push playet up speedxyz[2] = 4.0f; // when swimming underwater and goung up by press JUMP // if too close to surface - don't aplpy much UP impulse // to prevent jumping out of water if(m_stats.fInWater<.75f) speedxyz[2] = 0; else if(m_stats.fInWater<2.25f) speedxyz[2] *= (m_stats.fInWater/2.25f)*.2f; } } } } pe_status_living status; pPhysEnt->GetStatus((pe_status*)&status); m_stats.jumping = (bJump)?true:false; pe_action_move hike; hike.iJump = bJump && !bNoGravity; hike.dir=speedxyz; DampInputVector(hike.dir,m_input_accel,m_input_stop_accel,true,false); pPhysEnt->Action(&hike); float moveVel = speedxyz[0]*speedxyz[0] + speedxyz[1]*speedxyz[1]; // if the velosity is too low - not sprinting then if(moveVel<2.0f)//|| (m_CurStance!=eStand && m_CurStance!=eRelaxed && m_CurStance!=eSwim)) m_Sprinting = false; /*if(pPhysEnt) do { pPhysEnt->Step(min(fTimeDelta,0.02f)); fTimeDelta -= 0.02f; } while(fTimeDelta>1E-4f);*/ float *pfSlices,sumSlice; int i,nSlices = cmd.GetTimeSlices(pfSlices); for(i=0,sumSlice=0; iStartStep(sumSlice); for(i=0; iStep(pfSlices[i]); pPhysEnt = m_pEntity->GetPhysics(); // update pPhysEnt since Step callback might change it (destroy/create new) } m_NotRunTimeClient = m_NotRunTime; m_RunningTimeClient = m_RunningTime; m_WalkingTimeClient = m_WalkingTime; m_staminaClient = m_stats.stamina; // m_breathClient = m_stats.breath; if (nSlices>0 && pPhysEnt) // the last slice is teh 'server catch-up' slice, client doesn't have it pPhysEnt->Step(pfSlices[nSlices-1]); } /////////////////////////////////////////////// /*! Retrieves the currently selected weapon @return selected weapon */ CWeaponClass* CPlayer::GetSelectedWeapon() const { CWeaponClass *pSelectedWeapon = m_pGame->GetWeaponSystemEx()->GetWeaponClassByID(m_nSelectedWeaponID); // make sure the weapon class (models, scripts, etc..) is loaded ... if (pSelectedWeapon && !pSelectedWeapon->IsLoaded()) { pSelectedWeapon->Load(); } return pSelectedWeapon; } ////////////////////////////////////////////////////////////////////////// void CPlayer::SelectNextWeapon() { int curr=0; if(m_nSelectedWeaponID != -1) curr = m_nSelectedWeaponID; int slot=0; while(slot=0 && (m_vWeaponSlots[slot]==0))slot--; if(slot<0)slot=0; } } if(m_vWeaponSlots[slot]!=0 && m_vWeaponSlots[slot]!=curr) SelectWeapon(m_vWeaponSlots[slot]); } /*! Updates the weapons of the player @param ProcessingCmd structure of commands to process */ ////////////////////////////////////////////////////////////////////////// void CPlayer::ProcessWeapons(CXEntityProcessingCmd &cmd) { // do not allow to use weapons and move when underwater or in a wtaer volume, and // he is actively swimming if(!IsMyPlayer()) m_walkParams.fCurrLean=cmd.GetLeaning(); if (cmd.CheckAction(ACTION_NEXT_WEAPON) && (!m_stats.lock_weapon)) { SelectNextWeapon(); } else if (cmd.CheckAction(ACTION_PREV_WEAPON) && (!m_stats.lock_weapon)) { SelectPrevWeapon(); } else { if(!m_stats.lock_weapon) { bool bSkip = false; int w = 0; int WeaponGroup=0; if (cmd.CheckAction(ACTION_WEAPON_0)) { WeaponGroup = 10; cmd.RemoveAction(ACTION_WEAPON_0); } else if (cmd.CheckAction(ACTION_WEAPON_1)) { WeaponGroup = 1; cmd.RemoveAction(ACTION_WEAPON_1); } else if (cmd.CheckAction(ACTION_WEAPON_2)) { WeaponGroup = 2; cmd.RemoveAction(ACTION_WEAPON_2); } else if (cmd.CheckAction(ACTION_WEAPON_3)) { WeaponGroup = 3; cmd.RemoveAction(ACTION_WEAPON_3); } else if (cmd.CheckAction(ACTION_WEAPON_4)) { WeaponGroup = 4; cmd.RemoveAction(ACTION_WEAPON_4); } /* else if (cmd.CheckAction(ACTION_WEAPON_5)) { WeaponGroup = 5; cmd.RemoveAction(ACTION_WEAPON_5); } else if (cmd.CheckAction(ACTION_WEAPON_6)) { WeaponGroup = 6; cmd.RemoveAction(ACTION_WEAPON_6); } else if (cmd.CheckAction(ACTION_WEAPON_7)) { WeaponGroup = 7; cmd.RemoveAction(ACTION_WEAPON_7); } else if (cmd.CheckAction(ACTION_WEAPON_8)) { WeaponGroup = 8; cmd.RemoveAction(ACTION_WEAPON_8); } */ else bSkip = true; if (!bSkip) { w = m_nSelectedWeaponID; bool bFirstWeapon = true; int iFirstID=-1; WeaponGroup-=1; if(WeaponGroupSendScriptEvent(ScriptEvent_CycleGrenade,0); cmd.RemoveAction(ACTION_CYCLE_GRENADE); } if (cmd.CheckAction(ACTION_FIREMODE)) { SwitchFiremode(); cmd.RemoveAction(ACTION_FIREMODE); } if (cmd.CheckAction(ACTION_RELOAD) && (!m_stats.lock_weapon) && (m_stats.underwater==0.0f) && !m_bSwimming) { if (pSelectedWeapon) { pSelectedWeapon->ScriptOnStopFiring(m_pEntity); pSelectedWeapon->ScriptReload(m_pEntity); } cmd.RemoveAction(ACTION_RELOAD); } // Check if the player is dropping his weapon if(cmd.CheckAction(ACTION_DROPWEAPON) && (!m_stats.lock_weapon)) { if (pSelectedWeapon && (CountAvaliableWeapons()>1)) { // Remove the current weapon from the player's inventory PlayerWeaponsItor WeaponIterator = m_mapPlayerWeapons.find(m_nSelectedWeaponID); if (WeaponIterator == m_mapPlayerWeapons.end()) { // This should not ever happen TRACE("Could not find dropped weapon in weapon list"); return; } // Notify the weapon that it has been dropped _SmartScriptObject pTable(m_pScriptSystem); pTable->SetValue("Player", m_pEntity->GetScriptObject()); pTable->SetValue("WeaponID", m_nSelectedWeaponID); pSelectedWeapon->ScriptDrop(pTable); } // Remove the command cmd.RemoveAction(ACTION_DROPWEAPON); } if (m_bWaitForFireRelease && !cmd.CheckAction(ACTION_FIRE0)) { m_bWaitForFireRelease = false; } if (!m_bWaitForFireRelease) { m_stats.firing = (cmd.CheckAction(ACTION_FIRE0)) != 0; } else m_stats.firing = false; if(cmd.CheckAction(ACTION_FIRECANCEL)) m_stats.cancelFireFlag = true; else m_stats.cancelFireFlag = false; if (!GetGame()->IsMultiplayer() || m_bWriteOccured) m_stats.firing_grenade = false; if ((cmd.CheckAction(ACTION_FIRE_GRENADE))) { if(m_stats.weapon_busy<=0) { m_stats.firing_grenade = true; } } if (m_bWriteOccured) m_bWriteOccured = false; } ////////////////////////////////////////////////////////////////////////// void CPlayer::FireGrenade(const Vec3d &origin, const Vec3d &angles, IEntity *pIShooter) { _SmartScriptObject pTable(m_pScriptSystem); m_ssoHitPosVec=origin; m_ssoHitNormVec=angles;//angles; Vec3d dir=angles; dir=ConvertToRadAngles(dir); // projectiles (grenades, for ex) should inherit player's velocity m_ssoHitDirVec=dir; //+(m_walkParams.dir.normalize()*(m_stats.fVel*0.125f)); //m_pGame->GetSystem()->GetILog()->LogToConsole("vel=%f,dir=%f,%f,%f",m_stats.fVel,m_walkParams.dir.x,m_walkParams.dir.y,m_walkParams.dir.z); float fWaterLevel=m_p3DEngine->GetWaterLevel(m_pEntity); pTable->SetValue("pos",m_ssoHitPosVec); pTable->SetValue("angles",m_ssoHitNormVec); pTable->SetValue("dir",m_ssoHitDirVec); pTable->SetValue("lifetime",3000); if(m_nSelectedWeaponID != -1 && m_stats.firing && !(m_stats.grenadetype!= 1 && m_stats.numofgrenades <= 0)) GetSelectedWeapon()->ScriptOnStopFiring(m_pEntity); if (fWaterLevel>origin.z) pTable->SetValue("underwater",0); else pTable->SetToNull("underwater"); m_pEntity->SendScriptEvent(ScriptEvent_FireGrenade, *pTable); if(m_pMountedWeapon) // stop fire animation on mounted weapon m_pMountedWeapon->SendScriptEvent(ScriptEvent_Fire,0); } ////////////////////////////////////////////////////////////////////////// void CPlayer::SetFiring(bool bIsFiring) { eFireType lastft=m_stats.FiringType; if (/*m_pSelectedWeapon &&*/ bIsFiring && IsAlive() && !m_stats.onLadder) { switch (m_stats.FiringType) { case ePressing: m_stats.FiringType=eHolding; break; case eHolding: m_stats.FiringType=eHolding; break; case eReleasing: m_stats.FiringType=ePressing; break; case eNotFiring: m_stats.FiringType=ePressing; break; default: m_stats.FiringType=eNotFiring; } } else { switch (m_stats.FiringType) { case ePressing: m_stats.FiringType=eNotFiring; break; case eHolding: m_stats.FiringType=eReleasing; break; case eReleasing: m_stats.FiringType=eNotFiring; break; case eNotFiring: m_stats.FiringType=eNotFiring; break; default: m_stats.FiringType=eNotFiring; } } m_stats.LastFiringType=lastft; /*if (m_stats.FiringType == PlayerStats::eFireType::ePressing) m_stats.FiringType = PlayerStats::eFireType::eHolding; else if (m_stats.FiringType == PlayerStats::eFireType::eReleasing) m_stats.FiringType = PlayerStats::eFireType::eNotFiring;*/ } ////////////////////////////////////////////////////////////////////////// void CPlayer::DrawThirdPersonWeapon(bool bDraw) { ICryCharInstance *character = m_pEntity->GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); if (character && m_nSelectedWeaponID != -1) { WeaponInfo &wi = GetWeaponInfo(); if(bDraw) { CWeaponClass* pSelectedWeapon = GetSelectedWeapon(); string sBone = pSelectedWeapon->GetBindBone(); if (!sBone.empty()) { wi.hBindInfo = character->AttachObjectToBone( pSelectedWeapon->GetObject(), sBone.c_str() ); } } else { wi.DetachBindingHandles(character); character->AttachObjectToBone(NULL,NULL); } } } /////////////////////////////////////////////// /*! Selects a weapon the player should hold @param weapon id of weapon to set */ bool CPlayer::SelectWeapon( int weapon, bool bCheckForAvailability ) { PlayerWeaponsItor wi; // if it's shooter from the vehicle - can't select weapons if (m_nSelectedWeaponID>0 && m_pVehicle && m_pVehicle->GetWeaponUser()==this) return false; if (m_stats.onLadder) return false; if ((wi = m_mapPlayerWeapons.find(weapon)) == m_mapPlayerWeapons.end() && weapon != -1) return false; if (bCheckForAvailability && weapon != -1) { if ((* wi).second.owns == false) return false; } m_pEntity->SendScriptEvent(ScriptEvent_SelectWeapon,0); // deselect old weapon, if selected if (m_nSelectedWeaponID != -1 && (weapon!=m_stats.weapon)) { // unbind the entity from the player WeaponInfo &info=GetWeaponInfo(); info.iFireMode=m_stats.firemode; _SmartScriptObject cOnUpdateParam(m_pScriptSystem); cOnUpdateParam->SetValue("shooter", m_pEntity->GetScriptObject()); GetSelectedWeapon()->ScriptOnDeactivate(m_pEntity); } SetWeapon(weapon); // call OnActivate when the weapon changes if (m_nSelectedWeaponID != -1 && (weapon != m_stats.weapon)) { WeaponInfo &info=GetWeaponInfo(); m_stats.firemode=info.iFireMode; m_bWaitForFireRelease = true; GetSelectedWeapon()->ScriptOnActivate(m_pEntity); // make sure that the firemode is correct on script side _SmartScriptObject pObj(m_pScriptSystem); pObj->SetValue( "firemode", m_stats.firemode); pObj->SetValue( "ignoreammo", true); bool bCanSwitch; m_pEntity->SendScriptEvent(ScriptEvent_FireModeChange, *pObj, &bCanSwitch); // force animation update on first person weapon if (GetSelectedWeapon()->GetCharacter()) GetSelectedWeapon()->GetCharacter()->ForceUpdate(); } m_stats.weapon = m_nSelectedWeaponID; return true; } void CPlayer::SetWeapon(int iClsID) { // ValidateHeap(); // check if we want to select the weapon we are holding if(iClsID == m_nSelectedWeaponID) return; // we want to hide the currently selected weapon if(m_nSelectedWeaponID != -1) { if (m_stats.LastFiringType!=eNotFiring) GetSelectedWeapon()->ScriptOnStopFiring(m_pEntity); SetWeaponPositionState(WEAPON_POS_UNDEFINED); if (IsMyPlayer()) { ICryCharInstance *pChar = GetEntity()->GetCharInterface()->GetCharacter(1); if (pChar) pChar->DetachAll(); GetEntity()->DrawCharacter(1,0); GetEntity()->ResetAnimations(1); } } // here we actually set the new weapon m_nSelectedWeaponID = iClsID; // returns the newly selected weapon!!! CWeaponClass* pSelectedWeapon = GetSelectedWeapon(); if(pSelectedWeapon) { // attach to bone for third person view HoldWeapon(); // set first person weapon if (IsMyPlayer() || m_pGame->IsMultiplayer()) GetEntity()->GetCharInterface()->SetCharacter(1, pSelectedWeapon->GetCharacter()); m_pScriptObject->SetValue("weapon", pSelectedWeapon->GetScriptObject()); m_pScriptObject->SetValue("weaponid", m_nSelectedWeaponID); } else { GetEntity()->GetCharInterface()->SetCharacter(1, 0); m_pScriptObject->SetToNull("weapon"); m_pScriptObject->SetToNull("weaponid"); m_stats.fSpeedScale = 1.0f; m_nSelectedWeaponID = -1; } } /*! return the position and orientation of the firepoint of a player @param firePos position @param fireAngles orientation(in degrees) */ void CPlayer::GetFirePosAngles(Vec3d& firePos, Vec3d& fireAngles) { if (!m_bIsAI) { //we must always take leaning into account! fireAngles = m_vEyeAngles+m_vShake; //fireAngles = m_pEntity->GetAngles()+m_vShake; firePos = m_vEyePos; /* if (m_pMountedWeapon) { IEntityCharacter *pIChar = m_pMountedWeapon->GetCharInterface(); ICryCharInstance * cmodel = pIChar->GetCharacter(0); if (!cmodel) return; ICryBone * pBone = cmodel->GetBoneByName("spitfire"); if(!pBone) return; Vec3 vBonePos = pBone->GetBonePosition(); Vec3 angles = m_pMountedWeapon->GetAngles(); Matrix44 m=Matrix34::GetRotationXYZ34( Deg2Rad(angles), m_pMountedWeapon->GetPos() ); //set rotation and translation in one function call m = GetTransposed44(m); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 // get result firePos = m.TransformPointOLD(vBonePos); } else */ if (m_pVehicle) { // if we are in vehicle, the cam offset seems to be wrong - // move to the exact camera position //[kirill] - coz camera is offseted a bit for gunner - away from weapon // if gunner's camera moved - add same offset here - now it's eyePos // firePos=m_pEntity->GetCamera()->GetPos(); // if driver - using vehicle's autoveapon // - just get current weapon pos/angles from the vehicle // if (m_stats.inVehicleState==PVS_DRIVER && m_pVehicle->GetWeaponUser()==this ) if (m_stats.inVehicleState==PVS_DRIVER ) { string vehicleWeaponName = m_pVehicle->GetWeaponName( m_stats.inVehicleState ); if(!vehicleWeaponName.empty() && vehicleWeaponName != m_pVehicle->m_sNoWeaponName) m_pVehicle->GetFirePosAngles( firePos, fireAngles ); else if(IsMyPlayer()) // use vehicle camera position only for local player firePos = m_pVehicle->GetCamPos(); else firePos += Vec3(0.0f, 0.0f, -1.04f); // UGLY FIX FOR THE INFLATABLE BOAT! // firePos on the server was 0.6 meters less than the client } // if passenger - get camera position from vehicle, it's smoothed with some spring simulation else if (m_stats.inVehicleState == PVS_PASSENGER) { if(IsMyPlayer()) // use vehicle camera position only for local player firePos = m_pVehicle->GetCamPos(); else // in MP on server use eye helper position m_pVehicle->GetEntity()->GetHelperPosition(m_sVehicleEyeHelper.c_str(), firePos); } return; } } else { if (m_pEntity->GetAI()) { //<> this code will go - testing purposes firePos = m_pEntity->GetAI()->GetPos(); // Vec3d angles = m_pEntity->GetAI()->GetState()->vFireDir; // angles=ConvertVectorToCameraAngles(angles); if(!m_pMountedWeapon) // normal weapon - shoot wherever fireAngles = m_vDEBUGAIFIREANGLES; // fireAngles = angles; else // if using mounted weapon - angles are restricted fireAngles = m_pEntity->GetAngles(); } } } float CPlayer::CalculateAccuracyFactor(float accuracy) { if(m_nSelectedWeaponID == -1) return 0.0f; CWeaponClass *pSelectedWeapon = GetSelectedWeapon(); WeaponParams wp; GetCurrentWeaponParams(wp); float factor = (wp.fMaxAccuracy - wp.fMinAccuracy) * (1.0f-accuracy) + wp.fMinAccuracy; // factor 0 = bad, 1 = good if (m_stats.aiming) { factor = factor + wp.fAimImprovement; } // apply sprinting if the sprint button is pressed and the player is moving if (m_Sprinting && m_stats.moving) { factor = factor - wp.fSprintPenalty; } factor = 1 - factor; factor = __max(factor, 0.000f); float stanceAccuracyModifier = 1.0f; // process weapon specific accuracy modifier based on stance stanceAccuracyModifier = wp.fAccuracyModifierStanding; if (m_CurStance == eCrouch) { stanceAccuracyModifier = wp.fAccuracyModifierCrouch; } else if (m_CurStance == eProne) { stanceAccuracyModifier = wp.fAccuracyModifierProne; } return factor * stanceAccuracyModifier; } void CPlayer::UpdateMelee() { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); //MELEE ATTACK///////////////////////////////////// if(m_stats.melee_attack && (m_stats.weapon_busy<=0) && (m_stats.melee_distance > 0.0f)) { //TRACE("UATTATATATATATATATA!"); _SmartScriptObject so(m_pScriptSystem); Vec3d firePos; Vec3d fireAngles; GetFirePosAngles(firePos, fireAngles); Vec3d dir(0,-1,0); Matrix44 tm = Matrix44::CreateRotationZYX(-fireAngles*gf_DEGTORAD); //NOTE: angles in radians and negated ; dir = GetTransposed44(tm)*(dir); m_ssoHitPosVec = firePos; m_ssoHitDirVec = dir; so->BeginSetGetChain(); so->SetValueChain("pos",m_ssoHitPosVec); so->SetValueChain("dir",m_ssoHitDirVec); so->SetValueChain("distance", m_stats.melee_distance); so->SetValueChain("shooter",m_pEntity->GetScriptObject()); _SmartScriptObject soTarget(m_pScriptSystem,true); if (GetScriptObject()->GetValue("melee_target", soTarget)) { so->SetValueChain("melee_target", *soTarget); } so->EndSetGetChain(); m_pEntity->SendScriptEvent(ScriptEvent_MeleeAttack,so); m_stats.melee_attack = false; m_pEntity->GetScriptObject()->SetToNull("melee_target"); } } /////////////////////////////////////////////// /*! Updates the selected weapon */ void CPlayer::UpdateWeapon() { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); float frametime=m_pTimer->GetFrameTime(); if(m_stats.firing_grenade) { FRAME_PROFILER( "UpdateWeapon::Grenade",GetISystem(),PROFILE_GAME ); Vec3d firePos; Vec3d fireAngles; GetFirePosAngles(firePos, fireAngles); if(m_stats.weapon_busy<=0) FireGrenade(firePos, fireAngles, m_pEntity); if (!GetGame()->IsMultiplayer()) m_stats.firing_grenade = false; } if(m_nSelectedWeaponID == -1) return; WeaponParams wp; CWeaponClass* pSelectedWeapon = GetSelectedWeapon(); GetCurrentWeaponParams(wp); m_fAccuracyMod=1; if(m_pGame->IsServer()) { // make sure that the AI is always using the right firemode if(m_bIsAI) { FRAME_PROFILER( "UpdateWeapon::SwitchFiremode",GetISystem(),PROFILE_GAME ); SwitchFiremode(pSelectedWeapon->GetAIFireMode()); } if(m_stats.firing){ m_fAccuracy=m_fAccuracy-(frametime); }else{ m_fAccuracy=m_fAccuracy-(m_pGame->w_accuracy_gain_scale*frametime); } m_fAccuracy=__max(m_fAccuracy, 0.1f); if(!m_pMountedWeapon && !m_pVehicle) { if(m_stats.running) { if(m_fAccuracy0) { m_stats.firing=false; } else{ if(m_stats.weapon_busy!=0) { FRAME_PROFILER( "UpdateWeapon::ScriptWeaponReady",GetISystem(),PROFILE_GAME ); wi.fireTime=m_pTimer->GetCurrTime(); //wi->fireFirstBulletTime=0; pSelectedWeapon->ScriptWeaponReady(m_pEntity); } m_stats.weapon_busy=0; } //Update the weapon entity if (IsMyPlayer() || (m_pGame->cl_scope_flare->GetIVal() == 1 && m_weaponPositionState == WEAPON_POS_HOLD )) pSelectedWeapon->Update(this); if (m_stats.cancelFireFlag) pSelectedWeapon->CancelFire(); if((m_stats.ammo_in_clip<=0 && m_stats.ammo>0 && (!m_stats.reloading) && (m_stats.weapon_busy<=0)) && (!m_stats.firing) && (m_stats.underwater==0.0f) && !m_bSwimming) { pSelectedWeapon->ScriptOnStopFiring(m_pEntity); pSelectedWeapon->ScriptReload(m_pEntity); wi.fireTime=m_pTimer->GetCurrTime(); } else if(!m_stats.reloading && m_stats.weapon_busy<=0) { if(m_stats.ammo_in_clip>0 || wp.no_ammo) { if ((m_pTimer->GetCurrTime() - wi.fireLastShot) < pSelectedWeapon->GetFireRate(m_stats.FiringType)) m_stats.fire_rate_busy = true; else m_stats.fire_rate_busy = false; m_stats.canfire = true; } else { if(m_stats.canfire) pSelectedWeapon->ScriptOnStopFiring(m_pEntity); m_stats.canfire=false; } SetFiring(m_stats.firing); if (m_stats.FiringType != eNotFiring) { float fFireRate = pSelectedWeapon->GetFireRate(m_stats.FiringType); //is in the right state for firing if( wp.fire_activation&m_stats.FiringType) { Vec3d firePos; Vec3d fireAngles; GetFirePosAngles(firePos, fireAngles); if(!(m_stats.LastFiringType&wp.fire_activation)) { wi.fireTime=m_pTimer->GetCurrTime(); wi.fireFirstBulletTime=wi.fireTime; m_fRecoilXDelta=0; } float deltatime=max(0.001f,m_pTimer->GetCurrTime()-wi.fireFirstBulletTime); int bullets=0; //[kirill] - can't shoot if using mounted vehicle weapon and weapon is off of angle limits if (m_pGame->m_nDEBUG_TIMING==1) { m_pGame->m_nDEBUG_TIMING = 2; //[Michael Glueck] has nothing to do with AI if(m_bIsAI) { float fTime = m_pGame->GetSystem()->GetITimer()->GetAsyncCurTime(); m_pGame->m_pLog->Log(" Time between loading and having AI shooting is %.3f seconds",fTime-m_pGame->m_fDEBUG_STARTTIMER); } } if(m_stats.crosshairOnScreen) if(bullets = pSelectedWeapon->Fire( firePos,fireAngles, this, wi, GetRedirected() ? GetRedirected()->GetPhysics() : NULL)) { FRAME_PROFILER( "UpdateWeapon::ScriptEvent_Fire",GetISystem(),PROFILE_GAME ); if(m_pMountedWeapon) // start fire animation on mounted weapon m_pMountedWeapon->SendScriptEvent(ScriptEvent_Fire,1); else if( m_pVehicle ) { WeaponInfo &wi = GetWeaponInfo(); m_pVehicle->WeaponState( m_pEntity->GetId(), true, wi.iFireMode); } if(m_pGame->IsServer()) { m_stats.last_accuracy=(BYTE)(m_stats.accuracy/(1.f/255)); m_fAccuracy=min(1,m_fAccuracy+((deltatime*(m_pGame->w_accuracy_decay_speed*fFireRate)*bullets))); BYTE acc=(BYTE)((m_fAccuracy*m_fAccuracyMod)/(1.f/255)); m_stats.accuracy=(float)((acc)*(1.f/255)); } //add recoil if(!IsAI()) //IsMyPlayer()) // every player should have recoil { //pseudo realistic recoil if((m_fRecoilXDelta+1)w_recoil_max_degree) { float delta=min(1, (m_pTimer->GetCurrTime()-wi.fireFirstBulletTime)+wp.fFireRate); float recoilx=wp.max_recoil*((delta*2)*2); float recoilz=recoilx; uint8 ucSeed = m_SynchedRandomSeed.GetRandomSeedC(); m_SynchedRandomSeed.IncreaseRandomSeedC(); // GetISystem()->GetILog()->Log(">> Recoil Recoil %d %d",(int)GetEntity()->GetId(),(int)(ucSeed)); // debug float fRandA = m_SynchedRandomSeed.GetRandTable(ucSeed); float fRandB = m_SynchedRandomSeed.GetRandTable(ucSeed+13); m_fRecoilXUp=m_fRecoilX=(min(wp.max_recoil,wp.min_recoil+recoilx+(recoilx*fRandA)))*bullets; m_fRecoilZUp=m_fRecoilZ=((m_fRecoilXUp)-(m_fRecoilXUp*fRandA)*2)*bullets; if (m_stats.aiming) { m_fRecoilX *= wp.fAimRecoilModifier; m_fRecoilXUp *= wp.fAimRecoilModifier; m_fRecoilZ *= wp.fAimRecoilModifier; m_fRecoilZUp *= wp.fAimRecoilModifier; } } else { //m_fRecoilXUp=0; } } } } // } else if(m_stats.LastFiringType!=eNotFiring) { FRAME_PROFILER( "UpdateWeapon::ScriptOnStopFiring",GetISystem(),PROFILE_GAME ); //was firing and now stoppped //sending an event for stooping various effects wi.fireFirstBulletTime=0; pSelectedWeapon->ScriptOnStopFiring(m_pEntity); if(m_pMountedWeapon) // stop fire animation on mounted weapon m_pMountedWeapon->SendScriptEvent(ScriptEvent_Fire,0); else if( m_pVehicle ) // stop fire animation on weapon on wehicle m_pVehicle->WeaponState( m_pEntity->GetId(), false ); } } } // returns ACTUAL plaerys direction angle - the one it's shooting/looking at Vec3d CPlayer::GetActualAngles() { Vec3d angles; if( m_pGame->p_EyeFire->GetIVal()==0) { if(m_aimLook) { Vec3d pos; GetFirePosAngles( pos, angles ); } else angles = m_pEntity->GetAngles(); } else angles = m_pEntity->GetAngles(); angles.x = Snap_s180(angles.x); angles.y = Snap_s180(angles.y); angles.z = Snap_s180(angles.z); return angles; } /////////////////////////////////////////////// /*! Updates procedural animation of character */ void CPlayer::UpdateBonesRotation( ) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); //m_pGame->m_pLog->LogToConsole("\003 %.2f %.2f ",m_LegAngle,m_LegDeltaAngle); //also when on ladder we have to rotate some bones (head) if (m_LegRotation || m_stats.onLadder) { // apply lean angles to player character ICryCharInstance *pChar = m_pEntity->GetCharInterface()->GetCharacter(0); Vec3d angles = GetActualAngles(); if (pChar) { CryQuat qtH; CryQuat qtV; CryQuat qtR; CryQuat qtTotal; CryQuat qtParent; CryQuat qtParentCnj; float xAngle = -angles.x; // float xAngle = Snap180(-angles.x); /* if( xAngle>180 ) xAngle = xAngle - 360.0f; else if( xAngle<-180 ) xAngle = 360.0f+xAngle; */ // float leanBodyAngle = -(180.0f - (180 - m_walkParams.fCurrLean*m_pGame->p_lean->GetFVal()*2.0f)); float leanBodyAngle = -(180.0f - (180 - m_walkParams.fCurrLean*m_LeanDegree*2.0f)); // leanBodyAngle = 0; leanBodyAngle *= 2.0f; // leanBodyAngle *= 5.0f; // float zAngle = m_LegDeltaAngle + m_LegMovingAngle; float zAngle = m_LegDeltaAngle; /* if(m_pBoneSpine1) qtParent = m_pBoneSpine1->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtH.SetRotationAA( DEG2RAD(m_LegDeltaAngle), Vec3(0.0f, 0.0f, 1.0f) ); //qtH.SetRotationAA( DEG2RAD(45), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle), Vec3(1.0f, 0.0f, 0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle), Vec3(0.0f, 1.0f, 0.0f) ); qtTotal = qtR*qtV*qtH; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; if(m_pBoneSpine1) m_pBoneSpine1->SetPlusRotation( qtTotal ); return; //*/ //on ladder we rotate just the head bone to follow the player view. if (m_stats.onLadder) { if(m_pBoneNeck) m_pBoneNeck->ResetPlusRotation(); if(m_pBoneSpine1) m_pBoneSpine1->ResetPlusRotation(); if(m_pBoneSpine2) m_pBoneSpine2->ResetPlusRotation(); if (m_stats.onLadder) { zAngle = Snap_s180(m_vLadderAngles.z - m_pEntity->GetAngles().z); //m_pGame->GetSystem()->GetILog()->Log("head:%.1f,body:%.1f",zAngle,m_vLadderAngles.z); } //limit the head angle in a 120 degree range. if (zAngle > 60.0f) zAngle = 60.0f; if (zAngle < -60.0f) zAngle = -60.0f; //smooth the head rotation. Ang3 delta = Ang3(xAngle*0.5f,0,zAngle) - m_vHeadAngles; m_vHeadAngles = m_vHeadAngles + delta*min(1.0f,m_pTimer->GetFrameTime()*10.0f); m_vHeadAngles.Snap180(); //apply rotation qtH.SetRotationAA( DEG2RAD(m_vHeadAngles.z), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(m_vHeadAngles.x), Vec3(1.0f, 0.0f, 0.0f) ); qtParent = m_pBoneHead->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtV*qtH*qtParentCnj; m_pBoneHead->SetPlusRotation( qtTotal ); } else if(m_CurStance == eProne) { m_LegADeltaLimit = 0; if(m_pBoneHead) m_pBoneHead->SetPlusRotation(0,0,angles.x); if(m_pBoneNeck) m_pBoneNeck->ResetPlusRotation(); if(m_pBoneSpine1) m_pBoneSpine1->ResetPlusRotation(); if(m_pBoneSpine2) m_pBoneSpine2->ResetPlusRotation(); } // else if(m_aimLook) //aiming else if(m_CurStance == eCrouch || m_CurStance == eStealth || m_CurStance == eStand) { //force weapon (shoulders) be same direction as ent direstion //m_LegADeltaLimit = 60; m_LegADeltaLimit = m_pGame->pa_leg_limitaim->GetFVal();// 60; if(m_pBoneHead) m_pBoneHead->ResetPlusRotation(); if(m_pBoneNeck) m_pBoneNeck->ResetPlusRotation(); float amountSpine = m_pGame->pa_spine->GetFVal(); float amountSpine1 = m_pGame->pa_spine1->GetFVal(); float amountSpine2; amountSpine2 = 1.0f - (amountSpine1 + amountSpine); if(m_pBoneSpine) { qtH.SetRotationAA( DEG2RAD(zAngle*amountSpine), Vec3(0.0f,0.0f,1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.2f), Vec3(1.0f,0.0f,0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.2f), Vec3(0.0f,1.0f,0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine->SetPlusRotation( qtTotal ); } if(m_pBoneSpine1) { qtH.SetRotationAA( DEG2RAD(zAngle*amountSpine1), Vec3(0.0f,0.0f,1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.4f), Vec3(1.0f,0.0f,0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.4f), Vec3(0.0f,1.0f,0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine1->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine1->SetPlusRotation( qtTotal ); } if(m_pBoneSpine2) { qtH.SetRotationAA( DEG2RAD(zAngle*amountSpine2), Vec3(0.0f,0.0f,1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.4f), Vec3(1.0f,0.0f,0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.4f), Vec3(0.0f,1.0f,0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine2->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine2->SetPlusRotation( qtTotal ); } } else { //m_LegADeltaLimit = 110; m_LegADeltaLimit = m_pGame->pa_leg_limitidle->GetFVal(); if(m_pBoneHead) { qtH.SetRotationAA( DEG2RAD(zAngle*.3f), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.5f), Vec3(1.0f, 0.0f, 0.0f) ); // qtTotal = qtV*qtH; qtParent = m_pBoneHead->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtV*qtH*qtParentCnj; m_pBoneHead->SetPlusRotation( qtTotal ); } if(m_pBoneNeck) { qtH.SetRotationAA( DEG2RAD(zAngle*.3f), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.2f), Vec3(1.0f, 0.0f, 0.0f) ); // qtTotal = qtV*qtH; qtParent = m_pBoneNeck->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtV*qtH*qtParentCnj; m_pBoneNeck->SetPlusRotation( qtTotal ); } if(m_pBoneSpine) { qtH.SetRotationAA( DEG2RAD(zAngle*.1f), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.1f), Vec3(1.0f, 0.0f, 0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.1f), Vec3(0.0f, 1.0f, 0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine->SetPlusRotation( qtTotal ); } if(m_pBoneSpine1) { qtH.SetRotationAA( DEG2RAD(zAngle*.1f), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.1f), Vec3(1.0f, 0.0f, 0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.5f), Vec3(0.0f, 1.0f, 0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine1->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine1->SetPlusRotation( qtTotal ); } if(m_pBoneSpine2) { qtH.SetRotationAA( DEG2RAD(zAngle*.2f), Vec3(0.0f, 0.0f, 1.0f) ); qtV.SetRotationAA( DEG2RAD(xAngle*.1f), Vec3(1.0f, 0.0f, 0.0f) ); qtR.SetRotationAA( DEG2RAD(leanBodyAngle*.4f), Vec3(0.0f, 1.0f, 0.0f) ); // qtTotal = qtR*qtV*qtH; qtParent = m_pBoneSpine2->GetParentWQuat(); qtParentCnj = qtParent; qtParentCnj.w = -qtParentCnj.w; qtTotal = qtParent*qtR*qtV*qtH*qtParentCnj; m_pBoneSpine2->SetPlusRotation( qtTotal ); } //m_pBoneSpine1->SetPlusRotation(-m_LegDeltaAngle*.10f, -leanBodyAngle*.5f, // -xAngle*.1f);//GetAngleDifference360(0, -angles.x)*.5f); } } } else { ResetRotateHead(); } } /////////////////////////////////////////////// /////////////////////////////////////////////// /*! */ void CPlayer::ResetRotateHead() { if(m_pBoneHead) m_pBoneHead->ResetPlusRotation(); if(m_pBoneNeck) m_pBoneNeck->ResetPlusRotation(); if(m_pBoneSpine) m_pBoneSpine->ResetPlusRotation(); if(m_pBoneSpine1) m_pBoneSpine1->ResetPlusRotation(); if(m_pBoneSpine2) m_pBoneSpine2->ResetPlusRotation(); } /////////////////////////////////////////////// /////////////////////////////////////////////// /*! */ void CPlayer::UpdateFireAnimations() { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); //fixme - when dead - needs different update if(!IsAlive()) return; if( m_pGame->pa_blend1->GetFVal()<0.0f ) return; // stop fire animation when // in relaxed stance // swimming // on ladder if(!m_AnimationSystemEnabled || m_CurStance == eRelaxed || m_bSwimming || m_stats.onLadder) { // m_pEntity->GetCurrentAnimation(0, 1) m_sPrevAniNameLayer1.clear(); m_pEntity->StartAnimation(0, NULL, 1, .15f); return; } const char *weaponShoot = "_utshoot"; // two hands animations const char *weaponAim = "_utaim"; char aniName[64]; if (!GenerateAnimationName( aniName )) { return; } char aniName0[64]; // store the base layer0 animation name memcpy( aniName0, aniName, strlen(aniName) ); // fixme - has to be changed for multyplayer //m_stats.weapon_busy<=0 //check for melee attack/reloading/whatever CWeaponClass *pSelectedWeapon = GetSelectedWeapon(); if(m_JumpStage==1) { m_pEntity->StartAnimation(0, NULL, 1, .15f); //m_pEntity->StartAnimation(0, "aidle", 1, .15f); m_sPrevAniNameLayer1 = ""; return; } if(m_JumpStage!=0) return; if(!pSelectedWeapon ) // no weapon in hands - use machete animations { weaponShoot = "_umshoot"; weaponAim = "_umaim"; } else if(pSelectedWeapon->m_HoldingType == 2) // pistol animations { weaponShoot = "_upshoot"; weaponAim = "_upaim"; } else if(pSelectedWeapon->m_HoldingType == 3) // machete animations { weaponShoot = "_umshoot"; weaponAim = "_umaim"; } else if(pSelectedWeapon->m_HoldingType == 4) // building tool animations { weaponShoot = "_ubshoot"; weaponAim = "_ubaim"; } else if(pSelectedWeapon->m_HoldingType == 5) // scouttool/healthpack animations { weaponShoot = "_usshoot"; weaponAim = "_usaim"; } if ( m_stats.firing_grenade) m_bGrenadeAnimation = true; if ( m_bGrenadeAnimation ) { if(m_stats.weapon_busy<=0) m_bGrenadeAnimation = false; else { const char *grenadeAim = "grenade_mp"; if(m_sPrevAniNameLayer1 != grenadeAim) if(m_pEntity->StartAnimation(0, grenadeAim, 1, .2f, true)) //.2f m_sPrevAniNameLayer1 = grenadeAim; } } else if( m_aimLook && m_stats.canfire ) { m_fShootAniLength -= m_pTimer->GetFrameTime(); if(m_bWeaponJustFired) // do fire animation { m_bWeaponJustFired = false; strcat( aniName,weaponShoot ); m_fShootAniLength = m_pEntity->GetAnimationLength( aniName ); } else if( m_fShootAniLength<=0 ) // fire animation over - do aim animation { strcat( aniName,weaponAim ); } else { // still playing fire animation strcat( aniName,weaponShoot ); //return; } if(m_sPrevAniNameLayer1 == aniName) return; // float blendTime = m_pGame->pa_blend1->GetFVal(); // GetBlendTime( aniName, blendTime ); // blend time has to be the same as for base animation in layer 0 // float blendTime = m_pGame->pa_blend1->GetFVal(); // GetBlendTime( aniName0, blendTime ); float blendTime = m_pGame->pa_blend0->GetFVal(); // if animation is shorter than blend time - it blands to base animation ( which is bad ) float aniLenght = m_pEntity->GetAnimationLength(aniName); if( blendTime>aniLenght ) blendTime = aniLenght; //0; if(m_pEntity->StartAnimation(0, aniName, 1, blendTime, true)) //.2f m_sPrevAniNameLayer1 = aniName; } else // not shooting/aiming - weapon down animation { if(pSelectedWeapon && pSelectedWeapon->m_HoldingType == 2) // pistol animations { strcat( aniName,"_upidle" ); // PETAR no animations for all stances - temporary HACK strcpy(aniName,"aidle_upidle"); //-------------------- if(m_sPrevAniNameLayer1 == aniName) return; // float blendTime = m_pGame->pa_blend1->GetFVal(); // GetBlendTime( aniName, blendTime ); // blend time has to be the same as for base animation in layer 0 float blendTime = m_pGame->pa_blend0->GetFVal(); GetBlendTime( aniName0, blendTime ); // if animation is shorter than blend time - it blands to base animation ( which is bad ) float aniLenght = m_pEntity->GetAnimationLength(aniName); if( blendTime>aniLenght ) blendTime = aniLenght; //0; // try to start pistol idle for current stance/movement if(m_pEntity->StartAnimation(0, aniName, 1, blendTime, true)) { m_sPrevAniNameLayer1 = aniName; return; } strcpy( aniName,"aidle_upidle" ); if(m_sPrevAniNameLayer1 == aniName) return; // if animation is shorter than blend time - it blands to base animation ( which is bad ) aniLenght = m_pEntity->GetAnimationLength(aniName); if( blendTime>aniLenght ) blendTime = aniLenght; //0; // use aim aidle stance if(m_pEntity->StartAnimation(0, aniName, 1, blendTime, true)) { m_sPrevAniNameLayer1 = aniName; return; } } if(m_sPrevAniNameLayer1.empty()) return; m_pEntity->StartAnimation(0, NULL, 1, .15f); m_sPrevAniNameLayer1 = ""; m_fShootAniLength = 0.0f; } } /////////////////////////////////////////////// /////////////////////////////////////////////// /*! */ void CPlayer::UpdateJumpAnimations() { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); if( m_pGame->pa_blend2->GetFVal()<0.0f ) return; if(!m_AnimationSystemEnabled) return; const char *aniName; // if(m_FlyTime<.15f && !m_stats.landing) if( m_FlyTime<.35f ) { if( m_sPrevAniNameLayer2 == "jump_air" ) { m_pEntity->StartAnimation(0, NULL, 2, .1f); m_sPrevAniNameLayer2.clear(); } return; } if( m_stats.flying ) aniName = "jump_air"; if(m_stats.landing) aniName = "jump_land"; if(m_pEntity->GetCurrentAnimation(0, 2)<0) m_sPrevAniNameLayer2.clear(); if (strlen(aniName) == 0 || m_sPrevAniNameLayer2 == aniName ) return; float blendTime = m_pGame->pa_blend2->GetFVal(); GetBlendTime( aniName, blendTime ); if(m_pEntity->StartAnimation(0, aniName, 2, blendTime)) m_sPrevAniNameLayer2 = aniName; } /////////////////////////////////////////////// /////////////////////////////////////////////// /*! Updates walking-flags for the player so the correct animation can be played back */ void CPlayer::UpdateCharacterAnimations( SPlayerUpdateContext &ctx ) { if(!IsAlive()) // if dead - deadBodyPhysics takes over return; if(m_pMountedWeapon) { UpdateCharacterAnimationsMounted( ctx ); return; } FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); //float MIN_WALK_SPEED = m_WalkSpeed*.15f; // float LEFT_RIGHT_COS = .8f; // float LEFT_RIGHT_COS_TRH = 0.91f; float MIN_CROUCH_SPEED = m_CrouchSpeed*.17f; float MIN_PRONE_SPEED = m_ProneSpeed*.17f; //bool moving = false; bool flying = false; //bool running = false; bool forward = false; // New variables for script based version int nStanding = 0; // 0 = Standing( combat ), 1 = Crouching, 2 = Prone, 3 = Stealth, 4 = Standing( relaxed ) int nStrafe = 0; int nForward = 0; int nMode = 0; // 0 = Idle, 1 = Walking, 2 = Running, 3 = Jump, 4 = Flying, 6 = Turning, 7 = sprinting // Create a new status object. The fields are initialized for us pe_status_living &status = ctx.status; // Get a pointer to the physics engine IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); PhysicsVars *physvars = m_pGame->GetSystem()->GetIPhysicalWorld()->GetPhysVars(); m_stats.landing = false; // If the physics says the player is flying, set the flag if (status.bFlying) { flying = true; m_FlyTime += m_pTimer->GetFrameTime(); m_LandTime = 0; // Calculate our current Vertical speed - to use for falling damage Vec3 vel = status.vel; if (status.pGroundCollider) vel -= status.velGround; m_walkParams.vertSpeedFalling = vel.z; } else { // If we were flying previously, but have stopped, set the flag if (m_stats.flying) { if(m_FlyTime>.5f) // if more than .5 sec in air - play land animation m_stats.landing = true; // m_pEntity->SendScriptEvent(ScriptEvent_AnimationKey,1); if(m_Dynamics.gravity!=0.0f) // in 0 gravity landing is not possible if(m_walkParams.vertSpeedFalling<0.0f ) // falling down m_pEntity->SendScriptEvent(ScriptEvent_Land, (int)(-m_walkParams.vertSpeedFalling*100)); } m_FlyTime = 0; m_LandTime += m_pTimer->GetFrameTime(); } // Store the current flying and landing states m_stats.flying = flying; // [Anton] a piece of code moved to UpdatePhysics // here goes stance determination -stuff // Check if the player is stealth if (m_CurStance == eStealth) { nStanding = 3; // Check if the player is moving // This is true if the player is running, moving left, moving right, or moving at all if (m_stats.fVel > MIN_CROUCH_SPEED) { if (m_stats.running) nMode = 2; else nMode = 1; } } // Check if the player is crouching else if (m_CurStance == eCrouch) { nStanding = 1; // Check if the player is moving // This is true if the player is running, moving left, moving right, or moving at all if (m_stats.fVel > MIN_CROUCH_SPEED) { // Crouching movement. nMode = 1; } } else if(m_CurStance == eProne) { if (m_stats.fVel > MIN_PRONE_SPEED) nMode = 1; nStanding = 2; } else // must be standing { if (m_CurStance == eRelaxed) //Standing( relaxed ), otherwise - standing combat nStanding = 4; // Check if the player is moving // This is true if the player is running, moving left, moving right, or moving at all if (m_stats.moving) { // Check if the player is running if (m_stats.running) { nMode = 2; if( m_Sprinting ) nMode = 7; } else { // Walking nMode = 1; } } else { nMode = 0; } } // stance stuff over // here goes movement direction/type determination -stuff if(m_stats.moving) { Ang3 playerLookAngles = GetActualAngles(); Ang3 playerMoveAngles = (Ang3)status.vel; playerMoveAngles.normalize(); playerMoveAngles=ConvertUnitVectorToCameraAngles(playerMoveAngles); playerMoveAngles.Snap180(); // float moveDeltaAngle = playerLookAngles.DifferenceZ( playerMoveAngles ); float moveDeltaAngle = Snap_s180(playerLookAngles.z-playerMoveAngles.z); if(m_bSwimming) { // if( moveDeltaAngle<120 && moveDeltaAngle>-80 ) if( Ffabs(moveDeltaAngle)<120 ) { // forward nForward = 1; forward = true; m_LegAngleDesired = Snap_s180(playerMoveAngles.z); m_LegAngleVel = m_LegAngleVelMoving; } else { // backwards m_LegAngleDesired = Snap_s180(playerMoveAngles.z-180); m_LegAngleVel = m_LegAngleVelMoving; } } else { if( moveDeltaAngle<120 && moveDeltaAngle>-80 ) // if( Ffabs(moveDeltaAngle)<120 ) { // forward nForward = 1; forward = true; m_LegAngleDesired = Snap_s180(playerMoveAngles.z); m_LegAngleVel = m_LegAngleVelMoving; } else { // backwards m_LegAngleDesired = Snap_s180(playerMoveAngles.z-180); m_LegAngleVel = m_LegAngleVelMoving; } } } else { if(m_stats.moving) // if was moving - just stopped stop rotating legs in movement direction m_LegAngleDesired = m_LegAngle; } // [Anton] a piece of code moved to UpdatePhysics if(nMode==0) // do rotation if needed { float aDir = Snap_s180(m_LegAngle - m_LegAngleDesired); if( Ffabs(aDir)>.2 ) { nMode = 6; if( aDir<0 ) nStrafe = 1; // rotate left else nStrafe = 2; // rotate right } } //if(speed>0.0f) //m_pGame->m_pLog->LogToConsole("\001 %.2f %.2f ",speed, m_WalkSpeed); m_nStanding = nStanding; m_nMode = nMode; m_nStrafe = nStrafe; m_nForward = nForward; Vec3 vel = status.vel; if (status.pGroundCollider) vel -= status.velGround; float speed2d = Vec2(vel).len(); if(m_AnimationSystemEnabled ) // if enabled { if( m_stats.moving ) { ScaleAnimationSpeed( speed2d ); } else { m_pEntity->SetAnimationSpeed( 1.0f ); } StartAnimation( ctx ); } return; } ///////////////////////////////////////////////////////////////////////////////////// void CPlayer::ScaleAnimationSpeed( const float speed2d ) { float curAniRefSpeed = -1; //if is jumping/flying use a normal anim speed if (m_stats.flying || m_JumpAniLenght>0) { curAniRefSpeed = -1; } else if(m_stats.bIsLimping ) { curAniRefSpeed = -1; } else if(m_nMode==7) // run sprint - don't scale it now { curAniRefSpeed = -1; } else if(m_nMode==3) // jump { curAniRefSpeed = -1; } else if(m_nMode==2) // run { if( m_nStanding==3 ) // stealth { if( m_nForward ) curAniRefSpeed = m_AniSpeedXRun[0]; else curAniRefSpeed = m_AniSpeedXRun[2]; } else { if( m_nForward ) curAniRefSpeed = m_AniSpeedRun[0]; else curAniRefSpeed = m_AniSpeedRun[2]; } } else if( m_nStanding==0 ) // walking { if( m_CurStance == eRelaxed ) { if( m_nForward ) curAniRefSpeed = m_AniSpeedWalkRelaxed[0]; else curAniRefSpeed = m_AniSpeedWalkRelaxed[2]; } else { if( m_nForward ) curAniRefSpeed = m_AniSpeedWalk[0]; else curAniRefSpeed = m_AniSpeedWalk[2]; } } else if( m_nStanding==3 ) // stealth { if( m_nForward ) curAniRefSpeed = m_AniSpeedXWalk[0]; else curAniRefSpeed = m_AniSpeedXWalk[2]; } else if( m_nStanding==1 ) //crouch { if( m_nForward ) curAniRefSpeed = m_AniSpeedCrouch[0]; else curAniRefSpeed = m_AniSpeedCrouch[2]; } else if( m_nStanding==4 ) // walking relaxed { if( m_nForward ) curAniRefSpeed = m_AniSpeedWalkRelaxed[0]; else curAniRefSpeed = m_AniSpeedWalkRelaxed[2]; } if(curAniRefSpeed>0.0f) m_pEntity->SetAnimationSpeed( speed2d/curAniRefSpeed ); else m_pEntity->SetAnimationSpeed( 1.0f ); } ///////////////////////////////////////////////////////////////////////////////////// void CPlayer::StartAnimation( const SPlayerUpdateContext &ctx ) { if( m_pGame->pa_blend0->GetFVal()<0.0f ) return; if (!IsAlive()) return; FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); char aniName[32]; if( m_stats.onLadder) { strcpy( aniName,"ladder_loop" ); m_JumpStage=0; m_JumpAniLenght=0; //play ladder animation at a speed between 0.001 and 1, depending on the palyer velocity. //we cant use a speed = 0 because it would be impossible to rotate the head bone, fix this in the anim sys? float aspeed = max(0.001f,min(1.0f,fabs(ctx.status.vel.z))); m_pEntity->SetAnimationSpeed( aspeed ); /*if( fabs(ctx.status.vel.z) > .1f ) m_pEntity->SetAnimationSpeed( 1.0f ); else m_pEntity->SetAnimationSpeed( 0.0f );*/ } else if(m_bSwimming) // m_stats.fInWater>1.0f) { // the idle animation should depend on current depth float depth = m_p3DEngine->GetWaterLevel(m_pEntity) - m_p3DEngine->GetTerrainElevation(m_pEntity->GetPos().x, m_pEntity->GetPos().y); ray_hit hit; Vec3 pos = m_pEntity->GetPos(); pos.z = m_p3DEngine->GetWaterLevel(m_pEntity); if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all, rwi_stop_at_pierceable,&hit,1, GetEntity()->GetPhysics())) depth = hit.dist; if( ctx.status.vel.len2()>.3f || depth<1.5f) { if(GetSelectedWeapon()) strcpy( aniName,"swim_weapon" ); else strcpy( aniName,"swim" ); } else { if(GetSelectedWeapon()) strcpy( aniName,"swim_weapon_idle" ); else strcpy( aniName,"swim_idle" ); } m_JumpStage=0; m_JumpAniLenght=0; m_pEntity->SetAnimationSpeed( 1.0f ); } else { GenerateAnimationName( aniName ); if (!m_bIsAI) { if(m_JumpStage==2 && !m_stats.flying) m_JumpStage=0; if(m_JumpAniLenght>0) { m_JumpAniLenght-=m_pTimer->GetFrameTime(); if(m_JumpStage==2) { return; } if(m_JumpAniLenght<=0) { if(m_JumpStage==1) { m_JumpStage=2; } } else if (m_JumpStage==1 || m_JumpStage==3) return; } if( m_stats.flying ) { if(m_JumpStage==0 || m_JumpStage==1 || m_JumpStage==3 ) // if jumping immidiately after prev jump - still in landing state { //strcat( aniName,"_jump_start" ); if (m_nForward /*|| m_stats.fVel<0.1f*/) strcpy( aniName,"jump_forward" ); else strcpy( aniName,"jump_back" ); m_JumpStage=1; } else//flying? dont play anything but the jump start animation. So return. { return; //strcat( aniName,"_jump_air" ); } } if(m_stats.landing) { strcat( aniName,"_jump_land" ); if (m_JumpStage!=3) { m_JumpStage=3; m_JumpAniLenght = 0; } } } // else // m_JumpStage=0; } if(m_sPrevAniName == aniName) return; if(m_stats.landing && !m_bIsAI) { if(m_JumpAniLenght>0) return; m_JumpAniLenght = m_pEntity->GetAnimationLength( aniName ); } else if(m_JumpStage==1) { //we dont care anymore about the length of jumpin animations, because after this we keep the last frame until land. m_JumpAniLenght = 0; //m_JumpAniLenght = m_pEntity->GetAnimationLength( aniName ); } else if(m_JumpAniLenght>0) return; // don't start other anims before jump landing animation is over // float blendTime = m_pGame->pa_blend0->GetFVal(); // GetBlendTime( aniName, blendTime ); float blendTime = m_pGame->pa_blend0->GetFVal(); //use a different bleding for the jump start anim if (m_JumpStage==1) { //when jump start reset current animations, so when the jump anim will finish player will keep its the last frame. m_pEntity->ResetAnimations(0); //blendTime = 0.15f; //set jumpstage directly to 2 (inair) m_JumpStage = 2; } if(m_JumpStage==3 && m_JumpAniLenght<=0) { // m_sPrevAniNameLayer1 = ""; m_JumpStage=0; blendTime = 0.15f; //blendTime = 0.05f; } m_pEntity->StartAnimation(0, aniName, 0, blendTime );//.3f); m_sPrevAniName = aniName; /* if(m_pEntity->StartAnimation(0, aniName, 0, blendTime ))//.3f)) { m_sPrevAniName = aniName; //m_pGame->m_pLog->LogToConsole("\002 %s ",aniName.c_str()); } */ } ///////////////////////////////////////////////////////////////////////////////////// /* -- generates string with appropriate animation name, according to naming convention -- depending on parametrs -- -- m_nForward: 0 = Backward, 1 = Forward -- m_nStrafe: 0 = None, 1 = Left, 2 = Right -- m_nStanding: 0 = Standing, 1 = Crouching, 2 = Prone, 3 = Stealth, -- m_nMode: 0 = Idle, 1 = Walking, 2 = Running, 3 = Jump, 4 = Flying, 5 = Dead, 6 = Turning, */ bool CPlayer::GenerateAnimationName( char *aniName ) { strcpy( aniName,"" ); //aniName = "aidle"; //return; if(m_stats.bIsLimping) strcpy( aniName,"l" ); else switch(m_nStanding) { case 0: // standing combat strcpy( aniName,"a" ); break; case 1: // crouching combat strcpy( aniName,"c" ); break; case 2: // proning combat strcpy( aniName,"p" ); break; case 3: // stealth combat strcpy( aniName,"x" ); break; case 4: // standing relaxed strcpy( aniName,"s" ); break; default: return false; } switch(m_nMode) { case 0: strcat( aniName,"idle" ); return true; case 1: strcat( aniName,"walk" ); break; case 2: strcat( aniName,"run" ); break; case 6: strcat( aniName,"rotate" ); break; case 7: strcat( aniName,"sprint" ); break; default: return true; } if(m_nStrafe) { if(m_nStrafe == 1) strcat( aniName,"left" ); else strcat( aniName,"right" ); } else if(m_nForward) strcat( aniName,"fwd" ); else strcat( aniName,"back" ); return true; } /////////////////////////////////////////////// /*! Let the player die by playing the animation and disabling physics */ /* void CPlayer::Die() { m_pEntity->KillTimer(); // m_deathTime = GetCurrTime(); IEntityCharacter *ichar = m_pEntity->GetCharInterface(); // UpdateCharacterAnimations(); ICryCharInstance *character = ichar->GetCharacter(PLAYER_MODEL_IDX); if (character) { // Hide weapon. character->EnableLastIdleAnimationRestart(0,false); character->EnableLastIdleAnimationRestart(1,false); character->AttachObjectToBone(NULL,NULL); if (m_bIsAI) character->ResetAnimations(); } m_pEntity->EnableAI(false); if (m_pEntity->GetPhysics()->GetType()==PE_ARTICULATED) return; // Disable physics on this player. m_pEntity->EnablePhysics(false); } */ /////////////////////////////////////////////// /*! Respawns the player by enabling physics and calling the script */ void CPlayer::Respawn() { // Exit all areas to turn off water sound. //m_pGame->m_XAreaMgr.ExitAllAreas( this ); m_walkParams.shakeDegree=0.0f; m_walkParams.leanAmount = 0.0f; m_walkParams.leanStart = 0.0f; m_walkParams.leanEnd = 0.0f; m_walkParams.leanFactor = 0.0f; // Call game rules and try to revive this player. CXServerRules *rules = m_pGame->GetRules(); // If no server rules, ignore this function. if (rules) { rules->OnPlayerRespawn( m_pEntity ); } // m_physicsEnabled = true; m_pEntity->EnablePhysics(true); m_pEntity->EnableAI(true); // SetTime2Respawn( -1 ); m_bStayCrouch = false; } /////////////////////////////////////////////// /*! Retrieves if this player is mine @return returns true if it is my player, false otherwise */ bool CPlayer::IsMyPlayer() const { if(!m_pGame->m_pClient) return false; return (m_pEntity->GetId()==m_pGame->m_pClient->GetPlayerId()); } /////////////////////////////////////////////// /*! Retrieves if we are in 1st person view @return returns true if we are in 1st person view, false otherwise */ bool CPlayer::IsFirstPerson() const { return m_bFirstPerson; } /////////////////////////////////////////////// /*! Writes all data which needs to be synchronized to the network-stream @param stream stream to write to @param cs state of the entity per serverslot @return true if the function succeeds, false otherwise */ bool CPlayer::Write( CStream &stream,EntityCloneState *cs) { bool bLocalHostEntity=true; if(cs) bLocalHostEntity=cs->m_bLocalplayer; WRITE_COOKIE(stream); stream.Write(bLocalHostEntity); if(bLocalHostEntity) { assert(m_stats.health>=0); if(m_stats.health>255) { GameWarning(" Player health=%d is bigger than 255 (name %s). Change properties", m_stats.health,m_pEntity->GetName()); m_stats.health = 255; } // assert(m_stats.health<=255); assert(m_stats.armor>=0); assert(m_stats.armor<=255); if(m_stats.numofgrenades<0) { GameWarning(" Player numofgrenades=%d is less than 0 (name %s). Change properties", m_stats.numofgrenades,m_pEntity->GetName()); m_stats.numofgrenades = 0; } if(m_stats.numofgrenades>255) { GameWarning(" Player numofgrenades=%d is bigger than 255 (name %s). Change properties", m_stats.numofgrenades,m_pEntity->GetName()); m_stats.numofgrenades = 255; } assert(m_stats.stamina>=0 && m_stats.stamina<255); stream.WritePkd( (BYTE)m_stats.stamina ); // assert(m_stats.breath>=0 && m_stats.breath<255); // stream.WritePkd( (BYTE)m_stats.breath ); stream.WritePkd( (BYTE)m_stats.health ); stream.WritePkd( (BYTE)m_stats.armor ); if((unsigned int)(m_stats.ammo_in_clip)>1023) { m_pGame->GetSystem()->GetILog()->LogError("Ammo in clip (%d) is more than 1023, Value will not be restored correctly",m_stats.ammo_in_clip); if(m_stats.ammo_in_clip>0) m_stats.ammo_in_clip=1023; } stream.WriteNumberInBits(m_stats.ammo_in_clip,10); if((unsigned int)(m_stats.ammo)>1023) { m_pGame->GetSystem()->GetILog()->LogError("Ammo (%d) is more than 1023, Value will not be restored correctly",m_stats.ammo); if(m_stats.ammo>0) m_stats.ammo=1023; } stream.WriteNumberInBits(m_stats.ammo,10); stream.WritePkd((BYTE)m_stats.numofgrenades); stream.WriteNumberInBits(m_stats.grenadetype, 4); stream.Write(m_stats.holding_breath); for(int i=0;i=0); assert(m_vWeaponSlots[i]<=255); stream.WritePkd((BYTE)m_vWeaponSlots[i]); } } } else { stream.Write((bool)(m_stats.ammo_in_clip!=0)); stream.Write((bool)(m_stats.ammo!=0)); stream.Write( m_stats.firing ); stream.Write(m_walkParams.fCurrLean!=0); if(m_walkParams.fCurrLean!=0) { stream.Write(m_walkParams.fCurrLean>0); //sign stream.WritePkd(((BYTE)(fabs(m_walkParams.fCurrLean)/(1.0f/255)))); } } WRITE_COOKIE(stream); bool bSendFireGrenade = m_stats.firing_grenade; if((m_stats.grenadetype != 1 && m_stats.numofgrenades<=0)) bSendFireGrenade = false; stream.Write(bSendFireGrenade); // [kirill] // this is needed to be able to keep PrevWeapon when doing quicksave/quickload // while on ladder - so when player gets out of ladder after QuickLoad he has // correct weapon //[filippo] //in MP we need to send always the right "m_stats.weapon" value, otherwise the ladders would not works correctly. if( m_stats.onLadder && !m_pGame->IsMultiplayer()) stream.WritePkd((BYTE)m_PrevWeaponID); else stream.WritePkd((BYTE)m_stats.weapon); stream.WritePkd((BYTE)m_stats.firemode); // stream.Write( m_stats.onLadder ); stream.WriteNumberInBits((unsigned int)m_CurStance,3); stream.Write( m_stats.jumping ); // sync player random seed value to the clients { bool bSyncToClients=m_SynchedRandomSeed.GetSynchToClientsS(); stream.Write(bSyncToClients); if(bSyncToClients) stream.Write(m_SynchedRandomSeed.GetStartRandomSeedS()); } stream.Write( m_stats.reloading ); BYTE acc=(BYTE)(m_stats.firing?m_stats.last_accuracy:m_stats.accuracy/(1.f/255.f)); stream.WritePkd(acc); WRITE_COOKIE(stream); if (m_pRedirected && m_pRedirected->GetId()<50000) { stream.Write(true); stream.Write(m_pRedirected->GetId()); } else stream.Write(false); stream.Write(m_bFirstPerson); m_bWriteOccured = true; return true; } /////////////////////////////////////////////// /*! Reads all data which needs to be synchronized from the network-stream @param stream stream to read from @return true if the function succeeds, false otherwise */ bool CPlayer::Read( CStream &stream ) { unsigned short dirty = 0; PlayerStats &stats=m_stats; // GetPlayerStats( stats ); BYTE health,armor,weapon,firemode,staminaBuff; //int weaponid = stats.weaponid; bool bLocalHostEntity; VERIFY_COOKIE(stream); stream.Read(bLocalHostEntity); if(bLocalHostEntity) { stream.ReadPkd( staminaBuff ); m_stats.stamina = staminaBuff; // stream.ReadPkd( staminaBuff ); // m_stats.breath = staminaBuff; stream.ReadPkd( health ); stats.health=health; stream.ReadPkd( armor ); stats.armor=armor; unsigned int ammo_in_clip; stream.ReadNumberInBits(ammo_in_clip,10); stats.ammo_in_clip=ammo_in_clip; unsigned int ammo; stream.ReadNumberInBits(ammo,10); stats.ammo=ammo; BYTE b; stream.ReadPkd(b); stats.numofgrenades=b; stream.ReadNumberInBits(stats.grenadetype, 4); stream.Read(stats.holding_breath); bool isnotzero; for(int i=0;i=PATCH1_SAVEVERSION) // to be backward compatible with old samegames { bool bSyncToAllClients; stream.Read(bSyncToAllClients); if(bSyncToAllClients) { uint8 ucStartRandomSeedCS; stream.Read(ucStartRandomSeedCS); m_SynchedRandomSeed.SetStartRandomSeedC(ucStartRandomSeedCS); } } //TRIGGER REALOAD ON CLIENT ONLY (think if can be done better) bool bReloading; stream.Read(bReloading); if(bReloading && (stats.reloading==false) && m_nSelectedWeaponID != -1) { CWeaponClass *pSelectedWeapon = GetSelectedWeapon(); pSelectedWeapon->ScriptOnStopFiring(m_pEntity); pSelectedWeapon->ScriptReload(m_pEntity); } stats.reloading=bReloading; BYTE acc; stream.ReadPkd(acc); stats.accuracy=acc*(1.f/255.f); VERIFY_COOKIE(stream); if(stats.firing_grenade) { //invoke script event (client) m_bGrenadeAnimation = true; m_pEntity->SendScriptEvent(ScriptEvent_FireGrenade,0,NULL); } bool bRedirected; EntityId nID; stream.Read(bRedirected); if (bRedirected) stream.Read(nID); if ((m_pRedirected!=0)!=bRedirected || m_pRedirected && m_pRedirected->GetId()!=nID) { if (m_pRedirected) { m_pRedirected->OnUnBind(m_pEntity,0); m_pRedirected = 0; } if (bRedirected && (m_pRedirected=m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(nID))) m_pRedirected->OnBind(m_pEntity,0); } if (m_pRedirected && !bLocalHostEntity) m_pRedirected->SetAngles(m_pEntity->GetAngles()); stream.Read(m_bFirstPersonLoaded); return true; } bool CPlayer::Save( CStream &stream) { EntityCloneState cs; cs.m_bSyncYAngle = false; cs.m_bOffSync = false; cs.m_bLocalplayer = true; //[PETAR] Serialize flashlight for singleplayer stream.Write(m_bLightOn); bool bRet=Write(stream,&cs); ASSERT(bRet); return true; } bool CPlayer::Load( CStream &stream) { bool bPrevLightStatus; stream.Read(bPrevLightStatus); if (bPrevLightStatus!=m_bLightOn) SwitchFlashLight(bPrevLightStatus); return Read(stream); } // these two functions save and load the player state which is NOT already saved using read/write bool CPlayer::SaveGame(CStream &stm) { for(PlayerWeaponsItor itor=m_mapPlayerWeapons.begin(); itor!=m_mapPlayerWeapons.end(); ++itor) { WeaponInfo &wi = itor->second; if(wi.owns) { stm.Write(itor->first); ASSERT(itor->first>=0 && itor->first<50); stm.Write(wi.iFireMode); } } stm.Write((int)0); for(int i=0;iScriptOnStopFiring(m_pEntity); _SmartScriptObject pObj(m_pScriptSystem); pObj->SetValue( "firemode", m_stats.firemode); pObj->SetValue( "ignoreammo", true); bool bCanSwitch; m_pEntity->SendScriptEvent(ScriptEvent_FireModeChange, *pObj, &bCanSwitch); wi.iFireMode = m_stats.firemode; } if (m_bIsAI) { IPuppet *pPuppet=0; if (m_pEntity->GetAI()->CanBeConvertedTo(AIOBJECT_PUPPET,(void**)&pPuppet)) { if (pPuppet->GetPuppetParameters().m_bSpecial) m_stats.health = nStartHealth; } } return true; }; void CPlayer::SetScriptObject(IScriptObject *pObject) { m_pScriptObject=pObject; m_pUpdateAnimation=NULL; } /*! Retrieves the ScriptObject of this container @return pointer to the ScriptObject */ IScriptObject *CPlayer::GetScriptObject() { return m_pScriptObject; } int CPlayer::MakeWeaponAvailable(int nWeaponID, bool bAvailable) { //make sure that we don't remove the currently selected weapon if (nWeaponID == m_nSelectedWeaponID && !bAvailable) { DeselectWeapon(); SelectFirstWeapon(); } CWeaponClass* pWC = GetGame()->GetWeaponSystemEx()->GetWeaponClassByID(nWeaponID); if (pWC && !pWC->IsLoaded()) { pWC->Load(); } PlayerWeaponsItor it; if ((it = m_mapPlayerWeapons.find(nWeaponID)) == m_mapPlayerWeapons.end()) { TRACE("ERROR: Trying to make invalid weapon ID %i available", nWeaponID); return -1; } bool bWasOwning=(* it).second.owns; (* it).second.owns = bAvailable; int slot=0; if(bAvailable && (!bWasOwning)) { (* it).second.iFireMode = 0; slot=0; while(slotIsMultiplayer()) { pSelectedWeapon->ScriptOnDeactivate(m_pEntity); } // detach from bone ? if (!pSelectedWeapon->GetBindBone().empty()) m_pEntity->DetachObjectToBone(pSelectedWeapon->GetBindBone().c_str()); SetWeapon(-1); return pSelectedWeapon; } return 0; } /*ridirect the input of a player to another entity @param id the id of the entity that will receive the input @param angleDelta */ void CPlayer::RedirectInputToEntity(EntityId id, int angleDelta) { if (!id) { if (m_pRedirected) m_pRedirected = NULL; if(IsMyPlayer()) m_pGame->m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(Vec3d(0.0f, 0.0f, 0.0f)); } else { IEntity *pEntity = m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(id); if (pEntity) { m_pRedirected = pEntity; //[PETAR] // m_pGame->m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(pEntity->GetAngles()); //[KIRILL] make turret point in right direction /statmounted - ONLY for local player if(IsMyPlayer()) { if(angleDelta>=0) m_pGame->m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(Vec3d(0.0f, 0.0f, (float)angleDelta)); else m_pGame->m_pClient->m_PlayerProcessingCmd.SetDeltaAngles(pEntity->GetAngles(1)); } } } } bool CPlayer::QueryContainerInterface(ContainerInterfaceType desired_interface, void **ppInterface ) { if (desired_interface == CIT_IPLAYER) { *ppInterface = (void *) this; return true; } else { *ppInterface = 0; return false; } } IEntity * CPlayer::GetRedirected() { return m_pRedirected; } /*! set the offset of the camera from the player position. usually is the height of the player */ void CPlayer::SetCameraOffset(const Vec3d& Offset) { m_pEntity->GetCamera()->SetCameraOffset(Offset); } /*! retreive the offset of the camera relative to the player position @param Offset the offset in meters */ void CPlayer::GetCameraOffset(Vec3d& Offset) { m_pEntity->GetCamera()->GetCameraOffset(Offset); } void CPlayer::UpdateDrawAngles( ) { ICryCharInstance *pChar = m_pEntity->GetCharInterface()->GetCharacter(0); if (!pChar) return; float timeScale = 20.1f*m_pTimer->GetFrameTime(); if (timeScale>1.0f) timeScale = 1.0f; Vec3d angles = GetActualAngles(); Vec3d vAngles; //on ladder orient the player so he always look at the center of the ladder. if (m_stats.onLadder) { m_vLadderAngles = m_pEntity->GetPos() - m_vLadderPosition; m_vLadderAngles.z = 0; m_vLadderAngles.Normalize(); m_vLadderAngles.z = -atan2(m_vLadderAngles.x,m_vLadderAngles.y)*(180.0f/gf_PI); m_vLadderAngles.x = 0; m_vLadderAngles.y = 0; vAngles = m_vLadderAngles; } else if (m_pEntity->IsBound()) vAngles = angles; else if (m_CurStance == eProne ) vAngles = m_EnvTangent; else { if(m_LegRotation) vAngles = Vec3d(0, 0, m_LegAngle); else vAngles = Vec3d(0,0,angles.z); } //[kirill] smooth here Character angles (all but Z) // to make it not jurk when proning on some bumpy enviroment vAngles.x = Snap_s180(vAngles.x); vAngles.y = Snap_s180(vAngles.y); m_vCurEntAngle.x = Snap_s180(m_vCurEntAngle.x); m_vCurEntAngle.y = Snap_s180(m_vCurEntAngle.y); Vec3d delta = (vAngles - m_vCurEntAngle); delta.x = Snap_s180(delta.x); delta.y = Snap_s180(delta.y); m_vCurEntAngle += (delta*timeScale); m_vCurEntAngle.z = vAngles.z; m_vCharacterAngles = m_vCurEntAngle; // CheckIfNAN( vAngles ); // m_vCharacterAngles = vAngles; } /*!drawn the player called by the engine when the player has to be drawn */ void CPlayer::OnDraw(const SRendParams & _RendParams) { //return; OnDrawMountedWeapon( _RendParams ); // if nRecursionLevel is not 0 - use only 3tp person view ( for reflections ) int nRecursionLevel = (int)m_pGame->GetSystem()->GetIRenderer()->EF_Query(EFQ_RecurseLevel) - 1; // draw first person weapon if(m_bFirstPerson && !nRecursionLevel && m_stats.drawfpweapon && m_nSelectedWeaponID != -1) { CWeaponClass* pWeapon = GetSelectedWeapon(); ICryCharInstance *pInst = pWeapon->GetCharacter(); if (pInst && pInst->GetFlags()&CS_FLAG_DRAW_MODEL) { SRendParams RendParams = _RendParams; RendParams.vPos = pWeapon->GetPos(); RendParams.vAngles = pWeapon->GetAngles(); if (RendParams.pShadowVolumeLightSource) { if(m_pEntity->GetRndFlags()&ERF_CASTSHADOWVOLUME && !IsMyPlayer()) pInst->RenderShadowVolumes(&RendParams); } else { pInst->Draw(RendParams,m_pEntity->GetPos()); } } return; } ICryCharInstance *pChar = m_pEntity->GetCharInterface()->GetCharacter(0); if (!pChar) return; if (!(pChar->GetFlags() & CS_FLAG_DRAW_MODEL) && !nRecursionLevel) return; SRendParams RendParams = _RendParams; // RendParams.vPos = m_pEntity->GetPos(); // position is not always entity position if(m_pVehicle ) // it' gunner in vehicle { // create the matrix here Matrix44 matParent; matParent.SetIdentity(); matParent=Matrix44::CreateRotationZYX(-gf_DEGTORAD*m_pVehicle->GetEntity()->GetAngles())*matParent; //NOTE: angles in radians and negated CryQuat cxquat = Quat( GetTransposed44(matParent) ); CryQuat rxquat; Vec3d gunnerAngle; // if(m_pMountedWeapon) // it' gunner in vehicle if(m_stats.inVehicleState == PVS_GUNNER) // it' gunner in vehicle gunnerAngle = GetEntity()->GetAngles(1); else // it' driver/passenger gunnerAngle.Set(0,0,180); gunnerAngle.x=0; gunnerAngle.y=0; rxquat.SetRotationXYZ(DEG2RAD(gunnerAngle)); CryQuat result = cxquat*rxquat; Vec3d finalangles = Ang3::GetAnglesXYZ(Matrix33(result)); RendParams.vAngles = RAD2DEG(finalangles); m_pEntity->SetPhysAngles( RendParams.vAngles ); } else // if (!m_pVehicle || m_bIsAI ) { if( IsAlive()) RendParams.vAngles = m_vCharacterAngles; else RendParams.vAngles.Set(0, 0, m_pEntity->GetAngles().z); } /* else { // if the player is bound to the vehicle and in 3rd person mode, // force the character's angles to the correct direction RendParams.vAngles = m_pVehicle->GetEntity()->GetAngles(); // RendParams.vAngles.z+=180; // RendParams.vAngles.x=-RendParams.vAngles.x; } */ if (_RendParams.pShadowVolumeLightSource) { if(m_pEntity->GetRndFlags()&ERF_CASTSHADOWVOLUME) pChar->RenderShadowVolumes(&RendParams, (m_pEntity->GetRndFlags()&ERF_SELFSHADOW) ? 0 : 10); } else pChar->Draw(RendParams,m_pEntity->GetPos()); } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedRunRelaxed(const float fwd, const float side, const float back ) { m_AniSpeedRunRelaxed[0] = fwd; m_AniSpeedRunRelaxed[1] = side; m_AniSpeedRunRelaxed[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedWalkRelaxed(const float fwd, const float side, const float back ) { m_AniSpeedWalkRelaxed[0] = fwd; m_AniSpeedWalkRelaxed[1] = side; m_AniSpeedWalkRelaxed[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedWalk(const float fwd, const float side, const float back ) { m_AniSpeedWalk[0] = fwd; m_AniSpeedWalk[1] = side; m_AniSpeedWalk[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedXRun(const float fwd, const float side, const float back ) { m_AniSpeedXRun[0] = fwd; m_AniSpeedXRun[1] = side; m_AniSpeedXRun[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedRun(const float fwd, const float side, const float back ) { m_AniSpeedRun[0] = fwd; m_AniSpeedRun[1] = side; m_AniSpeedRun[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedXWalk(const float fwd, const float side, const float back ) { m_AniSpeedXWalk[0] = fwd; m_AniSpeedXWalk[1] = side; m_AniSpeedXWalk[2] = back; } /*!sets the animation speed of the player - used to scale animation playback speed depending on actuall movement speed (to awoid skeiting) */ void CPlayer::SetAnimationRefSpeedCrouch(const float fwd, const float side, const float back ) { m_AniSpeedCrouch[0] = fwd; m_AniSpeedCrouch[1] = side; m_AniSpeedCrouch[2] = back; } /*!sets the running speed of the player @param speed the running speed */ void CPlayer::SetRunSpeed(const float speed) { m_RunSpeed = speed; if(!m_bIsAI) // if(IsMyPlayer()) m_pGame->p_speed_run->Set(m_RunSpeed); } /*!sets the walk speed of the player @param speed the walk speed */ void CPlayer::SetWalkSpeed(const float speed) { m_WalkSpeed = speed; if(!m_bIsAI) // if(IsMyPlayer()) m_pGame->p_speed_walk->Set(m_WalkSpeed); } /*!sets the walk speed of the player when is croching @param speed the croching speed */ void CPlayer::SetCrouchSpeed(const float speed) { m_CrouchSpeed = speed; if(!m_bIsAI) // if(IsMyPlayer()) m_pGame->p_speed_crouch->Set(m_CrouchSpeed); } /*!sets the walk speed of the player when is proning @param speed the proning speed */ void CPlayer::SetProneSpeed(const float speed) { m_ProneSpeed = speed; if(!m_bIsAI) // if(IsMyPlayer()) m_pGame->p_speed_prone->Set(m_ProneSpeed); } /*!sets the walk speed of the player when is proning @param speed the proning speed */ void CPlayer::SetSwimSpeed(const float speed) { m_SwimSpeed = speed; } //---------------------------------------------------------------------------------------------------- /*!sets the camera bob @param pitch @param roll @param length */ void CPlayer::SetCameraBob(const float pitch, const float roll, const float length) { m_walkParams.runRoll = roll; m_walkParams.runPitch = pitch; m_walkParams.stepLength = length; if (!m_bIsAI) { m_pGame->p_bob_pitch->Set(m_walkParams.runPitch); m_pGame->p_bob_roll->Set(m_walkParams.runRoll); m_pGame->p_bob_length->Set(m_walkParams.stepLength); } } //---------------------------------------------------------------------------------------------------- // /*!sets the weapon bob */ void CPlayer::SetWeaponBob(const float ampl) { m_walkParams.weaponCycle = ampl; if (!m_bIsAI) m_pGame->p_bob_weapon->Set(m_walkParams.weaponCycle); } //---------------------------------------------------------------------------------------------------- /*!sets the amount of force applied to the player when he jump @param force the amount of force */ void CPlayer::SetJumpForce(const float force) { m_JumpForce = force; if (!m_bIsAI) m_pGame->p_jump_force->Set(m_JumpForce); } //---------------------------------------------------------------------------------------------------- // /*!set the player's lean angle @param lean angle in degrees */ void CPlayer::SetLean(const float lean) { m_LeanDegree = lean; //[KIRILL] this console variable makes lean global - it has to be player's per-instance property // to fix bug in MP with leaning // if (!m_bIsAI) // m_pGame->p_lean->Set(m_LeanDegree); } //---------------------------------------------------------------------------------------------------- /*!sets the player dimension in stealth mode @param pDim struct containing the player dimensions @see pe_player_dimensions */ void CPlayer::SetDimStealth(const pe_player_dimensions* const pDim) { if(!pDim) { m_PlayerDimStealth.heightEye = (m_PlayerDimNormal.heightEye + m_PlayerDimCrouch.heightEye)*.5f; m_PlayerDimStealth.heightCollider = (m_PlayerDimNormal.heightCollider + m_PlayerDimCrouch.heightCollider)*.5f; m_PlayerDimStealth.sizeCollider = (m_PlayerDimNormal.sizeCollider+m_PlayerDimCrouch.sizeCollider)*.5f; return; } m_PlayerDimStealth = *pDim; m_CurStance = eNone; GoStand( ); } //---------------------------------------------------------------------------------------------------- /*!sets the player dimension in normal(standing) mode @param pDim struct containing the player dimensions @see pe_player_dimensions */ void CPlayer::SetDimNormal(const pe_player_dimensions* const pDim) { if(!pDim) { m_PlayerDimNormal.heightEye = 1.7f; m_PlayerDimNormal.heightCollider = 1.1f; m_PlayerDimNormal.sizeCollider.Set(0.4f,0.4f,0.7f); return; } m_PlayerDimNormal = *pDim; SetDimStealth(); m_CurStance = eNone; GoStand( ); // m_PlayerDimSwim = m_PlayerDimNormal; // m_PlayerDimSwim.sizeCollider.z = .55f; // m_PlayerDimSwim.heightEye = .65f; // m_PlayerDimSwim = m_PlayerDimNo; // m_PlayerDimSwim.sizeCollider.z = .55f; // m_PlayerDimSwim.heightEye = .65f; } //---------------------------------------------------------------------------------------------------- /*!sets the player dimension in crouch mode @param pDim struct containing the player dimensions @see pe_player_dimensions */ void CPlayer::SetDimCrouch(const pe_player_dimensions* const pDim) { if(!pDim) { m_PlayerDimCrouch.heightEye = 1.0f; m_PlayerDimCrouch.heightCollider = 0.6f; m_PlayerDimCrouch.sizeCollider.Set(0.4f,0.4f,0.5f); return; } m_PlayerDimCrouch = *pDim; SetDimStealth(); m_CurStance = eNone; GoStand( ); } //---------------------------------------------------------------------------------------------------- /*!sets the player dimension in prone mode @param pDim struct containing the player dimensions @see pe_player_dimensions */ void CPlayer::SetDimProne(const pe_player_dimensions* const pDim) { if(!pDim) { m_PlayerDimProne.heightEye = 0.5f; m_PlayerDimProne.heightCollider = 0.3f; m_PlayerDimProne.sizeCollider.Set(0.4f,0.4f,0.2f); return; } m_PlayerDimProne = *pDim; m_PlayerDimSwim = m_PlayerDimProne; m_PlayerDimSwim.sizeCollider.z = .55f; m_PlayerDimSwim.heightCollider = .75f; m_PlayerDimSwim.heightEye = .70f; m_CurStance = eNone; GoStand( ); } ////////////////////////////////////////////////////////////////////////// int CPlayer::GetBoneHitZone( int boneIdx ) const { return m_pEntity->GetBoneHitZone(boneIdx); } ////////////////////////////////////////////////////////////////////////// Vec3d CPlayer::CalcTangentOnEnviroment( const Vec3d &angle ) { Vec3d forward = angle; Vec3d tangent=forward; if(!m_pEntity->GetPhysics()) return tangent; pe_status_living pStat; m_pEntity->GetPhysics()->GetStatus(&pStat); // Vec3d forward = m_pEntity->GetAngles(); // Vec3d normal = (Vec3d)pStat.groundSlope; Vec3d normal = m_vProneEnvNormal; forward=ConvertToRadAngles(forward); forward = normal.Cross( forward ); forward = forward.Cross( normal ); forward.Normalize(); normal.Normalize(); Vec3d vA[3]; vA[1] = -forward; vA[0] = normal.Cross(forward); vA[2] = normal; matrix3x3RMf &mtrx((matrix3x3RMf&)(*(float*)vA)); mtrx.Transpose(); tangent = Ang3::GetAnglesXYZ(mtrx); Ang3 angles=tangent; angles.Rad2Deg(); tangent=angles; return tangent; } //---------------------------------------------------------------------------------------------------- // //! returns arm damage in percent 100 - (arm_health/max_arm_health)*100 int CPlayer::GetArmDamage(void) const { //TRACE("ARM damage %d", 100 - ((float)m_stats.armHealth/(float)m_stats.maxArmHealth)*100.0f); return 100 - (int)(((float)m_stats.armHealth/(float)m_stats.maxArmHealth)*100.0f); } //! returns leg damage in percent 100 - (leg_health/max_leg_health)*100 int CPlayer::GetLegDamage(void) const { return 100 - (int)((m_stats.legHealth/m_stats.maxLegHealth)*100.0f); } // //!return true if the player is colliding with something bool CPlayer::HasCollided( ) { IPhysicalEntity *pent = m_pEntity->GetPhysics(); if(!pent) return false; if (pent->GetType()==PE_LIVING) { // Create a new status object. The fields are initialized for us pe_status_living status; // Get new player status from physics engine pent->GetStatus(&status); if (!status.bFlying) return true; return false; } else { pe_status_dynamics sd; pent->GetStatus(&sd); float fMassTot=sd.mass, fMassColl=0; int nColls,i,j; coll_history_item item[8]; pe_status_collisions sc; sc.pHistory = item; sc.len = 8; sc.age = 0.5f; nColls = pent->GetStatus(&sc); for(i=0; iGetStatus(&sd); fMassColl += sd.mass; } } return fMassColl>fMassTot*0.3f; } } //----------------------------------------------------------------------------- void CPlayer::StartDie( const Vec3d& hitImpuls, const Vec3d hitPoint, int hitpartid, const int deathType ) { // if(!m_pGame->IsMultiplayer()) { float deathTimer=m_pGame->p_deathtime->GetFVal(); m_fDeathTimer = deathTimer; } else m_pGame->m_DeadPlayers.push_back(this); //*/ if(IsMyPlayer()) { /* // if it was first person view - go to third person if(m_bFirstPerson) m_pGame->SetViewMode(true); Vec3d camPos = m_pEntity->GetCamera()->GetPos(); float fWaterLevel=m_p3DEngine->GetWaterLevel(&camPos); if( fWaterLevel>m_pEntity->GetCamera()->GetPos().z ) { camPos.z = fWaterLevel + 1; m_pEntity->GetCamera()->SetPos( camPos ); } */ InitCameraTransition( PCM_CASUAL ); IAISystem *pAISystem =m_pGame->GetSystem()->GetAISystem(); if (pAISystem) { if (pAISystem->GetAutoBalanceInterface()) pAISystem->GetAutoBalanceInterface()->RegisterPlayerDeath(); } } m_sPrevAniName.clear(); m_sPrevAniNameLayer1.clear(); m_sPrevAniNameLayer2.clear(); m_pEntity->ResetAnimations( 0 ); SwitchFlashLight( false ); // turn off flashlight IPhysicalEntity *pent = m_pEntity->GetPhysics(); IEntityCharacter *ichar = m_pEntity->GetCharInterface(); ICryCharInstance *character = ichar->GetCharacter(PLAYER_MODEL_IDX); // Hide weapon. DeselectWeapon(); if (character) { hitpartid = character->TranslatePartIdToDeadBody(hitpartid); } if (pent) { if (pent->GetType()==PE_LIVING) { pe_action_move am; am.dir = hitImpuls/100.0f; am.iJump = 2; pent->Action(&am); } else if (hitpartid>=0) { pe_action_impulse ai; ai.partid = hitpartid; if (hitPoint.len2()>0) ai.point = hitPoint; ai.impulse = hitImpuls; pent->Action(&ai); } } // m_deathTime = GetCurrTime(); // UpdateCharacterAnimations(); m_pEntity->EnableAI(false); m_pEntity->KillTimer(); /* // m_deathTime = GetCurrTime(); IEntityCharacter *ichar = m_pEntity->GetCharInterface(); // UpdateCharacterAnimations(); ICryCharInstance *character = ichar->GetCharacter(PLAYER_MODEL_IDX); if (character) { // Hide weapon. character->EnableLastIdleAnimationRestart(0,false); character->EnableLastIdleAnimationRestart(1,false); character->AttachObjectToBone(NULL,NULL); if (m_bIsAI) character->ResetAnimations(); } if (m_pEntity->GetPhysics()->GetType()==PE_ARTICULATED) return; // Disable physics on this player. m_pEntity->EnablePhysics(false); */ } /*! sets the player dynamics parameters @param pDyn a structure containing the dynamics parameters air_control gravity swimming_gravity inertia swimming_inertia */ void CPlayer::SetDynamics(const PlayerDynamics *pDyn) { if (pDyn->air_control!=1E10f) m_Dynamics.air_control = pDyn->air_control; if (pDyn->gravity!=1E10f) m_Dynamics.gravity = pDyn->gravity; if (pDyn->swimming_gravity!=1E10f) m_Dynamics.swimming_gravity = pDyn->swimming_gravity; if (pDyn->inertia!=1E10f) m_Dynamics.inertia = pDyn->inertia; if (pDyn->swimming_inertia!=1E10f) m_Dynamics.swimming_inertia = pDyn->swimming_inertia; if (pDyn->jump_gravity!=1E10f) m_Dynamics.jump_gravity = pDyn->jump_gravity; } void CPlayer::RemoveAllWeapons() { // Takes away the owns flag of all weapons in the player's weapon map PlayerWeaponsItor it; for (it=m_mapPlayerWeapons.begin(); it!=m_mapPlayerWeapons.end(); it++) (* it).second.owns = false; } // //----------------------------------------------------------------------------------------------- // returns true if can stand in given position bool CPlayer::CanStand( const Vec3& pos) { bool result=true; IPhysicalEntity* phys = m_pEntity->GetPhysics(); pe_player_dynamics pd; phys->GetParams(&pd); bool bActive = pd.bActive; m_pEntity->ActivatePhysics( true ); Vec3 curPos=m_pEntity->GetPos(); pe_params_pos ppos; ppos.pos = pos; phys->SetParams(&ppos); if(!phys->SetParams( &m_PlayerDimCrouch )) result=false; if(result && !phys->SetParams( &m_PlayerDimNormal )) result=false; // restore active/position m_pEntity->ActivatePhysics( bActive ); ppos.pos = curPos; phys->SetParams(&ppos); phys->SetParams( &m_PlayerDimNormal ); // m_CurStance = eNone; // GoStand( ); return result; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise return false bool CPlayer::GoStand(bool ignoreSpam) { //prevent prone-standing position spamming if (!ignoreSpam && m_fLastProneTime > m_pTimer->GetCurrTime()) return false; IPhysicalEntity* phys = m_pEntity->GetPhysics(); if ( phys && m_CurStance != eStand ) //m_currDimensions!=eDimNormal) { // Use normal physics dimensions. if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( Vec3(0,0,0) ); // reset angle if proning if(phys->SetParams( &m_PlayerDimNormal )) { //InitCameraTransition( PCM_CASUAL ,true ); m_AngleLimitBase.Set(0,0,0); if(m_pEntity->GetAI()) { m_pEntity->GetAI()->SetEyeHeight(m_PlayerDimNormal.heightEye); } m_currDimensions = eDimNormal; m_PrevStance = m_CurStance; m_CurStance = eStand; m_stats.aim = false; m_stats.crouch = false; m_stats.prone = false; // [kirill] need this to use only z component of entity angles (same as for drawing) // when calculating BBox GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_ZROTATE ); #if defined(LINUX64) m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, 0); #else m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, NULL); #endif m_bStayCrouch = false; m_fLastProneTime = m_pTimer->GetCurrTime() + 0.5f; return true; } // could not change - restore angle if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( m_EnvTangent ); return false; } else // was standing already m_PrevStance = eStand; m_bStayCrouch = false; return true; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise return false bool CPlayer::GoStealth( ) { IPhysicalEntity* phys = m_pEntity->GetPhysics(); if ( phys && m_CurStance != eStealth ) //m_currDimensions!=eDimStealth) { if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( Vec3(0,0,0) ); // reset angle if proning // Use stealth physics dimensions. if(phys->SetParams( &m_PlayerDimStealth )) //if can stealth here { //InitCameraTransition( PCM_CASUAL ); m_AngleLimitBase.Set(0,0,0); if(m_pEntity->GetAI()) { m_pEntity->GetAI()->SetEyeHeight(m_PlayerDimStealth.heightEye); } m_currDimensions = eDimStealth; m_PrevStance = m_CurStance; m_CurStance = eStealth; m_stats.aim = false; m_stats.crouch = false; m_stats.prone = false; // [kirill] need this to use only z component of entity angles (same as for drawing) // when calculating BBox GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_ZROTATE ); m_Running = false; #if defined(LINUX64) m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, 0); #else m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, NULL); #endif return true; } // could not change - restore angle if proning if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( m_EnvTangent ); return false; } return true; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise return false bool CPlayer::GoCrouch( ) { // don't go to crouch mode if using mounted weapon if(m_pMountedWeapon) return false; // don't change stance in water if( m_bSwimming ) return false; IPhysicalEntity* phys = m_pEntity->GetPhysics(); if ( phys && m_CurStance != eCrouch ) //m_currDimensions!=eDimCrouch) { if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( Vec3(0,0,0) ); // reset angle if proning // Use crouching physics dimensions. if(phys->SetParams( &m_PlayerDimCrouch )) { //InitCameraTransition( PCM_CASUAL, true ); m_AngleLimitBase.Set(0,0,0); if(m_pEntity->GetAI()) { m_pEntity->GetAI()->SetEyeHeight(m_PlayerDimCrouch.heightEye); } m_currDimensions = eDimCrouch; m_PrevStance = m_CurStance; m_CurStance = eCrouch; m_stats.aim = false; m_stats.crouch = true; m_stats.prone = false; // [kirill] need this to use only z component of entity angles (same as for drawing) // when calculating BBox GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_ZROTATE ); m_Running = false; #if defined(LINUX64) m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, 0); #else m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, NULL); #endif return true; } // could not change - restore angle if proning if( m_CurStance == eProne ) m_pEntity->SetPhysAngles( m_EnvTangent ); return false; } return true; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise returns false bool CPlayer::GoProne( ) { IPhysicalEntity* phys = m_pEntity->GetPhysics(); if(!CanProne(false)) return false; if ( phys && m_CurStance != eProne) //!=eDimProne) { // Use proning physics dimensions. // if can't change dimentions at current position - put player little up // and try again if(!phys->SetParams( &m_PlayerDimProne )) { Vec3d pos = m_pEntity->GetPos(); pos.z += .5f; m_pEntity->SetPos( pos ); if(!phys->SetParams( &m_PlayerDimProne )) { // restore position pos.z -= .5f; m_pEntity->SetPos( pos ); return false; } } // Vec3d ang = m_pEntity->GetAngles(); m_EnvTangent = CalcTangentOnEnviroment( m_pEntity->GetAngles() ); m_pEntity->SetPhysAngles( m_EnvTangent ); // set angels for phisics (body is flat on surfece tangent space) //InitCameraTransition( PCM_CASUAL, true ); if(m_pEntity->GetAI()) { m_pEntity->GetAI()->SetEyeHeight(m_PlayerDimProne.heightEye); } m_currDimensions = eDimProne; m_PrevStance = m_CurStance; m_CurStance = eProne; m_stats.aim = false; m_stats.crouch = false; m_stats.prone = true; // [kirill] when in prone - use all the rotations (same as for drawing) // when calculating BBox GetEntity()->ClearFlags( ETY_FLAG_CALCBBOX_ZROTATE ); m_Running = false; #if defined(LINUX64) m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, 0); #else m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, NULL); #endif } m_bStayCrouch = false; m_fLastProneTime = m_pTimer->GetCurrTime() + 0.5f; return true; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise returns false bool CPlayer::GoSwim( ) { IPhysicalEntity* phys = m_pEntity->GetPhysics(); if ( phys && m_CurStance != eSwim) //!=eDimProne) { // // Vec3d ang = m_pEntity->GetAngles(); // m_EnvTangent = CalcTangentOnEnviroment( m_pEntity->GetAngles() ); // m_pEntity->SetPhysAngles( m_EnvTangent ); // set angels for phisics (body is flat on surfece tangent space) // Use proning physics dimensions for swimming. if(phys->SetParams( &m_PlayerDimSwim )) { InitCameraTransition( PCM_CASUAL, true ); m_AngleLimitBase.Set(0,0,0); if(m_pEntity->GetAI()) { m_pEntity->GetAI()->SetEyeHeight(m_PlayerDimProne.heightEye); } m_currDimensions = eDimProne; m_PrevStance = m_CurStance; m_CurStance = eSwim; // m_stats.aim = false; // m_stats.crouch = false; // m_stats.prone = true; m_stats.aim = false; m_stats.crouch = false; m_stats.prone = false; m_stats.prone = false; // [kirill] need this to use only z component of entity angles (same as for drawing) // when calculating BBox GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_ZROTATE ); m_Running = false; #if defined(LINUX64) m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, 0); #else m_pEntity->SendScriptEvent(ScriptEvent_StanceChange, NULL); #endif m_bStayCrouch = false; return true; } return false; } m_bStayCrouch = false; return true; } // //----------------------------------------------------------------------------------------------- // returns true if slope angle is ok to be in prone (not too steep) AND not falling/jumping bool CPlayer::CanProne(bool ignoreSpam) { float slopeProneLimit=.7f; // don't go to prone mode if using mounted weapon if(m_pMountedWeapon) return false; //prevent prone-standing position spamming if (!ignoreSpam && m_fLastProneTime > m_pTimer->GetCurrTime()) return false; // don't go to prone mode if falling/jumping if(m_FlyTime>.5f && m_CurStance!=eSwim) return false; if(!m_pEntity->GetPhysics()) return false; pe_status_living pStat; m_pEntity->GetPhysics()->GetStatus(&pStat); m_vProneEnvNormal = (Vec3d)pStat.groundSlope; ray_hit hit; Vec3d pos=m_pEntity->GetPos(); int hitCount=0; float fHitCount=1.0f; if( m_vProneEnvNormal.z>slopeProneLimit ) ++hitCount; pos.z += .3f; pos.x += .1f; if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all-ent_independent, rwi_stop_at_pierceable | geom_colltype_player<GetPhysics())) { m_vProneEnvNormal += hit.n; ++fHitCount; if( hit.n.z>slopeProneLimit ) ++hitCount; } pos.x -= .2f; if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all-ent_independent, rwi_stop_at_pierceable | geom_colltype_player<GetPhysics())) { m_vProneEnvNormal += hit.n; ++fHitCount; if( hit.n.z>slopeProneLimit ) ++hitCount; } pos.x += .1f; pos.y += .1f; if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all-ent_independent, rwi_stop_at_pierceable | geom_colltype_player<GetPhysics())) { m_vProneEnvNormal += hit.n; ++fHitCount; if( hit.n.z>slopeProneLimit ) ++hitCount; } pos.y -= .2f; if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all-ent_independent, rwi_stop_at_pierceable | geom_colltype_player<GetPhysics())) { m_vProneEnvNormal += hit.n; ++fHitCount; if( hit.n.z>slopeProneLimit ) ++hitCount; } // there are no points with allowed slope around - can't prone here if( hitCount<1 ) return false; m_vProneEnvNormal = m_vProneEnvNormal/fHitCount; // can't prone when standing on boat/car IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); if (physEnt) { pe_status_living status; if (physEnt->GetStatus(&status)) { if(status.pGroundCollider) { IEntity* pEnt=(IEntity*)status.pGroundCollider->GetForeignData(); if(pEnt && pEnt->GetContainer()) { CVehicle* pVehicleBelow; if(pEnt->GetContainer()->QueryContainerInterface(CIT_IVEHICLE,(void**)&pVehicleBelow)) { return false; } } } } } return true; } // //----------------------------------------------------------------------------------------------- // returns true if switch to stance or already in stance, otherwise return false bool CPlayer::GoRelaxed( ) { if(!GoStand()) { return false; } m_PrevStance = m_CurStance; m_CurStance = eRelaxed; return true; } // //----------------------------------------------------------------------------------------------- bool CPlayer::RestorePrevStence() { switch( m_PrevStance ) { case eStand: case eSwim: return GoStand(); case eStealth: return GoStealth(); case eCrouch: return GoCrouch(); case eProne: return GoProne(); } return false; } // //----------------------------------------------------------------------------------------------- // for debug/test purposes - ////////////////////////////////////////////////////////////////////////// void CPlayer::StartFire() { Vec3d pos = m_pEntity->GetPos(); } ////////////////////////////////////////////////////////////////////////// /*void CPlayer::PlaySound(ISound * pSound, float fSoundScale, Vec3d &Offset) { if(IsMyPlayer()) { m_lstAttachedSounds.push_back(SAttachedSound(pSound, Offset)); pSound->SetPosition(CalcSoundPos()); } else { pSound->SetPosition(m_pEntity->GetPos()); } pSound->Play(fSoundScale); }*/ Vec3d CPlayer::CalcSoundPos() { IEntityCamera *pEC; if (IsMyPlayer() && (pEC=m_pEntity->GetCamera())) { CCamera &cam=pEC->GetCamera(); Vec3d vPos=cam.GetPos(); vPos+=cam.m_vOffset; Vec3d vAngles=cam.GetAngles(); Vec3d vTrans=vPos-cam.GetPos(); Matrix44 vRot; vRot.SetIdentity(); //rotate the matrix ingnoring the Y rotation //vRot.RotateMatrix(Vec3d(vAngles.x,0,vAngles.z)); vRot=Matrix44::CreateRotationZYX(-Vec3d(vAngles.x,0,vAngles.z)*gf_DEGTORAD)*vRot; //NOTE: angles in radians and negated vPos=vRot.TransformPointOLD(vTrans); //translate back vPos+=cam.GetPos(); return vPos; } return m_pEntity->GetPos(); } // calculates vertical and horizontal speed to make player jump on dist and height void CPlayer::CalcJumpSpeed( float dist, float height, float &horV, float &vertV ) { assert(height>=0.0f); // vz = srtq(2*g*h) // vx = len*g/(2*vz) float fGravity; //we are using a different gravity for jump? use it to calculate jump speeds. if (m_Dynamics.jump_gravity!=1E10) fGravity = m_Dynamics.jump_gravity; else fGravity = m_Dynamics.gravity; fGravity *= m_pGame->p_gravity_modifier->GetFVal(); vertV = cry_sqrtf( 2.0f*fGravity*height ); // horV = dist*m_Dynamics.gravity/( 2.0f*vertV ); if(height!=0.0f) horV=(dist*cry_sqrtf(fGravity)) / (2*cry_sqrtf(2*height)); else horV=0.0f; } void CPlayer::SetViewMode(bool bThirdPerson) { if(!IsMyPlayer()) return; if( !bThirdPerson ) { m_pEntity->DrawCharacter(0, 0); m_pEntity->NeedsUpdateCharacter(0, true); } else { if (m_stats.bModelHidden) m_pEntity->DrawCharacter(0, 0); else m_pEntity->DrawCharacter(0, 1); } // if( !bThirdPerson && IsMyPlayer() ) // { // m_pEntity->DrawCharacter(0, 0); // m_pEntity->NeedsUpdateCharacter(0, true); // } } /* // calcualtes aproximate point of lending for granade on terrain // firePos - start point // dir - normolized direction // vel - velocity // timeStep - time interval for iteration // timeLimit - time to stop iterations if no collision with terrain is detected Vec3d CPlayer::TraceGrenade( const Vec3d& firePos, const Vec3d& dir, const float vel, const float timeStep, const float timeLimit) { Vec3d curPos; float time = 0.0f; curPos = firePos; while( curPos.z > m_p3DEngine->GetTerrainElevation(curPos.x, curPos.y) && timeGetTerrainElevation(curPos.x, curPos.y); return curPos; } */ void CPlayer::SwitchFiremode(int nforce) { if (m_nSelectedWeaponID != -1) { WeaponParams temp; bool bChanged = true, bCanSwitch; int iNewFireMode; CWeaponClass* pSelectedWeapon = GetSelectedWeapon(); WeaponInfo &wi = GetWeaponInfo(); if(nforce==-1) { // Check what the next firemode would be, bChanged will be false in case // we can't switch to another mode iNewFireMode = pSelectedWeapon->GetNextFireMode(wi.iFireMode, m_bIsAI); } else { iNewFireMode=nforce; } bChanged = (wi.iFireMode != iNewFireMode); if (bChanged && (!m_stats.reloading) && (!m_stats.weapon_busy)) { // Call the script to find out if we can switch _SmartScriptObject pObj(m_pScriptSystem); pObj->SetValue( "firemode",iNewFireMode); pSelectedWeapon->ScriptOnStopFiring(m_pEntity); m_stats.firemode=iNewFireMode; m_pEntity->SendScriptEvent(ScriptEvent_FireModeChange, *pObj, &bCanSwitch); // Apply the change to the C++ state wi.iFireMode = iNewFireMode; } } } void CPlayer::CounterAdd( const string name, const float timeScale ) { CountersMap::iterator counter = m_UpdatedCounters.find( name ); if( counter == m_UpdatedCounters.end() ) { couterEntry newEntry; newEntry.scale = timeScale; m_UpdatedCounters[name] = newEntry; } else { (counter->second).scale = timeScale; } } void CPlayer::CounterIncrement( const string name, const float value ) { CountersMap::iterator counter = m_UpdatedCounters.find( name ); if( counter == m_UpdatedCounters.end() ) return; (counter->second).value += value; } float CPlayer::CounterGetValue( const string name ) { CountersMap::iterator counter = m_UpdatedCounters.find( name ); if( counter == m_UpdatedCounters.end() ) return 0.0f; return (counter->second).value; } void CPlayer::CounterUpdateAll( const float dt ) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); CountersMap::iterator counter = m_UpdatedCounters.begin( ); for(; counter!=m_UpdatedCounters.end(); counter++ ) { float prevValue = (counter->second).value; (counter->second).value += (counter->second).scale*dt; if( prevValue<(counter->second).eventTrhld && (counter->second).value>(counter->second).eventTrhld ) { IAIObject *pObject = m_pEntity->GetAI(); if (pObject) { pObject->SetSignal(1, (counter->second).eventToCall.c_str()); } } if((counter->second).value < 0.0f) (counter->second).value = 0.0f; } } void CPlayer::CounterSetValue( const string name, const float value ) { CountersMap::iterator counter = m_UpdatedCounters.find( name ); if( counter == m_UpdatedCounters.end() ) return; (counter->second).value = value; } void CPlayer::CounterSetEvent( const string name, const float thrhld, const string eventName ) { CountersMap::iterator counter = m_UpdatedCounters.find( name ); if( counter == m_UpdatedCounters.end() ) return; (counter->second).eventToCall = eventName; (counter->second).eventTrhld = thrhld; } // attaches weapon to weapon bone on character's back void CPlayer::HolsterWeapon(void) { SetWeaponPositionState(WEAPON_POS_HOLSTER); } // rebinds weapon to be bound to the weapon bone in the characters hands void CPlayer::HoldWeapon(void) { SetWeaponPositionState(WEAPON_POS_HOLD); } void CPlayer::SetWeaponPositionState(EWeaponPositionState weaponPositionState) { assert (IsHeapValid()); // attach to bone ? if (m_nSelectedWeaponID != -1 && m_weaponPositionState != weaponPositionState) { CWeaponClass* pSelectedWeapon = GetSelectedWeapon(); m_weaponPositionState = weaponPositionState; ICryCharInstance *character = m_pEntity->GetCharInterface()->GetCharacter(PLAYER_MODEL_IDX); WeaponInfo &wi = GetWeaponInfo(); assert (IsHeapValid()); if (character) { wi.DetachBindingHandles(character); if (weaponPositionState == WEAPON_POS_UNDEFINED) return; // ValidateHeap(); string sBone = pSelectedWeapon->GetBindBone(); // ValidateHeap(); if (!sBone.empty()) { // ValidateHeap(); if (weaponPositionState == WEAPON_POS_HOLSTER) sBone+="02"; // ValidateHeap(); wi.hBindInfo = character->AttachObjectToBone( pSelectedWeapon->GetObject(), sBone.c_str() ); // ValidateHeap(); // if there is an auxilary weapon bone, attach to that as well string sAuxBone = "aux_" + sBone; if (character->GetModel()->GetBoneByName(sAuxBone.c_str())>=0) { wi.hAuxBindInfo = character->AttachObjectToBone( pSelectedWeapon->GetObject(), sAuxBone.c_str() ); } } else { character->DetachAll(); } } } } // // void CPlayer::SetBlendTime(const char *sAniName, float fBlendTime) { if(strlen(sAniName)<3) // it has to be some name return; m_AniBlendTimes[sAniName] = fBlendTime; } void CPlayer::GetBlendTime(const char *sAniName, float&fBlendTime) { BlendTimesMap::iterator curAni=m_AniBlendTimes.find(sAniName); if( curAni == m_AniBlendTimes.end() ) return; fBlendTime = (curAni->second); } void CPlayer::UpdateCollisionDamage( ) { // Get a pointer to the physics engine IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); if (!physEnt) return; FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); // Create a new status object. pe_status_collisions status; coll_history_item history[4]; memset( history, 0, sizeof(history) ); status.pHistory = history; status.len = 4; status.bClearHistory = true; status.age = .1f; int colN=0,imax; float massLimitMin=50.0f; float massLimitMax=80.0f; float velLimit=3;//10;//6.5f; if ((m_timeDmgCollision+=m_pTimer->GetFrameTime())>2.0f) { m_timeDmgCollision = 0; m_pPrevDmgCollider = 0; } // Get new player status from physics engine if (colN=physEnt->GetStatus(&status)) { float damageValue = 0.0f; //find the most damaging collision in history for(imax=-1 ; colN>0; colN--) { float curVel = max(0.0f,history[colN-1].v[1]*history[colN-1].n); if(history[colN-1].mass[1]<=massLimitMin || curVelGetPhysicalEntityById(history[colN-1].idCollider); if (pCollider==m_pPrevDmgCollider && curDamageGetSystem()->GetILog()->Log( "\002 %d collision %s %.2f %.2f ", colN, GetName(), history[0].collMass, history[0].vrel.len() ); if(damageValue>=1.0f) { _SmartScriptObject pTable(m_pScriptSystem,false); CScriptObjectVector oDir(m_pScriptSystem); oDir=history[imax].n; pTable->SetValue("dir",*oDir); pTable->SetValue("damage",damageValue); pTable->SetValue("collider_mass",history[imax].mass[1]); pTable->SetValue("collider_velocity",-history[imax].v[1]*history[imax].n); IPhysicalEntity *pCollider = m_pPhysicalWorld->GetPhysicalEntityById(history[imax].idCollider); if (pCollider) { IEntity *pEntCollider = (IEntity*)pCollider->GetForeignData(); if (pEntCollider) pTable->SetValue("collider",pEntCollider->GetScriptObject()); } m_pEntity->SendScriptEvent( ScriptEvent_PhysCollision , pTable); //m_pGame->GetSystem()->GetILog()->Log( "\002 collision %s %.1f %d %.1f ", GetName(), damageValue, m_stats.health, maxVel); m_pPrevDmgCollider = pCollider; m_prevCollDamage = damageValue; m_timeDmgCollision = 0; } } } void CPlayer::SetSpeedMult( float run, float crouch, float prone, float xrun, float xwalk, float rrun, float rwalk ) { m_RunSpeedScale = run; m_CrouchSpeedScale = crouch; m_ProneSpeedScale = prone; m_XRunSpeedScale = xrun; // stealth m_XWalkSpeedScale = xwalk; m_RRunSpeedScale = rrun; // relaxed m_RWalkSpeedScale = rwalk; } // //----------------------------------------------------------------- void CPlayer::OnDrawMountedWeapon( const SRendParams & RendParams ) { if(m_nSelectedWeaponID == -1) return; // if the localplayer is using the mounted weapon, only then do we disable the first person weapon drawing if( m_pMountedWeapon && IsMyPlayer()) { m_pMountedWeapon->DrawObject(0,ETY_DRAW_NORMAL); m_pMountedWeapon->DrawCharacter(0,ETY_DRAW_NORMAL); Vec3d apos, aang; GetFirePosAngles( apos, aang ); apos = m_pMountedWeapon->GetAngles(1); // if(IsMyPlayer()) m_pMountedWeapon->SetAngles( aang, false, false, true ); // else // m_pMountedWeapon->SetAngles( m_pEntity->GetAngles(), false, false, true ); m_pMountedWeapon->ForceCharacterUpdate(0); m_pMountedWeapon->DrawEntity(RendParams); m_pMountedWeapon->SetAngles( apos ); m_pMountedWeapon->DrawObject(0,ETY_DRAW_NONE); // we don't want to draw it (already drawn by player) m_pMountedWeapon->DrawCharacter(0,ETY_DRAW_NONE); m_pMountedWeapon->NeedsUpdateCharacter( 0, true); // but we want to update animation for it GetEntity()->DrawCharacter(1, ETY_DRAW_NONE); } return; } void CPlayer::UpdateCharacterAnimationsMounted( SPlayerUpdateContext &ctx ) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); m_pEntity->SetAnimationSpeed( 1.0f ); if( m_pVehicle ) // mounted weapon on vehicle - don't do any animation return; float diff = m_pEntity->GetAngles().z - m_vPrevMntPos.z; float vel = diff/m_pTimer->GetFrameTime(); //if(diff!=0) //m_pGame->m_pLog->Log(" %.2f %.2f %.2f\n", diff, vel, m_pEntity->GetAngles().z ); string aniName; /* if(fabsf(vel)>50.6f) // too slow - no animations { m_RunningTime += m_pTimer->GetFrameTime(); } else { m_RunningTime = 0; } */ // if( m_RunningTime<.1f ) if( fabsf(vel)<60.6f ) aniName = "mount_fwd"; else { if( diff<0 ) aniName = "mount_left";//"arotateright"; else // aniName = "srotateleft"; aniName = "mount_right";//"arotateleft"; // float speedScale = 1 + (fabsf(vel)-60)*.5f; // m_pEntity->SetAnimationSpeed( speedScale ); } m_RunningTime += m_pTimer->GetFrameTime(); if(m_sPrevAniName != aniName) { if(aniName == "mount_fwd") { if( m_MntWeaponStandTime>.3f ) { if(m_pEntity->StartAnimation(0, aniName.c_str(), 0, .25f )) m_sPrevAniName = aniName; // m_RunningTime = 0; } m_MntWeaponStandTime += m_pTimer->GetFrameTime(); } else { if(m_pEntity->StartAnimation(0, aniName.c_str(), 0, .25f )) m_sPrevAniName = aniName; m_MntWeaponStandTime = 0.0f; } } m_vPrevMntPos = m_pEntity->GetAngles(); return; /* //m_pEntity->StartAnimation(0, "awalkback", 0, .1 ); //return; Vec3d diff = m_pEntity->GetPos() - m_vPrevMntPos; //Ang3 playerDir = GetActualAngles(); Vec3d playerDir = m_pEntity->GetAngles(); playerDir = ConvertToRadAngles(playerDir); playerDir.z=0; playerDir.normalize(); diff.z=0; float dist = diff.len(); float vel = dist/m_pTimer->GetFrameTime(); diff.normalize(); float dotz = playerDir.x*diff.x + playerDir.y*diff.y; float crossz = playerDir.x * diff.y - playerDir.y * diff.x; string aniName; if(vel<.6f) // too slow - no animations { m_NotRunTime += m_pTimer->GetFrameTime(); } else { m_NotRunTime = 0; } if( m_NotRunTime>.2f )//|| fabsf(crossz)<.3 ) aniName = "sidle"; else if( crossz<0 ) aniName = "awalkfwd";//"arotateright"; else aniName = "srotateleft"; // aniName = "swalkback";//"arotateleft"; if(m_sPrevAniNameLayer1 != aniName) if(m_pEntity->StartAnimation(0, aniName.c_str(), 0, .1 )) m_sPrevAniNameLayer1 = aniName; m_vPrevMntPos = m_pEntity->GetPos(); return; */ } void CPlayer::GiveBinoculars(bool val) { if (m_stats.has_binoculars == val) return; m_stats.has_binoculars = val; // FIXME ... if the binoculars are take away, then we should reset the viewlayer (must be done // on the script side } void CPlayer::PreloadInstanceResources(Vec3d vPrevPortalPos, float fPrevPortalDistance, float fTime) { int nRecursionLevel = (int)m_pGame->GetSystem()->GetIRenderer()->EF_Query(EFQ_RecurseLevel) - 1; if(m_bFirstPerson && !nRecursionLevel && m_stats.drawfpweapon && m_nSelectedWeaponID != -1) return; ICryCharInstance *pChar = m_pEntity->GetCharInterface()->GetCharacter(0); if (!pChar) return; if (!(pChar->GetFlags() & CS_FLAG_DRAW_MODEL) && !nRecursionLevel) return; float fDist = fPrevPortalDistance + vPrevPortalPos.GetDistance(m_pEntity->GetPos()); pChar->PreloadResources(fDist, 1.f, 0); } void CPlayer::SetStaminaTable( const StaminaTable& stTable ) { m_StaminaTable = stTable; } void CPlayer::UpdateStamina( float dTime ) { // stamina (for sprint run) update // update breath (blue bar) if(m_stats.underwater>0.0f) //we are under water - decrease breath level { if(m_stats.stamina>0) // if there is some stamina - { if((m_stats.stamina-=dTime*m_StaminaTable.BreathDecoyUnderwater)<0) m_stats.stamina = 0; } } else if( m_stats.holding_breath ) // we are sniping/holding breath - decrease breath level { if((m_stats.stamina-=dTime*m_StaminaTable.BreathDecoyAim)<1.0f) m_stats.stamina = 1.0f; } // update stamina (yellow bar) else if( m_Sprinting ) { if((m_stats.stamina -= dTime*m_StaminaTable.DecoyRun)<1.0f) m_stats.stamina = 1.0f; } // restore stamina - player is resting else if(m_stats.stamina<100 && (!m_stats.flying || m_bSwimming&&m_stats.underwater<=0.0f )) { if(m_Running) m_stats.stamina += dTime*m_StaminaTable.RestoreRun; else m_stats.stamina += dTime*m_StaminaTable.RestoreWalk; if(m_stats.stamina>100) m_stats.stamina = 100; } // to indicate stamina on HUD m_StaminaTable.StaminaHUD = m_stats.stamina*0.01f; } ////////////////////////////////////////////////////////////////////////// bool CPlayer::IsVisible() const { if(m_pGame->IsServer() && m_pGame->IsMultiplayer()) return true; if (m_pEntity) { // If was rendered recently. int nCurrRenderFrame = (int)m_pGame->GetSystem()->GetIRenderer()->GetFrameID(); if (abs(nCurrRenderFrame-m_pEntity->GetDrawFrame(0)) < 3) return true; } return false; } ////////////////////////////////////////////////////////////////////////// void CPlayer::UpdateSwimState(bool bAlive) { FUNCTION_PROFILER( GetISystem(),PROFILE_GAME ); // check if the player goes underwater. //if (m_p3DEngine->GetWaterLevel(&m_vEyePos)>m_vEyePos.z) float fWaterLevel=m_p3DEngine->GetWaterLevel(m_pEntity); m_stats.fInWater=fWaterLevel-m_pEntity->GetPos().z; if (m_stats.fInWater<0.0f) m_stats.fInWater=0.0f; Vec3 vPos=m_vEyePos; if (!bAlive && !IsMyPlayer()) { SetEyePosBone(); vPos=m_vEyePos; //vPos=m_pEntity->GetPos(); } if (fWaterLevel>vPos.z) { // if that is the case, start the EAX underwater effect // plus increase the underwater time m_stats.underwater+=m_pTimer->GetFrameTime(); if (!m_bIsAI) if(m_pGame->GetSystem()->GetISoundSystem()) m_pGame->GetSystem()->GetISoundSystem()->SetEaxListenerEnvironment(EAX_PRESET_UNDERWATER); // also call the script function to play the water splash sounds and // other stuff it this is the first time it goes underwater if (!m_bEyesInWater) { m_pEntity->SendScriptEvent(ScriptEvent_EnterWater,0); m_bEyesInWater=true; } } else { // remove EAX underwater, if he was in the water before if ((m_stats.underwater>0.001f) && (!m_bIsAI)) // epsilon if(m_pGame->GetSystem()->GetISoundSystem()) m_pGame->GetSystem()->GetISoundSystem()->SetEaxListenerEnvironment(EAX_PRESET_OFF); m_stats.underwater=0.0f; m_bEyesInWater=false; } m_stats.fKWater = 0; // no need for anymore checks if dead - only eyesInWater updated if(!IsAlive()) { m_bSwimming = false; return; } // ladder is priority to swim if(m_stats.onLadder) { m_bSwimming = false; return; } // in vehicle is priority to swim if(m_pVehicle) { m_bSwimming = false; return; } // not in water if(fWaterLevel < -500) { m_bSwimming = false; return; } // if(m_stats.fInWater<) /* //[kirill] //fixme //remove this //the check is for cathching strange positon outorrange problem //if( m_pGame->pa_blend2->GetFVal()>0.0f ) { Vec3d pos = m_pEntity->GetPos(); if(pos.z<-100 || pos.z>500 || pos.x<=0 || pos.x>10000 || pos.y<=0 || pos.y>10000 ) { if(IsAlive()) m_pGame->m_pLog->Log("\006 alive player < %s > position is invalid %.2f %.2f %.2f \n", m_pEntity->GetName(), pos.x, pos.y, pos.z ); else m_pGame->m_pLog->Log("\006 dead player < %s > position is invalid %.2f %.2f %.2f \n", m_pEntity->GetName(), pos.x, pos.y, pos.z ); } } //remove this over */ // to use value depending on current stance float depthOffset;// = GetEntity()->GetCamera()->GetCamOffset().z; //AIs don't have camera offset - use fixed value (could use eyeHeight) if( m_bIsAI ) depthOffset = 1.5f; else { switch( m_CurStance ) { case eProne: case eSwim: depthOffset = .45f; break; // case eStand: default: depthOffset = 1.5f; break; } } //check if we are standing on something pe_status_living status; IPhysicalEntity *physEnt = m_pEntity->GetPhysics(); if (!physEnt || !physEnt->GetStatus(&status)) { GameWarning( "Bad Phyics Entity for Player %s",m_pEntity->GetName() ); return; } if (status.pGroundCollider) { if( status.groundHeight+depthOffset > fWaterLevel ) { m_bSwimming = false; return; } } // if(m_pEntity->GetEntityVisArea()==0) // if outdoors - check how deep we are { Vec3 rPos = m_pEntity->GetPos(); bool bChanged = false; if(_isnan(rPos.x)) { bChanged = true; rPos.x = 0; } if(_isnan(rPos.y)) { bChanged = true; rPos.y = 0; } if(_isnan(rPos.z)) { bChanged = true; rPos.z = 0; } if(bChanged) m_pEntity->SetPos(rPos); float fTerrainLevel=m_p3DEngine->GetTerrainElevation(m_pEntity->GetPos().x, m_pEntity->GetPos().y); if(fWaterLevel - fTerrainLevel < depthOffset )//1.5f) { m_bSwimming = false; return; } } // else if(fWaterLevel - m_pEntity->GetPos().z < depthOffset )//1.5f) // check if it's deep enough to sweem { vectorf pos = m_pEntity->GetPos(); pos.z = fWaterLevel; ray_hit hit; if (m_pGame->GetSystem()->GetIPhysicalWorld()->RayWorldIntersection( pos, vectorf(0,0,-1.5f), ent_all, rwi_stop_at_pierceable,&hit,1, GetEntity()->GetPhysics())) { m_bSwimming = false; return; } } m_stats.fKWater = min(1.0f,m_stats.fInWater*(1.0f/.60f)); m_bSwimming = m_stats.fKWater>0.0f; m_stats.fKWater*=m_stats.fKWater; } ////////////////////////////////////////////////////////////////////////// void CPlayer::DampInputVector(vectorf &vec ,float speed ,float stopspeed ,bool only2d ,bool linear) { if (speed<=0.0f) return; float spd = speed; float saveZ = vec.z; if (only2d) vec.z = 0; float goallen = vec.len(); if (goallen<0.001f) spd = stopspeed; vectorf delta = vec - m_vLastMotionDir; if (linear)//if linear cap the delta to "ai_smoothvel" { float deltalen = delta.len(); if (deltalen>spd) delta = delta * (deltalen==0?0.001f:1.0f/deltalen) * spd; } float dt = m_fLastDeltaTime;//m_pTimer->GetFrameTime();//m_pGame->GetSystem()->GetITimer()->GetFrameTime(); vec = m_vLastMotionDir + delta*min(dt*spd,1.0f); if (vec.len()<0.001f) vec.Set(0,0,0); m_vLastMotionDir = vec; if (only2d) vec.z = saveZ; } void CPlayer::SaveAIState(CStream & stm, CScriptObjectStream & scriptStream) { IScriptSystem *pScriptSystem = m_pGame->GetSystem()->GetIScriptSystem(); HSCRIPTFUNCTION saveOverallFunction=NULL; if( m_pEntity->GetScriptObject() && m_pEntity->GetScriptObject()->GetValue("OnSaveOverall", saveOverallFunction) ) { pScriptSystem->BeginCall(saveOverallFunction); pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject()); pScriptSystem->PushFuncParam(scriptStream.GetScriptObject()); pScriptSystem->EndCall(); } if (!m_bIsAI) return; IAIObject *pObject = m_pEntity->GetAI(); pObject->Save(stm); IPipeUser *pUser =0; // save weapon state int nGunOut = 0; m_pEntity->GetScriptObject()->GetValue("AI_GunOut",nGunOut); stm.Write(nGunOut); ICryCharInstance *pCharacter = m_pEntity->GetCharInterface()->GetCharacter(0); bool bAnimWritten = false; if (pCharacter) { int nAnimID = pCharacter->GetCurrentAnimation(3); if (nAnimID>=0) { ICryCharModel *pModel = pCharacter->GetModel(); if (pModel) { const char *szAnimName = pModel->GetAnimationSet()->GetName(nAnimID); stm.Write(szAnimName); bAnimWritten = true; } } } if (!bAnimWritten) stm.Write("NA"); // save any conversation that this guy may be in _SmartScriptObject pCurrentConversation(m_pScriptSystem,true); if (m_pEntity->GetScriptObject()->GetValue("CurrentConversation",pCurrentConversation)) { const char *szName; pCurrentConversation->GetValue("NAME",szName); stm.Write(szName); int iProgress,conv_id; pCurrentConversation->GetValue("CONV_ID",conv_id); stm.Write(conv_id); pCurrentConversation->GetValue("Progress",iProgress); stm.Write(iProgress); // save all the actors in the conversation _SmartScriptObject pActors(m_pScriptSystem,true); pCurrentConversation->GetValue("Actor",pActors); int i=1; stm.Write(pActors->Count()); while (i<=pActors->Count()) { _SmartScriptObject pActorEntity(m_pScriptSystem,true); if (pActors->GetAt(i,pActorEntity)) { int Actor_ID; pActorEntity->GetValue("id",Actor_ID); stm.Write(Actor_ID); } i++; } } else stm.Write("NA"); } void CPlayer::LoadAIState(CStream & stm, CScriptObjectStream & scriptStream) { IScriptSystem *pScriptSystem = m_pGame->GetSystem()->GetIScriptSystem(); HSCRIPTFUNCTION loadOverallFunction=NULL; if( m_pEntity->GetScriptObject() && m_pEntity->GetScriptObject()->GetValue("OnLoadOverall", loadOverallFunction) ) { pScriptSystem->BeginCall(loadOverallFunction); pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject()); pScriptSystem->PushFuncParam(scriptStream.GetScriptObject()); pScriptSystem->EndCall(); } if (!m_bIsAI) return; IAIObject *pObject = m_pEntity->GetAI(); pObject->Load(stm); int nGunOut=0; stm.Read(nGunOut); if (nGunOut) { SetWeaponPositionState(WEAPON_POS_HOLD); m_pEntity->GetScriptObject()->SetValue("AI_GunOut",1); m_pScriptSystem->BeginCall("BasicAI","MakeAlerted"); m_pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject()); m_pScriptSystem->EndCall(); } char str[255]; stm.Read(str,255); if (stricmp(str,"NA")) { ICryCharInstance *pCharacter = m_pEntity->GetCharInterface()->GetCharacter(0); if (pCharacter) { CryCharAnimationParams ccap; ccap.fBlendInTime = 0; ccap.fBlendOutTime = 0; ccap.nLayerID = 3; pCharacter->StartAnimation(str,ccap); } } stm.Read(str,255); if (stricmp(str,"NA")) { _SmartScriptObject pCurrentConversation(m_pScriptSystem,true); if (!m_pEntity->GetScriptObject()->GetValue("CurrentConversation",pCurrentConversation)) { // get the saved conversation int conv_id; stm.Read(conv_id); _SmartScriptObject pNewConversation(m_pScriptSystem,true); _SmartScriptObject pConvManager(m_pScriptSystem,true); m_pScriptSystem->GetGlobalValue("AI_ConvManager",pConvManager); m_pScriptSystem->BeginCall("AI_ConvManager","GetSpecificConversation"); m_pScriptSystem->PushFuncParam(pConvManager); m_pScriptSystem->PushFuncParam(str); m_pScriptSystem->PushFuncParam(conv_id); m_pScriptSystem->EndCall(pNewConversation); // remember how far we were stm.Read(conv_id); // go back one line if (conv_id>0) conv_id--; pNewConversation->SetValue("Progress",conv_id); // now loop through all the actors and make them join this conversation int nrActors,curr_actor=0; stm.Read(nrActors); while (curr_actorGetSystem()->GetIEntitySystem()->GetEntity(ActorID); if (pActorEntity) { IScriptObject *pActorScriptObject = pActorEntity->GetScriptObject(); pActorScriptObject->SetValue("CurrentConversation",pNewConversation); unsigned int funcHandle; pNewConversation->GetValue("Join",funcHandle); m_pScriptSystem->BeginCall(funcHandle); m_pScriptSystem->PushFuncParam(pNewConversation); m_pScriptSystem->PushFuncParam(pActorEntity->GetScriptObject()); m_pScriptSystem->EndCall(); } curr_actor++; } // if joined is equal to amount of participants, continue the conversation int iJoined,iParticipants; pNewConversation->GetValue("Joined",iJoined); pNewConversation->GetValue("Participants",iParticipants); if (iJoined == iParticipants) { unsigned int funcHandle; pNewConversation->GetValue("Continue",funcHandle); m_pScriptSystem->BeginCall(funcHandle); m_pScriptSystem->PushFuncParam(pNewConversation); m_pScriptSystem->EndCall(); } } else { int dummy; stm.Read(dummy); //conv_id stm.Read(dummy); //progress int nrActors; stm.Read(nrActors); while (nrActors--) stm.Read(dummy); } } } void CPlayer::LoadAIState_RELEASE(CStream & stm) { if (!m_bIsAI) return; IAIObject *pObject = m_pEntity->GetAI(); //AIObject Load is empty in release version // pObject->Load(stm); int nGunOut=0; stm.Read(nGunOut); if (nGunOut) { SetWeaponPositionState(WEAPON_POS_HOLD); m_pEntity->GetScriptObject()->SetValue("AI_GunOut",1); m_pScriptSystem->BeginCall("BasicAI","MakeAlerted"); m_pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject()); m_pScriptSystem->EndCall(); } /* if (!m_bIsAI) return; IAIObject *pObject = m_pEntity->GetAI(); //pObject->Load(stm); IPipeUser *pUser =0; if (pObject->CanBeConvertedTo(AIOBJECT_PIPEUSER,(void**)&pUser)) { int nId; stm.Read(nId); if (nId) { if (nId<0) { // player is the target IEntity *pMyPlayer = m_pGame->GetMyPlayer(); if (pMyPlayer) { SAIEVENT event; event.bFuzzySight = false; event.fThreat = 1000000.f; event.pSeen = pMyPlayer->GetAI(); pObject->Event(AIEVENT_ONVISUALSTIMULUS,&event); } } else { // some puppet or vehicle is the target IEntity *pMyTarget = m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(nId); if (pMyTarget) { SAIEVENT event; event.bFuzzySight = false; event.fThreat = 1000000.f; event.pSeen = pMyTarget->GetAI(); pObject->Event(AIEVENT_ONVISUALSTIMULUS,&event); } } SetWeaponPositionState(WEAPON_POS_HOLD); m_pEntity->GetScriptObject()->SetValue("AI_GunOut",1); } } */ } void CPlayer::LoadAIState_PATCH_1(CStream & stm) { if (!m_bIsAI) return; IAIObject *pObject = m_pEntity->GetAI(); pObject->Load_PATCH_1(stm); int nGunOut=0; stm.Read(nGunOut); if (nGunOut) { SetWeaponPositionState(WEAPON_POS_HOLD); m_pEntity->GetScriptObject()->SetValue("AI_GunOut",1); m_pScriptSystem->BeginCall("BasicAI","MakeAlerted"); m_pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject()); m_pScriptSystem->EndCall(); } } void CPlayer::OnEntityNetworkUpdate( const EntityId &idViewerEntity, const Vec3d &v3dViewer, uint32 &inoutPriority, EntityCloneState &inoutCloneState) const { inoutPriority+=100000; if(m_stats.moving) inoutPriority+=10000; if(m_stats.firing) inoutPriority+=20000; // if(m_stats.reloading) // inoutPriority-=10000; inoutCloneState.m_bSyncYAngle=false; } // // //------------------------------------------------------------------------------------- bool CPlayer::LoadGame_PATCH_1(CStream &stm) { RemoveAllWeapons(); // [Petar] preserve the original health in case we need to use it // to restore the health of special AI on a checkpoint int nStartHealth = m_stats.health; if (!m_bIsAI) DeselectWeapon(); for(int cl = 0; (stm.Read(cl),cl); ) { WeaponInfo &wi = m_mapPlayerWeapons[cl]; wi.owns = true; stm.Read(wi.iFireMode); } for(int i=0;iScriptOnStopFiring(m_pEntity); _SmartScriptObject pObj(m_pScriptSystem); pObj->SetValue( "firemode", m_stats.firemode); pObj->SetValue( "ignoreammo", true); bool bCanSwitch; m_pEntity->SendScriptEvent(ScriptEvent_FireModeChange, *pObj, &bCanSwitch); wi.iFireMode = m_stats.firemode; } if (m_bIsAI) { IPuppet *pPuppet=0; if (m_pEntity->GetAI()->CanBeConvertedTo(AIOBJECT_PUPPET,(void**)&pPuppet)) { if (pPuppet->GetPuppetParameters().m_bSpecial) m_stats.health = nStartHealth; } } return true; }; bool CPlayer::Load_PATCH_1( CStream &stream) { // bool bPrevLightStatus; // stream.Read(bPrevLightStatus); // if (bPrevLightStatus!=m_bLightOn) // SwitchFlashLight(bPrevLightStatus); return Read_PATCH_1(stream); } /////////////////////////////////////////////// /*! Reads all data which needs to be synchronized from the network-stream @param stream stream to read from @return true if the function succeeds, false otherwise */ bool CPlayer::Read_PATCH_1( CStream &stream ) { unsigned short dirty = 0; PlayerStats &stats=m_stats; // GetPlayerStats( stats ); BYTE health,armor,weapon,firemode,staminaBuff; //int weaponid = stats.weaponid; bool bHostEntity; VERIFY_COOKIE(stream); stream.Read(bHostEntity); if(bHostEntity) { stream.ReadPkd( staminaBuff ); m_stats.stamina = staminaBuff; // stream.ReadPkd( staminaBuff ); // m_stats.breath = staminaBuff; stream.ReadPkd( health ); stats.health=health; stream.ReadPkd( armor ); stats.armor=armor; unsigned int ammo_in_clip; stream.ReadNumberInBits(ammo_in_clip,10); stats.ammo_in_clip=ammo_in_clip; unsigned int ammo; stream.ReadNumberInBits(ammo,10); stats.ammo=ammo; BYTE b; stream.ReadPkd(b); stats.numofgrenades=b; stream.ReadNumberInBits(stats.grenadetype, 4); stream.Read(stats.holding_breath); bool isnotzero; for(int i=0;i=PATCH1_SAVEVERSION) // to be backward compatible with old samegames { bool bSyncToAllClients; stream.Read(bSyncToAllClients); if(bSyncToAllClients) { uint8 ucStartRandomSeedCS; stream.Read(ucStartRandomSeedCS); // if(!bHostEntity) m_SynchedRandomSeed.SetStartRandomSeedC(ucStartRandomSeedCS); // GetISystem()->GetILog()->Log(">> Player Read %d %d",(int)(this->GetEntity()->GetId()),(int)ucStartRandomSeedCS); // debug } } //TRIGGER REALOAD ON CLIENT ONLY (think if can be done better) bool bReloading; stream.Read(bReloading); if(bReloading && (stats.reloading==false) && m_nSelectedWeaponID != -1) { CWeaponClass *pSelectedWeapon = GetSelectedWeapon(); pSelectedWeapon->ScriptOnStopFiring(m_pEntity); pSelectedWeapon->ScriptReload(m_pEntity); } stats.reloading=bReloading; BYTE acc; stream.ReadPkd(acc); stats.accuracy=acc*(1.f/255.f); VERIFY_COOKIE(stream); if(stats.firing_grenade) { //invoke script event (client) m_bGrenadeAnimation = true; m_pEntity->SendScriptEvent(ScriptEvent_FireGrenade,0,NULL); } bool bRedirected; EntityId nID; stream.Read(bRedirected); if (bRedirected) stream.Read(nID); if ((m_pRedirected!=0)!=bRedirected || m_pRedirected && m_pRedirected->GetId()!=nID) { if (m_pRedirected) { m_pRedirected->OnUnBind(m_pEntity,0); m_pRedirected = 0; } if (bRedirected && (m_pRedirected=m_pGame->GetSystem()->GetIEntitySystem()->GetEntity(nID))) m_pRedirected->OnBind(m_pEntity,0); } if (m_pRedirected && !bHostEntity) m_pRedirected->SetAngles(m_pEntity->GetAngles()); stream.Read(m_bFirstPersonLoaded); return true; }