Simulated Bullets: Difference between revisions
Jump to navigation
Jump to search
Note:This code is a work in progress. Feel free to make helpful changes. To test the code, you can bind a key to
m (→Body) |
|||
Line 8: | Line 8: | ||
==Body== | ==Body== | ||
This code is WIP by ts2do | This code is WIP by ts2do | ||
=== | ===game_shared=== | ||
====bullet_manager.h==== | ====bullet_manager.h==== | ||
<pre>#define MAX_BULLETS 256 | <pre>#define MAX_BULLETS 256 | ||
#define BULLET_SPEED 240//inches per hundredths of a second | #define BULLET_SPEED 240//inches per hundredths of a second | ||
#ifdef CLIENT_DLL | |||
#define CBulletManager C_BulletManager | |||
#endif | |||
class CBulletManager; | class CBulletManager; | ||
extern CBulletManager *g_pBulletManager; | extern CBulletManager *g_pBulletManager; | ||
Line 31: | Line 27: | ||
{ | { | ||
m_Bullet = FireBulletsInfo_t(); | m_Bullet = FireBulletsInfo_t(); | ||
m_vOrigin | m_vOrigin.Init(); | ||
m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; | ||
m_flEntryDensity = m_flLagCompensation = 0.0f; | m_flEntryDensity = m_flLagCompensation = 0.0f; | ||
Line 43: | Line 39: | ||
m_flEntryDensity = 0.0f; | m_flEntryDensity = 0.0f; | ||
m_flLagCompensation = lagCompensation; | m_flLagCompensation = lagCompensation; | ||
#ifndef CLIENT_DLL | |||
m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); | m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); | ||
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); | ||
#endif | |||
m_pTwoEnts = 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) | ||
{ | { | ||
#ifndef CLIENT_DLL | |||
//leave the compensation considerations because they're entities | //leave the compensation considerations because they're entities | ||
delete m_pIgnoreList; | delete m_pIgnoreList; | ||
#endif | |||
delete m_pTwoEnts; | delete m_pTwoEnts; | ||
} | } | ||
Line 61: | Line 61: | ||
float m_flLagCompensation; | float m_flLagCompensation; | ||
float m_flEntryDensity; | float m_flEntryDensity; | ||
bool m_bWasInWater; | |||
#ifndef CLIENT_DLL | |||
CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist | CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist | ||
CTraceFilterSimpleList *m_pIgnoreList;//already hit | CTraceFilterSimpleList *m_pIgnoreList;//already hit | ||
#endif | |||
CTraceFilterSkipTwoEntities *m_pTwoEnts; | CTraceFilterSkipTwoEntities *m_pTwoEnts; | ||
}; | }; | ||
Line 74: | Line 77: | ||
CBulletManager(); | CBulletManager(); | ||
void Spawn(void); | void Spawn(void); | ||
#ifdef CLIENT_DLL | |||
void ClientThink(void); | |||
#else | |||
void Think(void); | void Think(void); | ||
#endif | |||
void SimulateBullet(int index, float time=1.0f); | void SimulateBullet(int index, float time=1.0f); | ||
void AddBullet(FireBulletsInfo_t &bullet, float lagCompensation); | void AddBullet(FireBulletsInfo_t &bullet, float lagCompensation); | ||
Line 85: | Line 92: | ||
#include "util_shared.h" | #include "util_shared.h" | ||
#include "bullet_manager.h" | #include "bullet_manager.h" | ||
#include "shot_manipulator.h" | |||
#pragma warning(disable:4800) | |||
ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT ); | ConVar g_debug_bullets( "g_debug_bullets", "0", FCVAR_CHEAT ); | ||
CBulletManager *g_pBulletManager; | CBulletManager *g_pBulletManager; | ||
Line 93: | Line 102: | ||
m_iLastBullet = -1; | m_iLastBullet = -1; | ||
} | } | ||
#ifdef CLIENT_DLL | |||
CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT) | |||
{ | |||
engine->ServerCmd(VarArgs("createbullet_s %f\n",gpGlobals->curtime)); | |||
} | |||
#else | |||
CON_COMMAND_F(createbullet_s,NULL,FCVAR_CHEAT) | CON_COMMAND_F(createbullet_s,NULL,FCVAR_CHEAT) | ||
{ | { | ||
Line 110: | Line 125: | ||
BulletManager()->AddBullet(info,lag); | BulletManager()->AddBullet(info,lag); | ||
} | } | ||
#endif | |||
void CBulletManager::Spawn(void) | void CBulletManager::Spawn(void) | ||
{ | { | ||
g_pBulletManager = this; | |||
#ifdef CLIENT_DLL | |||
ClientThink(); | |||
#else | |||
Think(); | Think(); | ||
#endif | |||
} | } | ||
void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/) | void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/) | ||
Line 123: | Line 144: | ||
Vector vecNewRay = CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time; | Vector vecNewRay = CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time; | ||
CUR.m_vOrigin += vecNewRay;//in/100th of a sec * 100th of a sec | CUR.m_vOrigin += vecNewRay;//in/100th of a sec * 100th of a sec | ||
bool bInWater = UTIL_PointContents(CUR.m_vOrigin)&MASK_SPLITAREAPORTAL; | |||
if(CUR.m_bWasInWater!=bInWater) | |||
{ | |||
#ifdef CLIENT_DLL | |||
//TODO: water impact effect | |||
//CBaseEntity::HandleShotImpactingWater | |||
#endif //CLIENT_DLL | |||
} | |||
if(bInWater) | |||
{ | |||
#ifdef CLIENT_DLL | |||
//TODO: 1 bubble clientside | |||
#endif //CLIENT_DLL | |||
} | |||
CUR.m_bWasInWater = bInWater; | |||
Vector vecEntryPosition; | Vector vecEntryPosition; | ||
Vector vecExitPosition; | Vector vecExitPosition; | ||
Line 141: | Line 176: | ||
else if(trace.startsolid)//exit solid | else if(trace.startsolid)//exit solid | ||
{ | { | ||
#ifdef CLIENT_DLL | |||
//TODO: penetration surface impact stuff | //TODO: penetration surface impact stuff | ||
#endif //CLIENT_DLL | |||
vecExitPosition = trace.fractionleftsolid * vecNewRay + m_vTraceStart; | vecExitPosition = trace.fractionleftsolid * vecNewRay + m_vTraceStart; | ||
flPenetrationDistance = vecEntryPosition.DistTo(vecExitPosition); | flPenetrationDistance = vecEntryPosition.DistTo(vecExitPosition); | ||
Line 153: | Line 190: | ||
return; | return; | ||
} | } | ||
#ifdef CLIENT_DLL | |||
//TODO: surface impact stuff | //TODO: surface impact stuff | ||
#endif //CLIENT_DLL | |||
CUR.m_flEntryDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; | CUR.m_flEntryDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; | ||
vecEntryPosition = trace.endpos; | vecEntryPosition = trace.endpos; | ||
#ifdef GAME_DLL | |||
if(trace.DidHitNonWorldEntity()) | if(trace.DidHitNonWorldEntity()) | ||
{ | { | ||
Line 164: | Line 204: | ||
} | } | ||
} | } | ||
#endif //GAME_DLL | |||
} | } | ||
} | } | ||
Line 169: | Line 210: | ||
}while(trace.endpos!=CUR.m_vOrigin&&CUR.m_flBulletSpeed>0.0f); | }while(trace.endpos!=CUR.m_vOrigin&&CUR.m_flBulletSpeed>0.0f); | ||
# | #ifdef GAME_DLL | ||
if(g_debug_bullets.GetBool()) | if(g_debug_bullets.GetBool()) | ||
{ | { | ||
NDebugOverlay::Line(m_vOldOrigin,CUR.m_vOrigin, 255, 255, 255, true, 10.0f ); | 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); | NDebugOverlay::Cross3D(CUR.m_vOrigin, 16, 255, 0, 0, true, 10.0f); | ||
} | } | ||
#endif //GAME_DLL | |||
if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index)) | if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index)) | ||
Line 181: | Line 222: | ||
#undef CUR | #undef CUR | ||
} | } | ||
#ifdef CLIENT_DLL | |||
void CBulletManager::ClientThink(void) | |||
#else | |||
void CBulletManager::Think(void) | void CBulletManager::Think(void) | ||
#endif | |||
{ | { | ||
for(int x=0;x<=m_iLastBullet;x++) | for(int x=0;x<=m_iLastBullet;x++) | ||
Line 187: | Line 232: | ||
SimulateBullet(x); | SimulateBullet(x); | ||
} | } | ||
SetNextThink( gpGlobals->curtime + 0.01f ); | #ifdef CLIENT_DLL | ||
SetNextClientThink | |||
#else | |||
SetNextThink | |||
#endif | |||
( gpGlobals->curtime + 0.01f ); | |||
} | } | ||
void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation) | void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation) | ||
{ | { | ||
if (bullet.m_iAmmoType == -1) | |||
{ | |||
DevMsg("ERROR: Undefined ammo type!\n"); | |||
return; | |||
} | |||
if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem | if(m_iLastBullet==MAX_BULLETS-1)//256 simultaneous bullets in the air is mayhem | ||
{//let's do something reckless on bullet mayhem | {//let's do something reckless on bullet mayhem | ||
Line 200: | Line 255: | ||
m_iLastBullet++; | m_iLastBullet++; | ||
DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation); | DevMsg("Bullet Created (%i) LagCompensation %f\n",m_iLastBullet,lagCompensation); | ||
CShotManipulator Manipulator( bullet.m_vecDirShooting ); | |||
bullet.m_vecDirShooting = Manipulator.ApplySpread(bullet.m_vecSpread); | |||
m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation); | m_rgBullets[m_iLastBullet] = SimulatedBullet_t(bullet,lagCompensation); | ||
if(lagCompensation!=0.0f) | if(lagCompensation!=0.0f) |
Revision as of 15:52, 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.

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
bullet_manager.h
#define MAX_BULLETS 256 #define BULLET_SPEED 240//inches per hundredths of a second #ifdef CLIENT_DLL #define CBulletManager C_BulletManager #endif 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.Init(); m_flInitialBulletSpeed = m_flBulletSpeed = BULLET_SPEED; m_flEntryDensity = 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_flEntryDensity = 0.0f; m_flLagCompensation = lagCompensation; #ifndef CLIENT_DLL m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE); m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAttacker); m_pIgnoreList->AddEntityToIgnore(m_Bullet.m_pAdditionalIgnoreEnt); #endif m_pTwoEnts = new CTraceFilterSkipTwoEntities(m_Bullet.m_pAttacker,m_Bullet.m_pAdditionalIgnoreEnt,COLLISION_GROUP_NONE); } void DeletePointers(void) { #ifndef CLIENT_DLL //leave the compensation considerations because they're entities delete m_pIgnoreList; #endif delete m_pTwoEnts; } FireBulletsInfo_t m_Bullet; Vector m_vOrigin; float m_flBulletSpeed; float m_flInitialBulletSpeed; float m_flLagCompensation; float m_flEntryDensity; bool m_bWasInWater; #ifndef CLIENT_DLL CUtlVector<CBaseEntity *> m_pCompensationConsiderations;//Couldn't resist CTraceFilterSimpleList *m_pIgnoreList;//already hit #endif CTraceFilterSkipTwoEntities *m_pTwoEnts; }; class CBulletManager : public CBaseEntity { DECLARE_CLASS( CBulletManager, CBaseEntity ); public: SimulatedBullet_t m_rgBullets[MAX_BULLETS]; int m_iLastBullet; CBulletManager(); void Spawn(void); #ifdef CLIENT_DLL void ClientThink(void); #else void Think(void); #endif void SimulateBullet(int index, float time=1.0f); void AddBullet(FireBulletsInfo_t &bullet, float lagCompensation); void RemoveBullet(int index); inline bool IsInWorld(int index); };
bullet_manager.cpp
#include "cbase.h" #include "util_shared.h" #include "bullet_manager.h" #include "shot_manipulator.h" #pragma warning(disable:4800) 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; } #ifdef CLIENT_DLL CON_COMMAND_F(createbullet,NULL,FCVAR_CHEAT) { engine->ServerCmd(VarArgs("createbullet_s %f\n",gpGlobals->curtime)); } #else 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); } #endif void CBulletManager::Spawn(void) { g_pBulletManager = this; #ifdef CLIENT_DLL ClientThink(); #else Think(); #endif } void CBulletManager::SimulateBullet(int index, float time/*=1.0f 100ths of sec*/) { #define CUR m_rgBullets[index] trace_t trace, trace_back; Vector m_vOldOrigin(CUR.m_vOrigin); Vector m_vTraceStart(CUR.m_vOrigin); DevMsg("BulletSpeed %f\n",CUR.m_flBulletSpeed); Vector vecNewRay = CUR.m_Bullet.m_vecDirShooting * CUR.m_flBulletSpeed * time; CUR.m_vOrigin += vecNewRay;//in/100th of a sec * 100th of a sec bool bInWater = UTIL_PointContents(CUR.m_vOrigin)&MASK_SPLITAREAPORTAL; if(CUR.m_bWasInWater!=bInWater) { #ifdef CLIENT_DLL //TODO: water impact effect //CBaseEntity::HandleShotImpactingWater #endif //CLIENT_DLL } if(bInWater) { #ifdef CLIENT_DLL //TODO: 1 bubble clientside #endif //CLIENT_DLL } CUR.m_bWasInWater = bInWater; Vector vecEntryPosition; Vector vecExitPosition; float flPenetrationDistance = 0.0f; do { UTIL_TraceLine( m_vTraceStart, CUR.m_vOrigin, MASK_SHOT, CUR.m_pTwoEnts, &trace ); if(!(trace.surface.flags&SURF_SKY)) { if(trace.allsolid)//in solid { trace.endpos = CUR.m_vOrigin; trace.fraction = 1.0f; CUR.m_flBulletSpeed -= CUR.m_flBulletSpeed * CUR.m_flEntryDensity / 1000.0f; break; } else if(trace.startsolid)//exit solid { #ifdef CLIENT_DLL //TODO: penetration surface impact stuff #endif //CLIENT_DLL vecExitPosition = trace.fractionleftsolid * vecNewRay + m_vTraceStart; flPenetrationDistance = vecEntryPosition.DistTo(vecExitPosition); CUR.m_flBulletSpeed -= flPenetrationDistance * CUR.m_flEntryDensity / 1000.0f; } else if(trace.fraction!=1.0f)//hit solid { if(FStrEq(trace.surface.name,"tools/toolsblockbullets")) { RemoveBullet(index); return; } #ifdef CLIENT_DLL //TODO: surface impact stuff #endif //CLIENT_DLL CUR.m_flEntryDensity = physprops->GetSurfaceData(trace.surface.surfaceProps)->physics.density; vecEntryPosition = trace.endpos; #ifdef GAME_DLL if(trace.DidHitNonWorldEntity()) { if(CUR.m_pIgnoreList->ShouldHitEntity(trace.m_pEnt,MASK_SHOT)) { CUR.m_pIgnoreList->AddEntityToIgnore(trace.m_pEnt); //TODO: entity impact stuff } } #endif //GAME_DLL } } m_vTraceStart = trace.endpos + CUR.m_Bullet.m_vecDirShooting; }while(trace.endpos!=CUR.m_vOrigin&&CUR.m_flBulletSpeed>0.0f); #ifdef GAME_DLL if(g_debug_bullets.GetBool()) { 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); } #endif //GAME_DLL if(CUR.m_flBulletSpeed<=0.0f||!IsInWorld(index)) RemoveBullet(index); #undef CUR } #ifdef CLIENT_DLL void CBulletManager::ClientThink(void) #else void CBulletManager::Think(void) #endif { for(int x=0;x<=m_iLastBullet;x++) { SimulateBullet(x); } #ifdef CLIENT_DLL SetNextClientThink #else SetNextThink #endif ( gpGlobals->curtime + 0.01f ); } void CBulletManager::AddBullet(FireBulletsInfo_t &bullet,float lagCompensation) { if (bullet.m_iAmmoType == -1) { DevMsg("ERROR: Undefined ammo type!\n"); return; } 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); CShotManipulator Manipulator( bullet.m_vecDirShooting ); bullet.m_vecDirShooting = Manipulator.ApplySpread(bullet.m_vecSpread); 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; }
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 ); |