void CWeaponFX::PlayBulletFlyBySound() { if (!m_pWeapon || !m_pAmmo) return; if (m_pAmmo->eType != VECTOR) return; // Camera pos HOBJECT hCamera = g_pGameClientShell->GetCamera(); LTVector vPos; g_pLTClient->GetObjectPos(hCamera, &vPos); // We only play the flyby sound if we won't hear an impact or // fire sound... LTVector vDist = m_vFirePos - vPos; if (vDist.Mag() < m_pWeapon->nFireSoundRadius) return; if (m_pAmmo->pImpactFX) { vDist = m_vPos - vPos; if (vDist.Mag() < m_pAmmo->pImpactFX->nSoundRadius) return; } // See if the camera is close enough to the bullet path to hear the // bullet... LTFLOAT fRadius = g_cvarFlyByRadius.GetFloat(); LTVector vDir = m_vDir; const LTVector vRelativePos = vPos - m_vFirePos; const LTFLOAT fRayDist = vDir.Dot(vRelativePos); LTVector vBulletDir = (vDir*fRayDist - vRelativePos); const LTFLOAT fDistSqr = vBulletDir.MagSqr(); if (fDistSqr < fRadius*fRadius) { vPos += vBulletDir; g_pClientSoundMgr->PlaySoundFromPos(vPos, "Guns\\Snd\\flyby.wav", g_cvarFlyBySoundRadius.GetFloat(), SOUNDPRIORITY_MISC_LOW); } }
inline void linesystem_CalcExtents(LineSystem *pSystem) { LTVector half; half = pSystem->m_MaxPos - pSystem->m_MinPos; half *= 0.5f; pSystem->m_SystemCenter = pSystem->m_MinPos + half; pSystem->m_SystemRadius = half.Mag() + 1.0f; }
void CRagDollDistanceConstraint::Apply(uint32 nPosIndex) { //find the distance between them LTVector vToOther = m_pNode[1]->m_vPosition[nPosIndex] - m_pNode[0]->m_vPosition[nPosIndex]; float fDist = vToOther.Mag(); float fScale = (fDist - m_fDistance) / fDist; //now offset the vertices m_pNode[0]->m_vPosition[nPosIndex] += vToOther * fScale * m_fNode1Weight; m_pNode[1]->m_vPosition[nPosIndex] -= vToOther * fScale * m_fNode2Weight; }
LTBOOL CProjectile::UpdateDoVectorValues(SURFACE & surf, LTFLOAT fThickness, LTVector vImpactPos, LTVector & vFrom, LTVector & vTo) { // See if we've traveled the distance... LTVector vDistTraveled = vImpactPos - m_vFirePos; LTFLOAT fDist = m_fRange - vDistTraveled.Mag(); if (fDist < 1.0f) return LTTRUE; // Just dampen based on the bute file values, don't worry about the // surface thinkness... m_fInstDamage *= surf.fBulletDamageDampen; fDist *= surf.fBulletRangeDampen; int nPerturb = surf.nMaxShootThroughPerturb; if (nPerturb) { LTRotation rRot; g_pLTServer->AlignRotation(&rRot, &m_vDir, LTNULL); LTVector vU, vR, vF; g_pLTServer->GetRotationVectors(&rRot, &vU, &vR, &vF); LTFLOAT fRPerturb = ((LTFLOAT)GetRandom(-nPerturb, nPerturb))/1000.0f; LTFLOAT fUPerturb = ((LTFLOAT)GetRandom(-nPerturb, nPerturb))/1000.0f; m_vDir += (vR * fRPerturb); m_vDir += (vU * fUPerturb); m_vDir.Norm(); } // Make sure we move the from position... if (vFrom.Equals(vImpactPos, 1.0f)) { vFrom += m_vDir; } else { vFrom = vImpactPos; } vTo = vFrom + (m_vDir * fDist); return LTFALSE; }
HLTSOUND CClientSoundMgr::PlaySound(PlaySoundInfo & psi) { HLTSOUND hSnd = LTNULL; if (!GetConsoleInt("SoundEnable",1)) return LTNULL; // Play the sound... // Optimization, if we can't hear the sound and it isn't looping // don't play it! if ((psi.m_dwFlags & PLAYSOUND_3D) && !(psi.m_dwFlags & PLAYSOUND_LOOP) && g_vtSoundPlayOnlyIfHeard.GetFloat()) { LTVector vListenerPos; bool bListenerInClient; LTRotation rRot; g_pLTClient->GetListener(&bListenerInClient, &vListenerPos, &rRot); LTVector vPos = psi.m_vPosition - vListenerPos; if (vPos.Mag() > psi.m_fOuterRadius) { return LTNULL; } } LTRESULT hResult = g_pLTClient->SoundMgr()->PlaySound(&psi, hSnd); if (hResult != LT_OK) { g_pLTClient->CPrint("ERROR in CClientSoundMgr::PlaySound() - Couldn't play sound '%s'", psi.m_szSoundName); return LTNULL; } // [RP] The sound handle that gets passed into PlaySound(), hSnd, will *always* get set. Since the // SoundTracks get recycled if we return hSnd we may be setting a handle to a SoundTrack that will // get removed by the engine and put back on the free list. Any future calls to KillSound() using that // handle will be killing the wrong sound or *worse*, a sound that doesn't exist. Returning the handle // of the PlaySoundInfo struct will ensure we only return a valid handle if explicitly told to (ie. PLAYSOUND_GETHANDLE); return psi.m_hSound; }
static void SetFallbackActivationObject(HOBJECT hObj, IntersectQuery* pQuery) { if (!pQuery) return; // Use the Query object to determine how far away this object is from the // activation start point. It must be within the activation range for us // to use it... LTVector vObjPos; g_pLTClient->GetObjectPos(hObj, &vObjPos); LTVector vDir = (pQuery->m_From - vObjPos); float fDist = vDir.Mag(); // Since fDist is the distance to the center of the object we need to account // for the object's radius. This may give us some false positives since the // object's using an axis-aligned bounding box, but a false positive is better // than a negative ;). Since we can't get the object's dims here's a nice // magic number for all you kiddies at home that are paying attention... fDist -= 35.0f; if (fDist > g_vtActivationDistance.GetFloat()) return; if (g_adFallbackActivationObject.m_hTarget) { // Don't set if my current target is activatable (doesn't matter // if new one is activatable or not, we'll just keep the one we have)... if ( !IsUserFlagSet(g_adFallbackActivationObject.m_hTarget, (USRFLG_CAN_ACTIVATE | USRFLG_CAN_SEARCH)) ) { g_adFallbackActivationObject.m_hTarget = hObj; } } else { g_adFallbackActivationObject.m_hTarget = hObj; } }
HLTSOUND CClientSoundMgr::PlaySound(PlaySoundInfo & psi) { HLTSOUND hSnd = LTNULL; if (!GetConsoleInt("SoundEnable",1)) return LTNULL; // Play the sound... // Optimization, if we can't hear the sound and it isn't looping // don't play it! if ((psi.m_dwFlags & PLAYSOUND_3D) && !(psi.m_dwFlags & PLAYSOUND_LOOP) && g_vtSoundPlayOnlyIfHeard.GetFloat()) { LTVector vListenerPos; LTBOOL bListenerInClient; LTRotation rRot; g_pLTClient->GetListener(&bListenerInClient, &vListenerPos, &rRot); LTVector vPos = psi.m_vPosition - vListenerPos; if (vPos.Mag() > psi.m_fOuterRadius) { return LTNULL; } } LTRESULT hResult = g_pLTClient->PlaySound(&psi); if (hResult == LT_OK) { hSnd = psi.m_hSound; } else { _ASSERT(LTFALSE); m_pInterface->CPrint("ERROR in CClientSoundMgr::PlaySound() - Couldn't play sound '%s'", psi.m_szSoundName); return LTNULL; } return hSnd; }
LTBOOL CAIMovement::IsAtDest(const LTVector& vDest) { LTVector vMove = vDest - m_pAI->GetPosition(); if ( !m_pAI->GetAnimationContext()->IsPropSet(kAPG_Movement, kAP_ClimbUp) && !m_pAI->GetAnimationContext()->IsPropSet(kAPG_Movement, kAP_ClimbDown) && !m_pAI->GetAnimationContext()->IsPropSet(kAPG_Movement, kAP_ClimbUp) && !m_pAI->GetAnimationContext()->IsPropSet(kAPG_Movement, kAP_ClimbDown) ) { vMove.y = 0.0f; } // See if we're already at the dest exactly. LTFLOAT fRemainingDist = vMove.Mag(); if(fRemainingDist == 0.f) { Clear(); m_eState = eStateDone; return LTTRUE; } return LTFALSE; }
static void GeneratePolyGridFresnelAlpha(const LTVector& vViewPos, CPolyGridVertex* pVerts, LTPolyGrid* pGrid, uint32 nNumVerts) { //we need to transform the camera position into our view space LTMatrix mInvWorldTrans; mInvWorldTrans.Identity(); mInvWorldTrans.SetTranslation(-pGrid->GetPos()); LTMatrix mOrientation; pGrid->m_Rotation.ConvertToMatrix(mOrientation); mInvWorldTrans = mOrientation * mInvWorldTrans; LTVector vCameraPos = mInvWorldTrans * vViewPos; //now generate the internals of the polygrid CPolyGridVertex* pCurrVert = pVerts; CPolyGridVertex* pEnd = pCurrVert + nNumVerts; //determine the fresnel table that we are going to be using const CFresnelTable* pTable = g_FresnelCache.GetTable(LTMAX(1.0003f, pGrid->m_fFresnelVolumeIOR), pGrid->m_fBaseReflection); //use a vector from the camera to the center of the grid to base our approximations off of. The further //we get to the edges the more likely this error will be, but it is better than another sqrt per vert LTVector vToPGPt; while(pCurrVert < pEnd) { //the correct but slow way, so only do it every once in a while //if((pCurrVert - g_TriVertList) % 4 == 0) { vToPGPt = vCameraPos - pCurrVert->m_Vec; } pCurrVert->m_nColor |= pTable->GetValue(vToPGPt.Dot(pCurrVert->m_Normal) / vToPGPt.Mag()); ++pCurrVert; } }
LTBOOL CProjectile::HandleVectorImpact(IntersectInfo & iInfo, LTVector & vFrom, LTVector & vTo) { // Get the surface type... SurfaceType eSurfType = GetSurfaceType(iInfo); // See if we hit an invisible surface... if (eSurfType == ST_INVISIBLE) { if (!CalcInvisibleImpact(iInfo, eSurfType)) { SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurfType); if (!pSurf) return LTTRUE; return UpdateDoVectorValues(*pSurf, 0, iInfo.m_Point, vFrom, vTo); } } // See if we hit an object that should be damaged... LTBOOL bHitWorld = IsMainWorld(iInfo.m_hObject); if (!bHitWorld && eSurfType != ST_LIQUID) { ImpactDamageObject(m_hFiredFrom, iInfo.m_hObject); } // If the fire position is the initial fire position, use the flash // position when building the impact special fx... LTVector vFirePos = (vFrom.Equals(m_vFirePos) ? m_vFlashPos : vFrom); AddImpact(iInfo.m_hObject, vFirePos, iInfo.m_Point, iInfo.m_Plane.m_Normal, eSurfType); // See if we can shoot through the surface... SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurfType); if (!pSurf) return LTTRUE; // Done. if (pSurf->bCanShootThrough) { int nMaxThickness = pSurf->nMaxShootThroughThickness; if (nMaxThickness == 0) { // Special case of always being able to shoot through surface... // Calculate new values for next DoVector iteration... return UpdateDoVectorValues(*pSurf, 0, iInfo.m_Point, vFrom, vTo); } // Test if object/wall intersected is thin enough to be shot // through... // Test object case first... if (!bHitWorld && iInfo.m_hObject) { // Test to see if we can shoot through the object... LTVector vDims; g_pLTServer->GetObjectDims(iInfo.m_hObject, &vDims); if (vDims.x*2.0f >= nMaxThickness && vDims.y*2.0f >= nMaxThickness && vDims.z*2.0f >= nMaxThickness) { // Can't shoot through this object... return LTTRUE; } } // Determine if we shot through the wall/object... IntersectInfo iTestInfo; IntersectQuery qTestInfo; qTestInfo.m_From = iInfo.m_Point + (m_vDir * (LTFLOAT)(nMaxThickness + 1)); qTestInfo.m_To = iInfo.m_Point - m_vDir; qTestInfo.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID | INTERSECT_HPOLY; qTestInfo.m_FilterFn = DoVectorFilterFn; qTestInfo.m_pUserData = m_hFiredFrom; if (g_pLTServer->IntersectSegment(&qTestInfo, &iTestInfo)) { // Calculate new values for next DoVector iteration... LTVector vThickness = iTestInfo.m_Point - iInfo.m_Point; return UpdateDoVectorValues(*pSurf, vThickness.Mag(), iTestInfo.m_Point, vFrom, vTo); } } return LTTRUE; }
LTBOOL CAIMovementHuman::Update() { LTFLOAT fTimeDelta = g_pLTServer->GetFrameTime(); switch ( m_eState ) { case eStateUnset: { } break; case eStateSet: { // Set our speed based on our movement type if ( GetAI()->GetAnimationContext()->IsPropSet(aniWalk) ) { GetAI()->Walk(); } else if ( GetAI()->GetAnimationContext()->IsPropSet(aniRun) ) { GetAI()->Run(); } else if ( GetAI()->GetAnimationContext()->IsPropSet(aniSwim) ) { GetAI()->Swim(); } else { // We're not moving yet... GetAI()->Stop(); } // Find our unit movement vector LTVector vMove = m_vDest - GetAI()->GetPosition(); if ( !m_bUnderwater ) { vMove.y = 0.0f; } // See if we'll overshoot our LTFLOAT fRemainingDist = vMove.Mag(); LTFLOAT fMoveDist; fMoveDist = GetAI()->GetSpeed()*fTimeDelta; // If we'd overshoot our destination, just move us there if ( fRemainingDist < fMoveDist ) { fMoveDist = fRemainingDist; m_eState = eStateDone; } // Scale based on our movement distance vMove.Norm(); vMove *= fMoveDist; // Calculate our new position LTVector vNewPos = GetAI()->GetPosition() + vMove; // Move us - this is an expensive call GetAI()->Move(vNewPos); // Face us in the right direction GetAI()->FacePos(vNewPos); } break; case eStateDone: { } break; } return LTTRUE; }
//function that handles the custom rendering void CTracerFX::RenderTracer(ILTCustomRenderCallback* pInterface, const LTRigidTransform& tCamera) { //track our performance CTimedSystemBlock TimingBlock(g_tsClientFXTracer); //first determine the length, position, and U range for this tracer (this allows for some //early outs) float fTracerLen = GetTracerLength(); float fTracerStart = m_fRayPosition; float fTracerEnd = fTracerStart - fTracerLen; if((fTracerStart <= 0.0f) || (fTracerEnd >= m_fRayLength)) { //the tracer has fully gone through the ray, don't render return; } //now we need to clip the extents to the range [0..ray length], and handle cropping of the texture float fUMin = 0.0f; float fUMax = 1.0f; if(fTracerEnd < 0.0f) { //adjust the U max if we are cropping if(GetProps()->m_bCropTexture) { fUMax += fTracerEnd / fTracerLen; } fTracerEnd = 0.0f; } if(fTracerStart >= m_fRayLength) { //adjust the U min if we are cropping if(GetProps()->m_bCropTexture) { fUMin += (fTracerStart - m_fRayLength) / fTracerLen; } fTracerStart = m_fRayLength; } //setup our vertex declaration if(pInterface->SetVertexDeclaration(g_ClientFXVertexDecl.GetTexTangentSpaceDecl()) != LT_OK) return; //bind a quad index stream if(pInterface->BindQuadIndexStream() != LT_OK) return; //sanity check to ensure that we can at least render a sprite LTASSERT(QUAD_RENDER_INDEX_STREAM_SIZE >= 6, "Error: Quad index list is too small to render a tracer"); LTASSERT(DYNAMIC_RENDER_VERTEX_STREAM_SIZE / sizeof(STexTangentSpaceVert) > 4, "Error: Dynamic vertex buffer size is too small to render a tracer"); //we need to determine the facing of this tracer. This is formed by creating a plane from the points //Camera, Start, and another point on the ray. The plane normal is then the up, the right is the ray //direction, and the normal is ray cross plane normal. LTVector vStartToCamera = m_vStartPos - tCamera.m_vPos; float fMag = vStartToCamera.Mag( ); if( fMag < 0.00001f ) { vStartToCamera = tCamera.m_rRot.Forward(); } else { vStartToCamera /= fMag; } //determine the up vector LTVector vUp = vStartToCamera.Cross(m_vDirection); vUp.Normalize(); //now determine the actual normal (doesn't need to be normalized since the vectors are //unit length and orthogonal) LTVector vNormal = vUp.Cross(m_vDirection); //lock down our buffer for rendering SDynamicVertexBufferLockRequest LockRequest; if(pInterface->LockDynamicVertexBuffer(4, LockRequest) != LT_OK) return; //fill in our sprite vertices STexTangentSpaceVert* pCurrOut = (STexTangentSpaceVert*)LockRequest.m_pData; //determine the color of this tracer float fUnitLifetime = GetUnitLifetime(); uint32 nColor = CFxProp_Color4f::ToColor(GetProps()->m_cfcColor.GetValue(fUnitLifetime)); //calculate the front of the tracer in world space LTVector vFront = m_vStartPos + m_vDirection * fTracerStart; LTVector vBack = m_vStartPos + m_vDirection * fTracerEnd; //and the thickness vector float fThickness = GetProps()->m_ffcThickness.GetValue(fUnitLifetime); LTVector vThickness = vUp * (fThickness * 0.5f); //fill in the particle vertices pCurrOut[0].m_vPos = vFront + vThickness; pCurrOut[0].m_vUV.Init(fUMin, 0.0f); pCurrOut[1].m_vPos = vBack + vThickness; pCurrOut[1].m_vUV.Init(fUMax, 0.0f); pCurrOut[2].m_vPos = vBack - vThickness; pCurrOut[2].m_vUV.Init(fUMax, 1.0f); pCurrOut[3].m_vPos = vFront - vThickness; pCurrOut[3].m_vUV.Init(fUMin, 1.0f); //setup the remaining vertex components for(uint32 nCurrVert = 0; nCurrVert < 4; nCurrVert++) { pCurrOut[nCurrVert].m_nPackedColor = nColor; pCurrOut[nCurrVert].m_vNormal = vNormal; pCurrOut[nCurrVert].m_vTangent = vUp; pCurrOut[nCurrVert].m_vBinormal = m_vDirection; } //unlock and render the batch pInterface->UnlockAndBindDynamicVertexBuffer(LockRequest); pInterface->RenderIndexed( eCustomRenderPrimType_TriangleList, 0, 6, LockRequest.m_nStartIndex, 0, 4); }
bool CTracerFX::Init(const FX_BASEDATA *pBaseData, const CBaseFXProps *pProps) { //track our performance CTimedSystemBlock TimingBlock(g_tsClientFXTracer); //cleanup any old data Term(); // Perform base class initialisation if( !CBaseFX::Init(pBaseData, pProps)) return false; //determine our starting position and orientation LTVector vPos; LTRotation rRot; GetCurrentTransform(0.0f, vPos, rRot); m_vStartPos = vPos; //determine what target position we want to use LTVector vTargetPos; //if we are using target data, just get the transform from this if(GetProps()->m_bUseTargetPos && pBaseData->m_bUseTargetData) { LTRigidTransform tTargetOffset(pBaseData->m_vTargetOffset, LTRotation::GetIdentity()); LTRigidTransform tWS = GetWorldSpaceTransform(NULL, pBaseData->m_hTargetObject, INVALID_MODEL_NODE, INVALID_MODEL_SOCKET, tTargetOffset); LTVector vToTarget = tWS.m_vPos - vPos; m_fRayLength = vToTarget.Mag(); m_vDirection = vToTarget / m_fRayLength; vTargetPos = tWS.m_vPos; } else { //determine the end point where we want to stop testing m_vDirection = rRot.Forward(); m_fRayLength = GetProps()->m_fMaxDistance; vTargetPos = vPos + m_vDirection * m_fRayLength; } //handle intersection if we need to if(GetProps()->m_bBlockedByGeometry) { //we need to perform a ray cast and determine if we actually hit anything in the world. If not, //we just want to use the maximum tracer length IntersectQuery iQuery; IntersectInfo iInfo; iQuery.m_Flags = INTERSECT_HPOLY | INTERSECT_OBJECTS | IGNORE_NONSOLID; iQuery.m_FilterFn = TracerListFilterFn; iQuery.m_pUserData = NULL; iQuery.m_From = m_vStartPos; iQuery.m_To = vTargetPos; //now find the point of intersection if( g_pLTClient->IntersectSegment( iQuery, &iInfo ) ) { //we hit something, so use that as our ending position m_fRayLength = (iInfo.m_Point - m_vStartPos).Mag(); } } //now determine our starting position (this is zero unless we start emitted, in which case //it is the length of the tracer) if(GetProps()->m_bStartEmitted) { //determine the length of this tracer m_fRayPosition = GetTracerLength(); } // Combine the direction we would like to face with our parents rotation... ObjectCreateStruct ocs; //create a custom render object with the associated material ocs.m_Pos = vPos; ocs.m_Rotation = rRot; ocs.m_ObjectType = OT_CUSTOMRENDER; if(!GetProps()->m_bSolid) ocs.m_Flags2 |= FLAG2_FORCETRANSLUCENT; if(!GetProps()->m_bTranslucentLight) ocs.m_Flags |= FLAG_NOLIGHT; m_hObject = g_pLTClient->CreateObject( &ocs ); if( !m_hObject ) return false; //setup our rendering layer if(GetProps()->m_bPlayerView) g_pLTClient->GetRenderer()->SetObjectDepthBiasTableIndex(m_hObject, eRenderLayer_Player); //setup the callback on the object so that it will render us g_pLTClient->GetCustomRender()->SetRenderingSpace(m_hObject, eRenderSpace_World); g_pLTClient->GetCustomRender()->SetRenderCallback(m_hObject, CustomRenderCallback); g_pLTClient->GetCustomRender()->SetCallbackUserData(m_hObject, this); //load up the material for this particle system, and assign it to the object HMATERIAL hMaterial = g_pLTClient->GetRenderer()->CreateMaterialInstance(GetProps()->m_pszMaterial); g_pLTClient->GetCustomRender()->SetMaterial(m_hObject, hMaterial); g_pLTClient->GetRenderer()->ReleaseMaterialInstance(hMaterial); //we don't setup the visibility for this object yet since when it starts out it is not visible //setup the dynamic light object if the user specified a radius if(GetProps()->m_fLightRadius > 0.1f) { ObjectCreateStruct LightOCS; LightOCS.m_Pos = vPos; LightOCS.m_Rotation = rRot; LightOCS.m_ObjectType = OT_LIGHT; m_hTracerLight = g_pLTClient->CreateObject( &LightOCS ); //setup the properties of the light g_pLTClient->SetLightRadius(m_hTracerLight, GetProps()->m_fLightRadius); g_pLTClient->SetObjectColor(m_hTracerLight, VEC4_EXPAND(GetProps()->m_vLightColor)); g_pLTClient->SetLightType(m_hTracerLight, eEngineLight_Point); g_pLTClient->SetLightDetailSettings(m_hTracerLight, GetProps()->m_eLightLOD, GetProps()->m_eWorldShadowsLOD, GetProps()->m_eObjectShadowsLOD); } // Success !! return true; }
LTBOOL CAIMovementHelicopter::Update() { LTFLOAT fTimeDelta = g_pLTServer->GetFrameTime(); switch ( m_eState ) { case eStateUnset: { } break; case eStateSet: { // Find our unit movement vector LTVector vMove = m_vDest - GetAI()->GetPosition(); // vMove.y = 0.0f; // See if we'll overshoot our LTFLOAT fRemainingDist = vMove.Mag(); LTFLOAT fMoveDist; fMoveDist = GetAI()->GetSpeed()*fTimeDelta; vMove.Norm(); LTBOOL bCrossed = LTFALSE; // See if we crossed the dest plane if ( (vMove.Dot(m_vDestDir) < 0.0f) ) { bCrossed = LTTRUE; } // If we'd overshoot our destination, just move us there if ( (fRemainingDist < fMoveDist) || bCrossed ) { fMoveDist = fRemainingDist; m_eState = eStateDone; } // Scale based on our movement distance vMove *= fMoveDist; // Calculate our new position LTVector vNewPos = GetAI()->GetPosition() + vMove; // Move us - this is an expensive call // GetAI()->Move(vNewPos); // Face us in the right direction GetAI()->FacePos(vNewPos); } break; case eStateDone: { } break; } return LTTRUE; }
LTBOOL CSearchLightFX::Update() { if (!m_pClientDE || !m_hServerObject || m_bWantRemove || !m_hBeam) return LTFALSE; uint32 dwFlags = 0; // Update the lens flare... m_LensFlare.Update(); // Hide/show the fx if necessary... if (m_hServerObject) { uint32 dwUserFlags; m_pClientDE->GetObjectUserFlags(m_hServerObject, &dwUserFlags); if (!(dwUserFlags & USRFLG_VISIBLE)) // Hide fx { if (m_hBeam) { dwFlags = m_pClientDE->GetObjectFlags(m_hBeam); m_pClientDE->SetObjectFlags(m_hBeam, dwFlags & ~FLAG_VISIBLE); } if (m_hLight) { dwFlags = m_pClientDE->GetObjectFlags(m_hLight); m_pClientDE->SetObjectFlags(m_hLight, dwFlags & ~FLAG_VISIBLE); } return LTTRUE; } else // Make all fx visible { if (m_hBeam) { dwFlags = m_pClientDE->GetObjectFlags(m_hBeam); m_pClientDE->SetObjectFlags(m_hBeam, dwFlags | FLAG_VISIBLE); } if (m_hLight) { dwFlags = m_pClientDE->GetObjectFlags(m_hLight); m_pClientDE->SetObjectFlags(m_hLight, dwFlags | FLAG_VISIBLE); } } } // Update the position/rotation of the beam... LTVector vPos; m_pClientDE->GetObjectPos(m_hServerObject, &vPos); LTRotation rRot; m_pClientDE->GetObjectRotation(m_hServerObject, &rRot); LTVector vU, vR, vF; m_pClientDE->GetRotationVectors(&rRot, &vU, &vR, &vF); // See how long to make the beam... LTVector vDest = vPos + (vF * m_cs.fBeamLength); IntersectInfo iInfo; IntersectQuery qInfo; qInfo.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID; qInfo.m_FilterFn = AttackerLiquidFilterFn; qInfo.m_pUserData = m_hServerObject; qInfo.m_From = vPos; qInfo.m_To = vDest; if (g_pLTClient->IntersectSegment(&qInfo, &iInfo)) { vDest = iInfo.m_Point; } LTVector vDir = vDest - vPos; LTFLOAT fDistance = vDir.Mag(); vDir.Norm(); LTVector vNewPos = vPos + vDir * fDistance/2.0f; m_pClientDE->AlignRotation(&rRot, &vDir, NULL); if (m_cs.fBeamRotTime > 0.0f) { m_fBeamRotation += (360.0f/m_cs.fBeamRotTime * g_pGameClientShell->GetFrameTime()); m_fBeamRotation = m_fBeamRotation > 360.0f ? m_fBeamRotation - 360.0f : m_fBeamRotation; m_pClientDE->RotateAroundAxis(&rRot, &vDir, DEG2RAD(m_fBeamRotation)); } m_pClientDE->SetObjectRotation(m_hBeam, &rRot); m_pClientDE->SetObjectPos(m_hBeam, &vNewPos); LTVector vScale(m_cs.fBeamRadius, m_cs.fBeamRadius, fDistance); m_pClientDE->SetObjectScale(m_hBeam, &vScale); // Move the dynamic light... if (m_hLight) { vDest -= (vDir * 5.0f); m_pClientDE->SetObjectPos(m_hLight, &vDest); } return LTTRUE; }
void CAIMovement::AvoidDynamicObstacles(LTVector* pvNewPos, EnumAnimMovement eMovementType) { LTFLOAT fRadius = 128.f; LTFLOAT fRadiusSqr = fRadius * fRadius; LTVector vMyPos = m_pAI->GetPosition(); // Calculate the horizontal velocity. LTVector vVel = *pvNewPos - vMyPos; vVel.y = 0.f; // Bail if no velocity. if( ( vVel.x == 0.f ) && ( vVel.z == 0.f ) ) { return; } LTFLOAT fMag = vVel.Mag(); LTVector vTotalForce(0.f, 0.f, 0.f); LTVector vObstaclePos; LTFLOAT fDistSqr; LTFLOAT fForce; LTVector vForce; CTList<CCharacter*>* lstChars = LTNULL; CCharacter** pCur = LTNULL; // Iterate over all characters in the world. int cCharLists = g_pCharacterMgr->GetNumCharacterLists(); for ( int iList = 0 ; iList < cCharLists ; ++iList ) { lstChars = g_pCharacterMgr->GetCharacterList(iList); pCur = lstChars->GetItem(TLIT_FIRST); while( pCur ) { CCharacter* pChar = (CCharacter*)*pCur; pCur = lstChars->GetItem(TLIT_NEXT); // Ignore myself. if( pChar == m_pAI ) { continue; } // Ignore characters that are too close to our dest. // The pathfinding system requires AIs to reach waypoints. g_pLTServer->GetObjectPos( pChar->m_hObject, &vObstaclePos ); if( vObstaclePos.DistSqr( m_vDest ) <= fRadiusSqr ) { continue; } // Only characters within radius have forces that affect me. fDistSqr = vObstaclePos.DistSqr( vMyPos ); if( fDistSqr >= fRadiusSqr ) { continue; } // Calculate the force vector from the obstacle to myself. fForce = fRadius - (LTFLOAT)sqrt( fDistSqr ); fForce /= fRadius; fForce *= fForce; fForce *= ( 2.f * fMag ); vForce = vMyPos - vObstaclePos; vForce.y = 0.f; vForce.Normalize(); vForce *= fForce; // Accumulate the total force from all obstacles. vTotalForce += vForce; } } // Bail if no forces are affecting me. if( ( vTotalForce.x == 0.f ) && ( vTotalForce.z == 0.f ) ) { return; } // Calculate a new velocity vector. LTVector vNewVel = vVel + vTotalForce; // Constrain velocity so that is never deviates more than // 90 degrees in either direction. This prevents AIs from ever // reversing their direction when the forces are stronger than // the initial velocity. if( vNewVel.Dot( vVel ) < 0.f ) { vVel.Normalize(); LTVector vUp( 0.f, 1.f, 0.f ); LTVector vRight = vUp.Cross( vVel ); if( vRight.Dot( vNewVel ) < 0.f ) { vNewVel = -vRight; } else { vNewVel = vRight; } } // Keep magnitude of velocity constant. vNewVel.Normalize(); vNewVel *= fMag; // Calculate new position. // Bail if new position is out of volumes. // Bail if new position is in wrong volume. LTVector vNewPos = vMyPos + vNewVel; if( !m_pDestVolume->Inside2d( vNewPos, m_pAI->GetRadius() ) ) { return; } // Move toward new position. *pvNewPos = vNewPos; if( eMovementType == kAM_Encode_GB ) { m_pAI->FacePosMoving( m_pAI->GetPosition() ); } else { m_pAI->FacePosMoving( vNewPos ); } }
bool i_IntersectSegment(IntersectQuery *pQuery, IntersectInfo *pInfo, WorldTree *pWorldTree) { float InvVV, VP, testMag; ++g_nIntersectCalls; CountAdder cTicks_Intersect(&g_Ticks_Intersect); // Init.. g_pCurQuery = pQuery; g_pIntersection = LTNULL; g_pWorldIntersection = LTNULL; g_hWorldPoly = INVALID_HPOLY; g_hModelNode = INVALID_MODEL_NODE; g_bProcessNonSolid = !(pQuery->m_Flags & IGNORE_NONSOLID); g_bProcessObjects = !!(pQuery->m_Flags & INTERSECT_OBJECTS); g_bCheckIfFromPointIsInsideObject = !!(pQuery->m_Flags & CHECK_FROM_POINT_INSIDE_OBJECTS); g_bProcessModelObbs = !!(pQuery->m_Flags & INTERSECT_MODELOBBS); g_IntersectionBestDistSqr = (pQuery->m_From - pQuery->m_To).MagSqr() + 1.0f; // Precalculate stuff to totally accelerate i_QuickSphereTest. g_VOrigin = pQuery->m_From; g_V = pQuery->m_To - pQuery->m_From; // Calc Direction g_VDir = g_V.Unit(); g_LineLen = g_V.Mag(); g_V /= g_LineLen; // Was it too short? testMag = g_V.MagSqr(); if (testMag < 0.5f || testMag > 2.0f) { return false; } VP = g_V.Dot(pQuery->m_From); InvVV = 1.0f / g_V.MagSqr(); g_VTimesInvVV = g_V * InvVV; g_VPTimesInvVV = VP * InvVV; if (pQuery->m_Flags & INTERSECT_HPOLY) { g_FindIntersectionsFn = i_FindIntersectionsHPoly; } else { g_FindIntersectionsFn = i_FindIntersections; } // Start at the world tree. pWorldTree->IntersectSegment((LTVector*)&pQuery->m_From, (LTVector*)&pQuery->m_To, i_ISCallback, LTNULL); // If an object was hit, use it! if (g_pIntersection) { pInfo->m_Point = g_IntersectionPos; pInfo->m_Plane = g_IntersectionPlane; pInfo->m_hObject = (HOBJECT)g_pIntersection; pInfo->m_hPoly = g_hWorldPoly; pInfo->m_hNode = g_hModelNode; if (g_pWorldIntersection) { pInfo->m_SurfaceFlags = g_pWorldIntersection->m_pPoly->GetSurface()->m_TextureFlags; } else { pInfo->m_SurfaceFlags = 0; } return true; } else { return false; } }
//function that handles the custom rendering void CBaseSpriteFX::RenderSprite(ILTCustomRenderCallback* pInterface, const LTRigidTransform& tCamera) { //setup our vertex declaration if(pInterface->SetVertexDeclaration(g_ClientFXVertexDecl.GetTexTangentSpaceDecl()) != LT_OK) return; //bind a quad index stream if(pInterface->BindQuadIndexStream() != LT_OK) return; //determine how many indices we are going to need uint32 nNumIndices = (GetProps()->m_bTwoSided) ? 12 : 6; uint32 nNumVertices = (GetProps()->m_bTwoSided) ? 8 : 4; //sanity check to ensure that we can at least render a sprite LTASSERT(QUAD_RENDER_INDEX_STREAM_SIZE >= nNumIndices, "Error: Quad index list is too small to render a sprite"); LTASSERT(DYNAMIC_RENDER_VERTEX_STREAM_SIZE / sizeof(STexTangentSpaceVert) >= nNumVertices, "Error: Dynamic vertex buffer size is too small to render a sprite"); //determine the up and right vectors for the sprite LTVector vTangent, vBinormal; //get the position of this sprite LTRigidTransform tObjTransform; g_pLTClient->GetObjectTransform(m_hObject, &tObjTransform); //determine the center of this sprite LTVector vCenter = tObjTransform.m_vPos; //determine the orientation of the sprite based upon its facing if(GetProps()->m_bAlignToCamera) { //apply the to camera offset LTVector vToCamera = tCamera.m_vPos - vCenter; float fScale = GetProps()->m_fToCameraOffset / vToCamera.Mag(); vCenter += vToCamera * fScale; //perform the rotation float fCosAng = LTCos(m_fCurrRotationRad); float fSinAng = LTSin(m_fCurrRotationRad); LTVector vRight = tCamera.m_rRot.Right(); LTVector vUp = -tCamera.m_rRot.Up(); vTangent = fCosAng * vRight + fSinAng * vUp; vBinormal = fCosAng * vUp - fSinAng * vRight; } else if(GetProps()->m_bAlignAroundZ) { //we want to orient around the Z axis and align to the camera //we need to determine our U vector, which is always our forward LTVector vU = tObjTransform.m_rRot.Forward(); //and now we want to offset our center so that we are anchored on the right hand //side of the sprite to the point vCenter += vU * (m_fWidth * 0.5f); //determine the axis from our camera to our object LTVector vToCamera = tCamera.m_vPos - vCenter; //and now derive our V vector from the forward and the direction to the camera LTVector vV = vToCamera.Cross(vU); //detect degenerate cases if(vV == LTVector::GetIdentity()) { //degenerate case, any orientation will work fine since the sprite won't //be visible anyway vV.Init(0.0f, 1.0f, 0.0f); } //and normalize our vector vV.Normalize(); //now we can determine our tangent and binormal vectors vTangent = -vU; vBinormal = vV; } else { vTangent = -tObjTransform.m_rRot.Right(); vBinormal = -tObjTransform.m_rRot.Up(); } LTVector vNormal = vBinormal.Cross(vTangent); //scale the right and down values to be the appropriate size LTVector vRight = vTangent * m_fWidth * -0.5f; LTVector vDown = vBinormal * m_fWidth * GetProps()->m_fAspectRatio * 0.5f; //lock down our buffer for rendering SDynamicVertexBufferLockRequest LockRequest; if(pInterface->LockDynamicVertexBuffer(nNumVertices, LockRequest) != LT_OK) return; //fill in our sprite vertices STexTangentSpaceVert* pCurrOut = (STexTangentSpaceVert*)LockRequest.m_pData; uint32 nColor = SETRGBA( (uint8)(m_vColor.x * 255.0f), (uint8)(m_vColor.y * 255.0f), (uint8)(m_vColor.z * 255.0f), (uint8)(m_vColor.w * 255.0f)); //fill in the particle vertices pCurrOut[0].m_vPos = vCenter + vRight - vDown; pCurrOut[0].m_vUV.Init(0.0f, 0.0f); pCurrOut[1].m_vPos = vCenter - vRight - vDown; pCurrOut[1].m_vUV.Init(1.0f, 0.0f); pCurrOut[2].m_vPos = vCenter - vRight + vDown; pCurrOut[2].m_vUV.Init(1.0f, 1.0f); pCurrOut[3].m_vPos = vCenter + vRight + vDown; pCurrOut[3].m_vUV.Init(0.0f, 1.0f); //setup the remaining vertex components for(uint32 nCurrVert = 0; nCurrVert < 4; nCurrVert++) { pCurrOut[nCurrVert].m_nPackedColor = nColor; pCurrOut[nCurrVert].m_vNormal = vNormal; pCurrOut[nCurrVert].m_vTangent = vTangent; pCurrOut[nCurrVert].m_vBinormal = vBinormal; } //and fill in the back side if appropriate if(GetProps()->m_bTwoSided) { pCurrOut[4].m_vPos = vCenter - vRight - vDown; pCurrOut[4].m_vUV.Init(1.0f, 0.0f); pCurrOut[5].m_vPos = vCenter + vRight - vDown; pCurrOut[5].m_vUV.Init(0.0f, 0.0f); pCurrOut[6].m_vPos = vCenter + vRight + vDown; pCurrOut[6].m_vUV.Init(0.0f, 1.0f); pCurrOut[7].m_vPos = vCenter - vRight + vDown; pCurrOut[7].m_vUV.Init(1.0f, 1.0f); //setup the remaining vertex components for(uint32 nCurrVert = 4; nCurrVert < 8; nCurrVert++) { pCurrOut[nCurrVert].m_nPackedColor = nColor; pCurrOut[nCurrVert].m_vNormal = -vNormal; pCurrOut[nCurrVert].m_vTangent = -vTangent; pCurrOut[nCurrVert].m_vBinormal = vBinormal; } } //unlock and render the batch pInterface->UnlockAndBindDynamicVertexBuffer(LockRequest); pInterface->RenderIndexed( eCustomRenderPrimType_TriangleList, 0, nNumIndices, LockRequest.m_nStartIndex, 0, nNumVertices); }
void CPolygonDebrisFX::CreateDebris(int i, LTVector vPos) { if (i < 0 || i >= m_ds.nNumDebris || i != m_nNumPolies || (m_Polies[i] != LTNULL)) return; // Create a poly debris object... PLFXCREATESTRUCT pls; LTVector vLength = (m_cs.vDir * GetRandom(m_cs.PolyDebrisFX.fMinLength, m_cs.PolyDebrisFX.fMaxLength)) / 2.0f; LTVector vMinC1 = m_cs.PolyDebrisFX.vMinColor1; LTVector vMaxC1 = m_cs.PolyDebrisFX.vMaxColor1; LTVector vMinC2 = m_cs.PolyDebrisFX.vMinColor2; LTVector vMaxC2 = m_cs.PolyDebrisFX.vMaxColor2; pls.pTexture = m_cs.PolyDebrisFX.szTexture[0] ? m_cs.PolyDebrisFX.szTexture : LTNULL; pls.vStartPos = vPos - vLength; pls.vEndPos = vPos + vLength; pls.vInnerColorStart = LTVector(GetRandom(vMinC1.x, vMaxC1.x), GetRandom(vMinC1.y, vMaxC1.y), GetRandom(vMinC1.z, vMaxC1.z)); pls.vInnerColorEnd = LTVector(GetRandom(vMinC2.x, vMaxC2.x), GetRandom(vMinC2.y, vMaxC2.y), GetRandom(vMinC2.z, vMaxC2.z)); pls.vOuterColorStart = pls.vInnerColorStart; pls.vOuterColorEnd = pls.vInnerColorEnd; pls.fAlphaStart = m_cs.PolyDebrisFX.fInitialAlpha; pls.fAlphaEnd = m_cs.PolyDebrisFX.fFinalAlpha; pls.fMinWidth = 0.0f; pls.fMaxWidth = GetRandom(m_cs.PolyDebrisFX.fMinWidth, m_cs.PolyDebrisFX.fMaxWidth); pls.fLifeTime = GetDebrisLife(i); pls.fAlphaLifeTime = GetDebrisLife(i); pls.bAdditive = m_cs.PolyDebrisFX.bAdditive; pls.bMultiply = m_cs.PolyDebrisFX.bMultiply; pls.bDontFadeAlphaAtEdge= !m_cs.PolyDebrisFX.bAdditive; pls.nWidthStyle = m_cs.PolyDebrisFX.nStyle > PLWS_CONSTANT ? GetRandom(PLWS_BIG_TO_SMALL, PLWS_CONSTANT) : m_cs.PolyDebrisFX.nStyle; pls.bUseObjectRotation = !m_cs.PolyDebrisFX.bShowTrail; pls.bNoZ = m_cs.PolyDebrisFX.bShowTrail; pls.nNumSegments = 1; pls.fMinDistMult = 1.0f; pls.fMaxDistMult = 1.0f; pls.fPerturb = 0.0f; // Scale the width based on the distance the camera is away from the // origin of the debris... HLOCALOBJ hCamera = g_pGameClientShell->GetCamera(); if (hCamera) { LTVector vCamPos; g_pLTClient->GetObjectPos(hCamera, &vCamPos); vCamPos -= vPos; LTFLOAT fScaleVal = vCamPos.Mag() / g_cvarPolyDebrisScaleDist.GetFloat(); fScaleVal = (fScaleVal < g_cvarPolyDebrisMinDistScale.GetFloat() ? g_cvarPolyDebrisMinDistScale.GetFloat() : (fScaleVal > g_cvarPolyDebrisMaxDistScale.GetFloat() ? g_cvarPolyDebrisMaxDistScale.GetFloat() : fScaleVal)); pls.fMaxWidth *= fScaleVal; } CPolyLineFX *pNewPoly = GetPolyLineFXBank()->New(); if (!pNewPoly->Init(&pls) || !pNewPoly->CreateObject(m_pClientDE)) { GetPolyLineFXBank()->Delete(pNewPoly); return; } else { m_Polies[m_nNumPolies] = pNewPoly; } m_nNumPolies++; }
bool CAIWeaponAbstract::DefaultThrow(CAI* pAI) { // Make sure the basic pointers are valid. ASSERT(m_pWeapon); ASSERT(pAI); if (!pAI || !m_pWeapon || !m_pAIWeaponRecord) { return false; } // Don't fire if AI has no target. if( !pAI->HasTarget( kTarget_Character | kTarget_Object ) ) { return false; } HOBJECT hTarget = pAI->GetAIBlackBoard()->GetBBTargetObject(); // Throw at the last known position. LTVector vTargetPos; CAIWMFact factTargetQuery; factTargetQuery.SetFactType( kFact_Character ); factTargetQuery.SetTargetObject( hTarget ); CAIWMFact* pFact = pAI->GetAIWorkingMemory()->FindWMFact( factTargetQuery ); if( pFact ) { vTargetPos = pFact->GetPos(); } else { g_pLTServer->GetObjectPos(hTarget, &vTargetPos); } // Offset the target pos a little so projectile lands in front of the target. LTVector vOffsetDir; vOffsetDir = pAI->GetPosition() - vTargetPos; vOffsetDir.y = 0.f; vOffsetDir.Normalize(); vTargetPos += vOffsetDir * 384.f; // Get our fire position LTVector vFirePos = GetFirePosition(pAI); // Velocity Vo LTVector vGravity; g_pPhysicsLT->GetGlobalForce( vGravity ); // Vo = (S - R - 1/2*G*t^2) / t // Vo = initial velocity // S = destination // R = origin // G = gravity // t = hangtime float fHangtime = 0.5f; LTVector vVelocity = ( vTargetPos - vFirePos - vGravity * .5f * fHangtime * fHangtime ) / fHangtime; float fVelocity = vVelocity.Mag(); LTVector vDir( vVelocity / fVelocity ); // Now fire the weapon WeaponFireInfo weaponFireInfo; static uint8 s_nCount = GetRandom( 0, 255 ); s_nCount++; weaponFireInfo.hFiredFrom = pAI->GetHOBJECT(); weaponFireInfo.vPath = vDir; weaponFireInfo.bOverrideVelocity = LTTRUE; weaponFireInfo.fOverrideVelocity = fVelocity; weaponFireInfo.vFirePos = vFirePos; weaponFireInfo.vFlashPos = vFirePos; weaponFireInfo.hTestObj = hTarget; weaponFireInfo.fPerturb = 1.0f * (1.0f - pAI->GetAccuracy() ); weaponFireInfo.nSeed = (uint8)GetRandom( 2, 255 ); weaponFireInfo.nPerturbCount = s_nCount; weaponFireInfo.nFireTimestamp = g_pLTServer->GetRealTimeMS( ); m_pWeapon->ReloadClip( LTFALSE ); if (m_pAIWeaponRecord->bAllowAmmoGeneration) { m_pWeapon->GetArsenal()->AddAmmo( m_pWeapon->GetAmmoRecord(), 999999 ); } m_pWeapon->UpdateWeapon( weaponFireInfo, LTTRUE ); return true; }
void CTargetMgr::CheckForIntersect(float &fDistAway) { m_hTarget = NULL; m_ActivationData.Init(); // Cast ray from the camera to see if there is an object to activate... LTRotation const& rRot = g_pPlayerMgr->GetPlayerCamera()->GetCameraRotation( );; LTVector const& vPos = g_pPlayerMgr->GetPlayerCamera()->GetCameraPos( ); m_ActivationData.m_vPos = vPos; m_ActivationData.m_rRot = rRot; IntersectQuery IQuery; IntersectInfo IInfo; IQuery.m_From = vPos; IQuery.m_To = IQuery.m_From + (rRot.Forward() * kMaxDistance); // NOTE the use of the CHECK_FROM_POINT_INSIDE_OBJECTS flag. This flag will // make sure that any objects that m_From is inside are considered IQuery.m_Flags = CHECK_FROM_POINT_INSIDE_OBJECTS | INTERSECT_HPOLY | INTERSECT_OBJECTS | IGNORE_NONSOLID; IQuery.m_FilterActualIntersectFn = ActivateFilterFn; IQuery.m_pActualIntersectUserData = (void*)&IQuery; IQuery.m_PolyFilterFn = NULL; // [KLS 8/3/02] - ActivateFilterFn may save an object to use that may not be // the best activation choice (i.e., a fallback choice). However, if a // better choice isn't found, the fallback choice should be used. That // fallback choice is stored in g_adFallbackActivationObject so we clear // it here... g_adFallbackActivationObject.Init(); if (g_pLTClient->IntersectSegment(IQuery, &IInfo)) { m_ActivationData.m_vIntersect = IInfo.m_Point; bool bHitSky = false; if (IsMainWorld(IInfo.m_hObject)) { if (IInfo.m_hPoly != INVALID_HPOLY) { SurfaceType eType = GetSurfaceType(IInfo.m_hPoly); HSURFACE hSurf = g_pSurfaceDB->GetSurface(eType); // See if the surface we tried to activate has an activation // sound...If so, the user can activate it... if (hSurf) { HRECORD hActSnd = g_pSurfaceDB->GetRecordLink(hSurf,SrfDB_Srf_rActivationSnd); if (hActSnd && g_pSoundDB->GetFloat(hActSnd,SndDB_fOuterRadius) > 0) { m_hTarget = IInfo.m_hObject; m_ActivationData.m_hTarget = m_hTarget; m_ActivationData.m_nSurfaceType = eType; m_ActivationData.m_hActivateSnd = hActSnd; } bHitSky = (ST_SKY == eType); } } } else { LTVector vObjPos = m_ActivationData.m_vIntersect; vObjPos -= vPos; if (vObjPos.Mag() <= kMaxDistance) { m_hTarget = IInfo.m_hObject; m_ActivationData.m_hTarget = m_hTarget; } } // Calculate how far away the object is... LTVector vDist = m_ActivationData.m_vIntersect - vPos; if (bHitSky) fDistAway = kMaxDistance; else fDistAway = vDist.Mag(); } // [KLS 8/3/02] - Use the fallback object if we have one and we didn't // find another object more suitable object... bool bCanUseFallback = (m_ActivationData.m_hTarget ? false : true); if (!bCanUseFallback) { // We can still use the fallback object if it isn't the world or a // world model... if (IsMainWorld(m_ActivationData.m_hTarget) || OT_WORLDMODEL == GetObjectType(m_ActivationData.m_hTarget)) { bCanUseFallback = true; } } if ( bCanUseFallback && g_adFallbackActivationObject.m_hTarget ) { // Ok we hit the fallback object reset some of our target data LTVector vObjPos; g_pLTClient->GetObjectPos(g_adFallbackActivationObject.m_hTarget, &vObjPos); m_ActivationData.m_vIntersect = vObjPos; vObjPos -= vPos; if (vObjPos.Mag() <= kMaxDistance) { m_hTarget = g_adFallbackActivationObject.m_hTarget; m_ActivationData.m_hTarget = m_hTarget; } // Calculate how far away the object is... LTVector vDist = m_ActivationData.m_vIntersect - vPos; fDistAway = vDist.Mag(); } }
//* // ----------------------------------------------------------------------- // // Returns the vector that the physics would have the object move by. // ----------------------------------------------------------------------- // void CalcMotion ( MotionInfo* pInfo, LTObject* pObj, //the object LTVector& dr, //displacement LTVector& v, //velocity LTVector& a, //acceleration const bool bApplyGravity, const float dt //time step ) { LTVector velocityDelta, accelDelta; LTVector q, slopeVel, slopeAccel, vel; const LTVector *n; LTVector objectNormal, vTemp, vTemp2; float fExp; float timeIntegral; float velocityMagSqr, accelMagSqr; LTBOOL bFriction; bFriction = LTFALSE; timeIntegral = dt * dt * 0.5f; velocityMagSqr = v.MagSqr(); accelMagSqr = a.MagSqr(); // [KLS - 3/12/02] - Added support for per-object force override... LTVector vForce = pObj->GetGlobalForceOverride(); // If the global force override is zero, use the MotionInfo force... if (LTVector(0.0, 0.0, 0.0) == vForce) { vForce = pInfo->m_Force; } /* // Debug variables LTVector vOldAccel, vOldVel, vOldPos; vOldAccel = a; vOldVel = v; vOldPos = pObj->GetPos(); //*/ // Stop objects that are moving very slowly... if(velocityMagSqr < 0.1f ) { v.Init(); velocityMagSqr = 0; } if( accelMagSqr < 0.1f ) { a.Init(); accelMagSqr = 0; } // Zero out the displacement to start with. dr.Init(); // Update objects affected by gravity... if(bApplyGravity) { // Add friction to objects standing on something... if(pObj->m_pStandingOn) { // Try to disable their physics. if( velocityMagSqr < 0.1f && accelMagSqr < 0.1f ) { pObj->m_Velocity.Init(); pObj->m_Acceleration.Init(); pObj->m_InternalFlags &= ~IFLAG_APPLYPHYSICS; return; } else { // Check if object on world geometry... if( pObj->m_pNodeStandingOn ) { // Calculate vector parallel to plane... n = &pObj->m_pNodeStandingOn->GetPlane()->m_Normal; } else { // Object standing on another object, so assume opposite to gravity... n = &objectNormal; objectNormal = -pInfo->m_UnitForce; } // Calculate the acceleration including the force LTVector vAccelWithForce = a + vForce; // If we're on a slope that's at enough of an angle, allow it to slide LTVector vForceDir = vForce; vForceDir.Norm(); if (vForceDir.Dot(*n) > pInfo->m_SlideRatio) { float fAccelMag = vAccelWithForce.Mag(); a = vAccelWithForce - *n * n->Dot(vAccelWithForce); // Don't allow it to accelerate up the slope.. float fAccelDotForce = a.Dot(vForceDir); if (fAccelDotForce < 0.0f) { a -= vForceDir * fAccelDotForce; } a.Norm(fAccelMag); // dsi_ConsolePrint("Steep"); } else { // Figure out what our new velocity would be if only the force was used (i.e. are they jumping?) LTVector vNewVel = v + vForce * dt; // If we're going to be moving away from the plane, use the full force if (vNewVel.Dot(*n) > 0.01f) { a = vAccelWithForce; // dsi_ConsolePrint("Jump"); } // If the acceleration without the force isn't moving into the surface, project it there else if (a.Dot(*n) > 0.01f) { float fAccelMag = a.Mag(); a -= *n * (n->Dot(a) + 1.0f); a.Norm(fAccelMag); bFriction = LTTRUE; // dsi_ConsolePrint("Downhill"); } else { bFriction = LTTRUE; // dsi_ConsolePrint("Walk"); } } } } // Otherwise just apply gravity else { a += vForce; // dsi_ConsolePrint("Fall"); } } // If there's no gravity and they aren't moving, then disable their physics else if( velocityMagSqr < 0.1f && accelMagSqr < 0.1f ) { pObj->m_Velocity.Init(); pObj->m_Acceleration.Init(); pObj->m_InternalFlags &= ~IFLAG_APPLYPHYSICS; return; } // If friction // new velocity is given by: v = ( a / k ) + ( v_0 - a / k ) * exp( -k * t ) // new position is given by: x = x_0 + ( a / k ) * t + ( k * v_0 - a ) * ( 1 - exp( -k * t )) / k^2 if( bFriction && pObj->m_FrictionCoefficient > 0.0f ) { // Velocity... fExp = ( float )exp( -pObj->m_FrictionCoefficient * dt ); vTemp = a / pObj->m_FrictionCoefficient; vTemp2 = v - vTemp; vTemp2 *= fExp; vel = vTemp2 + vTemp; // Position delta... dr = vTemp * dt; vTemp = v * pObj->m_FrictionCoefficient; vTemp -= a; vTemp *= (( 1.0f - fExp ) / pObj->m_FrictionCoefficient / pObj->m_FrictionCoefficient); dr += vTemp; pObj->m_Velocity = vel; v = pObj->m_Velocity; } // If no friction // new velocity is given by: v = v_0 + a * t // new position is given by: x = x_0 + v_0 * t + .5 * a * t^2 else { // Find the change in velocity... velocityDelta = a * dt; // Position delta... dr = v * dt; vTemp = a * (timeIntegral * 0.5f); dr += vTemp; // Add the final velocity to the new velocity. pObj->m_Velocity += velocityDelta; v = pObj->m_Velocity; } /* // Show debug information, filtering out the server-side player object if(bApplyGravity) { dsi_ConsolePrint("Old - A:<%7.1f,%7.1f,%7.1f> V:<%7.1f,%7.1f,%7.1f> P:<%7.1f,%7.1f,%7.1f>", VEC_EXPAND(vOldAccel), VEC_EXPAND(vOldVel), VEC_EXPAND(vOldPos)); LTVector vNewAccel, vNewVel, vNewPos; vNewAccel = a; vNewVel = v; vNewPos = vOldPos + dr; dsi_ConsolePrint("New - A:<%7.1f,%7.1f,%7.1f> V:<%7.1f,%7.1f,%7.1f> P:<%7.1f,%7.1f,%7.1f>", VEC_EXPAND(vNewAccel), VEC_EXPAND(vNewVel), VEC_EXPAND(vNewPos)); } //*/ return; }
void CTargetMgr::CheckForIntersect(float &fDistAway) { m_hTarget = LTNULL; m_ActivationData.Init(); uint32 dwUsrFlags = 0; const float fMaxDist = 100000.0f; // Cast ray from the camera to see if there is an object to activate... LTRotation rRot; LTVector vPos; HLOCALOBJ hCamera = g_pPlayerMgr->GetCamera(); g_pLTClient->GetObjectPos(hCamera, &vPos); g_pLTClient->GetObjectRotation(hCamera, &rRot); m_ActivationData.m_vPos = vPos; m_ActivationData.m_rRot = rRot; IntersectQuery IQuery; IntersectInfo IInfo; IQuery.m_From = vPos; IQuery.m_To = IQuery.m_From + (rRot.Forward() * fMaxDist); // NOTE the use of the CHECK_FROM_POINT_INSIDE_OBJECTS flag. This flag will // make sure that any objects that m_From is inside are considered IQuery.m_Flags = CHECK_FROM_POINT_INSIDE_OBJECTS | INTERSECT_HPOLY | INTERSECT_OBJECTS | IGNORE_NONSOLID; IQuery.m_FilterActualIntersectFn = ActivateFilterFn; IQuery.m_pActualIntersectUserData = (void*)&IQuery; IQuery.m_PolyFilterFn = DoVectorPolyFilterFn; // [KLS 8/3/02] - ActivateFilterFn may save an object to use that may not be // the best activation choice (i.e., a fallback choice). However, if a // better choice isn't found, the fallback choice should be used. That // fallback choice is stored in g_adFallbackActivationObject so we clear // it here... g_adFallbackActivationObject.Init(); if (g_pLTClient->IntersectSegment(&IQuery, &IInfo)) { m_ActivationData.m_vIntersect = IInfo.m_Point; if (IsMainWorld(IInfo.m_hObject)) { if (IInfo.m_hPoly != INVALID_HPOLY) { SurfaceType eType = GetSurfaceType(IInfo.m_hPoly); SURFACE *pSurf = g_pSurfaceMgr->GetSurface(eType); // See if the surface we tried to activate has an activation // sound...If so, the user can activate it... if (pSurf && pSurf->szActivationSnd[0] && pSurf->fActivationSndRadius > 0) { m_hTarget = IInfo.m_hObject; m_ActivationData.m_hTarget = m_hTarget; m_ActivationData.m_nSurfaceType = eType; } } } else { LTVector vObjPos = m_ActivationData.m_vIntersect; vObjPos -= vPos; if (vObjPos.Mag() <= fMaxDist) { m_hTarget = IInfo.m_hObject; m_ActivationData.m_hTarget = m_hTarget; } } // Calculate how far away the object is... LTVector vDist = m_ActivationData.m_vIntersect - vPos; fDistAway = vDist.Mag(); } // [KLS 8/3/02] - Use the fallback object if we have one and we didn't // find another object more suitable object... bool bCanUseFallback = (m_ActivationData.m_hTarget ? false : true); if (!bCanUseFallback) { // We can still use the fallback object if it isn't the world or a // world model... if (IsMainWorld(m_ActivationData.m_hTarget) || OT_WORLDMODEL == GetObjectType(m_ActivationData.m_hTarget)) { bCanUseFallback = true; } } if ( bCanUseFallback && g_adFallbackActivationObject.m_hTarget ) { // Ok we hit the fallback object reset some of our target data LTVector vObjPos; g_pLTClient->GetObjectPos(g_adFallbackActivationObject.m_hTarget, &vObjPos); m_ActivationData.m_vIntersect = vObjPos; vObjPos -= vPos; if (vObjPos.Mag() <= fMaxDist) { m_hTarget = g_adFallbackActivationObject.m_hTarget; m_ActivationData.m_hTarget = m_hTarget; } // Calculate how far away the object is... LTVector vDist = m_ActivationData.m_vIntersect - vPos; fDistAway = vDist.Mag(); } }
bool CHUDSubtitles::Show(const char* szStringId, LTVector vSpeakerPos, float fRadius, float fDuration, bool bSubtitlePriority) { // Only show subtitles if conversations in range... LTVector vListenerPos; bool bListenerInClient; LTRotation rRot; g_pLTClient->GetListener(&bListenerInClient, &vListenerPos, &rRot); LTVector vPos = vSpeakerPos - vListenerPos; float fAdjustedRadius = fRadius * g_vtAdjustedRadius.GetFloat(); float fDist = vPos.Mag(); if (vSpeakerPos == LTVector(0, 0, 0)) fDist = 0.0f; //should we override what ever is already playing? if (m_bVisible) { //if the old one has priority, and the new doesn't, don't play the new if (m_bSubtitlePriority && !bSubtitlePriority) { return false; } //if they have the same priority, check distances if (m_bSubtitlePriority == bSubtitlePriority) { LTVector vOldPos = m_vSpeakerPos - vListenerPos; float fOldDist = vOldPos.Mag(); if (m_vSpeakerPos == LTVector(0, 0, 0)) fOldDist = 0.0f; if (fOldDist < fDist) { return false; } } } const wchar_t *pStr = LoadString(szStringId); if (LTStrEmpty(pStr)) { DebugCPrint(2,"CHUDSubtitles::Show(%s) : No Text",szStringId); return false; } m_Text.SetText(pStr); m_bVisible = true; m_bSubtitlePriority = bSubtitlePriority; m_vSpeakerPos = vSpeakerPos; m_fRadius = fRadius; m_fDuration = fDuration; if (m_fDuration < 0.0f) m_fDuration = 0.04f * (float)LTStrLen(m_Text.GetText()); LTVector2n pos = m_vBasePos; uint32 width = m_nWidth; /* if( g_pPlayerMgr->GetPlayerCamera()->GetCameraMode() == CPlayerCamera::kCM_Cinematic ) { pos = m_CinematicPos; width = m_nCinematicWidth; } */ LTVector2 vfScale = g_pInterfaceResMgr->GetScreenScale(); uint32 x = (uint32)((float)pos.x * vfScale.x); uint32 y = (uint32)((float)pos.y * vfScale.y); width = (uint32 )((float)width * vfScale.x); uint32 height = (2 + m_nMaxLines * m_sTextFont.m_nHeight) ; float fFontHeight = (float)m_sTextFont.m_nHeight; m_Rect.Init(x,y,x+width,y+height); m_Text.WordWrap(m_Rect.GetWidth()); float textX = (float)m_Rect.Left(); float textY = (float)m_Rect.Top(); LTRect2n rExt; m_Text.CreateTexture(); m_Text.GetExtents(rExt); uint32 numLines = (uint32)(rExt.GetHeight() / fFontHeight); if (numLines > m_nMaxLines) { m_bOverflow = true; float fTimePerLine = m_fDuration / ((float)numLines + 1.0f); float fDelay = (float)m_nMaxLines * fTimePerLine; m_fScrollStartTime = fDelay; m_fScrollSpeed = fFontHeight / fTimePerLine; m_fMaxOffset = (float)(rExt.GetHeight() - m_Rect.GetHeight()); } else { m_bOverflow = false; textX += (float)(m_Rect.GetWidth() - width) / 2.0f; m_fOffset = (float)(m_Rect.GetHeight() - height); textY += m_fOffset; m_fScrollSpeed = 0.0f; } m_fEndTime = m_fDuration; //reset our time to the beginning m_fElapsedTime = 0.0f; m_Text.SetPos(LTVector2(textX,textY)); return true; }
LTBOOL CAIMovement::UpdateConstantVelocity( EnumAnimMovement eMovementType, LTVector* pvNewPos ) { // Find our unit movement vector LTVector vMove = m_vDest - m_pAI->GetPosition(); // Set our speed based on our movement type switch( eMovementType ) { case kAM_Set: vMove.y = 0.0f; m_pAI->SetSpeed( m_fSetSpeed ); break; case kAM_Walk: vMove.y = 0.0f; m_pAI->Walk(); break; case kAM_Run: vMove.y = 0.0f; m_pAI->Run(); break; case kAM_Hover: m_bFaceDest = LTTRUE; vMove.y = 0.0f; m_pAI->Hover(); break; case kAM_Swim: vMove.y = 0.0f; m_pAI->Swim(); break; case kAM_Climb: vMove.x = 0.0f; vMove.z = 0.0f; m_pAI->Walk(); // Turn off gravity m_pAI->SetCheapMovement(LTFALSE); m_bFaceDest = LTFALSE; break; case kAM_JumpOver: vMove.y = 0.0f; m_pAI->JumpOver(); // Turn off gravity m_pAI->SetCheapMovement(LTFALSE); if( m_eLastMovementType != kAM_JumpOver ) { SetupJump( eMovementType ); } break; case kAM_JumpUp: m_pAI->Jump(); m_bFaceDest = LTFALSE; // Turn off gravity m_pAI->SetCheapMovement(LTFALSE); if( m_eLastMovementType != kAM_JumpUp ) { SetupJump( eMovementType ); vMove = m_vDest - m_pAI->GetPosition(); } break; case kAM_Fall: m_pAI->Fall(); m_bFaceDest = LTFALSE; // Turn off gravity m_pAI->SetCheapMovement(LTFALSE); if( m_eLastMovementType != kAM_Fall ) { SetupJump( eMovementType ); vMove = m_vDest - m_pAI->GetPosition(); } break; default: AIASSERT( 0, m_pAI->GetHOBJECT(), "Unknown Movement type!" ); break; } // See if we'll overshoot our dest. LTFLOAT fRemainingDist = vMove.Mag(); if(fRemainingDist == 0.f) { Clear(); m_eState = eStateDone; return LTFALSE; } LTFLOAT fMoveDist; LTFLOAT fTimeDelta = g_pLTServer->GetFrameTime(); fMoveDist = m_pAI->GetSpeed()*fTimeDelta; // If we'd overshoot our destination, just move us there if ( fRemainingDist < fMoveDist ) { *pvNewPos = m_vDest; // If the movement does not include any vertical, then // do not affect the elevation of the AI. Let CheapMovement // take care of putting the AI on the ground. if( vMove.y == 0.f ) { pvNewPos->y = m_pAI->GetPosition().y; } Clear(); m_eState = eStateDone; return LTTRUE; } // Scale based on our movement distance vMove.Normalize(); vMove *= fMoveDist; // Calculate our new position *pvNewPos = m_pAI->GetPosition() + vMove; // Face us in the right direction if ( m_bFaceDest ) { m_pAI->FacePosMoving( *pvNewPos ); } return LTTRUE; }
// Wrap the textures, starting at a poly index void CRVTrackerTextureWrap::WrapTexture(CTWPolyInfo *pPoly, const CVector &vWrapDir, CTextExtents &cExtents) const { // Mark this poly as wrapped pPoly->m_bTouched = TRUE; CTexturedPlane& Texture = pPoly->m_pPoly->GetTexture(GetCurrTexture()); // Get the texture space LTVector vWrapO = Texture.GetO(); LTVector vWrapP = Texture.GetP(); LTVector vWrapQ = Texture.GetQ(); // Get the texture offset projections float fWrapOdotP = vWrapO.Dot(vWrapP); float fWrapOdotQ = vWrapO.Dot(vWrapQ); // Update the texturing extents for (uint32 nExtentLoop = 0; nExtentLoop < pPoly->m_aEdges.GetSize(); ++nExtentLoop) { LTVector vEdgePt = pPoly->m_aEdges[nExtentLoop]->m_aPt[0]; float fCurU = vWrapP.Dot(vEdgePt) - fWrapOdotP; float fCurV = vWrapQ.Dot(vEdgePt) - fWrapOdotQ; cExtents.m_fMinU = LTMIN(fCurU, cExtents.m_fMinU); cExtents.m_fMaxU = LTMAX(fCurU, cExtents.m_fMaxU); cExtents.m_fMinV = LTMIN(fCurV, cExtents.m_fMinV); cExtents.m_fMaxV = LTMAX(fCurV, cExtents.m_fMaxV); } CMoArray<uint32> aNeighbors; CMoArray<float> aDots; // Insert the neighbors into a list in dot-product order for (uint32 nNeighborLoop = 0; nNeighborLoop < pPoly->m_aNeighbors.GetSize(); ++nNeighborLoop) { CTWPolyInfo *pNeighbor = pPoly->m_aNeighbors[nNeighborLoop]; // Skip edges that don't have a neighbor if (!pNeighbor) continue; // Skip neighbors that are already wrapped if (pNeighbor->m_bTouched) continue; // Get our dot product float fCurDot = vWrapDir.Dot(pPoly->m_aEdges[nNeighborLoop]->m_Plane.m_Normal); if ((m_bRestrictWalkDir) && (fCurDot < 0.707f)) continue; // Mark this neighbor as touched (to avoid later polygons pushing it onto the stack) pNeighbor->m_bTouched = TRUE; // Insert it into the list for (uint32 nInsertLoop = 0; nInsertLoop < aNeighbors.GetSize(); ++nInsertLoop) { if (fCurDot > aDots[nInsertLoop]) break; } aDots.Insert(nInsertLoop, fCurDot); aNeighbors.Insert(nInsertLoop, nNeighborLoop); } // Recurse through its neighbors for (uint32 nWrapLoop = 0; nWrapLoop < aNeighbors.GetSize(); ++nWrapLoop) { CTWPolyInfo *pNeighbor = pPoly->m_aNeighbors[aNeighbors[nWrapLoop]]; CTWEdgeInfo *pEdge = pPoly->m_aEdges[aNeighbors[nWrapLoop]]; ////////////////////////////////////////////////////////////////////////////// // Wrap this neighbor // Create a matrix representing the basis of the polygon in relation to this edge LTMatrix mPolyBasis; mPolyBasis.SetTranslation(0.0f, 0.0f, 0.0f); mPolyBasis.SetBasisVectors(&pEdge->m_vDir, &pPoly->m_pPoly->m_Plane.m_Normal, &pEdge->m_Plane.m_Normal); // Create a new basis for the neighbor polygon LTMatrix mNeighborBasis; LTVector vNeighborForward; vNeighborForward = pNeighbor->m_pPoly->m_Plane.m_Normal.Cross(pEdge->m_vDir); // Just to be sure.. vNeighborForward.Norm(); mNeighborBasis.SetTranslation(0.0f, 0.0f, 0.0f); mNeighborBasis.SetBasisVectors(&pEdge->m_vDir, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vNeighborForward); // Create a rotation matrix from here to there LTMatrix mRotation; mRotation = mNeighborBasis * ~mPolyBasis; // Rotate the various vectors LTVector vNewP; LTVector vNewQ; LTVector vNewDir; mRotation.Apply3x3(vWrapP, vNewP); mRotation.Apply3x3(vWrapQ, vNewQ); mRotation.Apply3x3(vWrapDir, vNewDir); // Rotate the texture basis if we're following a path if (m_nWrapStyle == k_WrapPath) { LTVector vNeighborEdgeDir; if (GetSimilarEdgeDir(pNeighbor, vNewDir, vNeighborEdgeDir, 0.707f)) { LTMatrix mRotatedNeighbor; LTVector vNeighborRight; vNeighborRight = vNeighborEdgeDir.Cross(pNeighbor->m_pPoly->m_Plane.m_Normal); vNeighborRight.Norm(); // Make sure we're pointing the right way... if (vNeighborRight.Dot(pEdge->m_vDir) < 0.0f) vNeighborRight = -vNeighborRight; mRotatedNeighbor.SetTranslation(0.0f, 0.0f, 0.0f); mRotatedNeighbor.SetBasisVectors(&vNeighborRight, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vNeighborEdgeDir); // Build a basis based on an edge from the current polygon LTVector vBestPolyEdge; GetSimilarEdgeDir(pPoly, vWrapDir, vBestPolyEdge); LTVector vPolyRight = vBestPolyEdge.Cross(pNeighbor->m_pPoly->m_Plane.m_Normal); vPolyRight.Norm(); // Make sure we're pointing the right way... if (vPolyRight.Dot(pEdge->m_vDir) < 0.0f) vPolyRight = -vPolyRight; // Build the poly edge matrix LTMatrix mPolyEdgeBasis; mPolyEdgeBasis.SetTranslation(0.0f, 0.0f, 0.0f); mPolyEdgeBasis.SetBasisVectors(&vPolyRight, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vBestPolyEdge); // Get a matrix from here to there LTMatrix mRotator; mRotator = mRotatedNeighbor * ~mPolyEdgeBasis; // Rotate the texture basis mRotator.Apply3x3(vNewP); mRotator.Apply3x3(vNewQ); // And use the new edge as the new direction vNewDir = vNeighborEdgeDir; } // Remove skew from vNewP/vNewQ if ((float)fabs(vNewP.Dot(vNewQ)) > 0.001f) { float fMagP = vNewP.Mag(); float fMagQ = vNewQ.Mag(); vNewQ *= 1.0f / fMagQ; vNewP -= vNewQ * vNewQ.Dot(vNewP); vNewP.Norm(fMagP); vNewQ *= fMagQ; } } // Get the first edge point.. CVector vEdgePt = pEdge->m_aPt[0]; // Calculate the texture coordinate at this point float fWrapU = vWrapP.Dot(vEdgePt) - fWrapOdotP; float fWrapV = vWrapQ.Dot(vEdgePt) - fWrapOdotQ; // Build the new offset float fNewOdotP = vNewP.Dot(vEdgePt) - fWrapU; float fNewOdotQ = vNewQ.Dot(vEdgePt) - fWrapV; LTVector vNewO; vNewO.Init(); float fNewPMag = vNewP.MagSqr(); if (fNewPMag > 0.0f) vNewO += vNewP * (fNewOdotP / fNewPMag); float fNewQMag = vNewQ.MagSqr(); if (fNewQMag > 0.0f) vNewO += vNewQ * (fNewOdotQ / fNewQMag); pNeighbor->m_pPoly->SetTextureSpace(GetCurrTexture(), vNewO, vNewP, vNewQ); // Recurse into this neighbor WrapTexture(pNeighbor, vNewDir, cExtents); } }
void CGrenade::HandleImpact(HOBJECT hObj) { if (!g_vtGrenadeDampenPercent.IsInitted()) { g_vtGrenadeDampenPercent.Init(g_pLTServer, "GrenadeDampenPercent", LTNULL, DEFAULT_GRENADE_DAMPEN_PERCENT); } if (!g_vtGrenadeMinVelMag.IsInitted()) { g_vtGrenadeMinVelMag.Init(g_pLTServer, "GrenadeMinVelMag", LTNULL, DEFAULT_GRENADE_MIN_VELOCITY); } LTVector vVel; g_pLTServer->GetVelocity(m_hObject, &vVel); // See if we are impacting on liquid... LTBOOL bEnteringLiquid = LTFALSE; uint16 code; if (g_pLTServer->GetContainerCode(hObj, &code)) { if (IsLiquid((ContainerCode)code)) { bEnteringLiquid = LTTRUE; } } CollisionInfo info; g_pLTServer->GetLastCollision(&info); // Do the bounce, if the object we hit isn't liquid... if (!bEnteringLiquid) { vVel += (info.m_vStopVel * 2.0f); } // Dampen the grenade's new velocity based on the surface type... LTFLOAT fDampenPercent = g_vtGrenadeDampenPercent.GetFloat(); m_eLastHitSurface = GetSurfaceType(info); SURFACE* pSurf = g_pSurfaceMgr->GetSurface(m_eLastHitSurface); if (pSurf) { // Play a bounce sound (based on the surface type) if one isn't // already playing... if ( ShouldPlayBounceSound(pSurf) ) { // Only play one sound at a time... if (m_hBounceSnd) { g_pLTServer->KillSound(m_hBounceSnd); m_hBounceSnd = LTNULL; } uint32 dwFlags = PLAYSOUND_GETHANDLE | PLAYSOUND_TIME; int nVolume = IsLiquid(m_eContainerCode) ? 50 : 100; LTVector vPos; g_pLTServer->GetObjectPos(m_hObject, &vPos); m_hBounceSnd = g_pServerSoundMgr->PlaySoundFromPos(vPos, (char*)GetBounceSound(pSurf), pSurf->fGrenadeSndRadius, SOUNDPRIORITY_MISC_MEDIUM, dwFlags, nVolume); } fDampenPercent = (1.0f - pSurf->fHardness); } fDampenPercent = fDampenPercent > 1.0f ? 1.0f : (fDampenPercent < 0.0f ? 0.0f : fDampenPercent); vVel *= (1.0f - fDampenPercent); // See if we should come to a rest... LTVector vTest = vVel; vTest.y = 0.0f; if (vTest.Mag() < g_vtGrenadeMinVelMag.GetFloat()) { // If we're on the ground (or an object), stop movement... CollisionInfo standingInfo; g_pLTServer->GetStandingOn(m_hObject, &standingInfo); CollisionInfo* pInfo = standingInfo.m_hObject ? &standingInfo : &info; if (pInfo->m_hObject) { // Don't stop on walls... if (pInfo->m_Plane.m_Normal.y > 0.75f) { vVel.Init(); // Turn off gravity, solid, and touch notify.... uint32 dwFlags = g_pLTServer->GetObjectFlags(m_hObject); dwFlags &= ~(FLAG_GRAVITY | FLAG_TOUCH_NOTIFY | FLAG_SOLID); g_pLTServer->SetObjectFlags(m_hObject, dwFlags); // Rotate to rest... RotateToRest(); } } } // Reset rotation velocities due to the bounce... ResetRotationVel(&vVel, pSurf); // We need to subtact this out because the engine will add it back in, // kind of a kludge but necessary... vVel -= info.m_vStopVel; g_pLTServer->SetVelocity(m_hObject, &vVel); m_cBounces++; }