📄 weapon.cpp
字号:
//
// ROUTINE: CWeapon::PlayEmptyWeaponSound()
//
// PURPOSE: Plays the firing sound
//
// ----------------------------------------------------------------------- //
void CWeapon::PlayEmptyWeaponSound(DBOOL bAltFire)
{
CServerDE* pServerDE = BaseClass::GetServerDE();
char *sound;
if (bAltFire)
sound = m_szAltEmptyWeaponSound;
else
sound = m_szEmptyWeaponSound;
// if (pWeap->m_nAmmo > 0) sound = m_FireSound;
if (_mbstrlen(sound) > 0)
{
DFLOAT Radius = 800.0f;
DBYTE sndtype = IsOwnerAPlayer() ? SOUNDPRIORITYBASE_PLAYER : SOUNDPRIORITYBASE_AI;
PlaySoundFromObject(m_hOwner, sound, Radius, sndtype + SOUNDPRIORITYMOD_HIGH, DFALSE,
DFALSE, DFALSE, 100, DFALSE, DTRUE );
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::PlayEmptyWeaponSound()
//
// PURPOSE: Kills all of the currently playing weapon sounds
//
// ----------------------------------------------------------------------- //
void CWeapon::KillSounds()
{
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE) return;
for (int i=0; i < 4; i++)
{
if (m_hCurFireSounds[i])
{
pServerDE->KillSound(m_hCurFireSounds[i]);
m_hCurFireSounds[i] = DNULL;
}
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::UpdateWeaponFX()
//
// PURPOSE: Update the weapon FX variable according to weapon, surfacetype, and ammo type
//
// ----------------------------------------------------------------------- //
void CWeapon::UpdateWeaponFX(DDWORD &nFX, DDWORD &nExtras, WeaponFXExtras *ext, SurfaceType eType, DBYTE nAmmoType, DFLOAT fDamage)
{
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE) return;
ext->nAmmo = nAmmoType;
ext->nSurface = (DBYTE)eType;
nExtras |= WFX_EXTRA_SURFACETYPE | WFX_EXTRA_AMMOTYPE;
// Check surface type flesh to override the sparks (blood) and blood streams
if(eType == SURFTYPE_FLESH)
{
nFX |= WFX_SPARKS;
if(!pServerDE->IntRandom(0,3) || fDamage > 25.0f)
nFX |= WFX_BLOODSPURT;
}
if(m_nType == WEAP_MELEE)
nFX |= WFX_SOUND | WFX_SPARKS;
// Check the FX against the ammo type
if(nAmmoType == AMMO_BULLET || nAmmoType == AMMO_SHELL || nAmmoType == AMMO_BMG)
{
nFX |= WFX_MARK | WFX_SOUND;
if(m_nType != WEAP_MINIGUN)
nFX |= WFX_MUZZLESMOKE;
if(eType == SURFTYPE_WOOD || eType == SURFTYPE_STONE)
{
nFX |= WFX_SMOKE | WFX_FLASH;
if(m_nType != WEAP_MINIGUN)
nFX |= WFX_FRAGMENTS;
}
if(eType == SURFTYPE_METAL)
nFX |= WFX_SPARKS;
if(eType == SURFTYPE_LIQUID)
nFX |= WFX_SPLASH;
}
// Add in the extras flags for spark messages
if(nFX & WFX_SPARKS)
{
nExtras |= WFX_EXTRA_DAMAGE;
ext->fDamage = m_fDamage;
}
// Turn on or off the effect that emit from the weapon itself
if(m_bEjectShell)
{
// Should the ejected shell go the other direction?
if(m_bLeftHand)
nFX |= WFX_LEFTHANDED;
nFX |= WFX_EJECTSHELL;
m_bEjectShell = DFALSE;
}
else
{
nFX &= ~WFX_MUZZLESMOKE;
nFX &= ~WFX_MUZZLELIGHT;
nFX &= ~WFX_EJECTSHELL;
nFX &= ~WFX_TRACER;
}
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::FireVector()
//
// PURPOSE: Fires a vector for vector weapons
//
// ----------------------------------------------------------------------- //
DBOOL CWeapon::FireVector(DVector *vFire, DFLOAT dist, DBOOL bTracer)
{
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE) return DFALSE;
IntersectQuery iq;
IntersectInfo ii;
DVector vFrom, vTo, vDist;
DDWORD nFX = 0;
DDWORD nExtras = 0;
WeaponFXExtras ext;
int nNode = 999;
if(bTracer) nFX |= WFX_TRACER;
// Setup the origin and destination points
VEC_COPY(vFrom, m_vPosition);
VEC_MULSCALAR(vDist, *vFire, dist)
VEC_ADD(vTo, m_vPosition, vDist);
// Set the intersection query values
iq.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID | INTERSECT_HPOLY;
iq.m_FilterFn = DNULL;
iq.m_pUserData = DNULL;
HCLASS hVolume = g_pServerDE->GetClass("VolumeBrush");
HCLASS hObjClass = DNULL;
SurfaceType eType = SURFTYPE_UNKNOWN;
VEC_COPY(iq.m_From, vFrom);
VEC_COPY(iq.m_To, vTo);
while(1)
{
if(pServerDE->IntersectSegment(&iq, &ii))
{
if(m_hOwner != ii.m_hObject) // don't hit myself.
{
hObjClass = g_pServerDE->GetObjectClass(ii.m_hObject);
eType = GetSurfaceType(ii.m_hObject, ii.m_hPoly);
nFX = nExtras = 0;
UpdateWeaponFX(nFX, nExtras, &ext, eType, m_nAmmoType, m_fDamage);
// Test to see if we hit water... and set the FX accordingly
if(pServerDE->IsKindOf(hObjClass, hVolume))
{
VolumeBrush* liquid = (VolumeBrush*)g_pServerDE->HandleToObject(ii.m_hObject);
if(liquid)
{
ContainerCode code = liquid->GetCode();
if(code == CC_WATER || code == CC_BLOOD || code == CC_ACID)
{
liquid->GetSurfaceColor(&(ext.vColor1), &(ext.vColor2));
nExtras |= WFX_EXTRA_DAMAGE | WFX_EXTRA_COLOR1 | WFX_EXTRA_COLOR2;
AddImpact(&(iq.m_From), &(ii.m_Point), vFire, &(ii.m_Plane.m_Normal), nFX, nExtras, &ext);
}
}
}
// Check to see if we hit a world object that can be shot through
else if(ii.m_hObject == pServerDE->GetWorldObject() && eType != SURFTYPE_FIRETHROUGH)
{
AddImpact(&(iq.m_From), &(ii.m_Point), vFire, &(ii.m_Plane.m_Normal), nFX, nExtras, &ext);
SendSoundTrigger(m_hOwner, SOUND_GUNIMPACT, ii.m_Point, 200.0f);
if(eType != SURFTYPE_GLASS || m_nType == WEAP_MELEE) // Continue the vector for glass
return DTRUE;
}
// See if we should place a mark on a world object that was hit
else if(IsBrushObject(hObjClass) && nFX & WFX_MARK) // CDestructableBrush object
{
CDestructableBrush *pBrush = (CDestructableBrush*)pServerDE->HandleToObject(ii.m_hObject);
if(!pBrush->IsFireThrough())
{
eType = pBrush->GetSurfaceType();
if(pBrush->IsMarkable())
AddWorldModelMark(ii.m_Point, ii.m_Plane.m_Normal, ii.m_hObject, eType);
AddImpact(&(iq.m_From), &(ii.m_Point), vFire, &(ii.m_Plane.m_Normal), nFX, nExtras, &ext);
SendSoundTrigger(m_hOwner, SOUND_GUNIMPACT, ii.m_Point, 200.0f);
SendDamageMsg(ii.m_hObject, ii.m_Point, vFire, m_fDamage);
// Continue the vector for glass
if(eType != SURFTYPE_GLASS) return DTRUE;
}
}
// Did we hit a door?
else if(IsDoor(hObjClass))
{
Door *pDoor = (Door*)pServerDE->HandleToObject(ii.m_hObject);
if (!pDoor->IsFireThrough())
{
AddImpact(&(iq.m_From), &(ii.m_Point), vFire, &(ii.m_Plane.m_Normal), nFX, nExtras, &ext);
SendSoundTrigger(m_hOwner, SOUND_GUNIMPACT, ii.m_Point, 200.0f);
SendDamageMsg(ii.m_hObject, ii.m_Point, vFire, m_fDamage);
return DTRUE;
}
}
// General case
else
{
AddImpact(&(iq.m_From), &(ii.m_Point), vFire, &(ii.m_Plane.m_Normal), nFX, nExtras, &ext);
SendDamageMsg(ii.m_hObject, ii.m_Point, vFire, m_fDamage);
if(!pServerDE->IsKindOf(pServerDE->GetObjectClass(ii.m_hObject), pServerDE->GetClass("CBaseCharacter")))
SendSoundTrigger(m_hOwner, SOUND_GUNIMPACT, ii.m_Point, 200.0f);
return DTRUE;
}
}
// Add a bit to the vector and carry the shot through
VEC_ADD(iq.m_From, ii.m_Point, *vFire);
}
else
return DFALSE; //did not hit an object in max range
}
return DTRUE;
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::FireProjectile()
//
// PURPOSE: Fires a projectile
//
// ----------------------------------------------------------------------- //
CProjectile* CWeapon::FireProjectile(DVector *vFire, DFLOAT dist, DBOOL bAltFire)
{
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE)
return DFALSE;
ObjectCreateStruct ocStruct;
INIT_OBJECTCREATESTRUCT(ocStruct);
ROT_COPY(ocStruct.m_Rotation, m_rRotation);
VEC_COPY(ocStruct.m_Pos, m_vPosition);
if(m_hClient)
ocStruct.m_UserData = pServerDE->GetClientID(m_hClient);
else
ocStruct.m_UserData = 0;
CProjectile *pProject = NULL;
HCLASS hClass = NULL;
DFLOAT fVelocity;
int nRadius;
if (!bAltFire && m_szProjectileClass)
{
hClass = pServerDE->GetClass(m_szProjectileClass);
fVelocity = m_fProjVelocity;
nRadius = m_nDamageRadius;
// pServerDE->CPrint("Primary projectile fired! Velocity = %f", fVelocity);
}
else if (m_szAltProjectileClass)
{
hClass = pServerDE->GetClass(m_szAltProjectileClass);
fVelocity = m_fAltProjVelocity;
nRadius = m_nAltDamageRadius;
// pServerDE->CPrint("Alt projectile fired! Velocity = %f", fVelocity);
}
if (hClass)
{
if (pProject = (CProjectile*)pServerDE->CreateObject(hClass, &ocStruct))
{
if(g_pBloodServerShell->IsMultiplayerGame() && m_bMultiDamageBoost)
pProject->Setup(vFire, m_nType, m_fDamage * GetDamageMultiplier() * 2.0f, fVelocity, nRadius, m_hOwner);
else
pProject->Setup(vFire, m_nType, m_fDamage * GetDamageMultiplier(), fVelocity, nRadius, m_hOwner);
}
}
else // No class! there isn't really a projectile, so fire a vector.
{
FireVector(vFire, dist);
}
return pProject;
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::Fire()
//
// PURPOSE: Fires the weapon
//
// ----------------------------------------------------------------------- //
DDWORD CWeapon::Fire()
{
//***** Make sure the server and inventory systems are valid *****//
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE || !m_pInventoryMgr)
return 0;
//***** General variable declarations *****//
DFLOAT fReloadTime;
DBYTE nFiredType = 0;
DFLOAT cTime = pServerDE->GetTime();
DFLOAT range;
Spread *spread;
DDWORD dwShotsPerFire;
D_WORD nAmmoUse;
DBOOL bHitPlayerObj = DFALSE;
// Reload time depends on current barrel or alt fire
if(m_bLastFireAlt) fReloadTime = m_fAltReloadTime;
else fReloadTime = m_fReloadTime;
// See if any reload time modifiers are in effect
fReloadTime *= m_pInventoryMgr->GetFireRateMultiplier();
// Set up varaible for primary or alternate firing states
DBOOL bAltFire = (m_eState == WS_ALT_FIRING) || (m_eState == WS_START_ALT_FIRING) || (m_eState == WS_STOP_ALT_FIRING);
if(!bAltFire)
{
dwShotsPerFire = m_dwShotsPerFire;
spread = &m_Spread;
range = m_fRange;
m_fDamage = pServerDE->Random(m_fMinDamage, m_fMaxDamage);
nAmmoUse = m_nAmmoUse;
}
else
{
dwShotsPerFire = m_dwAltShotsPerFire;
spread = &m_AltSpread;
range = m_fAltRange;
m_fDamage = pServerDE->Random(m_fMinAltDamage, m_fMaxAltDamage);
nAmmoUse = m_nAltAmmoUse;
}
// Only fire ammo if we have the ammo
DFLOAT fAmmoCount = CheckAmmo(bAltFire);
if (fAmmoCount)
{
switch(GetAmmoType(bAltFire))
{
// Shells only use 1 shell per fire, no matter how many vectors
case AMMO_SHELL:
fAmmoCount -= (DFLOAT)nAmmoUse;
break;
// default is 1 bullet per vector
default:
if ((DFLOAT)(dwShotsPerFire * nAmmoUse) > fAmmoCount)
dwShotsPerFire = (int)fAmmoCount / nAmmoUse;
fAmmoCount -= (DFLOAT)(dwShotsPerFire * nAmmoUse);
break;
}
m_pInventoryMgr->SetAmmoCount(GetAmmoType(bAltFire), fAmmoCount);
}
else
dwShotsPerFire = 0;
//***** If no shots are available, play empty sound and exit the function *****//
if(dwShotsPerFire <= 0)
{
PlayEmptyWeaponSound(bAltFire);
if (!IsOwnerAPlayer()) m_eState = WS_REST; //SCHLEGZ
return 0; // Don't fire if we don't have ammo
}
//***** For non-melee weapons... display a muzzle flash *****//
if(m_nFireType != TYPE_MELEE)
ShowMuzzleFlash(&m_vPosition );
PlayFireSound(bAltFire);
DVector shellDir;
FireSpread(spread, dwShotsPerFire, range, bAltFire, &shellDir);
m_fLastShotTime = cTime;
if(m_eState == WS_ALT_FIRING)
{
m_bLastFireAlt = DTRUE;
nFiredType = 2;
}
else
{
m_bLastFireAlt = DFALSE;
nFiredType = 1;
}
if (!IsOwnerAPlayer()) m_eState = WS_REST; //SCHLEGZ
// Reset the shell eject flag for the next fire..
if(m_nAmmoType == AMMO_BULLET || m_nAmmoType == AMMO_SHELL)
m_bEjectShell = DTRUE;
else
m_bEjectShell = DFALSE;
return nFiredType;
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::FireSpread()
//
// PURPOSE: Fires the weapon
//
// ----------------------------------------------------------------------- //
void CWeapon::FireSpread(Spread *spread, DDWORD shots, DFLOAT range, DBOOL bAltFire, DVector *rDir)
{
//***** Make sure the server and inventory systems are valid *****//
CServerDE* pServerDE = BaseClass::GetServerDE();
if (!pServerDE || !m_pInventoryMgr) return;
//***** Variables to calculate the spread of the weapon fire *****//
DFLOAT hOffset, vOffset;
DVector vTmp, vFire;
DVector vU, vR, vF;
DBOOL bTracer = DFALSE;
pServerDE->GetRotationVectors(&m_rRotation, &vU, &vR, &vF);
// Check to see if the player is standing too close to something, and we need
// to move the firing position back in order to hit it
// if(IsPlayer(m_hOwner) && FiringTooClose(&vF, 60.0f, &vTmp))
// { VEC_COPY(m_vPosition, vTmp); }
for(int i = (shots - 1); i >= 0; i--)
{
VEC_COPY(vFire, vF)
if(spread->h)
{
hOffset = pServerDE->Random(-spread->h, spread->h) / 2000.0f;
VEC_MULSCALAR(vTmp, vR, hOffset)
VEC_ADD(vFire, vFire, vTmp)
}
if(spread->v)
{
vOffset = pServerDE->Random(-spread->v, spread->v) / 2000.0f;
VEC_MULSCALAR(vTmp, vU, vOffset)
VEC_ADD(vFire, vFire, vTmp)
}
VEC_NORM(vFire)
if(m_bAccuracyCheck && IsPlayer(m_hOwner))
AlignFireVector(&vFire, range);
// See if we should fire a tracer for bullets
if(GetAmmoType(bAltFire) == AMMO_BULLET)
bTracer = DTRUE;
// Reset the splash
m_bSplash = DTRUE;
// Fire the appropriate thing
if((bAltFire && !m_szAltProjectileClass) || (!bAltFire && !m_szProjectileClass))
FireVector(&vFire, range, bTracer);
else
FireProjectile(&vFire, range, bAltFire);
}
VEC_COPY(*rDir, vR);
}
// ----------------------------------------------------------------------- //
//
// ROUTINE: CWeapon::FiringTooClose
//
// PURPOSE: Trick to improve accuracy and keep the effect at the weapon end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -