Simulated Bullets: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
Line 9: Line 9:
This code is WIP by ts2do
This code is WIP by ts2do
===bullet_manager.h===
===bullet_manager.h===
<pre>#define MAX_BULLETS 256
<pre>#include "cbase.h"
#define BULLET_SPEED 240//inches per hundredths of a second
#include "movevars_shared.h"
class CBulletManager;
#include "util_shared.h"
extern CBulletManager *g_pBulletManager;
#include "bullet_manager.h"
inline CBulletManager *BulletManager()
ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT );
CBulletManager *g_pBulletManager;
LINK_ENTITY_TO_CLASS( bullet_manager, CBulletManager );
CBulletManager::CBulletManager()
{
{
return g_pBulletManager;
Q_memset( m_rgBullets, 0, MAX_BULLETS );
m_iLastBullet = -1;
}
}
extern ConVar g_debug_bullets;
CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT)
struct SimulatedBullet_t
{
{
SimulatedBullet_t()
Assert(BulletManager());
if(!BulletManager())
return;
CBasePlayer *pPlayer = UTIL_GetCommandClient();
if(!pPlayer)
return;
Vector dir;
pPlayer->EyeVectors(&dir);
FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0);
info.m_pAttacker = pPlayer;
float lag = gpGlobals->curtime - atof(engine->Cmd_Argv(1));
if(lag<0.0f||lag==gpGlobals->curtime)
lag=0.0f;
BulletManager()->AddBullet(info,lag);
}
void CBulletManager::Spawn(void)
{
Think();
}
void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/)
{
#define CUR m_rgBullets[index]
CUR.m_flBulletTime += time;
trace_t trace, trace_back;
Vector m_vOldOrigin, m_vTraceStart;
m_vOldOrigin = m_vTraceStart = CUR.m_vOrigin;
DevMsg("BulletSpeed %f LastDensity %f\n",CUR.m_flBulletSpeed, CUR.m_flLastDensity);
CUR.m_vOrigin += CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time;//in/100th of a sec * 100th of a sec
 
int sentinal=0;//debugging purposes
do
{
{
m_Bullet = FireBulletsInfo_t();
UTIL_TraceLine( m_vTraceStart + CUR.m_Bullet.m_vecDirShooting, CUR.m_vOrigin, MASK_SHOT, CUR.m_pIgnoreList, &trace );
m_vOrigin = vec3_origin;
DevMsg("%f\n",trace.endpos);
m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED;
if(trace.DidHitNonWorldEntity())
m_flBulletTime = 0;
{
m_flLagCompensation = 0.0f;
CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt);
//Do entity impact stuff here
}
//Do surface impact stuff here
UTIL_TraceLine( m_vTraceStart, m_vOldOrigin, MASK_SHOT, CUR.m_pTraceBack, &trace_back );
//Do penetration surface impact stuff here
m_vTraceStart = trace.endpos;
sentinal++;
}while(trace.endpos!=CUR.m_vOrigin&&sentinal<20);
if(sentinal==20)
DevMsg("INFINITE LOOP SPOTTED!\n");
 
if(CUR.m_flLastDensity!=0.0f)
{
if(trace.allsolid)
{
CUR.m_flBulletSpeed -= GetSpeedMod(index);
}
else if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY))
{//make sure we didnt hit an invalid (nodraw) or sky material
if(trace.startsolid)
{
CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index);
CUR.m_flLastDensity = 0.0f;
}
if( trace.fraction != 1.0f)
{
CUR.m_flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density;
}
}
}
#ifndef _DEBUG
if(g_debug_bullets.GetBool())
#endif
{
NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 10.0f );
NDebugOverlay::Cross3D(CUR.m_vOrigin, 16, 255, 0, 0, true, 10.0f);
}
}


