Simulated Bullets: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
Line 47: Line 47:
m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAttacker);
m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAttacker);
m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAdditionalIgnoreEnt);
m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAdditionalIgnoreEnt);
m_pTraceBack = new CTraceFilterSkipTwoEntities(m_Bullet.m_pAttacker,m_Bullet.m_pAdditionalIgnoreEnt,COLLISION_GROUP_NONE);
m_pTwoEnts = new CTraceFilterSkipTwoEntities(m_Bullet.m_pAttacker,m_Bullet.m_pAdditionalIgnoreEnt,COLLISION_GROUP_NONE);
}
}
void DeletePointers(void)
void DeletePointers(void)
Line 53: Line 53:
//leave the compensation considerations because they're entities
//leave the compensation considerations because they're entities
delete m_pIgnoreList;
delete m_pIgnoreList;
delete m_pTraceBack;
delete m_pTwoEnts;
}
}


Line 64: Line 64:
CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist
CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist
CTraceFilterSimpleList *m_pIgnoreList;//already hit
CTraceFilterSimpleList *m_pIgnoreList;//already hit
CTraceFilterSkipTwoEntities *m_pTraceBack;
CTraceFilterSkipTwoEntities *m_pTwoEnts;
float m_flLastDensity;
float m_flLastDensity;
};
};
Line 81: Line 81:
void RemoveBullet(int index);
void RemoveBullet(int index);
inline bool IsInWorld(int index);
inline bool IsInWorld(int index);
inline float GetSpeedMod(int index);
inline float GetSpeedMod(int index, float flDensity);
};</pre>
};</pre>



Revision as of 12:51, 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 line drawn along its path.

Body

This code is WIP by ts2do

cl_dll

c_bullet_manager.cpp

#include "cbase.h"
CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT)
{
	engine->ServerCmd(VarArgs("createbullet_s %f\n",gpGlobals->curtime));
}

dlls

bullet_manager.h

#define MAX_BULLETS 256
#define BULLET_SPEED 240//inches per hundredths of a second
class CBulletManager;
extern CBulletManager *g_pBulletManager;
inline CBulletManager *BulletManager()
{
	return g_pBulletManager;
}
extern ConVar g_debug_bullets;
struct SimulatedBullet_t
{
	SimulatedBullet_t()
	{
		m_Bullet = FireBulletsInfo_t();
		m_vOrigin = vec3_origin;
		m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED;
		m_flBulletTime = 0;
		m_flLagCompensation = 0.0f;
	}

	SimulatedBullet_t( FireBulletsInfo_t &bullet, float lagCompensation )
	{
		m_Bullet = bullet;
		m_vOrigin = bullet.m_vecSrc;
		m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED;
		m_flBulletTime = 0;
		m_flLagCompensation = lagCompensation;
		m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE);
		m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAttacker);
		m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAdditionalIgnoreEnt);
		m_pTwoEnts = new CTraceFilterSkipTwoEntities(m_Bullet.m_pAttacker,m_Bullet.m_pAdditionalIgnoreEnt,COLLISION_GROUP_NONE);
	}
	void DeletePointers(void)
	{
		//leave the compensation considerations because they're entities
		delete m_pIgnoreList;
		delete m_pTwoEnts;
	}

	FireBulletsInfo_t m_Bullet;
	Vector m_vOrigin;
	float m_flBulletSpeed;
	float m_flInitialBulletSpeed;
	float m_flBulletTime;
	float m_flLagCompensation;
	CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist
	CTraceFilterSimpleList *m_pIgnoreList;//already hit
	CTraceFilterSkipTwoEntities *m_pTwoEnts;
	float m_flLastDensity;
};
class CBulletManager : public CBaseEntity
{
	DECLARE_CLASS( CBulletManager, CBaseEntity );
public:
	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, float flDensity);
};

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==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(CUR.m_vOrigin);
	Vector 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

	float flLastDensity=0.0f;
	do
	{
		UTIL_TraceLine( m_vTraceStart, CUR.m_vOrigin, MASK_SHOT, CUR.m_pTwoEnts, &trace );
		if(trace.allsolid)
		{
			trace.endpos = CUR.m_vOrigin;
			trace.fraction = 1.0f;
			if(flLastDensity!=0.0f)
				CUR.m_flBulletSpeed -= GetSpeedMod(index, flLastDensity);
			break;
		}
		else if(trace.startsolid)
		{
			//Do penetration surface impact stuff here
			//trace.fractionleftsolid
			flLastDensity = 0.0f;
		}
		else
		{
			if(trace.surface.name[0]!='*'&&!(trace.surface.flags & SURF_SKY))
			{
				//Do surface impact stuff here
				flLastDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density;
				CUR.m_flBulletSpeed -= trace.fractionleftsolid * GetSpeedMod(index, flLastDensity);
			}
		}
		if(trace.DidHitNonWorldEntity())
		{
			if(CUR.m_pIgnoreList->ShouldHitEntity(trace.m_pEnt,MASK_SHOT))
			{
				CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt);
				//Do entity impact stuff here
			}
		}
		m_vTraceStart = trace.endpos + CUR.m_Bullet.m_vecDirShooting;
	}while(trace.endpos!=CUR.m_vOrigin);

#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, float flDensity)
{
	if(!flDensity)
		return 0.0f;
	if(flDensity>100)
		return fabs(100.0f-flDensity) / 100.0f;
	else
		return flDensity / 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