⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 weapon.cpp

📁 Blood 2全套源码
💻 CPP
📖 第 1 页 / 共 4 页
字号:
//
//	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 + -