SimulatedBullet_t( FireBulletsInfo_t &bullet, float lagCompensation )
if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index))
RemoveBullet(index);
#undef CUR
}
void CBulletManager::Think(void)
{
for(int x=0;x<=m_iLastBullet;x++)
{
{
m_Bullet = bullet;
SimulateBullet(x);
m_vOrigin = bullet.m_vecSrc;
m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED;
m_flBulletTime = 0;
m_flLagCompensation = lagCompensation;  
}
}
 
SetNextThink( gpGlobals->curtime + 0.01f );
FireBulletsInfo_t m_Bullet;
}
Vector m_vOrigin;
void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation)
float m_flBulletSpeed;
{
float m_flInitialBulletSpeed;
if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem
float m_flBulletTime;
{//let's do something reckless on bullet mayhem
float m_flLagCompensation;
Assert(m_iLastBullet!=MAX_BULLETS-1);
CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist
Warning("Bullet queue filled (Removing bullet #0)\n");
CUtlVector<CBaseEntity *> m_pHit;
RemoveBullet(0);
float m_flLastDensity;
}
};
else
class CBulletManager : public CBaseEntity
m_iLastBullet++;
DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation);
m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation);
if(lagCompensation!=0.0f)
SimulateBullet(m_iLastBullet, lagCompensation*100);
}
void CBulletManager::RemoveBullet(int index)
{
if(m_iLastBullet!=index)
{
m_rgBullets[index].DeletePointers();
m_rgBullets[index] = m_rgBullets[m_iLastBullet];
DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet);
}
else
{
m_rgBullets[index].DeletePointers();
DevMsg("Bullet #%i Destroyed\n",index);
}
m_rgBullets[m_iLastBullet] = SimulatedBullet_t();
m_iLastBullet--;
}
inline bool CBulletManager::IsInWorld(int index)
{
if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false;
if (m_rgBullets[index].m_vOrigin.y >= MAX_COORD_INTEGER) return false;
if (m_rgBullets[index].m_vOrigin.z >= MAX_COORD_INTEGER) return false;
if (m_rgBullets[index].m_vOrigin.x <= MIN_COORD_INTEGER) return false;
if (m_rgBullets[index].m_vOrigin.y <= MIN_COORD_INTEGER) return false;
if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false;
return true;
}
inline float CBulletManager::GetSpeedMod(int index)
{
{
DECLARE_CLASS( CBulletManager, CBaseEntity );
return fabs(100.0f-m_rgBullets[index].m_flLastDensity) / 100.0f;
public:
}</pre>
SimulatedBullet_t m_rgBullets[MAX_BULLETS];
int m_iLastBullet;
 
CBulletManager();
void Spawn(void);
void Think(void);
void SimulateBullet(int index, float time=1.0f);
void AddBullet(FireBulletsInfo_t &bullet, float lagCompensation);
void RemoveBullet(int index);
inline bool IsInWorld(int index);
inline float GetSpeedMod(int index);
};</pre>


===bullet_manager.cpp===
===bullet_manager.cpp===

Revision as of 00:42, 21 November 2005

Preface

Basically, with simulated bullets, the aspects of physics are going to tried to be captured by simulating them in batch simulation code. So far the code is all server-side and is later expected to be client-side simulated with similar code.


Note.pngNote:This code is a work in progress. Feel free to make helpful changes. To test the code, you can bind a key to createbullet ingame and that will fire a bullet from your view with a parabola drawn along its path.

Body

This code is WIP by ts2do

bullet_manager.h

