1
0
Fork 0
forked from fte/fteqw

Added EF_BRIGHTFIELD to classic particles.

fix r_softwarebanding
fix r_waterstyle

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4842 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-03-03 07:54:10 +00:00
parent bed989f529
commit f13a87f021
16 changed files with 241 additions and 100 deletions

View file

@ -2890,6 +2890,8 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
float *snew__origin;
float *sold__origin;
qboolean isnew;
vec3_t move;
float a1, a2;
@ -2927,8 +2929,6 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
rag_removedeltaent(le);
#endif
}
if (!sold) //I'm lazy
sold = snew;
if (snew->number >= cl.maxlerpents)
{
@ -2941,6 +2941,14 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
le->sequence = newsequence;
le->entstate = snew;
if (!sold)
{
isnew = true;
sold = snew; //don't crash if anything tries poking sold
}
else
isnew = false;
if (snew->u.q1.pmovetype)
{
if (!cl.do_lerp_players)
@ -2948,7 +2956,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
entity_state_t *from;
float age;
packet_entities_t *latest;
if (sold == snew)
if (isnew)
{
/*keep trails correct*/
le->isnew = true;
@ -2998,12 +3006,12 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
VectorSubtract(snew__origin, sold__origin, move);
if (DotProduct(move, move) > 200*200 || snew->modelindex != sold->modelindex)
{
sold = snew; //teleported?
isnew = true; //disable lerping (and indirectly trails)
VectorClear(move);
}
VectorCopy(le->origin, le->lastorigin);
if (sold == snew)
if (isnew)
{
//new this frame (or we noticed something changed significantly)
VectorCopy(snew__origin, le->origin);
@ -3075,7 +3083,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp
}
}
CL_UpdateNetFrameLerpState(sold == snew, snew->frame, le);
CL_UpdateNetFrameLerpState(isnew, snew->frame, le);
}
}
@ -3584,8 +3592,16 @@ void CL_LinkPacketEntities (void)
if (model2)
CL_AddVWeapModel (ent, model2);
//figure out which trail this entity is using
trailef = model->particletrail;
trailidx = model->traildefaultindex;
if (state->effects & EF_HASPARTICLETRAIL)
P_DefaultTrail (state->effects, modelflags, &trailef, &trailidx);
if (state->u.q1.traileffectnum)
trailef = CL_TranslateParticleFromServer(state->u.q1.traileffectnum);
// add automatic particle trails
if (!model || (!(modelflags&~MF_ROTATE) && model->particletrail<0 && model->particleeffect<0 && state->u.q1.traileffectnum==0))
if (!model || (!(modelflags&~MF_ROTATE) && trailef < 0))
continue;
if (!cls.allow_anyparticles && !(modelflags & ~MF_ROTATE))
@ -3609,14 +3625,6 @@ void CL_LinkPacketEntities (void)
}
}
//figure out which trail this entity is using
trailef = model->particletrail;
trailidx = model->traildefaultindex;
if (state->effects & 0xff800000)
P_DefaultTrail (modelflags, &trailef, &trailidx);
if (state->u.q1.traileffectnum)
trailef = CL_TranslateParticleFromServer(state->u.q1.traileffectnum);
//and emit it
if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, ent->keynum, ent->axis, &(le->trailstate)))
if (model->traildefaultindex >= 0)
@ -4977,6 +4985,7 @@ Made up of: clients, packet_entities, nails, and tents
*/
void CL_ClearEntityLists(void)
{
cl_framecount++;
if (cl_numvisedicts+128 >= cl_maxvisedicts)
{
int newnum = cl_maxvisedicts + 256;
@ -4994,6 +5003,7 @@ void CL_ClearEntityLists(void)
}
void CL_FreeVisEdicts(void)
{
cl_framecount++;
BZ_Free(cl_visedicts);
cl_visedicts = NULL;
cl_maxvisedicts = 0;

View file

@ -211,6 +211,7 @@ int rtlights_first, rtlights_max;
int cl_numvisedicts;
int cl_maxvisedicts;
entity_t *cl_visedicts;
int cl_framecount;
scenetris_t *cl_stris;
vecV_t *fte_restrict cl_strisvertv;

View file

@ -489,7 +489,7 @@ void P_LoadedModel(model_t *mod)
}
}
if (mod->particletrail == P_INVALID)
P_DefaultTrail(mod->flags, &mod->particletrail, &mod->traildefaultindex);
P_DefaultTrail(0, mod->flags, &mod->particletrail, &mod->traildefaultindex);
}
void CL_RefreshCustomTEnts(void);

