//------------------------------------------------------------------------ int CScriptBind_Game::RegisterWithAutoAimManager(IFunctionHandler *pH, ScriptHandle entityId, float innerRadiusFactor, float outerRadiusFactor, float snapRadiusFactor) { IEntity* pEntity = gEnv->pEntitySystem->GetEntity((EntityId)entityId.n); if (pEntity) { AABB entityBbox; pEntity->GetWorldBounds(entityBbox); const float entityRadius = (entityBbox.IsEmpty() == false) ? entityBbox.GetRadius() : 1.0f; SAutoaimTargetRegisterParams registerParams; registerParams.fallbackOffset = 0.0f; registerParams.innerRadius = entityRadius * innerRadiusFactor; registerParams.outerRadius = entityRadius * outerRadiusFactor; registerParams.snapRadius = entityRadius * snapRadiusFactor; registerParams.snapRadiusTagged = entityRadius; registerParams.primaryBoneId = -1; registerParams.physicsBoneId = -1; registerParams.secondaryBoneId = -1; g_pGame->GetAutoAimManager().RegisterAutoaimTargetObject((EntityId)entityId.n, registerParams); } return pH->EndFunction(); }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorBlowTire::Activate(bool activate) { if (activate == m_isActive) return; if (activate && m_pVehicle->IsDestroyed()) return; if (activate) { // NOTE: stance and physics position when getting into vehicles is set wrong if (!gEnv->pSystem->IsSerializingFile()) DamagePlayers(); } IVehicleComponent* pComponent = m_pVehicle->GetComponent(m_component.c_str()); if (!pComponent) return; IVehiclePart* pPart = pComponent->GetPart(0); if (!pPart) return; // if IVehicleWheel available, execute full damage behavior. if null, only apply effects IVehicleWheel* pWheel = pPart->GetIWheel(); if (activate) { IEntity* pEntity = m_pVehicle->GetEntity(); IPhysicalEntity* pPhysics = pEntity->GetPhysics(); const Matrix34& wheelTM = pPart->GetLocalTM(false); const SVehicleStatus& status = m_pVehicle->GetStatus(); if (pWheel) { const pe_cargeomparams* pParams = pWheel->GetCarGeomParams(); // handle destroyed wheel pe_params_wheel wheelParams; wheelParams.iWheel = pWheel->GetWheelIndex(); wheelParams.minFriction = wheelParams.maxFriction = 0.5f * pParams->maxFriction; pPhysics->SetParams(&wheelParams); if (IVehicleMovement* pMovement = m_pVehicle->GetMovement()) { SVehicleMovementEventParams params; params.pComponent = pComponent; params.iValue = pWheel->GetWheelIndex(); pMovement->OnEvent(IVehicleMovement::eVME_TireBlown, params); } if (status.speed > 0.1f) { // add angular impulse pe_action_impulse angImp; float amount = m_pVehicle->GetMass() * status.speed * Random(0.25f, 0.45f) * -sgn(wheelTM.GetTranslation().x); angImp.angImpulse = pEntity->GetWorldTM().TransformVector(Vec3(0,0,amount)); pPhysics->Action(&angImp); } m_aiImmobilizedTimer = m_pVehicle->SetTimer(-1, AI_IMMOBILIZED_TIME*1000, this); } if (!gEnv->pSystem->IsSerializingFile()) { // add linear impulse pe_action_impulse imp; imp.point = pPart->GetWorldTM().GetTranslation(); float amount = m_pVehicle->GetMass() * Random(0.1f, 0.15f); if (pWheel) { amount *= max(0.5f, min(10.f, status.speed)); if (status.speed < 0.1f) amount = -0.5f*amount; } else amount *= 0.5f; imp.impulse = pEntity->GetWorldTM().TransformVector(Vec3(0,0,amount)); pPhysics->Action(&imp); // effect IParticleEffect* pEffect = gEnv->pParticleManager->FindEffect(TIRE_BLOW_EFFECT); if (pEffect) { int slot = pEntity->LoadParticleEmitter(-1, pEffect); if (slot > -1) { float rotation = pWheel ? 0.5f * gf_PI * -sgn(wheelTM.GetTranslation().x) : gf_PI; Matrix34 tm = Matrix34::CreateRotationZ(rotation); tm.SetTranslation(wheelTM.GetTranslation()); pEntity->SetSlotLocalTM(slot, tm); } } // remove affected decals { Vec3 pos = pPart->GetWorldTM().GetTranslation(); AABB aabb = pPart->GetLocalBounds(); float radius = aabb.GetRadius(); Vec3 vRadius(radius,radius,radius); AABB areaBox(pos-vRadius, pos+vRadius); IRenderNode * pRenderNode = NULL; if (IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)pEntity->GetProxy(ENTITY_PROXY_RENDER)) pRenderNode = pRenderProxy->GetRenderNode(); gEnv->p3DEngine->DeleteDecalsInRange(&areaBox, pRenderNode); } } } else { if (pWheel) { // restore wheel properties IPhysicalEntity* pPhysics = m_pVehicle->GetEntity()->GetPhysics(); pe_params_wheel wheelParams; for (int i=0; i<m_pVehicle->GetWheelCount(); ++i) { const pe_cargeomparams* pParams = m_pVehicle->GetWheelPart(i)->GetIWheel()->GetCarGeomParams(); wheelParams.iWheel = i; wheelParams.bBlocked = 0; wheelParams.suspLenMax = pParams->lenMax; wheelParams.bDriving = pParams->bDriving; wheelParams.minFriction = pParams->minFriction; wheelParams.maxFriction = pParams->maxFriction; pPhysics->SetParams(&wheelParams); } if (IVehicleMovement* pMovement = m_pVehicle->GetMovement()) { SVehicleMovementEventParams params; params.pComponent = pComponent; params.iValue = pWheel->GetWheelIndex(); // reset the particle status pMovement->OnEvent(IVehicleMovement::eVME_TireRestored, params); } } m_aiImmobilizedTimer = -1; } m_isActive = activate; }
void CClaymore::Update(SEntityUpdateContext &ctx, int updateSlot) { CProjectile::Update(ctx, updateSlot); bool debug = (g_pGameCVars->g_debugMines != 0); if(gEnv->bServer) { if(m_armed) { CGameRules* pGR = g_pGame->GetGameRules(); if(pGR) { for(std::list<EntityId>::iterator it = m_targetList.begin(); it != m_targetList.end(); ++it) { IEntity* pEntity = gEnv->pEntitySystem->GetEntity(*it); if(!pEntity) continue; // if this is a team game, claymores aren't set off by their own team... if(pGR->GetTeamCount() > 0 && (m_teamId != 0 && pGR->GetTeam(pEntity->GetId()) == m_teamId)) continue; // otherwise, not set off by the player who dropped them. if(pGR->GetTeamCount() == 0 && m_ownerId == pEntity->GetId()) continue; IPhysicalEntity *pPhysics = pEntity->GetPhysics(); if(pPhysics) { pe_status_dynamics physStatus; if(0 != pPhysics->GetStatus(&physStatus) && physStatus.v.GetLengthSquared() > 0.01f) { // now check angle between this claymore and approaching object // to see if it is within the angular range m_triggerAngle. // If it is, then check distance is less than m_triggerRange, // and also check line-of-sight between the two entities. IRenderAuxGeom * pRAG = gEnv->pRenderer->GetIRenderAuxGeom(); pRAG->SetRenderFlags( e_Mode3D | e_AlphaBlended | e_DrawInFrontOff | e_FillModeSolid | e_CullModeNone ); AABB entityBBox; pEntity->GetWorldBounds(entityBBox); if(debug) { pRAG->DrawAABB( entityBBox, true, ColorF(1,0,0,0.4f), eBBD_Faceted ); } Vec3 enemyDir = entityBBox.GetCenter() - GetEntity()->GetPos(); Vec3 checkDir = enemyDir; checkDir.z = 0; float distanceSq = enemyDir.GetLengthSquared(); // for players a simple distance check is fine, but for vehicles use a better intersection check // so any corner of the vehicle going inside the zone sets off the claymore. static float playerRadius = 2.5f; bool inside = false; if(entityBBox.GetRadius() < playerRadius) { inside = (distanceSq < (m_triggerRadius * m_triggerRadius)); } else { static ray_hit hit; if(gEnv->pPhysicalWorld->CollideEntityWithBeam(pEntity->GetPhysics(), GetEntity()->GetWorldPos(), enemyDir, m_triggerRadius, &hit)) { inside = true; enemyDir = hit.pt - GetEntity()->GetWorldPos(); } } if(inside) { enemyDir.NormalizeSafe(); checkDir.NormalizeSafe(); float dotProd = checkDir.Dot(m_triggerDirection); if(debug) { pRAG->DrawLine(GetEntity()->GetPos(), ColorF(1,0,0,1), GetEntity()->GetPos() + Matrix33::CreateRotationZ(m_triggerAngle/2.0f)*m_triggerDirection*m_triggerRadius, ColorF(1,0,0,1), 5.0f); pRAG->DrawLine(GetEntity()->GetPos(), ColorF(1,0,0,1), GetEntity()->GetPos() + Matrix33::CreateRotationZ(-m_triggerAngle/2.0f)*m_triggerDirection*m_triggerRadius, ColorF(1,0,0,1), 5.0f); ColorF clr; clr.a = 0.3f; clr.b = 0.4f; clr.g = 0.1f; clr.r = 1.0f; pRAG->DrawLine(GetEntity()->GetPos(), clr, GetEntity()->GetPos() + (enemyDir * m_triggerRadius), clr, 5.0f); } if(dotProd > cry_cosf(m_triggerAngle/2.0f)) { static const int objTypes = ent_all&(~ent_terrain); static const unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any; ray_hit hit; int col = gEnv->pPhysicalWorld->RayWorldIntersection(GetEntity()->GetPos(), (enemyDir * m_triggerRadius * 1.5f), objTypes, flags, &hit, 1, GetEntity()->GetPhysics()); bool bang = false; if (!col) bang = true; else if (entityBBox.IsContainPoint(hit.pt)) bang = true; else if (hit.pt.GetSquaredDistance(GetEntity()->GetWorldPos()) >= distanceSq) bang = true; if (bang) { // pass in the explosion normal, which is -m_triggerDirection Explode(true, false, Vec3(0,0,0), -m_triggerDirection); if(debug) { ColorF clr; clr.a = 0.3f; clr.g = 0.1f; clr.r = 1.0f; clr.b = 1.0f; pRAG->DrawLine(GetEntity()->GetPos(), clr, GetEntity()->GetPos() + (enemyDir * m_triggerRadius), clr, 5.0f); } } } } } } } } } else { m_timeToArm -= gEnv->pTimer->GetFrameTime(); if(m_timeToArm <= 0.0f) { m_armed = true; IEntityTriggerProxy *pTriggerProxy = (IEntityTriggerProxy*)(GetEntity()->GetProxy(ENTITY_PROXY_TRIGGER)); if (!pTriggerProxy) { GetEntity()->CreateProxy(ENTITY_PROXY_TRIGGER); pTriggerProxy = (IEntityTriggerProxy*)GetEntity()->GetProxy(ENTITY_PROXY_TRIGGER); } if(pTriggerProxy) { // create a trigger volume a couple of metres bigger than we need, to ensure we catch vehicles. // Checks above will still make sure the entity is within the radius before detonating though. float radius = m_triggerRadius + 2.0f; AABB boundingBox = AABB(Vec3(-radius,-radius,-radius), Vec3(radius,radius,radius)); pTriggerProxy->SetTriggerBounds(boundingBox); } } } } if(debug && m_armed) { IRenderAuxGeom * pRAG = gEnv->pRenderer->GetIRenderAuxGeom(); ColorF clr; clr.a = 0.3f; clr.b = 0.4f; clr.g = 0.1f; clr.r = 1.0f; pRAG->SetRenderFlags( e_Mode3D | e_AlphaBlended | e_DrawInFrontOff | e_FillModeSolid | e_CullModeNone ); pRAG->DrawCylinder(GetEntity()->GetPos(), Vec3(0, 0, 1), m_triggerRadius, m_triggerRadius * 2.0f, clr); Vec3 size(m_triggerRadius + 2.0f, m_triggerRadius + 2.0f, m_triggerRadius + 2.0f); AABB box(GetEntity()->GetPos() - size, GetEntity()->GetPos() + size); pRAG->DrawAABB(box, false, ColorF(0.1f, 0.1f, 0.1f, 0.1f), eBBD_Faceted); pRAG->DrawLine(GetEntity()->GetPos(), clr, GetEntity()->GetPos() + m_triggerDirection, clr, 5.0f); } }
//----------------------------------------------------- void CThrow::ThrowObject(IEntity *pEntity, IPhysicalEntity *pPE) { bool strengthMode = false; CPlayer *pPlayer = static_cast<CPlayer *>(m_pWeapon->GetOwnerActor()); if (pPlayer) { // Report throw to AI system. if (pPlayer->GetEntity() && pPlayer->GetEntity()->GetAI()) { SAIEVENT AIevent; AIevent.targetId = pEntity->GetId(); pPlayer->GetEntity()->GetAI()->Event(AIEVENT_PLAYER_THROW, &AIevent); } } Vec3 hit = GetProbableHit(WEAPON_HIT_RANGE); Vec3 pos = GetFiringPos(hit); Vec3 dir = ApplySpread(GetFiringDir(hit, pos), GetSpread()); Vec3 vel = GetFiringVelocity(dir); float speed = 12.0f; if(strengthMode) { speed *= m_pShared->throwparams.strenght_scale; } speed = max(2.0f, speed); pe_params_pos ppos; ppos.pos = pEntity->GetWorldPos(); pPE->SetParams(&ppos); if(CheckForIntersections(pPE, dir)) { Matrix34 newTM = pEntity->GetWorldTM(); newTM.SetTranslation(newTM.GetTranslation() - (dir * 0.4f)); pEntity->SetWorldTM(newTM, ENTITY_XFORM_POS); } else { pe_action_set_velocity asv; asv.v = (dir * speed) + vel; AABB box; pEntity->GetWorldBounds(box); Vec3 finalW = -gEnv->pSystem->GetViewCamera().GetMatrix().GetColumn0() * (8.0f / max(0.1f, box.GetRadius())); finalW.x *= Random(0.5f, 1.3f); finalW.y *= Random(0.5f, 1.3f); finalW.z *= Random(0.5f, 1.3f); asv.w = finalW; //asv.w = Vec3(Random(-4.5f,3.5f),Random(-1.75f,2.5f),Random(-1.5f,2.2f)); pPE->Action(&asv); } SEntityEvent entityEvent; entityEvent.event = ENTITY_EVENT_PICKUP; entityEvent.nParam[0] = 0; if (pPlayer) { entityEvent.nParam[1] = pPlayer->GetEntityId(); } entityEvent.fParam[0] = speed; pEntity->SendEvent( entityEvent ); }
void CGame::Draw() { Vector vecForward = m_hPlayer->GetGlobalView(); Vector vecUp(0, 1, 0); // Cross-product http://www.youtube.com/watch?v=FT7MShdqK6w Vector vecRight = vecUp.Cross(vecForward).Normalized(); CRenderer* pRenderer = GetRenderer(); // Tell the renderer how to set up the camera. pRenderer->SetCameraPosition(m_hPlayer->GetGlobalOrigin() - vecForward * 6 + vecUp * 3 - vecRight * 0.5f); pRenderer->SetCameraDirection(vecForward); pRenderer->SetCameraUp(Vector(0, 1, 0)); pRenderer->SetCameraFOV(90); pRenderer->SetCameraNear(0.1f); pRenderer->SetCameraFar(1000); // This rendering context is a tool for rendering things to the screen. // All of our drawing commands are part of it. CRenderingContext r(pRenderer); // Clear the depth buffer and set a background color. r.ClearDepth(); r.ClearColor(Color(210, 230, 255)); // CRenderer::StartRendering() - This function sets up OpenGL with the // camera information that we passed it before. pRenderer->StartRendering(&r); m_oFrameFrustum = CFrustum(r.GetProjection() * r.GetView()); // First tell OpenGL what "shader" or "program" to use. r.UseProgram("model"); // Set the sunlight direction. The y component is -1 so the light is pointing down. Vector vecSunlight = Vector(1, -1, 1).Normalized(); // Uncomment this code to make the sunlight rotate: //Vector vecSunlight = Vector(cos(Game()->GetTime()), -1, sin(Game()->GetTime())).Normalized(); r.SetUniform("vecSunlight", vecSunlight); r.SetUniform("bLighted", false); r.SetUniform("bDiffuse", false); // Render the ground. r.SetUniform("vecColor", Vector4D(0.6f, 0.7f, 0.9f, 1)); r.SetUniform("vecCameraPosition", GetRenderer()->GetCameraPosition()); r.BeginRenderTriFan(); r.Normal(Vector(0, 1, 0)); r.Tangent(Vector(1, 0, 0)); r.Bitangent(Vector(0, 0, 1)); r.TexCoord(Vector2D(0, 1)); r.Vertex(Vector(-30, 0, -30)); r.TexCoord(Vector2D(0, 0)); r.Vertex(Vector(-30, 0, 30)); r.TexCoord(Vector2D(1, 0)); r.Vertex(Vector(30, 0, 30)); r.TexCoord(Vector2D(1, 1)); r.Vertex(Vector(30, 0, -30)); r.EndRender(); r.SetUniform("bLighted", true); // Prepare a list of entities to render. m_apRenderOpaqueList.clear(); m_apRenderTransparentList.clear(); for (size_t i = 0; i < MAX_CHARACTERS; i++) { CCharacter* pCharacter = GetCharacterIndex(i); if (!pCharacter) continue; // We need to scale the AABB using the character's scale values before we can use it to calculate our center/radius. AABB aabbSizeWithScaling = pCharacter->m_aabbSize * pCharacter->m_vecScaling; Vector vecCharacterCenter = pCharacter->GetGlobalOrigin() + aabbSizeWithScaling.GetCenter(); float flCharacterRadius = aabbSizeWithScaling.GetRadius(); // If the entity is outside the viewing frustum then the player can't see it - don't draw it. // http://youtu.be/4p-E_31XOPM if (!m_oFrameFrustum.SphereIntersection(vecCharacterCenter, flCharacterRadius)) continue; if (pCharacter->m_bDrawTransparent) m_apRenderTransparentList.push_back(pCharacter); else m_apRenderOpaqueList.push_back(pCharacter); } // Draw all opaque characters first. DrawCharacters(m_apRenderOpaqueList, false); for (size_t i = 0; i < MAX_CHARACTERS; i++) { CCharacter* pCharacter = GetCharacterIndex(i); if (!pCharacter) continue; if (!pCharacter->m_bEnemyAI) continue; float flRadius = 3.5f; Vector vecIndicatorOrigin = NearestPointOnSphere(m_hPlayer->GetGlobalOrigin(), flRadius, pCharacter->GetGlobalOrigin()); float flBoxSize = 0.1f; r.SetUniform("vecColor", Color(255, 0, 0, 255)); r.RenderBox(vecIndicatorOrigin - Vector(1, 1, 1)*flBoxSize, vecIndicatorOrigin + Vector(1, 1, 1)*flBoxSize); } // Sort the transparent render list so that we paint the items farther from the camera first. http://youtu.be/fEjZrwDKdi8 MergeSortTransparentRenderList(); // Now draw all transparent characters, sorted by distance from the camera. DrawCharacters(m_apRenderTransparentList, true); r.SetUniform("bDiffuse", false); // Render any bullet tracers that may have been created. float flBulletTracerTime = 0.1f; for (size_t i = 0; i < Game()->GetTracers().size(); i++) { if (Game()->GetTime() < Game()->GetTracers()[i].flTimeCreated + flBulletTracerTime) { Vector vecStart = Game()->GetTracers()[i].vecStart; Vector vecEnd = Game()->GetTracers()[i].vecEnd; r.SetUniform("vecColor", Vector4D(1, 0.9f, 0, 1)); r.BeginRenderLines(); r.Normal(Vector(0, 1, 0)); r.Vertex(vecStart); r.Vertex(vecEnd); r.EndRender(); } } // Render any puffs that may have been created. float flPuffTime = 0.3f; for (size_t i = 0; i < Game()->GetPuffs().size(); i++) { if (Game()->GetTime() < Game()->GetPuffs()[i].flTimeCreated + flPuffTime) { float flTimeCreated = Game()->GetPuffs()[i].flTimeCreated; float flTimeOver = Game()->GetPuffs()[i].flTimeCreated + flPuffTime; float flStartSize = 0.2f; float flEndSize = 2.0f; float flSize = Remap(Game()->GetTime(), flTimeCreated, flTimeOver, flStartSize, flEndSize); Vector vecOrigin = Game()->GetPuffs()[i].vecOrigin; int iOrange = (int)Remap(Game()->GetTime(), flTimeCreated, flTimeOver, 0, 255); r.SetUniform("vecColor", Color(255, iOrange, 0, 255)); r.RenderBox(vecOrigin - Vector(1, 1, 1)*flSize, vecOrigin + Vector(1, 1, 1)*flSize); } } GraphDraw(); pRenderer->FinishRendering(&r); // Call this last. Your rendered stuff won't appear on the screen until you call this. Application()->SwapBuffers(); }
void CAutoAimManager::UpdateTargetInfo(SAutoaimTarget& aaTarget, float fFrameTime) { IEntity * pTargetEntity = gEnv->pEntitySystem->GetEntity(aaTarget.entityId); if(pTargetEntity) { CActorPtr pTargetActor = aaTarget.pActorWeak.lock(); if (pTargetActor) { Vec3 characterPos; Quat characterRot; //Need this because of decouple catch-up movement if (IAnimatedCharacter* pAnimatedCharacter = pTargetActor->GetAnimatedCharacter()) { const QuatT& animationLocation = pAnimatedCharacter->GetAnimLocation(); characterPos = animationLocation.t; characterRot = animationLocation.q; } else { const Matrix34& targetWorldTM = pTargetEntity->GetWorldTM(); //Fallback to entity position characterPos = targetWorldTM.GetTranslation(); characterRot = Quat(targetWorldTM); } Vec3 primaryOffset(0.0f, 0.0f, aaTarget.fallbackOffset); Vec3 secondaryOffset(0.0f, 0.0f, aaTarget.fallbackOffset); if (aaTarget.primaryBoneId >= 0) { if (pTargetActor->HasBoneID(aaTarget.primaryBoneId)) { primaryOffset = pTargetActor->GetBoneTransform(aaTarget.primaryBoneId).t; } else { GameWarning("CAutoAimManager: Character %s missing primary boneID: %s", pTargetEntity->GetName(), s_BONE_ID_NAME[aaTarget.primaryBoneId]); aaTarget.primaryBoneId = -1; } } if (aaTarget.secondaryBoneId >= 0) { if (pTargetActor->HasBoneID(aaTarget.secondaryBoneId)) { secondaryOffset = pTargetActor->GetBoneTransform(aaTarget.secondaryBoneId).t; } else { GameWarning("CAutoAimManager: Character %s missing secondary boneID: %s", pTargetEntity->GetName(), s_BONE_ID_NAME[aaTarget.secondaryBoneId]); aaTarget.secondaryBoneId = -1; } } aaTarget.primaryAimPosition = characterPos + (characterRot * primaryOffset); aaTarget.secondaryAimPosition = characterPos + (characterRot * secondaryOffset); //Update hostility (changes during gameplay) if (!gEnv->bMultiplayer) { uint8 targetFaction = (aaTarget.aiFaction != IFactionMap::InvalidFactionID) ? aaTarget.aiFaction : GetTargetFaction(*pTargetEntity); if (gEnv->pAISystem->GetFactionMap().GetReaction(GetLocalPlayerFaction(), aaTarget.aiFaction) == IFactionMap::Hostile) { aaTarget.SetFlag(eAATF_AIHostile); } else { aaTarget.RemoveFlag(eAATF_AIHostile); } aaTarget.aiFaction = targetFaction; } } else if(aaTarget.hasSkeleton) { //Not an actor but has a skeleton (and so can use bone offsets) ISkeletonPose* pSkeletonPose = pTargetEntity->GetCharacter(0)->GetISkeletonPose(); const Matrix34& characterMat = pTargetEntity->GetWorldTM(); const Vec3 characterPos = characterMat.GetTranslation(); const Quat characterRot(characterMat); Vec3 primaryOffset(0.0f, 0.0f, aaTarget.fallbackOffset); Vec3 secondaryOffset(0.0f, 0.0f, aaTarget.fallbackOffset); if (aaTarget.primaryBoneId >= 0) { primaryOffset = pSkeletonPose->GetAbsJointByID(aaTarget.primaryBoneId).t; } if (aaTarget.secondaryBoneId >= 0) { secondaryOffset = pSkeletonPose->GetAbsJointByID(aaTarget.secondaryBoneId).t; } aaTarget.primaryAimPosition = characterPos + (characterRot * primaryOffset); aaTarget.secondaryAimPosition = characterPos + (characterRot * secondaryOffset); } else { //Must be an object const Matrix34& entityWorldTM = pTargetEntity->GetWorldTM(); Vec3 primaryPosition = entityWorldTM.GetTranslation(); Vec3 secondaryPosition = entityWorldTM.TransformPoint(Vec3(0.0f, 0.0f, 0.5f)); AABB entityLocalBBox; pTargetEntity->GetLocalBounds(entityLocalBBox); if (!entityLocalBBox.IsEmpty()) { const Vec3 offset (0.0f, 0.0f, entityLocalBBox.GetRadius() * 0.2f); const Vec3 objectCenter = entityLocalBBox.GetCenter(); primaryPosition = entityWorldTM.TransformPoint((objectCenter - offset)); secondaryPosition = entityWorldTM.TransformPoint((objectCenter + offset)); } aaTarget.primaryAimPosition = primaryPosition; aaTarget.secondaryAimPosition = secondaryPosition; } //The physics drags the render proxy and entity behind it. If we auto aim at the render position, // we will handicap the console players by failing to let them aim ahead of the target. if(IPhysicalEntity * pPhysicalEntity = pTargetEntity->GetPhysics()) { pe_status_dynamics dyn; if(pPhysicalEntity->GetStatus(&dyn)) { Vec3 lookAhead = (dyn.v * fFrameTime); aaTarget.primaryAimPosition = aaTarget.primaryAimPosition + lookAhead; aaTarget.secondaryAimPosition = aaTarget.secondaryAimPosition + lookAhead; } } } }
//------------------------------------------------------------------------ bool CTracer::Update(float frameTime, const Vec3 &camera, const float fovScale) { frameTime = (float)__fsel(-m_age, 0.002f, frameTime); const float tracerAge = (float)__fsel(-m_age, 0.002f, m_age+frameTime); m_age = tracerAge; if (tracerAge >= m_lifeTime) return false; Vec3 currentLimitDestination; if (gEnv->bMultiplayer) { if(m_tracerFlags & kTracerFlag_updateDestFromBullet) { IEntity* pBulletEntity = gEnv->pEntitySystem->GetEntity(m_boundToBulletId); if(pBulletEntity) { m_dest = pBulletEntity->GetPos(); } } currentLimitDestination = m_dest; } else { IEntity* pBulletEntity = gEnv->pEntitySystem->GetEntity(m_boundToBulletId); currentLimitDestination = pBulletEntity ? pBulletEntity->GetPos() : m_dest; } const Vec3 maxTravelledDistance = m_dest - m_startingPos; const float maxTravelledDistanceSqr = maxTravelledDistance.len2(); float dist = sqrt_tpl(maxTravelledDistanceSqr); if (dist <= 0.001f) return false; const Vec3 dir = maxTravelledDistance * (float)__fres(dist); Vec3 pos = m_pos; float lengthScale = 1.f; Vec3 newPos = m_pos; if (!(m_tracerFlags & kTracerFlag_dontTranslate)) { const float sqrRadius = GetGameConstCVar(g_tracers_slowDownAtCameraDistance) * GetGameConstCVar(g_tracers_slowDownAtCameraDistance); const float cameraDistance = (m_pos-camera).len2(); const float speed = m_speed * (float)__fsel(sqrRadius - cameraDistance, 0.35f + (cameraDistance/(sqrRadius*2)), 1.0f); //Slow down tracer when near the player newPos += dir * min(speed*frameTime, dist); pos = newPos; if(m_slideFrac > 0.f) { pos += (((2.f * cry_frand()) - 0.5f) * m_slideFrac * speed * frameTime * dir); } } // Now update visuals... if (IEntity *pEntity = gEnv->pEntitySystem->GetEntity(m_entityId)) { AABB tracerBbox; pEntity->GetWorldBounds(tracerBbox); float tracerHalfLength = !tracerBbox.IsEmpty() ? tracerBbox.GetRadius() : 0.0f; const Vec3 frontOfTracerPos = pos + (dir * tracerHalfLength); if((frontOfTracerPos-m_startingPos).len2() > maxTravelledDistanceSqr) { return false; } if (!(m_tracerFlags & kTracerFlag_dontTranslate) && tracerHalfLength > 0.f) { //Ensure that never goes in front of the bullet const Vec3 dirFromFrontOfTracerToDestination = currentLimitDestination - frontOfTracerPos; if (dir.dot(dirFromFrontOfTracerToDestination) < 0) { pos += dirFromFrontOfTracerToDestination; } // ... and check if back of tracer is behind starting point, so adjust length. const Vec3 backOfTracerPos = pos - (dir * tracerHalfLength); const Vec3 dirFromBackOfTracerToStart = m_startingPos - backOfTracerPos; if (dir.dot(dirFromBackOfTracerToStart) > 0) { if(dir.dot((m_startingPos - pos)) > 0) { pos = m_startingPos + (dir * cry_frand() * tracerHalfLength); } lengthScale = ((pos - m_startingPos).GetLength() / tracerHalfLength); } } m_pos = newPos; Matrix34 tm(Matrix33::CreateRotationVDir(dir)); tm.AddTranslation(pos); pEntity->SetWorldTM(tm); //Do not scale effects if((m_tracerFlags & kTracerFlag_useGeometry)) { float finalFovScale = fovScale; if((m_tracerFlags & kTracerFlag_scaleToDistance) != 0) { lengthScale = dist * 0.5f; } else { const float cameraDistanceSqr = (m_pos-camera).len2(); const float minScale = GetGameConstCVar(g_tracers_minScale); const float maxScale = GetGameConstCVar(g_tracers_maxScale); const float minDistanceRange = GetGameConstCVar(g_tracers_minScaleAtDistance) * GetGameConstCVar(g_tracers_minScaleAtDistance); const float maxDistanceRange = max(GetGameConstCVar(g_tracers_maxScaleAtDistance) * GetGameConstCVar(g_tracers_maxScaleAtDistance), minDistanceRange + 1.0f); const float currentRefDistance = clamp(cameraDistanceSqr, minDistanceRange, maxDistanceRange); const float distanceToCameraFactor = ((currentRefDistance - minDistanceRange) / (maxDistanceRange - minDistanceRange)); const float distanceToCameraScale = LERP(minScale, maxScale, distanceToCameraFactor); lengthScale = m_scale * distanceToCameraScale; finalFovScale *= distanceToCameraScale; } const float widthScale = fovScale; tm.SetIdentity(); tm.SetScale(Vec3(m_scale * finalFovScale, lengthScale, m_scale * finalFovScale)); pEntity->SetSlotLocalTM(m_geometrySlot,tm); } } return true; }
void CPlayerView::ViewSpectatorTarget(SViewParams &viewParams) { CActor *pTarget = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_in.stats_spectatorTarget); if(!pTarget) { return; } Matrix34 worldTM = pTarget->GetEntity()->GetWorldTM(); Vec3 worldPos = worldTM.GetTranslation(); worldPos.z += 1.5f; Ang3 worldAngles = Ang3::GetAnglesXYZ(Matrix33(worldTM)); float rot = worldAngles.z;// + m_rot; float distance = 3;//(m_defaultDistance != 0) ? m_defaultDistance : m_distance; if(IVehicle *pVehicle = pTarget->GetLinkedVehicle()) { AABB vehicleBox; pVehicle->GetEntity()->GetLocalBounds(vehicleBox); distance = 2.0f * vehicleBox.GetRadius(); } Vec3 goal; float zoom = 1.0f; goal.x = distance * zoom * cosf(rot + gf_PI * 1.5f) + worldPos.x; goal.y = distance * zoom * sinf(rot - gf_PI / 2.0f) + worldPos.y; AABB targetBounds; pTarget->GetEntity()->GetLocalBounds(targetBounds); goal.z = targetBounds.max.z; static float defaultOffset = 0.75f; float offset = defaultOffset; if(pTarget->GetLinkedVehicle()) { offset = 2.0f; } goal.z += pTarget->GetEntity()->GetWorldPos().z + offset; // store / interpolate the offset, not the world pos (reduces percieved jitter in vehicles) static Vec3 viewOffset(goal - worldPos); static Vec3 position(goal); static Vec3 entPos(worldPos); static EntityId lastSpectatorTarget(m_in.stats_spectatorTarget); // do a ray cast to check for camera intersection static ray_hit hit; IPhysicalEntity *pSkipEntities[10]; int nSkip = 0; IItem *pItem = pTarget->GetCurrentItem(); if (pItem) { CWeapon *pWeapon = (CWeapon *)pItem->GetIWeapon(); if (pWeapon) { nSkip = CSingle::GetSkipEntities(pWeapon, pSkipEntities, 10); } } else if(IVehicle *pVehicle = pTarget->GetLinkedVehicle()) { // vehicle drivers don't seem to have current items, so need to add the vehicle itself here nSkip = pVehicle->GetSkipEntities(pSkipEntities, 10); } const float wallSafeDistance = 0.2f; // how far to keep camera from walls Vec3 dir = goal - worldPos; primitives::sphere sphere; sphere.center = worldPos; sphere.r = wallSafeDistance; geom_contact *pContact = 0; float hitDist = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, geom_colltype_player, 0, 0, 0, pSkipEntities, nSkip); // even when we have contact, keep the camera the same height above the target float minHeightDiff = dir.z; if(hitDist > 0 && pContact) { goal = worldPos + (hitDist * dir.GetNormalizedSafe()); if(goal.z - worldPos.z < minHeightDiff) { // can't move the camera far enough away from the player in this direction. Try moving it directly up a bit sphere.center = goal; // (move back just slightly to avoid colliding with the wall we've already found...) sphere.center -= dir.GetNormalizedSafe() * 0.05f; float newHitDist = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, Vec3(0, 0, minHeightDiff), ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, geom_colltype_player, 0, 0, 0, pSkipEntities, nSkip); float raiseDist = minHeightDiff - (goal.z - worldPos.z) - wallSafeDistance; if(newHitDist != 0) { raiseDist = MIN(minHeightDiff, newHitDist); } raiseDist = MAX(0.0f, raiseDist); goal.z += raiseDist; worldPos.z += raiseDist * 0.8f; } } int thisFrameId = gEnv->pRenderer->GetFrameID(); static int frameNo(thisFrameId); if(thisFrameId - frameNo > 5) { // reset positions viewOffset = goal - worldPos; entPos = worldPos; position = goal; } if(lastSpectatorTarget != m_in.stats_spectatorTarget) { viewOffset = goal - worldPos; entPos = worldPos; position = goal; lastSpectatorTarget = m_in.stats_spectatorTarget; } frameNo = thisFrameId; if(pTarget->GetLinkedVehicle()) { Interpolate(viewOffset, goal - worldPos, 5.0f, viewParams.frameTime); entPos = worldPos; viewParams.position = worldPos + viewOffset; position = viewParams.position; } else { Vec3 camPosChange = goal - position; Vec3 entPosChange = worldPos - entPos; if(camPosChange.GetLengthSquared() > 100.0f) { position = goal; } if(entPosChange.GetLengthSquared() > 100.0f) { entPos = worldPos; } Interpolate(position, goal, 5.0f, viewParams.frameTime); Interpolate(entPos, worldPos, 5.0f, viewParams.frameTime); viewParams.position = position; } Matrix33 rotation = Matrix33::CreateRotationVDir((entPos - viewParams.position).GetNormalizedSafe()); viewParams.rotation = Quat(rotation); m_io.bUsePivot = true; m_io.stats_bobCycle = 0.0; }