#include "cbase.h"
#include "movevars_shared.h"
#include "util_shared.h"
#include "bullet_manager.h"
ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT );
CBulletManager *g_pBulletManager;
LINK_ENTITY_TO_CLASS( bullet_manager, CBulletManager );
CBulletManager::CBulletManager()
{
	Q_memset( m_rgBullets, 0, MAX_BULLETS );
	m_iLastBullet = -1;
}
CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT)
{
	Assert(BulletManager());
	if(!BulletManager())
		return;
	CBasePlayer *pPlayer = UTIL_GetCommandClient();
	if(!pPlayer)
		return;
	Vector dir;
	pPlayer->EyeVectors(&dir);
	FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0);
	info.m_pAttacker = pPlayer;
	float lag = gpGlobals->curtime - atof(engine->Cmd_Argv(1));
	if(lag<0.0f||lag==gpGlobals->curtime)
		lag=0.0f;
	BulletManager()->AddBullet(info,lag);
}
void CBulletManager::Spawn(void)
{
	Think();
}
void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/)
{
#define CUR m_rgBullets[index]
	CUR.m_flBulletTime += time;
	trace_t trace, trace_back;
	Vector m_vOldOrigin, m_vTraceStart;
	m_vOldOrigin = m_vTraceStart = CUR.m_vOrigin;
	DevMsg("BulletSpeed %f LastDensity %f\n",CUR.m_flBulletSpeed, CUR.m_flLastDensity);
	CUR.m_vOrigin += CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time;//in/100th of a sec * 100th of a sec

	int sentinal=0;//debugging purposes
	do
	{
		UTIL_TraceLine( m_vTraceStart + CUR.m_Bullet.m_vecDirShooting, CUR.m_vOrigin, MASK_SHOT, CUR.m_pIgnoreList, &trace );
		DevMsg("%f\n",trace.endpos);
		if(trace.DidHitNonWorldEntity())
		{
			CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt);
			//Do entity impact stuff here
		}
		//Do surface impact stuff here
		UTIL_TraceLine( m_vTraceStart, m_vOldOrigin, MASK_SHOT, CUR.m_pTraceBack, &trace_back );
		//Do penetration surface impact stuff here
		m_vTraceStart = trace.endpos;
		sentinal++;
	}while(trace.endpos!=CUR.m_vOrigin&&sentinal<20);
	if(sentinal==20)
		DevMsg("INFINITE LOOP SPOTTED!\n");

	if(CUR.m_flLastDensity!=0.0f)
	{
		if(trace.allsolid)
		{
			CUR.m_flBulletSpeed -= GetSpeedMod(index);
		}
		else if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY))
		{//make sure we didnt hit an invalid (nodraw) or sky material
			if(trace.startsolid)
			{
				CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index);
				CUR.m_flLastDensity = 0.0f;
			}
			if( trace.fraction != 1.0f)
			{
				CUR.m_flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density;
			}
		}
	}
#ifndef _DEBUG
	if(g_debug_bullets.GetBool())
#endif
	{
		NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 10.0f );
		NDebugOverlay::Cross3D(CUR.m_vOrigin, 16, 255, 0, 0, true, 10.0f);
	}

	if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index))
		RemoveBullet(index);
#undef CUR
}
void CBulletManager::Think(void)
{
	for(int x=0;x<=m_iLastBullet;x++)
	{
		SimulateBullet(x);
	}
	SetNextThink( gpGlobals->curtime + 0.01f );
}
void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation)
{
	if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem
	{//let's do something reckless on bullet mayhem
		Assert(m_iLastBullet!=MAX_BULLETS-1);
		Warning("Bullet queue filled (Removing bullet #0)\n");
		RemoveBullet(0);
	}
	else
		m_iLastBullet++;
	DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation);
	m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation);
	if(lagCompensation!=0.0f)
		SimulateBullet(m_iLastBullet, lagCompensation*100);
}
void CBulletManager::RemoveBullet(int index)
{
	if(m_iLastBullet!=index)
	{
		m_rgBullets[index].DeletePointers();
		m_rgBullets[index] = m_rgBullets[m_iLastBullet];
		DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet);
	}
	else
	{
		m_rgBullets[index].DeletePointers();
		DevMsg("Bullet #%i Destroyed\n",index);
	}
	m_rgBullets[m_iLastBullet] = SimulatedBullet_t();
	m_iLastBullet--;
}
inline bool CBulletManager::IsInWorld(int index)
{
	if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.y >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.z >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.x <= MIN_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.y <= MIN_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false;
	return true;
}
inline float CBulletManager::GetSpeedMod(int index)
{
	return fabs(100.0f-m_rgBullets[index].m_flLastDensity) / 100.0f;
}

bullet_manager.cpp