View file

@ -952,6 +952,7 @@ char *CL_TryingToConnect(void);
void CL_ExecInitialConfigs(char *defaultexec);
extern int cl_framecount; //number of times the entity lists have been cleared+reset.
extern int cl_numvisedicts;
extern int cl_maxvisedicts;
extern entity_t *cl_visedicts;

View file

@ -39,6 +39,8 @@ typedef enum {
TRACER2_TRAIL,
VOOR_TRAIL,
BRIGHTFIELD_POINT,
BLOBEXPLOSION_POINT,
LAVASPLASH_POINT,
EXPLOSION_POINT,
@ -64,7 +66,9 @@ typedef struct cparticle_s
pt_blob,
pt_blob2,
pt_grav,
pt_slowgrav
pt_slowgrav,
pt_oneframe
} type;
unsigned int rgb;
struct cparticle_s *next;
@ -77,6 +81,8 @@ static int r_numparticles;
static cparticle_t *particles, *fte_restrict active_particles, *free_particles;
extern cvar_t r_part_density, r_part_classic_expgrav;
static unsigned int particleframe;
extern qbyte default_quakepal[]; /*for ramps more than anything else*/
static int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
static int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
@ -130,6 +136,8 @@ static int PClassic_FindParticleType(const char *name)
return TELEPORTSPLASH_POINT;
if (!stricmp("te_muzzleflash", name))
return MUZZLEFLASH_POINT;
if (!stricmp("ef_brightfield", name))
return BRIGHTFIELD_POINT;
return P_INVALID;
}
@ -176,6 +184,9 @@ static qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen)
case TELEPORTSPLASH_POINT:
n = "te_teleport";
break;
case BRIGHTFIELD_POINT:
n = "ef_brightfield";
break;
}
if (!n)
@ -329,6 +340,27 @@ static void PClassic_ClearParticles (void)
particles[r_numparticles - 1].next = NULL;
}
//some particles (brightfield) must last only one frame
static void PClassic_ClearPerFrame(void)
{
if (particleframe != -1 && particleframe != cl_framecount)
{
cparticle_t **link, *kill;
for (link = &active_particles; *link; )
{
if ((*link)->type == pt_oneframe)
{
kill = *link;
*link = kill->next;
kill->next = free_particles;
free_particles = kill;
}
else
link = &(*link)->next;
}
}
}
//draws all the active particles.
static void PClassic_DrawParticles(void)
{
@ -351,6 +383,12 @@ static void PClassic_DrawParticles(void)
return;
}
if (particleframe != -1 && particleframe != cl_framecount)
{
PClassic_ClearPerFrame();
particleframe = -1;
}
r_partscale = 0.004 * tan (r_refdef.fov_x * (M_PI / 180) * 0.5f);
VectorScale (vup, 1.5, up);
VectorScale (vright, 1.5, right);
@ -486,6 +524,7 @@ static void PClassic_DrawParticles(void)
switch (p->type)
{
case pt_oneframe:
case pt_static:
break;
case pt_fire:
@ -743,6 +782,126 @@ static void Classic_TeleportSplash (vec3_t org)
}
}
#define NUMVERTEXNORMALS 162
//vec3_t avelocity = {23, 7, 3};
//float partstep = 0.01;
//float timescale = 0.01;
static vec3_t avelocities[NUMVERTEXNORMALS];
static void Classic_BrightField (vec3_t org)
{
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
float beamlength = 16;
int count;
int i;
cparticle_t *p;
float angle;
float sr, sp, sy, cr, cp, cy;
vec3_t forward;
float dist;
PClassic_ClearPerFrame();
particleframe = cl_framecount;
dist = 64;
count = 50;
if (!avelocities[0][0])
{
for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
avelocities[0][i] = (rand()&255) * 0.01;
}
for (i=0 ; i<NUMVERTEXNORMALS ; i++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
angle = cl.time * avelocities[i][0];
sy = sin(angle);
cy = cos(angle);
angle = cl.time * avelocities[i][1];
sp = sin(angle);
cp = cos(angle);
angle = cl.time * avelocities[i][2];
sr = sin(angle);
cr = cos(angle);
forward[0] = cp*cy;
forward[1] = cp*sy;
forward[2] = -sp;
p->die = cl.time;// + 0.01;
p->rgb = d_8to24rgbtable[0x6f];
p->type = pt_oneframe;
p->org[0] = org[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength;
p->org[1] = org[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength;
p->org[2] = org[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength;
}
}
//svc_tempentity support: this is the function that handles 'special' point effects.
//use the trail state so fast/slow frames keep the correct particle counts on certain every-frame effects
static int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk)
{
switch(typenum)
{
case BRIGHTFIELD_POINT:
Classic_BrightField(org);
break;
case BLOBEXPLOSION_POINT:
Classic_BlobExplosion(org);
break;
case LAVASPLASH_POINT:
Classic_LavaSplash(org);
break;
case EXPLOSION_POINT:
Classic_ParticleExplosion(org);
break;
case TELEPORTSPLASH_POINT:
Classic_TeleportSplash(org);
break;
case MUZZLEFLASH_POINT:
{
dlight_t *dl = CL_AllocDlight (0);
if (dir)
VectorCopy(dir, dl->axis[0]);
else
VectorSet(dir, 0, 0, 1);
VectorVectors(dl->axis[0], dl->axis[1], dl->axis[2]);
VectorInverse(dl->axis[1]);
if (dir)
VectorMA (org, 15, dl->axis[0], dl->origin);
else
VectorCopy (org, dl->origin);
dl->radius = 200 + (rand()&31);
dl->minlight = 32;
dl->die = cl.time + 0.1;
dl->color[0] = 1.5;
dl->color[1] = 1.3;
dl->color[2] = 1.0;
dl->channelfade[0] = 1.5;
dl->channelfade[1] = 0.75;
dl->channelfade[2] = 0.375;
dl->decay = 1000;
#ifdef RTLIGHTS
dl->lightcolourscales[2] = 4;
#endif
}
break;
default:
return 1;
}
return 0;
}
static float Classic_ParticleTrail (vec3_t start, vec3_t end, float leftover, effect_type_t type)
{
vec3_t point, delta, dir;
@ -751,6 +910,12 @@ static float Classic_ParticleTrail (vec3_t start, vec3_t end, float leftover, ef
cparticle_t *p;
static int tracercount;
if (type >= BRIGHTFIELD_POINT)
{
PClassic_RunParticleEffectState(end, vec3_origin, 1, type, NULL);
return 0;
}
VectorCopy (start, point);
VectorSubtract (end, start, delta);
if (!(len = VectorLength (delta)))
@ -882,60 +1047,6 @@ static int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dl
return 0;
}
//svc_tempentity support: this is the function that handles 'special' point effects.
//use the trail state so fast/slow frames keep the correct particle counts on certain every-frame effects
static int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk)
{
switch(typenum)
{
case BLOBEXPLOSION_POINT:
Classic_BlobExplosion(org);
break;
case LAVASPLASH_POINT:
Classic_LavaSplash(org);
break;
case EXPLOSION_POINT:
Classic_ParticleExplosion(org);
break;
case TELEPORTSPLASH_POINT:
Classic_TeleportSplash(org);
break;
case MUZZLEFLASH_POINT:
{
dlight_t *dl = CL_AllocDlight (0);
if (dir)
VectorCopy(dir, dl->axis[0]);
else
VectorSet(dir, 0, 0, 1);
VectorVectors(dl->axis[0], dl->axis[1], dl->axis[2]);
VectorInverse(dl->axis[1]);
if (dir)
VectorMA (org, 15, dl->axis[0], dl->origin);
else
VectorCopy (org, dl->origin);
dl->radius = 200 + (rand()&31);
dl->minlight = 32;
dl->die = cl.time + 0.1;
dl->color[0] = 1.5;
dl->color[1] = 1.3;
dl->color[2] = 1.0;
dl->channelfade[0] = 1.5;
dl->channelfade[1] = 0.75;
dl->channelfade[2] = 0.375;
dl->decay = 1000;
#ifdef RTLIGHTS
dl->lightcolourscales[2] = 4;
#endif
}
break;
default:
return 1;
}
return 0;
}
//svc_particle support: add X particles with the given colour, velocity, and aproximate origin.
static void PClassic_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
{

View file

@ -818,14 +818,29 @@ static void P_SelectableTrail(int *trailid, int *trailpalidx, cvar_t *selection,
//figure out which particle trail to use for the given model, filling in its values as required.
void P_DefaultTrail (unsigned int modelflags, int *trailid, int *trailpalidx)
void P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx)
{
// TODO: EF_BRIGHTFIELD should probably be handled in here somewhere
// TODO: make trail default color into RGB values instead of indexes
if (!pe)
return;
if (modelflags & MF_ROCKET)
if (entityeffects & EF_BRIGHTFIELD)
{
*trailid = P_FindParticleType("EF_BRIGHTFIELD");
*trailpalidx = 70;
}
else if (entityeffects & DPEF_FLAME)
{
*trailid = P_FindParticleType("EF_FLAME");
*trailpalidx = 70;
}
else if (entityeffects & DPEF_STARDUST)
{
*trailid = P_FindParticleType("EF_STARDUST");
*trailpalidx = 70;
}
else if (modelflags & MF_ROCKET)
P_SelectableTrail(trailid, trailpalidx, &r_rockettrail, P_FindParticleType("TR_ROCKET"), 109, P_FindParticleType("TR_GRENADE"), 6);
else if (modelflags & MF_GRENADE)
P_SelectableTrail(trailid, trailpalidx, &r_grenadetrail, P_FindParticleType("TR_GRENADE"), 6, P_FindParticleType("TR_ROCKET"), 109);

View file

@ -99,7 +99,7 @@ struct msurface_s;
void P_InitParticleSystem(void);
void P_Shutdown(void);
void P_LoadedModel(struct model_s *mod); /*checks a model's various effects*/
void P_DefaultTrail (unsigned int modelflags, int *trailid, int *trailpalidx);
void P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx);
void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk);//this is just a wrapper
#define P_FindParticleType pe->FindParticleType

View file

@ -1130,7 +1130,7 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass)
if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->paletted))
t = shaderstate.curtexnums->paletted;
else
t = missing_texture;
t = r_whiteimage;
break;
case T_GEN_NORMALMAP:
t = (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->bump))?shaderstate.curtexnums->bump:missing_texture_normal;

