예제 #1
0
	virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{
		switch (event)
		{
		case eFE_Activate:
			const int nDrawPlayer = GetPortInt(pActInfo, EIP_DrawPlayer);
			if (IsPortActive(pActInfo, EIP_Link))
			{
				IEntity* pEntity = gEnv->pEntitySystem->GetEntity(GetPortEntityId(pActInfo, EIP_Target));
				if (pEntity)
				{
					CActor *pPlayerActor = static_cast<CActor*>(gEnv->pGame->GetIGameFramework()->GetClientActor());
					if (pPlayerActor)
					{
						SActorStats* pActorStats = pPlayerActor->GetActorStats();
						if (pActorStats)
						{
							if (nDrawPlayer == -1)
								pActorStats->isHidden = true;
							else if (nDrawPlayer == 1)
								pActorStats->isHidden = false;
						}
						m_Position = pEntity->GetWorldPos();
						m_Rotation = pEntity->GetWorldRotation();

						pPlayerActor->LinkToEntity(pEntity->GetId());
						ActivateOutput(pActInfo, EOP_Linked, true);
					}
				}
			}
			if (IsPortActive(pActInfo, EIP_Unlink))
			{
				CActor *pPlayerActor = static_cast<CActor*>(gEnv->pGame->GetIGameFramework()->GetClientActor());
				if (pPlayerActor)
				{
					SActorStats* pActorStats = pPlayerActor->GetActorStats();
					if (pActorStats)
					{
						if (nDrawPlayer == -1)
							pActorStats->isHidden = true;
						else if (nDrawPlayer == 1)
							pActorStats->isHidden = false;
					}

					bool keepTransform = GetPortBool(pActInfo, EIP_KeepTransform);
					pPlayerActor->LinkToEntity(0, keepTransform);

					if(!keepTransform)
					{
						pPlayerActor->GetEntity()->SetPos(m_Position);
						pPlayerActor->GetEntity()->SetRotation(m_Rotation);
					}

					ActivateOutput(pActInfo, EOP_Unlinked, true);
				}
			}
			break;
		}
	}