#include "cbase.h"
#include "movevars_shared.h"
#include "util_shared.h"
#include "bullet_manager.h"
ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT );
CBulletManager *g_pBulletManager;
LINK_ENTITY_TO_CLASS( bullet_manager, CBulletManager );
CBulletManager::CBulletManager()
{
	Q_memset( m_rgBullets, 0, MAX_BULLETS );
	m_iLastBullet = -1;
}
CON_COMMAND_F(createbullet_s,NULL,FCVAR_CHEAT)
{
	Assert(BulletManager());
	if(!BulletManager())
		return;
	CBasePlayer *pPlayer = UTIL_GetCommandClient();
	if(!pPlayer)
		return;
	Vector dir;
	pPlayer->EyeVectors(&dir);
	FireBulletsInfo_t info(1,pPlayer->EyePosition(),dir,vec3_origin,0,0);
	info.m_pAttacker = pPlayer;
	float lag = gpGlobals->curtime - atof(engine->Cmd_Argv(1));
	if(lag<0.0f)
		lag=0.0f;
	BulletManager()->AddBullet(info,lag);
}
void CBulletManager::Spawn(void)
{
	Think();
}
void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/)
{
#define CUR m_rgBullets[index]
	CUR.m_flBulletTime += time;
	Vector m_vOldOrigin = CUR.m_vOrigin;
	DevMsg("BulletSpeed %f LastDensity %f\n",CUR.m_flBulletSpeed, CUR.m_flLastDensity);
	CUR.m_vOrigin += CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time;//in/100th of a sec * 100th of a sec

	CTraceFilterSkipTwoEntities traceFilter( CUR.m_Bullet.m_pAttacker, CUR.m_Bullet.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE );
	trace_t trace;
	UTIL_TraceLine( m_vOldOrigin, CUR.m_vOrigin, MASK_SHOT, &traceFilter, &trace );

	if(trace.m_pEnt)
	{
		if(!(CUR.m_pHit).Find(trace.m_pEnt))
		{
		}
	}
	if(CUR.m_flLastDensity!=0.0f)
	{
		if(trace.allsolid)
		{
			CUR.m_flBulletSpeed -= GetSpeedMod(index);
		}
		else if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY))
		{//make sure we didnt hit an invalid (nodraw) or sky material
			if(trace.startsolid)
			{
				CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index);
				CUR.m_flLastDensity = 0.0f;
			}
			if( trace.fraction != 1.0f)
			{
				CUR.m_flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density;
			}
		}
	}
#ifndef _DEBUG
	if(g_debug_bullets.GetBool())
#endif
	{
		NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 0.25f );
	}

	if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index))
		RemoveBullet(index);
#undef CUR
}
void CBulletManager::Think(void)
{
	for(int x=0;x<=m_iLastBullet;x++)
	{
		SimulateBullet(x);
	}
	SetNextThink( gpGlobals->curtime + 0.01f );
}
void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation)
{
	lagCompensation+=0.1f;
	if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem
	{//let's do something reckless on bullet mayhem
		Assert(m_iLastBullet!=MAX_BULLETS-1);
		Warning("Bullet queue filled (Removing bullet #0)\n");
		RemoveBullet(0);
	}
	else
		m_iLastBullet++;
	DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation);
	m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation);
	if(lagCompensation!=0.0f)
		SimulateBullet(m_iLastBullet, lagCompensation*100);
}
void CBulletManager::RemoveBullet(int index)
{
	if(m_iLastBullet!=index)
	{
		m_rgBullets[index] = m_rgBullets[m_iLastBullet];
		DevMsg("Bullet #%i Destroyed; Replacing with #%i\n",index, m_iLastBullet);
	}
	else
		DevMsg("Bullet #%i Destroyed\n",index);
	m_rgBullets[m_iLastBullet] = SimulatedBullet_t();
	m_iLastBullet--;
}
inline bool CBulletManager::IsInWorld(int index)
{
	if (m_rgBullets[index].m_vOrigin.x >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.y >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.z >= MAX_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.x <= MIN_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.y <= MIN_COORD_INTEGER) return false;
	if (m_rgBullets[index].m_vOrigin.z <= MIN_COORD_INTEGER) return false;
	return true;
}
inline float CBulletManager::GetSpeedMod(int index)
{
	return fabs(100.0f-m_rgBullets[index].m_flLastDensity) / 100.0f;
}

Implementation

bullet_manager entity

gamerules.cpp

Add
#include "bullet_manager.h"
After
#include "filesystem.h"

Add
g_pBulletManager = (CBulletManager*)CBaseEntity::Create( "bullet_manager", vec3_origin, vec3_angle );
After
g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "player_manager", vec3_origin, vec3_angle );

Making weapons shoot them