LTBOOL GetImpactSprite(SurfaceType eSurfType, LTFLOAT & fScale, int nAmmoId, char* pBuf, int nBufLen) { if (!g_pWeaponMgr || !ShowsMark(eSurfType) || !pBuf) { return LTFALSE; } AMMO* pAmmo = g_pWeaponMgr->GetAmmo(nAmmoId); if (!pAmmo || !pAmmo->pImpactFX) return LTFALSE; // Get the impact mark filename... strncpy(pBuf, pAmmo->pImpactFX->szMark, nBufLen); if (!pBuf[0]) return LTFALSE; if (stricmp(pBuf, "SURFACE") == 0) { // Use the surface to dtermine the bullet hole... SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurfType); if (pSurf && pSurf->szBulletHoleSpr[0]) { strcpy(pBuf, pSurf->szBulletHoleSpr); fScale = GetRandom(pSurf->fBulletHoleMinScale, pSurf->fBulletHoleMaxScale); } else { return LTFALSE; } } return LTTRUE; }
void CWeaponFX::CreateExitMark() { if (m_eExitSurface != ST_UNKNOWN && ShowsMark(m_eExitSurface)) { LTRotation rNormRot; g_pLTClient->AlignRotation(&rNormRot, &m_vExitNormal, LTNULL); CreateMark(m_vExitPos, m_vExitNormal, rNormRot, m_eExitSurface); } }
static void CreateServerExitMark(const CLIENTWEAPONFX & theStruct) { SURFACE* pSurf = g_pSurfaceMgr->GetSurface((SurfaceType)theStruct.nSurfaceType); if (!pSurf || !pSurf->bCanShootThrough) return; int nMaxThickness = pSurf->nMaxShootThroughThickness; if (nMaxThickness < 1) return; // Determine if there is an "exit" surface... IntersectQuery qInfo; IntersectInfo iInfo; LTVector vDir = theStruct.vPos - theStruct.vFirePos; vDir.Norm(); qInfo.m_From = theStruct.vPos + (vDir * (LTFLOAT)(nMaxThickness + 1)); qInfo.m_To = theStruct.vPos; qInfo.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID | INTERSECT_HPOLY; SurfaceType eType = ST_UNKNOWN; if (g_pLTServer->IntersectSegment(&qInfo, &iInfo)) { eType = GetSurfaceType(iInfo); if (ShowsMark(eType)) { LTRotation rNormRot; g_pLTServer->AlignRotation(&rNormRot, &(iInfo.m_Plane.m_Normal), LTNULL); CLIENTWEAPONFX exitStruct = theStruct; exitStruct.vPos = iInfo.m_Point + vDir; exitStruct.vSurfaceNormal = iInfo.m_Plane.m_Normal; CreateServerMark(exitStruct); } } }
void CWeaponFX::CreateSurfaceSpecificFX() { CGameSettings* pSettings = g_pInterfaceMgr->GetSettings(); if (!pSettings) return; // Don't do gore fx... if (m_eSurfaceType == ST_FLESH) { if (!pSettings->Gore()) { return; } if (m_pAmmo->eType == VECTOR && m_pAmmo->eInstDamageType == DT_BULLET) { CreateBloodSplatFX(); } } if ((m_wFireFX & WFX_EXITMARK) && ShowsMark(m_eExitSurface)) { CreateExitMark(); } if (m_wFireFX & WFX_EXITDEBRIS) { CreateExitDebris(); } if (!m_pAmmo || !m_pAmmo->pImpactFX) return; if (!m_pAmmo->pImpactFX->bDoSurfaceFX) return; // Create the surface specific fx... int i; SURFACE* pSurf = g_pSurfaceMgr->GetSurface(m_eSurfaceType); if (pSurf) { if (IsLiquid(m_eCode)) { // Create underwater fx... // Create any impact particle shower fx associated with this surface... for (i=0; i < pSurf->nNumUWImpactPShowerFX; i++) { CPShowerFX* pPShowerFX = g_pSurfaceMgr->GetPShowerFX(pSurf->aUWImpactPShowerFXIds[i]); g_pFXButeMgr->CreatePShowerFX(pPShowerFX, m_vPos, m_vDir, m_vSurfaceNormal); } } else { // Create normal fx... // Create any impact scale fx associated with this surface... for (i=0; i < pSurf->nNumImpactScaleFX; i++) { CScaleFX* pScaleFX = g_pSurfaceMgr->GetScaleFX(pSurf->aImpactScaleFXIds[i]); g_pFXButeMgr->CreateScaleFX(pScaleFX, m_vPos, m_vDir, &m_vSurfaceNormal, &m_rSurfaceRot); } // Create any impact particle shower fx associated with this surface... for (i=0; i < pSurf->nNumImpactPShowerFX; i++) { CPShowerFX* pPShowerFX = g_pSurfaceMgr->GetPShowerFX(pSurf->aImpactPShowerFXIds[i]); g_pFXButeMgr->CreatePShowerFX(pPShowerFX, m_vPos, m_vDir, m_vSurfaceNormal); } // Create any impact poly debris fx associated with this surface... if (g_vtCreatePolyDebris.GetFloat()) { for (i=0; i < pSurf->nNumImpactPolyDebrisFX; i++) { CPolyDebrisFX* pPolyDebrisFX = g_pSurfaceMgr->GetPolyDebrisFX(pSurf->aImpactPolyDebrisFXIds[i]); g_pFXButeMgr->CreatePolyDebrisFX(pPolyDebrisFX, m_vPos, m_vDir, m_vSurfaceNormal); } } } } }
LTBOOL CWeaponFX::CreateObject(ILTClient* pClientDE) { if (!CSpecialFX::CreateObject(pClientDE) || !g_pWeaponMgr) return LTFALSE; CGameSettings* pSettings = g_pInterfaceMgr->GetSettings(); if (!pSettings) return LTFALSE; // Set up our data members... // Set the local client id... uint32 dwId; g_pLTClient->GetLocalClientID(&dwId); m_nLocalId = (uint8)dwId; m_nDetailLevel = pSettings->SpecialFXSetting(); // Fire pos may get tweaked a little... m_vFirePos = CalcFirePos(m_vFirePos); m_vDir = m_vPos - m_vFirePos; m_fFireDistance = m_vDir.Mag(); m_vDir.Norm(); g_pLTClient->AlignRotation(&m_rSurfaceRot, &m_vSurfaceNormal, LTNULL); g_pLTClient->AlignRotation(&m_rDirRot, &m_vDir, LTNULL); SetupExitInfo(); // Calculate if the camera can see the fire position and the impact // position... g_bCanSeeImpactPos = LTTRUE; g_bCanSeeFirePos = LTTRUE; g_bDistantImpactPos = LTFALSE; g_bDistantFirePos = LTFALSE; if (g_vtWeaponFXUseFOVPerformance.GetFloat()) { HOBJECT hCamera = g_pGameClientShell->GetCamera(); LTVector vCameraPos, vU, vR, vF, vDir; LTRotation rCameraRot; g_pLTClient->GetObjectPos(hCamera, &vCameraPos); g_pLTClient->GetObjectRotation(hCamera, &rCameraRot); g_pLTClient->GetRotationVectors(&rCameraRot, &vU, &vR, &vF); vDir = m_vPos - vCameraPos; LTFLOAT fImpactDist = vDir.Mag(); if (fImpactDist > g_vtWeaponFXMaxImpactDist.GetFloat()) { g_bDistantImpactPos = LTTRUE; } vDir.Norm(); LTFLOAT fMul = VEC_DOT(vDir, vF); g_bCanSeeImpactPos = (fMul < g_vtWeaponFXMinImpactDot.GetFloat() ? LTFALSE : LTTRUE); // In multiplayer we need to account for impacts that occur around // our camera that we didn't cause (this is also an issue in single // player, but due to the singler player gameplay dynamics it isn't // as noticeable)... if (!g_bCanSeeImpactPos && IsMultiplayerGame()) { // Somebody else shot this...if the impact is close enough, we // "saw" it... if (m_nLocalId != m_nShooterId && fImpactDist <= g_vtWeaponFXMaxMultiImpactDist.GetFloat()) { g_bCanSeeImpactPos = LTTRUE; } } vDir = m_vFirePos - vCameraPos; if (vDir.Mag() > g_vtWeaponFXMaxFireDist.GetFloat()) { g_bDistantFirePos = LTTRUE; } vDir.Norm(); fMul = VEC_DOT(vDir, vF); g_bCanSeeFirePos = (fMul < g_vtWeaponFXMinFireDot.GetFloat() ? LTFALSE : LTTRUE); } // Determine what container the sfx is in... HLOCALOBJ objList[1]; LTVector vTestPos = m_vPos + m_vSurfaceNormal; // Test a little closer... uint32 dwNum = g_pLTClient->GetPointContainers(&vTestPos, objList, 1); if (dwNum > 0 && objList[0]) { uint32 dwUserFlags; g_pLTClient->GetObjectUserFlags(objList[0], &dwUserFlags); if (dwUserFlags & USRFLG_VISIBLE) { uint16 dwCode; if (g_pLTClient->GetContainerCode(objList[0], &dwCode)) { m_eCode = (ContainerCode)dwCode; } } } // Determine if the fire point is in liquid vTestPos = m_vFirePos + m_vDir; // Test a little further in... dwNum = g_pLTClient->GetPointContainers(&vTestPos, objList, 1); if (dwNum > 0 && objList[0]) { uint32 dwUserFlags; g_pLTClient->GetObjectUserFlags(objList[0], &dwUserFlags); if (dwUserFlags & USRFLG_VISIBLE) { uint16 dwCode; if (g_pLTClient->GetContainerCode(objList[0], &dwCode)) { m_eFirePosCode = (ContainerCode)dwCode; } } } if (IsLiquid(m_eCode)) { m_wImpactFX = m_pAmmo->pUWImpactFX ? m_pAmmo->pUWImpactFX->nFlags : 0; } else { m_wImpactFX = m_pAmmo->pImpactFX ? m_pAmmo->pImpactFX->nFlags : 0; } m_wFireFX = m_pAmmo->pFireFX ? m_pAmmo->pFireFX->nFlags : 0; // Assume alt-fire, silenced, and tracer...these will be cleared by // IgnoreFX if not used... m_wFireFX |= WFX_ALTFIRESND | WFX_SILENCED | WFX_TRACER; // Assume impact ding, it will be cleared if not used... m_wImpactFX |= WFX_IMPACTDING; // Clear all the fire fx we want to ignore... m_wFireFX &= ~m_wIgnoreFX; m_wImpactFX &= ~m_wIgnoreFX; // See if this is a redundant weapon fx (i.e., this client shot the // weapon so they've already seen this fx)... if (g_pGameClientShell->IsMultiplayerGame()) { if (m_pAmmo->eType != PROJECTILE) { if (!m_bLocal && m_nLocalId >= 0 && m_nLocalId == m_nShooterId) { if (m_wImpactFX & WFX_IMPACTDING) { if (g_vtMultiDing.GetFloat()) { PlayImpactDing(); } } return LTFALSE; } } } // Show the fire path...(debugging...) if (g_cvarShowFirePath.GetFloat() > 0) { PLFXCREATESTRUCT pls; pls.vStartPos = m_vFirePos; pls.vEndPos = m_vPos; pls.vInnerColorStart = LTVector(GetRandom(127.0f, 255.0f), GetRandom(127.0f, 255.0f), GetRandom(127.0f, 255.0f)); pls.vInnerColorEnd = pls.vInnerColorStart; pls.vOuterColorStart = LTVector(0, 0, 0); pls.vOuterColorEnd = LTVector(0, 0, 0); pls.fAlphaStart = 1.0f; pls.fAlphaEnd = 1.0f; pls.fMinWidth = 0; pls.fMaxWidth = 10; pls.fMinDistMult = 1.0f; pls.fMaxDistMult = 1.0f; pls.fLifeTime = 10.0f; pls.fAlphaLifeTime = 10.0f; pls.fPerturb = 0.0f; pls.bAdditive = LTFALSE; pls.nWidthStyle = PLWS_CONSTANT; pls.nNumSegments = 2; CSpecialFX* pFX = g_pGameClientShell->GetSFXMgr()->CreateSFX(SFX_POLYLINE_ID, &pls); if (pFX) pFX->Update(); } // If the surface is the sky, don't create any impact related fx... if (m_eSurfaceType != ST_SKY || (m_wImpactFX & WFX_IMPACTONSKY)) { CreateWeaponSpecificFX(); if (g_bCanSeeImpactPos) { if ((m_wImpactFX & WFX_MARK) && ShowsMark(m_eSurfaceType) && (LTBOOL)GetConsoleInt("MarkShow", 1)) { LTBOOL bCreateMark = LTTRUE; if (g_bDistantImpactPos && m_nLocalId == m_nShooterId) { // Assume we'll see the mark if we're zoomed in ;) bCreateMark = g_pGameClientShell->IsZoomed(); } if (bCreateMark) { CreateMark(m_vPos, m_vSurfaceNormal, m_rSurfaceRot, m_eSurfaceType); } } CreateSurfaceSpecificFX(); } PlayImpactSound(); } if (IsBulletTrailWeapon()) { if (IsLiquid(m_eFirePosCode)) { if (m_nDetailLevel != RS_LOW) { CreateBulletTrail(&m_vFirePos); } } } // No tracers under water... if ((LTBOOL)GetConsoleInt("Tracers", 1) && (m_wFireFX & WFX_TRACER) && !IsLiquid(m_eCode)) { CreateTracer(); } if (g_bCanSeeFirePos) { // Only do muzzle fx for the client (not for AIs)... if ((m_wFireFX & WFX_MUZZLE) && (m_nLocalId == m_nShooterId)) { CreateMuzzleFX(); } if (!g_bDistantFirePos && (LTBOOL)GetConsoleInt("ShellCasings", 1) && (m_wFireFX & WFX_SHELL)) { CreateShell(); } if ((m_wFireFX & WFX_LIGHT)) { CreateMuzzleLight(); } } if ((m_wFireFX & WFX_FIRESOUND) || (m_wFireFX & WFX_ALTFIRESND) || (m_wFireFX & WFX_SILENCED)) { PlayFireSound(); } // Only do fly-by sounds for weapons that leave bullet trails... if (IsBulletTrailWeapon()) { PlayBulletFlyBySound(); } return LTFALSE; // Just delete me, I'm done :) }