예제 #2
0
파일: ItemView.cpp 프로젝트: AiYong/CryGame
//------------------------------------------------------------------------
void CItem::UpdateFPPosition(float frameTime)
{
	CActor* pActor = GetOwnerActor();
	if (!pActor)
		return;

	SPlayerStats *pStats = static_cast<SPlayerStats *>(pActor->GetActorStats());
	if (!pStats)
		return;

	Matrix34 tm = Matrix33::CreateRotationXYZ(pStats->FPWeaponAngles);

	Vec3 offset(0.0f,0.0f,0.0f);

	float right(g_pGameCVars->i_offset_right);
	float front(g_pGameCVars->i_offset_front);
	float up(g_pGameCVars->i_offset_up);

	if (front!=0.0f || up!=0.0f || right!=0.0f)
	{
		offset += tm.GetColumn(0).GetNormalized() * right;
		offset += tm.GetColumn(1).GetNormalized() * front;
		offset += tm.GetColumn(2).GetNormalized() * up;
	}

	tm.SetTranslation(pStats->FPWeaponPos + offset);
	GetEntity()->SetWorldTM(tm);

	//CryLogAlways("weaponpos: %.3f,%.3f,%.3f // weaponrot: %.3f,%.3f,%.3f", tm.GetTranslation().x,tm.GetTranslation().y,tm.GetTranslation().z, pStats->FPWeaponAngles.x, pStats->FPWeaponAngles.y, pStats->FPWeaponAngles.z);
}
//------------------------------------------------------------------------
float CGameRulesCommonDamageHandling::GetAIPlayerAgainstColliderEnergyScale(const IEntity& collider) const
{
	const float energyScaleAgainstNonActor = 10.0f; 
	const float energyScaleAgainstRagdoll = 50.0f;

	CActor* pColliderActor = static_cast<CActor*>(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(collider.GetId()));

	if (pColliderActor)
	{
		uint8 physicsAspectProfile = pColliderActor->GetGameObject()->GetAspectProfile(eEA_Physics) ;
		if ((physicsAspectProfile == eAP_Ragdoll) || (physicsAspectProfile == eAP_Sleep))
		{
			return energyScaleAgainstRagdoll;
		}

		if (pColliderActor->GetActorClass() == CPlayer::GetActorClassType())
		{
			SPlayerStats* pColliderStats = static_cast<SPlayerStats*>(pColliderActor->GetActorStats());
			if (pColliderStats != NULL && pColliderStats->followCharacterHead)
			{
				return 0.0f;
			}
		}
	}

	return energyScaleAgainstNonActor;
}
예제 #4
0
//-----------------------------------------------------------------------
void CSpectacularKill::End(bool bKillerDied/* = false*/)
{
	CRY_ASSERT_MESSAGE(IsBusy(), "spectacular kill cannot be stopped if it is not in progress");
	if (!IsBusy())
		return;

	ICooperativeAnimationManager* pCooperativeAnimationManager = gEnv->pGame->GetIGameFramework()->GetICooperativeAnimationManager();

	CActor* pTarget = GetTarget();
	CRY_ASSERT(pTarget);
	if(pTarget)
	{
		pCooperativeAnimationManager->StopCooperativeAnimationOnActor(m_pOwner->GetAnimatedCharacter(), pTarget->GetAnimatedCharacter());

		// Enable AI again (for what it's worth - this helps editor)
		if (!pTarget->IsPlayer() && pTarget->GetEntity()->GetAI())
			pTarget->GetEntity()->GetAI()->Event(AIEVENT_ENABLE, 0);

		if (bKillerDied && (m_deathBlowState == eDBS_None) && static_cast<CPlayer*> (pTarget)->CanFall())
		{
			// Enable Fall n Play on target if killer dies before death blowing it
			pTarget->Fall();
		}
		else if (m_deathBlowState != eDBS_Done)
		{
			DeathBlow(*pTarget); // Call this in case the notification from the animation system got skipped or missed for some reason
		}

		SActorStats* pTargetStats = pTarget->GetActorStats();
		pTargetStats->spectacularKillPartner = 0;
	}
	else
	{
		pCooperativeAnimationManager->StopCooperativeAnimationOnActor(m_pOwner->GetAnimatedCharacter());
	}

	// Enable AI again (for what it's worth - this helps editor)
	if (m_pOwner && m_pOwner->GetEntity()->GetAI())
		m_pOwner->GetEntity()->GetAI()->Event(AIEVENT_ENABLE, 0);

	ClearState();

  assert(m_pOwner);
	SActorStats* pStats = m_pOwner->GetActorStats();
	if(pStats)
		pStats->spectacularKillPartner = 0;
}
예제 #5
0
/**
 * Turn the grabbed AnimatedCharacter on/off if necessary.  If the grabbed thing
 * has an AC, it's necessary to turn it off during the grab to prevent it from
 * interfering and messing with its Entity's transformation.
 */
void CBaseGrabHandler::DisableGrabbedAnimatedCharacter (bool enable) const
{
	CActor *pGrabbedActor = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_grabStats.grabId);
	if ( ! pGrabbedActor) return;

	IAnimatedCharacter * pGrabbedAC = pGrabbedActor->GetAnimatedCharacter();
	if (pGrabbedAC)
		pGrabbedAC->SetNoMovementOverride (enable);