View file

@ -1208,14 +1208,8 @@ void Mod_FinishTexture(texture_t *tx, const char *loadname)
else
{
unsigned int maps = 0;
if (r_softwarebanding.ival)
maps |= SHADER_HASPALETTED;
if (!r_softwarebanding.ival
#ifdef RTLIGHTS
|| r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival
#endif
)
maps |= SHADER_HASDIFFUSE;
maps |= SHADER_HASPALETTED;
maps |= SHADER_HASDIFFUSE;
if (r_fb_bmodels.ival)
maps |= SHADER_HASFULLBRIGHT;
if (r_loadbumpmapping || (r_waterstyle.ival > 1 && *tx->name == '*'))

View file

@ -162,20 +162,20 @@ m*_t structures are in-memory
#define EF_BLUE (1<<6)
#define EF_RED (1<<7)
#define H2EF_NODRAW (1<<7) //this is going to get complicated... emulated server side.
#define _DPEF_NOGUNBOB (1<<8) //viewmodel attachment does not bob
#define DPEF_NOGUNBOB_ (1<<8) //viewmodel attachment does not bob
#define EF_FULLBRIGHT (1<<9) //abslight=1
#define _DPEF_FLAME (1<<10) //'onfire'
#define _DPEF_STARDUST (1<<11) //'showering sparks'
#define DPEF_FLAME (1<<10) //'onfire'
#define DPEF_STARDUST (1<<11) //'showering sparks'
#define DPEF_NOSHADOW (1<<12) //doesn't cast a shadow
#define EF_NODEPTHTEST (1<<13) //shows through walls.
#define _DPEF_SELECTABLE (1<<14) //highlights when prydoncursored
#define _DPEF_DOUBLESIDED (1<<15) //disables culling
#define _DPEF_NOSELFSHADOW (1<<16) //doesn't cast shadows on any noselfshadow entities.
#define _DPEF_DYNAMICMODELLIGHT (1<<17)
#define DPEF_SELECTABLE_ (1<<14) //highlights when prydoncursored
#define DPEF_DOUBLESIDED_ (1<<15) //disables culling
#define DPEF_NOSELFSHADOW_ (1<<16) //doesn't cast shadows on any noselfshadow entities.
#define DPEF_DYNAMICMODELLIGHT_ (1<<17)
#define EF_UNUSED18 (1<<18)
#define EF_UNUSED19 (1<<19)
#define _DPEF_RESTARTANIM_BIT (1<<20) //exact semantics seems odd
#define _DPEF_TELEPORT_BIT (1<<21) //disable lerping while set
#define DPEF_RESTARTANIM_BIT_ (1<<20) //exact semantics seems odd
#define DPEF_TELEPORT_BIT_ (1<<21) //disable lerping while set
#define DPEF_LOWPRECISION (1<<22) //part of the protocol/server, not the client itself.
#define EF_NOMODELFLAGS (1<<23)
#define EF_MF_ROCKET (1<<24)
@ -187,6 +187,8 @@ m*_t structures are in-memory
#define EF_MF_TRACER2 (1u<<30)
#define EF_MF_TRACER3 (1u<<31)
#define EF_HASPARTICLETRAIL (0xff800000 | EF_BRIGHTFIELD|DPEF_FLAME|DPEF_STARDUST)
/*
==============================================================================

View file

@ -2235,7 +2235,7 @@ static void Shaderpass_Map (shader_t *shader, shaderpass_t *pass, char **ptr)
if (pass->tcgen == TC_GEN_UNSPECIFIED)
pass->tcgen = TC_GEN_BASE;
if (!*shader->mapname && pass->tcgen == TC_GEN_BASE)
if (!*shader->mapname && *token != '$' && pass->tcgen == TC_GEN_BASE)
Q_strncpyz(shader->mapname, token, sizeof(shader->mapname));
pass->anim_frames[0] = Shader_FindImage (token, flags);
}
@ -3879,7 +3879,10 @@ done:;
if (best)
{
if (best->texgen == T_GEN_ANIMMAP || best->texgen == T_GEN_SINGLEMAP)
s->defaulttextures.base = best->anim_frames[0];
{
if (best->anim_frames[0] && *best->anim_frames[0]->ident != '$')
s->defaulttextures.base = best->anim_frames[0];
}
#ifndef NOMEDIA
else if (pass->texgen == T_GEN_VIDEOMAP && pass->cin)
s->defaulttextures.base = Media_UpdateForShader(best->cin);
@ -4293,7 +4296,7 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *subpath, unsigned
if (!TEXVALID(tex->bump) && *shader->mapname)
tex->bump = R_LoadHiResTexture(va("%s_norm", shader->mapname), NULL, imageflags|IF_TRYBUMP);
if (!TEXVALID(tex->bump))
tex->bump = Image_GetTexture(va("%s_norm", imagename), subpath, imageflags|IF_TRYBUMP, r_shadow_bumpscale_basetexture.ival?mipdata[0]:NULL, palette, width, height, TF_HEIGHT8PAL);
tex->bump = Image_GetTexture(va("%s_norm", imagename), subpath, imageflags|IF_TRYBUMP, (r_shadow_bumpscale_basetexture.ival||*imagename=='*')?mipdata[0]:NULL, palette, width, height, TF_HEIGHT8PAL);
}
if (loadflags & SHADER_HASTOPBOTTOM)

View file

@ -277,7 +277,7 @@ extern entity_t r_worldentity;
extern vec3_t r_entorigin;
extern entity_t *currententity;
extern int r_visframecount; // ??? what difs?
extern int r_framecount;
extern int r_framecount; //number of scenes drawn (specifically, number of times the world is frustum culled)
extern qboolean r_loadbumpmapping;

View file

@ -53,6 +53,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#define s_refractdepth s_t3\n"
"uniform float cvar_r_glsl_turbscale;\n"
"uniform sampler2D s_normalmap;\n"
"uniform sampler2D s_diffuse;\n"
"uniform sampler2D s_refract; //refract\n"
"uniform sampler2D s_reflect; //reflection\n"
"uniform sampler2D s_refractdepth; //refraction depth\n"

View file

@ -46,6 +46,8 @@ void main (void)
#define s_refractdepth s_t3
uniform float cvar_r_glsl_turbscale;
uniform sampler2D s_normalmap;
uniform sampler2D s_diffuse;
uniform sampler2D s_refract; //refract
uniform sampler2D s_reflect; //reflection
uniform sampler2D s_refractdepth; //refraction depth

View file

@ -33,7 +33,7 @@
#define MAX_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_WEAPONMODELI 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5

View file

@ -798,7 +798,7 @@ void ParseUserInfo(cluster_t *cluster, viewer_t *viewer)
void NewNQClient(cluster_t *cluster, netadr_t *addr)
{
sv_t *initialserver;
// sv_t *initialserver;
int header;
int len;
int i;
@ -873,7 +873,7 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
{
sv_t *initialserver;
// sv_t *initialserver;
viewer_t *viewer;
char qport[32];
@ -1609,7 +1609,7 @@ void SendNQClientData(sv_t *tv, viewer_t *v, netmsg_t *msg)
if (bits & SU_ARMOR)
WriteByte (msg, pl->stats[STAT_ARMOR]);
if (bits & SU_WEAPON)
WriteByte (msg, pl->stats[STAT_WEAPON]);
WriteByte (msg, pl->stats[STAT_WEAPONMODELI]);
WriteShort (msg, pl->stats[STAT_HEALTH]);
WriteByte (msg, pl->stats[STAT_AMMO]);