//	SActorStats *stats = static_cast<SActorStats*>(pGrabbedActor->GetActorStats());
//	stats->isGrabbed = enable;

	if(pGrabbedActor->IsClient())
	{
		if(SActorStats *stats = static_cast<SActorStats*>(pGrabbedActor->GetActorStats()))
			stats->isGrabbed = enable;
	}
}
//------------------------------------------------------------------------
float CGameRulesCommonDamageHandling::GetPlayerAgainstColliderEnergyScale(const IEntity& collider) const
{
	CActor* pColliderActor = static_cast<CActor*>(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(collider.GetId()));

	if (pColliderActor)
	{
		if (pColliderActor->GetActorClass() == CPlayer::GetActorClassType())
		{
			SPlayerStats* pColliderStats = static_cast<SPlayerStats*>(pColliderActor->GetActorStats());
			if (pColliderStats != NULL && pColliderStats->followCharacterHead)
			{
				return 0.0f;
			}
		}
	}

	return 1.0f;
}
예제 #7
0
//----------------------------------------------------------------------
void CSingleTG::UpdateFPView(float frameTime)
{
	CSingle::UpdateFPView(frameTime);

	//Weapon can not be fired if there's not a target locked
	if(!m_bLocked)
	{
		m_pWeapon->LowerWeapon(true);

		if(!m_bLocking)
		{
			CActor *pPlayer = m_pWeapon->GetOwnerActor();
			SPlayerStats *stats = pPlayer?static_cast<SPlayerStats *>(pPlayer->GetActorStats()):NULL;

			if(stats && !gEnv->bMultiplayer)
				stats->bLookingAtFriendlyAI = true;
		}
	}
}
예제 #8
0
파일: Plant.cpp 프로젝트: kitnet/project-o
bool CPlant::PlayerStanceOK() const
{
	bool ok = true;
	if(m_plantparams.need_to_crouch && m_pWeapon->GetOwnerActor())
	{
		CActor* pActor = (CActor*)m_pWeapon->GetOwnerActor();
		ok = (pActor->GetStance() == STANCE_CROUCH);
		if(ok)
		{
			ok &= (pActor->GetActorStats()->velocity.GetLengthSquared() < 0.1f);

			if(ok)
			{
				// also fire a ray forwards to check the placement position is nearby
				Vec3 pos;
				Vec3 dir(FORWARD_DIRECTION);
				Vec3 vel(0,0,0);
				ok &= GetPlantingParameters(pos, dir, vel);
			}
		}
	}
	
	return ok;
}
//------------------------------------------------------------------------
void CMelee::Impulse(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, int partId, int ipart, int surfaceIdx, float impulseScale)
{
	if(m_noImpulse)
	{
		m_noImpulse = false;
		return;
	}

	if (pCollider && m_pShared->meleeparams.impulse > 0.001f)
	{
		bool strengthMode = false;
		CPlayer *pPlayer = (CPlayer *)m_pWeapon->GetOwnerActor();
		pe_status_dynamics dyn;

		if (!pCollider->GetStatus(&dyn))
		{
			if(strengthMode)
			{
				impulseScale *= 3.0f;
			}
		}
		else
		{
			impulseScale *= clamp((dyn.mass * 0.01f), 1.0f, 15.0f);
		}

		//[kirill] add impulse to phys proxy - to make sure it's applied to cylinder as well (not only skeleton) - so that entity gets pushed
		// if no pEntity - do it old way
		IEntity *pEntity = (IEntity *) pCollider->GetForeignData(PHYS_FOREIGN_ID_ENTITY);

		if(gEnv->bMultiplayer && pEntity)
		{
			if(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId()) == NULL)
			{
				impulseScale *= 0.33f;
			}
		}

		if(pEntity)
		{
			bool crapDollFilter = false;
#ifdef CRAPDOLLS
			static IEntityClass *pDeadBodyClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass("DeadBody");

			if (pEntity->GetClass() == pDeadBodyClass)
			{
				crapDollFilter = true;
			}

#endif //CRAPDOLLS

			if (!crapDollFilter)
			{
				IEntityPhysicalProxy *pPhysicsProxy = (IEntityPhysicalProxy *)pEntity->GetProxy(ENTITY_PROXY_PHYSICS);
				CActor *pActor = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId());

				if (pActor)
				{
					SActorStats *pAS = pActor->GetActorStats();

					if (pAS && pAS->isRagDoll)
					{
						//marcok: talk to me before touching this
						impulseScale = 1.0f; //jan: melee impulses were scaled down, I made sure it still "barely moves"
#ifdef CRAPDOLLS
						crapDollFilter = true;
#endif //CRAPDOLLS
					}
				}

				// scale impulse up a bit for player
				if (!crapDollFilter)
				{
					pPhysicsProxy->AddImpulse(partId, pt, dir * m_pShared->meleeparams.impulse * impulseScale * m_meleeScale, true, 1.f);
				}
			}
		}
		else
		{
			pe_action_impulse ai;
			ai.partid = partId;
			ai.ipart = ipart;
			ai.point = pt;
			ai.iApplyTime = 0;
			ai.impulse = dir * (m_pShared->meleeparams.impulse * impulseScale * m_meleeScale);
			pCollider->Action(&ai);
		}

		ISurfaceTypeManager *pSurfaceTypeManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();
		int invId = pSurfaceTypeManager->GetSurfaceTypeByName("mat_invulnerable")->GetId();
		// create a physical collision to break trees
		pe_action_register_coll_event collision;
		collision.collMass = 0.005f; // this is actually ignored
		collision.partid[1] = partId;
		// collisions involving partId<-1 are to be ignored by game's damage calculations
		// usually created articially to make stuff break.
		collision.partid[0] = -2;
		collision.idmat[1] = surfaceIdx;
		collision.idmat[0] = invId;
		collision.n = normal;
		collision.pt = pt;
		// scar bullet
		// m = 0.0125
		// v = 800
		// energy: 4000
		// in this case the mass of the active collider is a player part
		// so we must solve for v given the same energy as a scar bullet
		Vec3	v = dir;
		float speed = cry_sqrtf(4000.0f / (80.0f * 0.5f)); // 80.0f is the mass of the player
		// [marco] Check if an object. Should take lots of time to break stuff if not in nanosuit strength mode;
		// and still creates a very low impulse for stuff that might depend on receiving an impulse.
		IRenderNode *pBrush = (IRenderNode *)pCollider->GetForeignData(PHYS_FOREIGN_ID_STATIC);
		collision.vSelf = (v.normalized() * speed * m_meleeScale);
		collision.v = Vec3(0, 0, 0);
		collision.pCollider = pCollider;
		IEntity *pOwner = m_pWeapon->GetOwner();

		if (pOwner && pOwner->GetCharacter(0) && pOwner->GetCharacter(0)->GetISkeletonPose()->GetCharacterPhysics())
		{
			if (ISkeletonPose *pSkeletonPose = pOwner->GetCharacter(0)->GetISkeletonPose())
			{
				if (pSkeletonPose && pSkeletonPose->GetCharacterPhysics())
				{
					pSkeletonPose->GetCharacterPhysics()->Action(&collision);
				}
			}
		}
	}
}
예제 #10
0
void CMelee::StartFire()
{
	if (!CanFire())
	{
		return;
	}

	//Prevent fists melee exploit
	if ((m_pWeapon->GetEntity()->GetClass() == CItem::sFistsClass) && m_pWeapon->IsBusy())
	{
		return;
	}

	CActor *pOwner = m_pWeapon->GetOwnerActor();

	if(pOwner)
	{
		if(pOwner->GetStance() == STANCE_PRONE)
		{
			return;
		}

		if(SPlayerStats *stats = static_cast<SPlayerStats *>(pOwner->GetActorStats()))
		{
			if(stats->bLookingAtFriendlyAI)
			{
				return;
			}
		}
	}

	m_attacking = true;
	m_attacked = false;
	m_pWeapon->RequireUpdate(eIUS_FireMode);
	m_pWeapon->ExitZoom();
	bool isClient = pOwner ? pOwner->IsClient() : false;

	if (g_pGameCVars->bt_end_melee && isClient)
	{
		g_pGame->GetBulletTime()->Activate(false);
	}

	float speedOverride = -1.0f;

	if(pOwner)
	{
		CPlayer *pPlayer = (CPlayer *)pOwner;
		pPlayer->PlaySound(CPlayer::ESound_Melee);
	}

	m_pWeapon->PlayAction(m_pShared->meleeactions.attack.c_str(), 0, false, CItem::eIPAF_Default | CItem::eIPAF_CleanBlending, speedOverride);
	m_pWeapon->SetBusy(true);
	m_beginPos = m_pWeapon->GetSlotHelperPos( eIGS_FirstPerson, m_pShared->meleeparams.helper.c_str(), true);
	m_pWeapon->GetScheduler()->TimerAction(m_pWeapon->GetCurrentAnimationTime( eIGS_FirstPerson), CSchedulerAction<StopAttackingAction>::Create(this), true);
	m_delayTimer = m_pShared->meleeparams.delay;

	if (g_pGameCVars->dt_enable && m_delayTimer < g_pGameCVars->dt_time)
	{
		m_delayTimer = g_pGameCVars->dt_time;
	}

	m_durationTimer = m_pShared->meleeparams.duration;
	m_pWeapon->OnMelee(m_pWeapon->GetOwnerId());
	m_pWeapon->RequestStartMeleeAttack(m_pWeapon->GetMeleeFireMode() == this, false);
}
예제 #11
0
bool CGameRulesMPDamageHandling::SvOnHitScaled( const HitInfo &hitInfo )
{
	bool bReturnDied = false;
	IEntity *pTarget = gEnv->pEntitySystem->GetEntity(hitInfo.targetId);
	CActor *pActor = static_cast<CActor*>(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(hitInfo.targetId));

	if(!pActor)
	{
		IVehicle* pVictimVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(hitInfo.targetId);

		if(pVictimVehicle)
		{
			float vehicleDamageMultiplier = 1.f;

			//Additional damage scaling check for vehicles with associated body damage files
			CBodyDamageManager *pBodyDamageManager = g_pGame->GetBodyDamageManager();

			TBodyDamageProfileId bodyDamageProfileId;
			TVehicleBodyDamageMap::const_iterator iter = m_vehicleBodyDamageMap.find( hitInfo.targetId );
			if( iter != m_vehicleBodyDamageMap.end() )
			{
				bodyDamageProfileId = iter->second;
			}
			else
			{
				bodyDamageProfileId = pBodyDamageManager->GetBodyDamage( *pTarget );
				m_vehicleBodyDamageMap[hitInfo.targetId] = bodyDamageProfileId;
			}

			if(bodyDamageProfileId != INVALID_BODYDAMAGEPROFILEID)
			{
				vehicleDamageMultiplier *= pBodyDamageManager->GetDamageMultiplier(bodyDamageProfileId, *pTarget, hitInfo);
			}

			float fPreviousHealth = pVictimVehicle->GetStatus().health;

			// for vehicles, no need to call lua any more, we can just apply the hit directly to the vehicle
			{
				HitInfo hitInfoTemp = hitInfo;
				hitInfoTemp.damage *= vehicleDamageMultiplier;
				hitInfoTemp.explosion = false;
				pVictimVehicle->OnHit(hitInfoTemp);
			}


			float fNewHealth = pVictimVehicle->GetStatus().health;

			//Hit indicator for driver
			//	Health check is required to take into account damage removal due to friendly fire.
			if(fNewHealth != fPreviousHealth)
			{
				if(IActor* pDriver = pVictimVehicle->GetDriver())
				{
					if(IEntity* pShooterEntity = gEnv->pEntitySystem->GetEntity(hitInfo.shooterId))
					{
						Vec3 shooterPos = pShooterEntity->GetWorldPos();

						if(pDriver->IsClient())
						{
							SHUDEvent hitEvent(eHUDEvent_OnShowHitIndicatorPlayerUpdated);
							hitEvent.ReserveData(3);
							hitEvent.AddData(shooterPos.x);
							hitEvent.AddData(shooterPos.y);
							hitEvent.AddData(shooterPos.z);
							CHUDEventDispatcher::CallEvent(hitEvent);
						}
						else
						{
							m_pGameRules->GetGameObject()->InvokeRMI( CGameRules::ClActivateHitIndicator(), CGameRules::ActivateHitIndicatorParams(shooterPos), eRMI_ToClientChannel, pDriver->GetChannelId() );
						}
					}
				}
			}

			// no need for further processing, or calling OnEntityKilled, so just return early.
			if(pVictimVehicle->GetDamageRatio() >= 1.f)
			{
				if(fNewHealth != fPreviousHealth)
				{
					SVehicleDestroyedParams params(hitInfo.targetId, hitInfo.weaponId, hitInfo.type, hitInfo.projectileClassId);
					if(hitInfo.shooterId == g_pGame->GetClientActorId())
					{
						CPersistantStats::GetInstance()->OnClientDestroyedVehicle(params);
					}
					else if(IGameObject * pShooterGameObject = g_pGame->GetIGameFramework()->GetGameObject(hitInfo.shooterId))
					{
						m_pGameRules->GetGameObject()->InvokeRMIWithDependentObject(CGameRules::ClVehicleDestroyed(), params, eRMI_ToClientChannel, hitInfo.shooterId, pShooterGameObject->GetChannelId());
					}					
				}
				m_pGameRules->OnEntityKilled(hitInfo);
				return true; 
			}
			else
			{
				return false;
			}
		}
	}

	if (pTarget)
	{
		IScriptTable *pTargetScript = pTarget->GetScriptTable();
		if (pTargetScript)
		{
			m_pGameRules->CreateScriptHitInfo(m_scriptHitInfo, hitInfo);

			bool isDead = false;

			if (pActor)
			{
				if (pActor->IsDead())
				{
					isDead = true;

					// Target is already dead
					HSCRIPTFUNCTION pfnOnDeadHit = 0;
					if (pActor->GetActorStats()->isRagDoll && pTargetScript->GetValue("OnDeadHit", pfnOnDeadHit))
					{
						Script::CallMethod(pTargetScript, pfnOnDeadHit, m_scriptHitInfo);
						gEnv->pScriptSystem->ReleaseFunc(pfnOnDeadHit);
					}
				}
			}

			if (!isDead)
			{
				SmartScriptTable targetServerScript;
				if (pTargetScript->GetValue("Server", targetServerScript))
				{
					HSCRIPTFUNCTION pfnOnHit = 0;
					if (targetServerScript->GetValue("OnHit", pfnOnHit))
					{
						if (Script::CallReturn(gEnv->pScriptSystem, pfnOnHit, pTargetScript, m_scriptHitInfo, bReturnDied))
						{
							if (bReturnDied)
							{
								if (pActor)
								{
									ProcessDeath(hitInfo, *pActor);
								}
								else
								{
									m_pGameRules->OnEntityKilled(hitInfo);
								}
							}
						}
						gEnv->pScriptSystem->ReleaseFunc(pfnOnHit);
					}
				}
			}
		}
	}

	return bReturnDied;
}
예제 #12
0
void CAnimatedGrabHandler::UpdatePosVelRot(float frameTime)
{
	IEntity *pGrab = gEnv->pEntitySystem->GetEntity(m_grabStats.grabId);
	
	if ( !pGrab) return;

	IEntity *pEnt = m_pActor->GetEntity();

	if (m_grabStats.grabDelay<0.001f)
	{
		Vec3 grabWPos (GetGrabBoneWorldTM ().t);

		// NOTE Aug 3, 2007: <pvl> the second part of this test means don't enable
		// the correction if animation/ik wasn't used for grabbing in the first place
		if (m_grabStats.readIkInaccuracyCorrection && m_grabStats.grabAnimGraphSignal[0])
		{
			// NOTE Aug 2, 2007: <pvl> executed the first time this function is called
			// for a particular grabbing action
			m_grabStats.ikInaccuracyCorrection = grabWPos - (pGrab->GetWorldTM().GetTranslation() + m_grabStats.entityGrabSpot);
			m_grabStats.readIkInaccuracyCorrection = false;

			// FIXME Sep 13, 2007: <pvl> only putting it here because it's called just
			// once, at the instant when the object is grabbed - rename readIkInaccuracyCorrection
			// to make this clearer, or put this somewhere else
			DisableGrabbedAnimatedCharacter (true);
		}
		else
		{
			// NOTE Aug 2, 2007: <pvl> phase it out gradually
			m_grabStats.ikInaccuracyCorrection *= 0.9f;
			if (m_grabStats.ikInaccuracyCorrection.len2 () < 0.01f)
				m_grabStats.ikInaccuracyCorrection = Vec3 (0.0f, 0.0f, 0.0f);
		}
		// NOTE Sep 13, 2007: <pvl> this should prevent us from calling SetWPos()
		// later so that the IK "release" phase can take over
		m_grabStats.IKActive = false;

		Matrix34 tm(pGrab->GetWorldTM());
		tm.SetTranslation(grabWPos - (m_grabStats.ikInaccuracyCorrection + pGrab->GetRotation() * m_grabStats.entityGrabSpot));
		pGrab->SetWorldTM(tm,ENTITY_XFORM_USER);
	}

	//update IK
	for (int i=0;i<m_grabStats.limbNum;++i)
	{
		SIKLimb *pLimb = m_pActor->GetIKLimb(m_grabStats.limbId[i]);

		// NOTE Dez 14, 2006: <pvl> this class is always supposed to have
		// m_grabStats.usingAnimation == true
		if (m_grabStats.usingAnimation && m_grabStats.releaseIKTime>0.001f && m_grabStats.IKActive)
		{
			// NOTE Dez 15, 2006: <pvl> use IK to constantly offset the
			// animation so that the difference between where the animation
			// expects the object to be and where the object really is is taken
			// into account.
			Vec3 animPos = pEnt->GetSlotWorldTM(0) * pLimb->lAnimPos;
			Vec3 assumedGrabPos = pEnt->GetSlotWorldTM(0) * m_grabStats.grabbedObjOfs;
			Vec3 actualGrabPos = pGrab->GetWorldPos() + m_grabStats.entityGrabSpot;
			Vec3 adjustment = actualGrabPos - assumedGrabPos;
			pLimb->SetWPos(pEnt,animPos + adjustment,ZERO,0.5f,2.0f,1000);
			//gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(pGrab->GetWorldPos() + m_grabStats.entityGrabSpot, 0.5f, ColorB(0,255,0,100));
		}
		
		//if there are multiple limbs, only the first one sets the rotation of the object.
		if (m_grabStats.useIKRotation && i == 0 && m_grabStats.grabDelay<0.001f)
		{
			// NOTE Aug 8, 2007: <pvl> the idea here is to store current world
			// rotations of both the object being grabbed and the end bone of
			// a grabbing limb.  Then track how the end bone rotates with respect
			// to the stored original rotation and rotate the grabbed object
			// the same way.  That way, the grabbed object rotates the same as
			// the limb and appears to be "stabbed" by it.
			QuatT endBoneWorldRot = GetGrabBoneWorldTM ();
			endBoneWorldRot.q.Normalize();	// may not be necessary - just to be safe

			if ( ! m_grabStats.origRotationsValid)
			{
				m_grabStats.origRotation = pGrab->GetRotation();
				m_grabStats.origRotation.Normalize();			// may not be necessary - just to be safe
				m_grabStats.origEndBoneWorldRot = endBoneWorldRot;
				m_grabStats.origRotationsValid = true;
			}

			Quat grabQuat( (endBoneWorldRot*m_grabStats.origEndBoneWorldRot.GetInverted()).q * m_grabStats.origRotation);
			grabQuat.Normalize();

			// NOTE Dez 14, 2006: <pvl> this code sets up and look vectors for the grabbed
			// entity in case it's an Actor (the player, mostly) so that the player always
			// looks roughly at the grabber.  The grabber is supposed to be the Hunter here
			// so this code is somewhat Hunter-specific.
			// UPDATE Aug 7, 2007: <pvl> do the above for the player only
			// UPDATE Sep 13, 2007: <pvl> don't do it for anybody ATM, it doesn't seem useful
			CActor *pGrabbedActor = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_grabStats.grabId);
			if (false && pGrabbedActor && pGrabbedActor->IsClient() && pGrabbedActor->GetActorStats())
			{
				Vec3 upVec(Quat(endBoneWorldRot.q * m_grabStats.additionalRotation).GetColumn2());
				upVec.z = fabs_tpl(upVec.z) * 2.0f;
				upVec.NormalizeSafe(Vec3(0,0,1));

				SActorStats *pAS = pGrabbedActor->GetActorStats();
				if (pAS)
				{
					pAS->forceUpVector = upVec;
					pAS->forceLookVector = (pEnt->GetSlotWorldTM(0) * m_pActor->GetLocalEyePos(0)) - pGrabbedActor->GetEntity()->GetWorldPos();
					float lookLen(pAS->forceLookVector.len());
					pAS->forceLookVector *= (1.0f/lookLen)*0.33f;
					//pAS->forceLookVector = -Quat(boneRot * m_grabStats.additionalRotation).GetColumn1();//boneRot.GetColumn2();
				}
			}
			else
			{
				pGrab->SetRotation(grabQuat,ENTITY_XFORM_USER);
			}
		}
	}

	if (m_grabStats.grabDelay<0.001f)
	{
		// NOTE Sep 16, 2007: <pvl> now that grabbed entity rotation coming from
		// a grabbing bone (if any) is computed, bone-space offset can be applied
		Matrix34 tm(pGrab->GetWorldTM());
		tm.AddTranslation(GetGrabBoneWorldTM().q * m_grabStats.boneGrabOffset);
		pGrab->SetWorldTM(tm,ENTITY_XFORM_USER);

		/*
		{
			// debug draw for the grab bone
			QuatT grabBoneWorldTM = GetGrabBoneWorldTM();
			Vec3 start = grabBoneWorldTM.t;
			Vec3 end = start + grabBoneWorldTM.q * Vec3 (1,0,0) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (255,0,0), end, ColorB (0,0,255), 6.0f);
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.5f, ColorB (255,128,0));
		}
		*/
		/*
		{
			// draw complete coord systems for both the end bone and the grabbed thing
			QuatT grabBoneWorldTM = GetGrabBoneWorldTM();
			Vec3 start = grabBoneWorldTM.t;
			Vec3 end = start + grabBoneWorldTM.q * Vec3 (1,0,0) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (128,0,0), end, ColorB (128,0,0), 6.0f);
			end = start + grabBoneWorldTM.q * Vec3 (0,1,0) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,128,0), end, ColorB (0,128,0), 6.0f);
			end = start + grabBoneWorldTM.q * Vec3 (0,0,1) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,0,128), end, ColorB (0,0,128), 6.0f);
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (255,255,255));

			start = pGrab->GetWorldTM().GetTranslation();
			end = start + pGrab->GetRotation() * Vec3 (1,0,0) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (128,0,0), end, ColorB (128,0,0), 6.0f);
			end = start + pGrab->GetRotation() * Vec3 (0,1,0) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,128,0), end, ColorB (0,128,0), 6.0f);
			end = start + pGrab->GetRotation() * Vec3 (0,0,1) * 3;
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,0,128), end, ColorB (0,0,128), 6.0f);
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (64,64,64));
		}
		*/
	}

/*
	{
		// debug draw for the grabbed object
		Vec3 start = pGrab->GetWorldTM().GetTranslation();
		Vec3 end = start + pGrab->GetRotation() * Vec3 (0,0,1) * 3;
		gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (255,0,0), end, ColorB (0,0,255), 6.0f);
		gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (255,128,0));
	}
*/
}