First version.
This commit is contained in:
commit
ef1c2cc347
473 files changed
+305366
No files matched your search
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_RunLerpFrame
|
||||
|
||||
Sets cg.snap, cg.oldFrame, and cg.backlerp
|
||||
cg.time should be between oldFrameTime and frameTime after exit
|
||||
===============
|
||||
*/
|
||||
void CG_RunLerpFrame( lerpFrame_t *lf )
|
||||
{
|
||||
int f, numFrames;
|
||||
animation_t *anim;
|
||||
|
||||
// debugging tool to get no animations
|
||||
if( cg_animSpeed.integer == 0 )
|
||||
{
|
||||
lf->oldFrame = lf->frame = lf->backlerp = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have passed the current frame, move it to
|
||||
// oldFrame and calculate a new frame
|
||||
if( cg.time >= lf->frameTime )
|
||||
{
|
||||
lf->oldFrame = lf->frame;
|
||||
lf->oldFrameTime = lf->frameTime;
|
||||
|
||||
// get the next frame based on the animation
|
||||
anim = lf->animation;
|
||||
if( !anim->frameLerp )
|
||||
return; // shouldn't happen
|
||||
|
||||
if( cg.time < lf->animationTime )
|
||||
lf->frameTime = lf->animationTime; // initial lerp
|
||||
else
|
||||
lf->frameTime = lf->oldFrameTime + anim->frameLerp;
|
||||
|
||||
f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
|
||||
numFrames = anim->numFrames;
|
||||
if( anim->flipflop )
|
||||
numFrames *= 2;
|
||||
|
||||
if( f >= numFrames )
|
||||
{
|
||||
f -= numFrames;
|
||||
if( anim->loopFrames )
|
||||
{
|
||||
f %= anim->loopFrames;
|
||||
f += anim->numFrames - anim->loopFrames;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = numFrames - 1;
|
||||
// the animation is stuck at the end, so it
|
||||
// can immediately transition to another sequence
|
||||
lf->frameTime = cg.time;
|
||||
}
|
||||
}
|
||||
|
||||
if( anim->reversed )
|
||||
lf->frame = anim->firstFrame + anim->numFrames - 1 - f;
|
||||
else if( anim->flipflop && f >= anim->numFrames )
|
||||
lf->frame = anim->firstFrame + anim->numFrames - 1 - ( f % anim->numFrames );
|
||||
else
|
||||
lf->frame = anim->firstFrame + f;
|
||||
|
||||
if( cg.time > lf->frameTime )
|
||||
{
|
||||
lf->frameTime = cg.time;
|
||||
if( cg_debugAnim.integer )
|
||||
CG_Printf( "Clamp lf->frameTime\n" );
|
||||
}
|
||||
}
|
||||
|
||||
if( lf->frameTime > cg.time + 200 )
|
||||
lf->frameTime = cg.time;
|
||||
|
||||
if( lf->oldFrameTime > cg.time )
|
||||
lf->oldFrameTime = cg.time;
|
||||
|
||||
// calculate current lerp value
|
||||
if( lf->frameTime == lf->oldFrameTime )
|
||||
lf->backlerp = 0;
|
||||
else
|
||||
lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_DoorAnimation
|
||||
===============
|
||||
*/
|
||||
static void CG_DoorAnimation( centity_t *cent, int *old, int *now, float *backLerp )
|
||||
{
|
||||
CG_RunLerpFrame( ¢->lerpFrame );
|
||||
|
||||
*old = cent->lerpFrame.oldFrame;
|
||||
*now = cent->lerpFrame.frame;
|
||||
*backLerp = cent->lerpFrame.backlerp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_ModelDoor
|
||||
===============
|
||||
*/
|
||||
void CG_ModelDoor( centity_t *cent )
|
||||
{
|
||||
refEntity_t ent;
|
||||
entityState_t *es;
|
||||
animation_t anim;
|
||||
lerpFrame_t *lf = ¢->lerpFrame;
|
||||
|
||||
es = ¢->currentState;
|
||||
|
||||
if( !es->modelindex )
|
||||
return;
|
||||
|
||||
//create the render entity
|
||||
memset( &ent, 0, sizeof( ent ) );
|
||||
VectorCopy( cent->lerpOrigin, ent.origin );
|
||||
VectorCopy( cent->lerpOrigin, ent.oldorigin );
|
||||
AnglesToAxis( cent->lerpAngles, ent.axis );
|
||||
|
||||
ent.renderfx = RF_NOSHADOW;
|
||||
|
||||
//add the door model
|
||||
ent.skinNum = 0;
|
||||
ent.hModel = cgs.gameModels[ es->modelindex ];
|
||||
|
||||
//scale the door
|
||||
VectorScale( ent.axis[ 0 ], es->origin2[ 0 ], ent.axis[ 0 ] );
|
||||
VectorScale( ent.axis[ 1 ], es->origin2[ 1 ], ent.axis[ 1 ] );
|
||||
VectorScale( ent.axis[ 2 ], es->origin2[ 2 ], ent.axis[ 2 ] );
|
||||
ent.nonNormalizedAxes = qtrue;
|
||||
|
||||
//setup animation
|
||||
anim.firstFrame = es->powerups;
|
||||
anim.numFrames = es->weapon;
|
||||
anim.reversed = !es->legsAnim;
|
||||
anim.flipflop = qfalse;
|
||||
anim.loopFrames = 0;
|
||||
anim.frameLerp = 1000 / es->torsoAnim;
|
||||
anim.initialLerp = 1000 / es->torsoAnim;
|
||||
|
||||
//door changed state
|
||||
if( es->legsAnim != cent->doorState )
|
||||
{
|
||||
lf->animationTime = lf->frameTime + anim.initialLerp;
|
||||
cent->doorState = es->legsAnim;
|
||||
}
|
||||
|
||||
lf->animation = &anim;
|
||||
|
||||
//run animation
|
||||
CG_DoorAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp );
|
||||
|
||||
trap_R_AddRefEntityToScene( &ent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AMOAnimation
|
||||
===============
|
||||
*/
|
||||
static void CG_AMOAnimation( centity_t *cent, int *old, int *now, float *backLerp )
|
||||
{
|
||||
if( !( cent->currentState.eFlags & EF_MOVER_STOP ) || cent->animPlaying )
|
||||
{
|
||||
int delta = cg.time - cent->miscTime;
|
||||
|
||||
//hack to prevent "pausing" mucking up the lerping
|
||||
if( delta > 900 )
|
||||
{
|
||||
cent->lerpFrame.oldFrameTime += delta;
|
||||
cent->lerpFrame.frameTime += delta;
|
||||
}
|
||||
|
||||
CG_RunLerpFrame( ¢->lerpFrame );
|
||||
cent->miscTime = cg.time;
|
||||
}
|
||||
|
||||
*old = cent->lerpFrame.oldFrame;
|
||||
*now = cent->lerpFrame.frame;
|
||||
*backLerp = cent->lerpFrame.backlerp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_animMapObj
|
||||
==================
|
||||
*/
|
||||
void CG_AnimMapObj( centity_t *cent )
|
||||
{
|
||||
refEntity_t ent;
|
||||
entityState_t *es;
|
||||
float scale;
|
||||
animation_t anim;
|
||||
|
||||
es = ¢->currentState;
|
||||
|
||||
// if set to invisible, skip
|
||||
if( !es->modelindex || ( es->eFlags & EF_NODRAW ) )
|
||||
return;
|
||||
|
||||
memset( &ent, 0, sizeof( ent ) );
|
||||
|
||||
VectorCopy( es->angles, cent->lerpAngles );
|
||||
AnglesToAxis( cent->lerpAngles, ent.axis );
|
||||
|
||||
ent.hModel = cgs.gameModels[ es->modelindex ];
|
||||
|
||||
VectorCopy( cent->lerpOrigin, ent.origin);
|
||||
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
||||
|
||||
ent.nonNormalizedAxes = qfalse;
|
||||
|
||||
//scale the model
|
||||
if( es->angles2[ 0 ] )
|
||||
{
|
||||
scale = es->angles2[ 0 ];
|
||||
VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] );
|
||||
VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] );
|
||||
VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] );
|
||||
ent.nonNormalizedAxes = qtrue;
|
||||
}
|
||||
|
||||
//setup animation
|
||||
anim.firstFrame = es->powerups;
|
||||
anim.numFrames = es->weapon;
|
||||
anim.reversed = qfalse;
|
||||
anim.flipflop = qfalse;
|
||||
|
||||
// if numFrames is negative the animation is reversed
|
||||
if( anim.numFrames < 0 )
|
||||
{
|
||||
anim.numFrames = -anim.numFrames;
|
||||
anim.reversed = qtrue;
|
||||
}
|
||||
|
||||
anim.loopFrames = es->torsoAnim;
|
||||
|
||||
if( !es->legsAnim )
|
||||
{
|
||||
anim.frameLerp = 1000;
|
||||
anim.initialLerp = 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
anim.frameLerp = 1000 / es->legsAnim;
|
||||
anim.initialLerp = 1000 / es->legsAnim;
|
||||
}
|
||||
|
||||
cent->lerpFrame.animation = &anim;
|
||||
|
||||
if( !anim.loopFrames )
|
||||
{
|
||||
// add one frame to allow the animation to play the last frame
|
||||
// add another to get it to stop playing at the first frame
|
||||
anim.numFrames += 2;
|
||||
|
||||
if( !cent->animInit )
|
||||
{
|
||||
cent->animInit = qtrue;
|
||||
cent->animPlaying = !( cent->currentState.eFlags & EF_MOVER_STOP );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( cent->animLastState !=
|
||||
!( cent->currentState.eFlags & EF_MOVER_STOP ) )
|
||||
{
|
||||
cent->animPlaying = qtrue;
|
||||
cent->lerpFrame.animationTime = cg.time;
|
||||
cent->lerpFrame.frameTime = cg.time;
|
||||
}
|
||||
}
|
||||
cent->animLastState = !( cent->currentState.eFlags & EF_MOVER_STOP );
|
||||
}
|
||||
|
||||
//run animation
|
||||
CG_AMOAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp );
|
||||
|
||||
// add to refresh list
|
||||
trap_R_AddRefEntityToScene(&ent);
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_attachment.c -- an abstract attachment system
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachmentPoint
|
||||
|
||||
Return the attachment point
|
||||
===============
|
||||
*/
|
||||
qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v )
|
||||
{
|
||||
centity_t *cent;
|
||||
|
||||
if( !a )
|
||||
return qfalse;
|
||||
|
||||
// if it all breaks, then use the last point we know was correct
|
||||
VectorCopy( a->lastValidAttachmentPoint, v );
|
||||
|
||||
switch( a->type )
|
||||
{
|
||||
case AT_STATIC:
|
||||
if( !a->staticValid )
|
||||
return qfalse;
|
||||
|
||||
VectorCopy( a->origin, v );
|
||||
break;
|
||||
|
||||
case AT_TAG:
|
||||
if( !a->tagValid )
|
||||
return qfalse;
|
||||
|
||||
AxisCopy( axisDefault, a->re.axis );
|
||||
CG_PositionRotatedEntityOnTag( &a->re, &a->parent,
|
||||
a->model, a->tagName );
|
||||
VectorCopy( a->re.origin, v );
|
||||
break;
|
||||
|
||||
case AT_CENT:
|
||||
if( !a->centValid )
|
||||
return qfalse;
|
||||
|
||||
if( a->centNum == cg.predictedPlayerState.clientNum )
|
||||
{
|
||||
// this is smoother if it's the local client
|
||||
VectorCopy( cg.predictedPlayerState.origin, v );
|
||||
}
|
||||
else
|
||||
{
|
||||
cent = &cg_entities[ a->centNum ];
|
||||
VectorCopy( cent->lerpOrigin, v );
|
||||
}
|
||||
break;
|
||||
|
||||
case AT_PARTICLE:
|
||||
if( !a->particleValid )
|
||||
return qfalse;
|
||||
|
||||
if( !a->particle->valid )
|
||||
{
|
||||
a->particleValid = qfalse;
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
VectorCopy( a->particle->origin, v );
|
||||
break;
|
||||
|
||||
default:
|
||||
CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
if( a->hasOffset )
|
||||
VectorAdd( v, a->offset, v );
|
||||
|
||||
VectorCopy( v, a->lastValidAttachmentPoint );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachmentDir
|
||||
|
||||
Return the attachment direction
|
||||
===============
|
||||
*/
|
||||
qboolean CG_AttachmentDir( attachment_t *a, vec3_t v )
|
||||
{
|
||||
vec3_t forward;
|
||||
centity_t *cent;
|
||||
|
||||
if( !a )
|
||||
return qfalse;
|
||||
|
||||
switch( a->type )
|
||||
{
|
||||
case AT_STATIC:
|
||||
return qfalse;
|
||||
break;
|
||||
|
||||
case AT_TAG:
|
||||
if( !a->tagValid )
|
||||
return qfalse;
|
||||
|
||||
VectorCopy( a->re.axis[ 0 ], v );
|
||||
break;
|
||||
|
||||
case AT_CENT:
|
||||
if( !a->centValid )
|
||||
return qfalse;
|
||||
|
||||
cent = &cg_entities[ a->centNum ];
|
||||
AngleVectors( cent->lerpAngles, forward, NULL, NULL );
|
||||
VectorCopy( forward, v );
|
||||
break;
|
||||
|
||||
case AT_PARTICLE:
|
||||
if( !a->particleValid )
|
||||
return qfalse;
|
||||
|
||||
if( !a->particle->valid )
|
||||
{
|
||||
a->particleValid = qfalse;
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
VectorCopy( a->particle->velocity, v );
|
||||
break;
|
||||
|
||||
default:
|
||||
CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
VectorNormalize( v );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachmentAxis
|
||||
|
||||
Return the attachment axis
|
||||
===============
|
||||
*/
|
||||
qboolean CG_AttachmentAxis( attachment_t *a, vec3_t axis[ 3 ] )
|
||||
{
|
||||
centity_t *cent;
|
||||
|
||||
if( !a )
|
||||
return qfalse;
|
||||
|
||||
switch( a->type )
|
||||
{
|
||||
case AT_STATIC:
|
||||
return qfalse;
|
||||
break;
|
||||
|
||||
case AT_TAG:
|
||||
if( !a->tagValid )
|
||||
return qfalse;
|
||||
|
||||
AxisCopy( a->re.axis, axis );
|
||||
break;
|
||||
|
||||
case AT_CENT:
|
||||
if( !a->centValid )
|
||||
return qfalse;
|
||||
|
||||
cent = &cg_entities[ a->centNum ];
|
||||
AnglesToAxis( cent->lerpAngles, axis );
|
||||
break;
|
||||
|
||||
case AT_PARTICLE:
|
||||
return qfalse;
|
||||
break;
|
||||
|
||||
default:
|
||||
CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachmentVelocity
|
||||
|
||||
If the attachment can have velocity, return it
|
||||
===============
|
||||
*/
|
||||
qboolean CG_AttachmentVelocity( attachment_t *a, vec3_t v )
|
||||
{
|
||||
if( !a )
|
||||
return qfalse;
|
||||
|
||||
if( a->particleValid && a->particle->valid )
|
||||
{
|
||||
VectorCopy( a->particle->velocity, v );
|
||||
return qtrue;
|
||||
}
|
||||
else if( a->centValid )
|
||||
{
|
||||
centity_t *cent = &cg_entities[ a->centNum ];
|
||||
|
||||
VectorCopy( cent->currentState.pos.trDelta, v );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachmentCentNum
|
||||
|
||||
If the attachment has a centNum, return it
|
||||
===============
|
||||
*/
|
||||
int CG_AttachmentCentNum( attachment_t *a )
|
||||
{
|
||||
if( !a || !a->centValid )
|
||||
return -1;
|
||||
|
||||
return a->centNum;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_Attached
|
||||
|
||||
If the attachment is valid, return qtrue
|
||||
===============
|
||||
*/
|
||||
qboolean CG_Attached( attachment_t *a )
|
||||
{
|
||||
if( !a )
|
||||
return qfalse;
|
||||
|
||||
return a->attached;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachToPoint
|
||||
|
||||
Attach to a point in space
|
||||
===============
|
||||
*/
|
||||
void CG_AttachToPoint( attachment_t *a )
|
||||
{
|
||||
if( !a || !a->staticValid )
|
||||
return;
|
||||
|
||||
a->type = AT_STATIC;
|
||||
a->attached = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachToCent
|
||||
|
||||
Attach to a centity_t
|
||||
===============
|
||||
*/
|
||||
void CG_AttachToCent( attachment_t *a )
|
||||
{
|
||||
if( !a || !a->centValid )
|
||||
return;
|
||||
|
||||
a->type = AT_CENT;
|
||||
a->attached = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachToTag
|
||||
|
||||
Attach to a model tag
|
||||
===============
|
||||
*/
|
||||
void CG_AttachToTag( attachment_t *a )
|
||||
{
|
||||
if( !a || !a->tagValid )
|
||||
return;
|
||||
|
||||
a->type = AT_TAG;
|
||||
a->attached = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AttachToParticle
|
||||
|
||||
Attach to a particle
|
||||
===============
|
||||
*/
|
||||
void CG_AttachToParticle( attachment_t *a )
|
||||
{
|
||||
if( !a || !a->particleValid )
|
||||
return;
|
||||
|
||||
a->type = AT_PARTICLE;
|
||||
a->attached = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SetAttachmentPoint
|
||||
===============
|
||||
*/
|
||||
void CG_SetAttachmentPoint( attachment_t *a, vec3_t v )
|
||||
{
|
||||
if( !a )
|
||||
return;
|
||||
|
||||
VectorCopy( v, a->origin );
|
||||
a->staticValid = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SetAttachmentCent
|
||||
===============
|
||||
*/
|
||||
void CG_SetAttachmentCent( attachment_t *a, centity_t *cent )
|
||||
{
|
||||
if( !a || !cent )
|
||||
return;
|
||||
|
||||
a->centNum = cent->currentState.number;
|
||||
a->centValid = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SetAttachmentTag
|
||||
===============
|
||||
*/
|
||||
void CG_SetAttachmentTag( attachment_t *a, refEntity_t parent,
|
||||
qhandle_t model, char *tagName )
|
||||
{
|
||||
if( !a )
|
||||
return;
|
||||
|
||||
a->parent = parent;
|
||||
a->model = model;
|
||||
strncpy( a->tagName, tagName, MAX_STRING_CHARS );
|
||||
a->tagValid = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SetAttachmentParticle
|
||||
===============
|
||||
*/
|
||||
void CG_SetAttachmentParticle( attachment_t *a, particle_t *p )
|
||||
{
|
||||
if( !a )
|
||||
return;
|
||||
|
||||
a->particle = p;
|
||||
a->particleValid = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SetAttachmentOffset
|
||||
===============
|
||||
*/
|
||||
void CG_SetAttachmentOffset( attachment_t *a, vec3_t v )
|
||||
{
|
||||
if( !a )
|
||||
return;
|
||||
|
||||
VectorCopy( v, a->offset );
|
||||
a->hasOffset = qtrue;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_consolecmds.c -- text commands typed in at the local console, or
|
||||
// executed by a key binding
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
|
||||
void CG_TargetCommand_f( void )
|
||||
{
|
||||
int targetNum;
|
||||
char test[ 4 ];
|
||||
|
||||
targetNum = CG_CrosshairPlayer( );
|
||||
if( !targetNum )
|
||||
return;
|
||||
|
||||
trap_Argv( 1, test, 4 );
|
||||
trap_SendConsoleCommand( va( "gc %i %i", targetNum, atoi( test ) ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_SizeUp_f
|
||||
|
||||
Keybinding command
|
||||
=================
|
||||
*/
|
||||
static void CG_SizeUp_f( void )
|
||||
{
|
||||
trap_Cvar_Set( "cg_viewsize", va( "%i", (int)( cg_viewsize.integer + 10 ) ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_SizeDown_f
|
||||
|
||||
Keybinding command
|
||||
=================
|
||||
*/
|
||||
static void CG_SizeDown_f( void )
|
||||
{
|
||||
trap_Cvar_Set( "cg_viewsize", va( "%i", (int)( cg_viewsize.integer - 10 ) ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_Viewpos_f
|
||||
|
||||
Debugging command to print the current position
|
||||
=============
|
||||
*/
|
||||
static void CG_Viewpos_f( void )
|
||||
{
|
||||
CG_Printf( "(%i %i %i) : %i\n", (int)cg.refdef.vieworg[ 0 ],
|
||||
(int)cg.refdef.vieworg[ 1 ], (int)cg.refdef.vieworg[ 2 ],
|
||||
(int)cg.refdefViewAngles[ YAW ] );
|
||||
}
|
||||
|
||||
qboolean CG_RequestScores( void )
|
||||
{
|
||||
if( cg.scoresRequestTime + 2000 < cg.time )
|
||||
{
|
||||
// the scores are more than two seconds out of data,
|
||||
// so request new ones
|
||||
cg.scoresRequestTime = cg.time;
|
||||
//TA: added \n SendClientCommand doesn't call flush( )?
|
||||
trap_SendClientCommand( "score\n" );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
else
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
extern menuDef_t *menuScoreboard;
|
||||
|
||||
static void CG_scrollScoresDown_f( void )
|
||||
{
|
||||
if( menuScoreboard && cg.scoreBoardShowing )
|
||||
{
|
||||
Menu_ScrollFeeder( menuScoreboard, FEEDER_ALIENTEAM_LIST, qtrue );
|
||||
Menu_ScrollFeeder( menuScoreboard, FEEDER_HUMANTEAM_LIST, qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CG_scrollScoresUp_f( void )
|
||||
{
|
||||
if( menuScoreboard && cg.scoreBoardShowing )
|
||||
{
|
||||
Menu_ScrollFeeder( menuScoreboard, FEEDER_ALIENTEAM_LIST, qfalse );
|
||||
Menu_ScrollFeeder( menuScoreboard, FEEDER_HUMANTEAM_LIST, qfalse );
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_ScoresDown_f( void )
|
||||
{
|
||||
if( !cg.showScores )
|
||||
{
|
||||
Menu_SetFeederSelection( menuScoreboard, FEEDER_ALIENTEAM_LIST, 0, NULL );
|
||||
Menu_SetFeederSelection( menuScoreboard, FEEDER_HUMANTEAM_LIST, 0, NULL );
|
||||
}
|
||||
|
||||
if( CG_RequestScores( ) )
|
||||
{
|
||||
// leave the current scores up if they were already
|
||||
// displayed, but if this is the first hit, clear them out
|
||||
if( !cg.showScores )
|
||||
{
|
||||
if( cg_debugRandom.integer )
|
||||
CG_Printf( "CG_ScoresDown_f: scores out of date\n" );
|
||||
|
||||
cg.showScores = qtrue;
|
||||
cg.numScores = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// show the cached contents even if they just pressed if it
|
||||
// is within two seconds
|
||||
cg.showScores = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_ScoresUp_f( void )
|
||||
{
|
||||
if( cg.showScores )
|
||||
{
|
||||
cg.showScores = qfalse;
|
||||
cg.scoreFadeTime = cg.time;
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_TellTarget_f( void )
|
||||
{
|
||||
int clientNum;
|
||||
char command[ 128 ];
|
||||
char message[ 128 ];
|
||||
|
||||
clientNum = CG_CrosshairPlayer( );
|
||||
if( clientNum == -1 )
|
||||
return;
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
static void CG_TellAttacker_f( void )
|
||||
{
|
||||
int clientNum;
|
||||
char command[ 128 ];
|
||||
char message[ 128 ];
|
||||
|
||||
clientNum = CG_LastAttacker( );
|
||||
if( clientNum == -1 )
|
||||
return;
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *cmd;
|
||||
void (*function)( void );
|
||||
} consoleCommand_t;
|
||||
|
||||
static consoleCommand_t commands[ ] =
|
||||
{
|
||||
{ "testgun", CG_TestGun_f },
|
||||
{ "testmodel", CG_TestModel_f },
|
||||
{ "nextframe", CG_TestModelNextFrame_f },
|
||||
{ "prevframe", CG_TestModelPrevFrame_f },
|
||||
{ "nextskin", CG_TestModelNextSkin_f },
|
||||
{ "prevskin", CG_TestModelPrevSkin_f },
|
||||
{ "viewpos", CG_Viewpos_f },
|
||||
{ "+scores", CG_ScoresDown_f },
|
||||
{ "-scores", CG_ScoresUp_f },
|
||||
{ "scoresUp", CG_scrollScoresUp_f },
|
||||
{ "scoresDown", CG_scrollScoresDown_f },
|
||||
{ "sizeup", CG_SizeUp_f },
|
||||
{ "sizedown", CG_SizeDown_f },
|
||||
{ "weapnext", CG_NextWeapon_f },
|
||||
{ "weapprev", CG_PrevWeapon_f },
|
||||
{ "weapon", CG_Weapon_f },
|
||||
{ "tell_target", CG_TellTarget_f },
|
||||
{ "tell_attacker", CG_TellAttacker_f },
|
||||
{ "tcmd", CG_TargetCommand_f },
|
||||
{ "testPS", CG_TestPS_f },
|
||||
{ "destroyTestPS", CG_DestroyTestPS_f },
|
||||
{ "testTS", CG_TestTS_f },
|
||||
{ "destroyTestTS", CG_DestroyTestTS_f },
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_ConsoleCommand
|
||||
|
||||
The string has been tokenized and can be retrieved with
|
||||
Cmd_Argc() / Cmd_Argv()
|
||||
=================
|
||||
*/
|
||||
qboolean CG_ConsoleCommand( void )
|
||||
{
|
||||
const char *cmd;
|
||||
const char *arg1;
|
||||
int i;
|
||||
|
||||
cmd = CG_Argv( 0 );
|
||||
|
||||
//TA: ugly hacky special case
|
||||
if( !Q_stricmp( cmd, "ui_menu" ) )
|
||||
{
|
||||
arg1 = CG_Argv( 1 );
|
||||
trap_SendConsoleCommand( va( "menu %s\n", arg1 ) );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
for( i = 0; i < sizeof( commands ) / sizeof( commands[ 0 ] ); i++ )
|
||||
{
|
||||
if( !Q_stricmp( cmd, commands[ i ].cmd ) )
|
||||
{
|
||||
commands[ i ].function( );
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_InitConsoleCommands
|
||||
|
||||
Let the client system know about all of our commands
|
||||
so it can perform tab completion
|
||||
=================
|
||||
*/
|
||||
void CG_InitConsoleCommands( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0 ; i < sizeof( commands ) / sizeof( commands[ 0 ] ) ; i++ )
|
||||
trap_AddCommand( commands[ i ].cmd );
|
||||
|
||||
//
|
||||
// the game server will interpret these commands, which will be automatically
|
||||
// forwarded to the server after they are not recognized locally
|
||||
//
|
||||
trap_AddCommand( "kill" );
|
||||
trap_AddCommand( "say" );
|
||||
trap_AddCommand( "say_team" );
|
||||
trap_AddCommand( "tell" );
|
||||
trap_AddCommand( "vsay" );
|
||||
trap_AddCommand( "vsay_team" );
|
||||
trap_AddCommand( "vtell" );
|
||||
trap_AddCommand( "vtaunt" );
|
||||
trap_AddCommand( "vosay" );
|
||||
trap_AddCommand( "vosay_team" );
|
||||
trap_AddCommand( "votell" );
|
||||
trap_AddCommand( "give" );
|
||||
trap_AddCommand( "god" );
|
||||
trap_AddCommand( "notarget" );
|
||||
trap_AddCommand( "noclip" );
|
||||
trap_AddCommand( "team" );
|
||||
trap_AddCommand( "follow" );
|
||||
trap_AddCommand( "levelshot" );
|
||||
trap_AddCommand( "addbot" );
|
||||
trap_AddCommand( "setviewpos" );
|
||||
trap_AddCommand( "callvote" );
|
||||
trap_AddCommand( "vote" );
|
||||
trap_AddCommand( "callteamvote" );
|
||||
trap_AddCommand( "teamvote" );
|
||||
trap_AddCommand( "stats" );
|
||||
trap_AddCommand( "teamtask" );
|
||||
trap_AddCommand( "class" );
|
||||
trap_AddCommand( "build" );
|
||||
trap_AddCommand( "buy" );
|
||||
trap_AddCommand( "sell" );
|
||||
trap_AddCommand( "reload" );
|
||||
trap_AddCommand( "itemact" );
|
||||
trap_AddCommand( "itemdeact" );
|
||||
trap_AddCommand( "itemtoggle" );
|
||||
trap_AddCommand( "destroy" );
|
||||
trap_AddCommand( "deconstruct" );
|
||||
trap_AddCommand( "menu" );
|
||||
trap_AddCommand( "ui_menu" );
|
||||
trap_AddCommand( "mapRotation" );
|
||||
trap_AddCommand( "stopMapRotation" );
|
||||
trap_AddCommand( "advanceMapRotation" );
|
||||
trap_AddCommand( "alienWin" );
|
||||
trap_AddCommand( "humanWin" );
|
||||
}
|
||||
+3524
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_DrawPlane
|
||||
|
||||
Draw a quad in 3 space - basically CG_DrawPic in 3 space
|
||||
===============
|
||||
*/
|
||||
void CG_DrawPlane( vec3_t origin, vec3_t down, vec3_t right, qhandle_t shader )
|
||||
{
|
||||
polyVert_t verts[ 4 ];
|
||||
vec3_t temp;
|
||||
|
||||
VectorCopy( origin, verts[ 0 ].xyz );
|
||||
verts[ 0 ].st[ 0 ] = 0;
|
||||
verts[ 0 ].st[ 1 ] = 0;
|
||||
verts[ 0 ].modulate[ 0 ] = 255;
|
||||
verts[ 0 ].modulate[ 1 ] = 255;
|
||||
verts[ 0 ].modulate[ 2 ] = 255;
|
||||
verts[ 0 ].modulate[ 3 ] = 255;
|
||||
|
||||
VectorAdd( origin, right, temp );
|
||||
VectorCopy( temp, verts[ 1 ].xyz );
|
||||
verts[ 1 ].st[ 0 ] = 1;
|
||||
verts[ 1 ].st[ 1 ] = 0;
|
||||
verts[ 1 ].modulate[ 0 ] = 255;
|
||||
verts[ 1 ].modulate[ 1 ] = 255;
|
||||
verts[ 1 ].modulate[ 2 ] = 255;
|
||||
verts[ 1 ].modulate[ 3 ] = 255;
|
||||
|
||||
VectorAdd( origin, right, temp );
|
||||
VectorAdd( temp, down, temp );
|
||||
VectorCopy( temp, verts[ 2 ].xyz );
|
||||
verts[ 2 ].st[ 0 ] = 1;
|
||||
verts[ 2 ].st[ 1 ] = 1;
|
||||
verts[ 2 ].modulate[ 0 ] = 255;
|
||||
verts[ 2 ].modulate[ 1 ] = 255;
|
||||
verts[ 2 ].modulate[ 2 ] = 255;
|
||||
verts[ 2 ].modulate[ 3 ] = 255;
|
||||
|
||||
VectorAdd( origin, down, temp );
|
||||
VectorCopy( temp, verts[ 3 ].xyz );
|
||||
verts[ 3 ].st[ 0 ] = 0;
|
||||
verts[ 3 ].st[ 1 ] = 1;
|
||||
verts[ 3 ].modulate[ 0 ] = 255;
|
||||
verts[ 3 ].modulate[ 1 ] = 255;
|
||||
verts[ 3 ].modulate[ 2 ] = 255;
|
||||
verts[ 3 ].modulate[ 3 ] = 255;
|
||||
|
||||
trap_R_AddPolyToScene( shader, 4, verts );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_AdjustFrom640
|
||||
|
||||
Adjusted for resolution and screen aspect ratio
|
||||
================
|
||||
*/
|
||||
void CG_AdjustFrom640( float *x, float *y, float *w, float *h )
|
||||
{
|
||||
#if 0
|
||||
// adjust for wide screens
|
||||
if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) {
|
||||
*x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) );
|
||||
}
|
||||
#endif
|
||||
// scale for screen sizes
|
||||
*x *= cgs.screenXScale;
|
||||
*y *= cgs.screenYScale;
|
||||
*w *= cgs.screenXScale;
|
||||
*h *= cgs.screenYScale;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FillRect
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_FillRect( float x, float y, float width, float height, const float *color )
|
||||
{
|
||||
trap_R_SetColor( color );
|
||||
|
||||
CG_AdjustFrom640( &x, &y, &width, &height );
|
||||
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawSides
|
||||
|
||||
Coords are virtual 640x480
|
||||
================
|
||||
*/
|
||||
void CG_DrawSides( float x, float y, float w, float h, float size )
|
||||
{
|
||||
CG_AdjustFrom640( &x, &y, &w, &h );
|
||||
size *= cgs.screenXScale;
|
||||
trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
}
|
||||
|
||||
void CG_DrawTopBottom( float x, float y, float w, float h, float size )
|
||||
{
|
||||
CG_AdjustFrom640( &x, &y, &w, &h );
|
||||
size *= cgs.screenYScale;
|
||||
trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawRect
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color )
|
||||
{
|
||||
trap_R_SetColor( color );
|
||||
|
||||
CG_DrawTopBottom( x, y, width, height, size );
|
||||
CG_DrawSides( x, y, width, height, size );
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawPic
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader )
|
||||
{
|
||||
CG_AdjustFrom640( &x, &y, &width, &height );
|
||||
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawFadePic
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_DrawFadePic( float x, float y, float width, float height, vec4_t fcolor,
|
||||
vec4_t tcolor, float amount, qhandle_t hShader )
|
||||
{
|
||||
vec4_t finalcolor;
|
||||
float inverse;
|
||||
|
||||
inverse = 100 - amount;
|
||||
|
||||
CG_AdjustFrom640( &x, &y, &width, &height );
|
||||
|
||||
finalcolor[ 0 ] = ( ( inverse * fcolor[ 0 ] ) + ( amount * tcolor[ 0 ] ) ) / 100;
|
||||
finalcolor[ 1 ] = ( ( inverse * fcolor[ 1 ] ) + ( amount * tcolor[ 1 ] ) ) / 100;
|
||||
finalcolor[ 2 ] = ( ( inverse * fcolor[ 2 ] ) + ( amount * tcolor[ 2 ] ) ) / 100;
|
||||
finalcolor[ 3 ] = ( ( inverse * fcolor[ 3 ] ) + ( amount * tcolor[ 3 ] ) ) / 100;
|
||||
|
||||
trap_R_SetColor( finalcolor );
|
||||
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawStrlen
|
||||
|
||||
Returns character count, skiping color escape codes
|
||||
=================
|
||||
*/
|
||||
int CG_DrawStrlen( const char *str )
|
||||
{
|
||||
const char *s = str;
|
||||
int count = 0;
|
||||
|
||||
while( *s )
|
||||
{
|
||||
if( Q_IsColorString( s ) )
|
||||
s += 2;
|
||||
else
|
||||
{
|
||||
count++;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_TileClearBox
|
||||
|
||||
This repeats a 64*64 tile graphic to fill the screen around a sized down
|
||||
refresh window.
|
||||
=============
|
||||
*/
|
||||
static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader )
|
||||
{
|
||||
float s1, t1, s2, t2;
|
||||
|
||||
s1 = x / 64.0;
|
||||
t1 = y / 64.0;
|
||||
s2 = ( x + w ) / 64.0;
|
||||
t2 = ( y + h ) / 64.0;
|
||||
trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_TileClear
|
||||
|
||||
Clear around a sized down screen
|
||||
==============
|
||||
*/
|
||||
void CG_TileClear( void )
|
||||
{
|
||||
int top, bottom, left, right;
|
||||
int w, h;
|
||||
|
||||
w = cgs.glconfig.vidWidth;
|
||||
h = cgs.glconfig.vidHeight;
|
||||
|
||||
if( cg.refdef.x == 0 && cg.refdef.y == 0 &&
|
||||
cg.refdef.width == w && cg.refdef.height == h )
|
||||
return; // full screen rendering
|
||||
|
||||
top = cg.refdef.y;
|
||||
bottom = top + cg.refdef.height - 1;
|
||||
left = cg.refdef.x;
|
||||
right = left + cg.refdef.width - 1;
|
||||
|
||||
// clear above view screen
|
||||
CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader );
|
||||
|
||||
// clear below view screen
|
||||
CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader );
|
||||
|
||||
// clear left of view screen
|
||||
CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader );
|
||||
|
||||
// clear right of view screen
|
||||
CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FadeColor
|
||||
================
|
||||
*/
|
||||
float *CG_FadeColor( int startMsec, int totalMsec )
|
||||
{
|
||||
static vec4_t color;
|
||||
int t;
|
||||
|
||||
if( startMsec == 0 )
|
||||
return NULL;
|
||||
|
||||
t = cg.time - startMsec;
|
||||
|
||||
if( t >= totalMsec )
|
||||
return NULL;
|
||||
|
||||
// fade out
|
||||
if( totalMsec - t < FADE_TIME )
|
||||
color[ 3 ] = ( totalMsec - t ) * 1.0 / FADE_TIME;
|
||||
else
|
||||
color[ 3 ] = 1.0;
|
||||
|
||||
color[ 0 ] = color[ 1 ] = color[ 2 ] = 1;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_WorldToScreen
|
||||
================
|
||||
*/
|
||||
qboolean CG_WorldToScreen( vec3_t point, float *x, float *y )
|
||||
{
|
||||
vec3_t trans;
|
||||
float xc, yc;
|
||||
float px, py;
|
||||
float z;
|
||||
|
||||
px = tan( cg.refdef.fov_x * M_PI / 360.0 );
|
||||
py = tan( cg.refdef.fov_y * M_PI / 360.0 );
|
||||
|
||||
VectorSubtract( point, cg.refdef.vieworg, trans );
|
||||
|
||||
xc = 640.0f / 2.0f;
|
||||
yc = 480.0f / 2.0f;
|
||||
|
||||
z = DotProduct( trans, cg.refdef.viewaxis[ 0 ] );
|
||||
if( z <= 0.001f )
|
||||
return qfalse;
|
||||
|
||||
if( x )
|
||||
*x = xc - DotProduct( trans, cg.refdef.viewaxis[ 1 ] ) * xc / ( z * px );
|
||||
|
||||
if( y )
|
||||
*y = yc - DotProduct( trans, cg.refdef.viewaxis[ 2 ] ) * yc / ( z * py );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_KeyBinding
|
||||
================
|
||||
*/
|
||||
char *CG_KeyBinding( const char *bind )
|
||||
{
|
||||
static char key[ 32 ];
|
||||
char bindbuff[ MAX_CVAR_VALUE_STRING ];
|
||||
int i;
|
||||
|
||||
key[ 0 ] = '\0';
|
||||
// NOTE: change K_LAST_KEY to MAX_KEYS for full key support (eventually)
|
||||
for( i = 0; i < K_LAST_KEY; i++ )
|
||||
{
|
||||
trap_Key_GetBindingBuf( i, bindbuff, sizeof( bindbuff ) );
|
||||
if( !Q_stricmp( bindbuff, bind ) )
|
||||
{
|
||||
trap_Key_KeynumToStringBuf( i, key, sizeof( key ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !key[ 0 ] )
|
||||
{
|
||||
Q_strncpyz( key, "\\", sizeof( key ) );
|
||||
Q_strcat( key, sizeof( key ), bind );
|
||||
}
|
||||
return key;
|
||||
}
|
||||
+1256
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1840
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_marks.c -- wall marks
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
===================================================================
|
||||
|
||||
MARK POLYS
|
||||
|
||||
===================================================================
|
||||
*/
|
||||
|
||||
|
||||
markPoly_t cg_activeMarkPolys; // double linked list
|
||||
markPoly_t *cg_freeMarkPolys; // single linked list
|
||||
markPoly_t cg_markPolys[ MAX_MARK_POLYS ];
|
||||
static int markTotal;
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_InitMarkPolys
|
||||
|
||||
This is called at startup and for tournement restarts
|
||||
===================
|
||||
*/
|
||||
void CG_InitMarkPolys( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
memset( cg_markPolys, 0, sizeof( cg_markPolys ) );
|
||||
|
||||
cg_activeMarkPolys.nextMark = &cg_activeMarkPolys;
|
||||
cg_activeMarkPolys.prevMark = &cg_activeMarkPolys;
|
||||
cg_freeMarkPolys = cg_markPolys;
|
||||
|
||||
for( i = 0; i < MAX_MARK_POLYS - 1; i++ )
|
||||
cg_markPolys[ i ].nextMark = &cg_markPolys[ i + 1 ];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_FreeMarkPoly
|
||||
==================
|
||||
*/
|
||||
void CG_FreeMarkPoly( markPoly_t *le )
|
||||
{
|
||||
if( !le->prevMark )
|
||||
CG_Error( "CG_FreeLocalEntity: not active" );
|
||||
|
||||
// remove from the doubly linked active list
|
||||
le->prevMark->nextMark = le->nextMark;
|
||||
le->nextMark->prevMark = le->prevMark;
|
||||
|
||||
// the free list is only singly linked
|
||||
le->nextMark = cg_freeMarkPolys;
|
||||
cg_freeMarkPolys = le;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AllocMark
|
||||
|
||||
Will allways succeed, even if it requires freeing an old active mark
|
||||
===================
|
||||
*/
|
||||
markPoly_t *CG_AllocMark( void )
|
||||
{
|
||||
markPoly_t *le;
|
||||
int time;
|
||||
|
||||
if( !cg_freeMarkPolys )
|
||||
{
|
||||
// no free entities, so free the one at the end of the chain
|
||||
// remove the oldest active entity
|
||||
time = cg_activeMarkPolys.prevMark->time;
|
||||
|
||||
while( cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time )
|
||||
CG_FreeMarkPoly( cg_activeMarkPolys.prevMark );
|
||||
}
|
||||
|
||||
le = cg_freeMarkPolys;
|
||||
cg_freeMarkPolys = cg_freeMarkPolys->nextMark;
|
||||
|
||||
memset( le, 0, sizeof( *le ) );
|
||||
|
||||
// link into the active list
|
||||
le->nextMark = cg_activeMarkPolys.nextMark;
|
||||
le->prevMark = &cg_activeMarkPolys;
|
||||
cg_activeMarkPolys.nextMark->prevMark = le;
|
||||
cg_activeMarkPolys.nextMark = le;
|
||||
return le;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_ImpactMark
|
||||
|
||||
origin should be a point within a unit of the plane
|
||||
dir should be the plane normal
|
||||
|
||||
temporary marks will not be stored or randomly oriented, but immediately
|
||||
passed to the renderer.
|
||||
=================
|
||||
*/
|
||||
#define MAX_MARK_FRAGMENTS 128
|
||||
#define MAX_MARK_POINTS 384
|
||||
|
||||
void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir,
|
||||
float orientation, float red, float green, float blue, float alpha,
|
||||
qboolean alphaFade, float radius, qboolean temporary )
|
||||
{
|
||||
vec3_t axis[ 3 ];
|
||||
float texCoordScale;
|
||||
vec3_t originalPoints[ 4 ];
|
||||
byte colors[ 4 ];
|
||||
int i, j;
|
||||
int numFragments;
|
||||
markFragment_t markFragments[ MAX_MARK_FRAGMENTS ], *mf;
|
||||
vec3_t markPoints[ MAX_MARK_POINTS ];
|
||||
vec3_t projection;
|
||||
|
||||
if( !cg_addMarks.integer )
|
||||
return;
|
||||
|
||||
if( radius <= 0 )
|
||||
CG_Error( "CG_ImpactMark called with <= 0 radius" );
|
||||
|
||||
//if ( markTotal >= MAX_MARK_POLYS ) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
// create the texture axis
|
||||
VectorNormalize2( dir, axis[ 0 ] );
|
||||
PerpendicularVector( axis[ 1 ], axis[ 0 ] );
|
||||
RotatePointAroundVector( axis[ 2 ], axis[ 0 ], axis[ 1 ], orientation );
|
||||
CrossProduct( axis[ 0 ], axis[ 2 ], axis[ 1 ] );
|
||||
|
||||
texCoordScale = 0.5 * 1.0 / radius;
|
||||
|
||||
// create the full polygon
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
originalPoints[ 0 ][ i ] = origin[ i ] - radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ];
|
||||
originalPoints[ 1 ][ i ] = origin[ i ] + radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ];
|
||||
originalPoints[ 2 ][ i ] = origin[ i ] + radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ];
|
||||
originalPoints[ 3 ][ i ] = origin[ i ] - radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ];
|
||||
}
|
||||
|
||||
// get the fragments
|
||||
VectorScale( dir, -20, projection );
|
||||
numFragments = trap_CM_MarkFragments( 4, (void *)originalPoints,
|
||||
projection, MAX_MARK_POINTS, markPoints[ 0 ],
|
||||
MAX_MARK_FRAGMENTS, markFragments );
|
||||
|
||||
colors[ 0 ] = red * 255;
|
||||
colors[ 1 ] = green * 255;
|
||||
colors[ 2 ] = blue * 255;
|
||||
colors[ 3 ] = alpha * 255;
|
||||
|
||||
for( i = 0, mf = markFragments; i < numFragments; i++, mf++ )
|
||||
{
|
||||
polyVert_t *v;
|
||||
polyVert_t verts[ MAX_VERTS_ON_POLY ];
|
||||
markPoly_t *mark;
|
||||
|
||||
// we have an upper limit on the complexity of polygons
|
||||
// that we store persistantly
|
||||
if( mf->numPoints > MAX_VERTS_ON_POLY )
|
||||
mf->numPoints = MAX_VERTS_ON_POLY;
|
||||
|
||||
for( j = 0, v = verts; j < mf->numPoints; j++, v++ )
|
||||
{
|
||||
vec3_t delta;
|
||||
|
||||
VectorCopy( markPoints[ mf->firstPoint + j ], v->xyz );
|
||||
|
||||
VectorSubtract( v->xyz, origin, delta );
|
||||
v->st[ 0 ] = 0.5 + DotProduct( delta, axis[ 1 ] ) * texCoordScale;
|
||||
v->st[ 1 ] = 0.5 + DotProduct( delta, axis[ 2 ] ) * texCoordScale;
|
||||
*(int *)v->modulate = *(int *)colors;
|
||||
}
|
||||
|
||||
// if it is a temporary (shadow) mark, add it immediately and forget about it
|
||||
if( temporary )
|
||||
{
|
||||
trap_R_AddPolyToScene( markShader, mf->numPoints, verts );
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise save it persistantly
|
||||
mark = CG_AllocMark( );
|
||||
mark->time = cg.time;
|
||||
mark->alphaFade = alphaFade;
|
||||
mark->markShader = markShader;
|
||||
mark->poly.numVerts = mf->numPoints;
|
||||
mark->color[ 0 ] = red;
|
||||
mark->color[ 1 ] = green;
|
||||
mark->color[ 2 ] = blue;
|
||||
mark->color[ 3 ] = alpha;
|
||||
memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[ 0 ] ) );
|
||||
markTotal++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AddMarks
|
||||
===============
|
||||
*/
|
||||
#define MARK_TOTAL_TIME 10000
|
||||
#define MARK_FADE_TIME 1000
|
||||
|
||||
void CG_AddMarks( void )
|
||||
{
|
||||
int j;
|
||||
markPoly_t *mp, *next;
|
||||
int t;
|
||||
int fade;
|
||||
|
||||
if( !cg_addMarks.integer )
|
||||
return;
|
||||
|
||||
mp = cg_activeMarkPolys.nextMark;
|
||||
for ( ; mp != &cg_activeMarkPolys; mp = next )
|
||||
{
|
||||
// grab next now, so if the local entity is freed we
|
||||
// still have it
|
||||
next = mp->nextMark;
|
||||
|
||||
// see if it is time to completely remove it
|
||||
if( cg.time > mp->time + MARK_TOTAL_TIME )
|
||||
{
|
||||
CG_FreeMarkPoly( mp );
|
||||
continue;
|
||||
}
|
||||
|
||||
// fade all marks out with time
|
||||
t = mp->time + MARK_TOTAL_TIME - cg.time;
|
||||
if( t < MARK_FADE_TIME )
|
||||
{
|
||||
fade = 255 * t / MARK_FADE_TIME;
|
||||
if( mp->alphaFade )
|
||||
{
|
||||
for( j = 0; j < mp->poly.numVerts; j++ )
|
||||
mp->verts[ j ].modulate[ 3 ] = fade;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( j = 0; j < mp->poly.numVerts; j++ )
|
||||
{
|
||||
mp->verts[ j ].modulate[ 0 ] = mp->color[ 0 ] * fade;
|
||||
mp->verts[ j ].modulate[ 1 ] = mp->color[ 1 ] * fade;
|
||||
mp->verts[ j ].modulate[ 2 ] = mp->color[ 2 ] * fade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
#define POOLSIZE (256 * 1024)
|
||||
#define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value
|
||||
#define ROUNDBITS 31 // Round to 32 bytes
|
||||
|
||||
struct freememnode
|
||||
{
|
||||
// Size of ROUNDBITS
|
||||
int cookie, size; // Size includes node (obviously)
|
||||
struct freememnode *prev, *next;
|
||||
};
|
||||
|
||||
static char memoryPool[ POOLSIZE ];
|
||||
static struct freememnode *freehead;
|
||||
static int freemem;
|
||||
|
||||
void *CG_Alloc( int size )
|
||||
{
|
||||
// Find a free block and allocate.
|
||||
// Does two passes, attempts to fill same-sized free slot first.
|
||||
|
||||
struct freememnode *fmn, *prev, *next, *smallest;
|
||||
int allocsize, smallestsize;
|
||||
char *endptr;
|
||||
int *ptr;
|
||||
|
||||
allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary
|
||||
ptr = NULL;
|
||||
|
||||
smallest = NULL;
|
||||
smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :)
|
||||
for( fmn = freehead; fmn; fmn = fmn->next )
|
||||
{
|
||||
if( fmn->cookie != FREEMEMCOOKIE )
|
||||
CG_Error( "CG_Alloc: Memory corruption detected!\n" );
|
||||
|
||||
if( fmn->size >= allocsize )
|
||||
{
|
||||
// We've got a block
|
||||
if( fmn->size == allocsize )
|
||||
{
|
||||
// Same size, just remove
|
||||
|
||||
prev = fmn->prev;
|
||||
next = fmn->next;
|
||||
if( prev )
|
||||
prev->next = next; // Point previous node to next
|
||||
if( next )
|
||||
next->prev = prev; // Point next node to previous
|
||||
if( fmn == freehead )
|
||||
freehead = next; // Set head pointer to next
|
||||
ptr = (int *) fmn;
|
||||
break; // Stop the loop, this is fine
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep track of the smallest free slot
|
||||
if( fmn->size < smallestsize )
|
||||
{
|
||||
smallest = fmn;
|
||||
smallestsize = fmn->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !ptr && smallest )
|
||||
{
|
||||
// We found a slot big enough
|
||||
smallest->size -= allocsize;
|
||||
endptr = (char *) smallest + smallest->size;
|
||||
ptr = (int *) endptr;
|
||||
}
|
||||
|
||||
if( ptr )
|
||||
{
|
||||
freemem -= allocsize;
|
||||
if( cg_debugAlloc.integer )
|
||||
CG_Printf( "CG_Alloc of %i bytes (%i left)\n", allocsize, freemem );
|
||||
memset( ptr, 0, allocsize );
|
||||
*ptr++ = allocsize; // Store a copy of size for deallocation
|
||||
return( (void *) ptr );
|
||||
}
|
||||
|
||||
CG_Error( "CG_Alloc: failed on allocation of %i bytes\n", size );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void CG_Free( void *ptr )
|
||||
{
|
||||
// Release allocated memory, add it to the free list.
|
||||
|
||||
struct freememnode *fmn;
|
||||
char *freeend;
|
||||
int *freeptr;
|
||||
|
||||
freeptr = ptr;
|
||||
freeptr--;
|
||||
|
||||
freemem += *freeptr;
|
||||
if( cg_debugAlloc.integer )
|
||||
CG_Printf( "CG_Free of %i bytes (%i left)\n", *freeptr, freemem );
|
||||
|
||||
for( fmn = freehead; fmn; fmn = fmn->next )
|
||||
{
|
||||
freeend = ((char *) fmn) + fmn->size;
|
||||
if( freeend == (char *) freeptr )
|
||||
{
|
||||
// Released block can be merged to an existing node
|
||||
|
||||
fmn->size += *freeptr; // Add size of node.
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No merging, add to head of list
|
||||
|
||||
fmn = (struct freememnode *) freeptr;
|
||||
fmn->size = *freeptr; // Set this first to avoid corrupting *freeptr
|
||||
fmn->cookie = FREEMEMCOOKIE;
|
||||
fmn->prev = NULL;
|
||||
fmn->next = freehead;
|
||||
freehead->prev = fmn;
|
||||
freehead = fmn;
|
||||
}
|
||||
|
||||
void CG_InitMemory( void )
|
||||
{
|
||||
// Set up the initial node
|
||||
|
||||
freehead = (struct freememnode *) memoryPool;
|
||||
freehead->cookie = FREEMEMCOOKIE;
|
||||
freehead->size = POOLSIZE;
|
||||
freehead->next = NULL;
|
||||
freehead->prev = NULL;
|
||||
freemem = sizeof(memoryPool);
|
||||
}
|
||||
|
||||
void CG_DefragmentMemory( void )
|
||||
{
|
||||
// If there's a frenzy of deallocation and we want to
|
||||
// allocate something big, this is useful. Otherwise...
|
||||
// not much use.
|
||||
|
||||
struct freememnode *startfmn, *endfmn, *fmn;
|
||||
|
||||
for( startfmn = freehead; startfmn; )
|
||||
{
|
||||
endfmn = (struct freememnode *)(((char *) startfmn) + startfmn->size);
|
||||
for( fmn = freehead; fmn; )
|
||||
{
|
||||
if( fmn->cookie != FREEMEMCOOKIE )
|
||||
CG_Error( "CG_DefragmentMemory: Memory corruption detected!\n" );
|
||||
|
||||
if( fmn == endfmn )
|
||||
{
|
||||
// We can add fmn onto startfmn.
|
||||
|
||||
if( fmn->prev )
|
||||
fmn->prev->next = fmn->next;
|
||||
if( fmn->next )
|
||||
{
|
||||
if( !(fmn->next->prev = fmn->prev) )
|
||||
freehead = fmn->next; // We're removing the head node
|
||||
}
|
||||
startfmn->size += fmn->size;
|
||||
memset( fmn, 0, sizeof(struct freememnode) ); // A redundant call, really.
|
||||
|
||||
startfmn = freehead;
|
||||
endfmn = fmn = NULL; // Break out of current loop
|
||||
}
|
||||
else
|
||||
fmn = fmn->next;
|
||||
}
|
||||
|
||||
if( endfmn )
|
||||
startfmn = startfmn->next; // endfmn acts as a 'restart' flag here
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_playerstate.c -- this file acts on changes in a new playerState_t
|
||||
// With normal play, this will be done after local prediction, but when
|
||||
// following another player or playing back a demo, it will be checked
|
||||
// when the snapshot transitions like all the other entities
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_DamageFeedback
|
||||
==============
|
||||
*/
|
||||
void CG_DamageFeedback( int yawByte, int pitchByte, int damage )
|
||||
{
|
||||
float left, front, up;
|
||||
float kick;
|
||||
int health;
|
||||
float scale;
|
||||
vec3_t dir;
|
||||
vec3_t angles;
|
||||
float dist;
|
||||
float yaw, pitch;
|
||||
|
||||
// show the attacking player's head and name in corner
|
||||
cg.attackerTime = cg.time;
|
||||
|
||||
// the lower on health you are, the greater the view kick will be
|
||||
health = cg.snap->ps.stats[STAT_HEALTH];
|
||||
|
||||
if( health < 40 )
|
||||
scale = 1;
|
||||
else
|
||||
scale = 40.0 / health;
|
||||
|
||||
kick = damage * scale;
|
||||
|
||||
if( kick < 5 )
|
||||
kick = 5;
|
||||
|
||||
if( kick > 10 )
|
||||
kick = 10;
|
||||
|
||||
// if yaw and pitch are both 255, make the damage always centered (falling, etc)
|
||||
if( yawByte == 255 && pitchByte == 255 )
|
||||
{
|
||||
cg.damageX = 0;
|
||||
cg.damageY = 0;
|
||||
cg.v_dmg_roll = 0;
|
||||
cg.v_dmg_pitch = -kick;
|
||||
}
|
||||
else
|
||||
{
|
||||
// positional
|
||||
pitch = pitchByte / 255.0 * 360;
|
||||
yaw = yawByte / 255.0 * 360;
|
||||
|
||||
angles[ PITCH ] = pitch;
|
||||
angles[ YAW ] = yaw;
|
||||
angles[ ROLL ] = 0;
|
||||
|
||||
AngleVectors( angles, dir, NULL, NULL );
|
||||
VectorSubtract( vec3_origin, dir, dir );
|
||||
|
||||
front = DotProduct( dir, cg.refdef.viewaxis[ 0 ] );
|
||||
left = DotProduct( dir, cg.refdef.viewaxis[ 1 ] );
|
||||
up = DotProduct( dir, cg.refdef.viewaxis[ 2 ] );
|
||||
|
||||
dir[ 0 ] = front;
|
||||
dir[ 1 ] = left;
|
||||
dir[ 2 ] = 0;
|
||||
dist = VectorLength( dir );
|
||||
|
||||
if( dist < 0.1f )
|
||||
dist = 0.1f;
|
||||
|
||||
cg.v_dmg_roll = kick * left;
|
||||
|
||||
cg.v_dmg_pitch = -kick * front;
|
||||
|
||||
if( front <= 0.1 )
|
||||
front = 0.1f;
|
||||
|
||||
cg.damageX = -left / front;
|
||||
cg.damageY = up / dist;
|
||||
}
|
||||
|
||||
// clamp the position
|
||||
if( cg.damageX > 1.0 )
|
||||
cg.damageX = 1.0;
|
||||
|
||||
if( cg.damageX < - 1.0 )
|
||||
cg.damageX = -1.0;
|
||||
|
||||
if( cg.damageY > 1.0 )
|
||||
cg.damageY = 1.0;
|
||||
|
||||
if( cg.damageY < - 1.0 )
|
||||
cg.damageY = -1.0;
|
||||
|
||||
// don't let the screen flashes vary as much
|
||||
if( kick > 10 )
|
||||
kick = 10;
|
||||
|
||||
cg.damageValue = kick;
|
||||
cg.v_dmg_time = cg.time + DAMAGE_TIME;
|
||||
cg.damageTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_Respawn
|
||||
|
||||
A respawn happened this snapshot
|
||||
================
|
||||
*/
|
||||
void CG_Respawn( void )
|
||||
{
|
||||
// no error decay on player movement
|
||||
cg.thisFrameTeleport = qtrue;
|
||||
|
||||
// display weapons available
|
||||
cg.weaponSelectTime = cg.time;
|
||||
|
||||
// select the weapon the server says we are using
|
||||
cg.weaponSelect = cg.snap->ps.weapon;
|
||||
|
||||
CG_ResetPainBlend( );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_CheckPlayerstateEvents
|
||||
|
||||
==============
|
||||
*/
|
||||
void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops )
|
||||
{
|
||||
int i;
|
||||
int event;
|
||||
centity_t *cent;
|
||||
|
||||
if( ps->externalEvent && ps->externalEvent != ops->externalEvent )
|
||||
{
|
||||
cent = &cg_entities[ ps->clientNum ];
|
||||
cent->currentState.event = ps->externalEvent;
|
||||
cent->currentState.eventParm = ps->externalEventParm;
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
}
|
||||
|
||||
cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ];
|
||||
|
||||
// go through the predictable events buffer
|
||||
for( i = ps->eventSequence - MAX_PS_EVENTS; i < ps->eventSequence; i++ )
|
||||
{
|
||||
// if we have a new predictable event
|
||||
if( i >= ops->eventSequence ||
|
||||
// or the server told us to play another event instead of a predicted event we already issued
|
||||
// or something the server told us changed our prediction causing a different event
|
||||
( i > ops->eventSequence - MAX_PS_EVENTS && ps->events[ i & ( MAX_PS_EVENTS - 1 ) ] !=
|
||||
ops->events[ i & ( MAX_PS_EVENTS - 1 ) ] ) )
|
||||
{
|
||||
event = ps->events[ i & ( MAX_PS_EVENTS - 1 ) ];
|
||||
|
||||
cent->currentState.event = event;
|
||||
cent->currentState.eventParm = ps->eventParms[ i & ( MAX_PS_EVENTS - 1 ) ];
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event;
|
||||
|
||||
cg.eventSequence++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_CheckChangedPredictableEvents
|
||||
==================
|
||||
*/
|
||||
void CG_CheckChangedPredictableEvents( playerState_t *ps )
|
||||
{
|
||||
int i;
|
||||
int event;
|
||||
centity_t *cent;
|
||||
|
||||
cent = &cg.predictedPlayerEntity;
|
||||
|
||||
for( i = ps->eventSequence - MAX_PS_EVENTS; i < ps->eventSequence; i++ )
|
||||
{
|
||||
//
|
||||
if( i >= cg.eventSequence )
|
||||
continue;
|
||||
|
||||
// if this event is not further back in than the maximum predictable events we remember
|
||||
if( i > cg.eventSequence - MAX_PREDICTED_EVENTS )
|
||||
{
|
||||
// if the new playerstate event is different from a previously predicted one
|
||||
if( ps->events[ i & ( MAX_PS_EVENTS - 1 ) ] != cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] )
|
||||
{
|
||||
event = ps->events[ i & ( MAX_PS_EVENTS - 1 ) ];
|
||||
cent->currentState.event = event;
|
||||
cent->currentState.eventParm = ps->eventParms[ i & ( MAX_PS_EVENTS - 1 ) ];
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
|
||||
cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event;
|
||||
|
||||
if( cg_showmiss.integer )
|
||||
CG_Printf( "WARNING: changed predicted event\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_CheckLocalSounds
|
||||
==================
|
||||
*/
|
||||
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops )
|
||||
{
|
||||
int reward;
|
||||
|
||||
// don't play the sounds if the player just changed teams
|
||||
if( ps->persistant[ PERS_TEAM ] != ops->persistant[ PERS_TEAM ] )
|
||||
return;
|
||||
|
||||
// health changes of more than -1 should make pain sounds
|
||||
if( ps->stats[ STAT_HEALTH ] < ops->stats[ STAT_HEALTH ] - 1 )
|
||||
{
|
||||
if( ps->stats[ STAT_HEALTH ] > 0 )
|
||||
CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[ STAT_HEALTH ] );
|
||||
}
|
||||
|
||||
|
||||
// if we are going into the intermission, don't start any voices
|
||||
if( cg.intermissionStarted )
|
||||
return;
|
||||
|
||||
// reward sounds
|
||||
reward = qfalse;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionPlayerState
|
||||
|
||||
===============
|
||||
*/
|
||||
void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops )
|
||||
{
|
||||
// check for changing follow mode
|
||||
if( ps->clientNum != ops->clientNum )
|
||||
{
|
||||
cg.thisFrameTeleport = qtrue;
|
||||
// make sure we don't get any unwanted transition effects
|
||||
*ops = *ps;
|
||||
|
||||
CG_ResetPainBlend( );
|
||||
}
|
||||
|
||||
// damage events (player is getting wounded)
|
||||
if( ps->damageEvent != ops->damageEvent && ps->damageCount )
|
||||
CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
|
||||
|
||||
// respawning
|
||||
if( ps->persistant[ PERS_SPAWN_COUNT ] != ops->persistant[ PERS_SPAWN_COUNT ] )
|
||||
CG_Respawn( );
|
||||
|
||||
if( cg.mapRestart )
|
||||
{
|
||||
CG_Respawn( );
|
||||
cg.mapRestart = qfalse;
|
||||
}
|
||||
|
||||
if( cg.snap->ps.pm_type != PM_INTERMISSION &&
|
||||
ps->persistant[ PERS_TEAM ] != TEAM_SPECTATOR )
|
||||
CG_CheckLocalSounds( ps, ops );
|
||||
|
||||
// run events
|
||||
CG_CheckPlayerstateEvents( ps, ops );
|
||||
|
||||
// smooth the ducking viewheight change
|
||||
if( ps->viewheight != ops->viewheight )
|
||||
{
|
||||
cg.duckChange = ps->viewheight - ops->viewheight;
|
||||
cg.duckTime = cg.time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,878 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_predict.c -- this file generates cg.predictedPlayerState by either
|
||||
// interpolating between snapshots from the server or locally predicting
|
||||
// ahead the client's movement.
|
||||
// It also handles local physics interaction, like fragments bouncing off walls
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
static pmove_t cg_pmove;
|
||||
|
||||
static int cg_numSolidEntities;
|
||||
static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
|
||||
static int cg_numTriggerEntities;
|
||||
static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_BuildSolidList
|
||||
|
||||
When a new cg.snap has been set, this function builds a sublist
|
||||
of the entities that are actually solid, to make for more
|
||||
efficient collision detection
|
||||
====================
|
||||
*/
|
||||
void CG_BuildSolidList( void )
|
||||
{
|
||||
int i;
|
||||
centity_t *cent;
|
||||
snapshot_t *snap;
|
||||
entityState_t *ent;
|
||||
|
||||
cg_numSolidEntities = 0;
|
||||
cg_numTriggerEntities = 0;
|
||||
|
||||
if( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport )
|
||||
snap = cg.nextSnap;
|
||||
else
|
||||
snap = cg.snap;
|
||||
|
||||
for( i = 0; i < snap->numEntities; i++ )
|
||||
{
|
||||
cent = &cg_entities[ snap->entities[ i ].number ];
|
||||
ent = ¢->currentState;
|
||||
|
||||
if( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER )
|
||||
{
|
||||
cg_triggerEntities[ cg_numTriggerEntities ] = cent;
|
||||
cg_numTriggerEntities++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( cent->nextState.solid && ent->eType != ET_MISSILE )
|
||||
{
|
||||
cg_solidEntities[ cg_numSolidEntities ] = cent;
|
||||
cg_numSolidEntities++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_ClipMoveToEntities
|
||||
|
||||
====================
|
||||
*/
|
||||
static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins,
|
||||
const vec3_t maxs, const vec3_t end, int skipNumber,
|
||||
int mask, trace_t *tr, traceType_t collisionType )
|
||||
{
|
||||
int i, j, x, zd, zu;
|
||||
trace_t trace;
|
||||
entityState_t *ent;
|
||||
clipHandle_t cmodel;
|
||||
vec3_t bmins, bmaxs;
|
||||
vec3_t origin, angles;
|
||||
centity_t *cent;
|
||||
|
||||
//SUPAR HACK
|
||||
//this causes a trace to collide with the local player
|
||||
if( skipNumber == MAGIC_TRACE_HACK )
|
||||
j = cg_numSolidEntities + 1;
|
||||
else
|
||||
j = cg_numSolidEntities;
|
||||
|
||||
for( i = 0; i < j; i++ )
|
||||
{
|
||||
if( i < cg_numSolidEntities )
|
||||
cent = cg_solidEntities[ i ];
|
||||
else
|
||||
cent = &cg.predictedPlayerEntity;
|
||||
|
||||
ent = ¢->currentState;
|
||||
|
||||
if( ent->number == skipNumber )
|
||||
continue;
|
||||
|
||||
if( ent->solid == SOLID_BMODEL )
|
||||
{
|
||||
// special value for bmodel
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
VectorCopy( cent->lerpAngles, angles );
|
||||
BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin );
|
||||
}
|
||||
else
|
||||
{
|
||||
// encoded bbox
|
||||
x = ( ent->solid & 255 );
|
||||
zd = ( ( ent->solid >> 8 ) & 255 );
|
||||
zu = ( ( ent->solid >> 16 ) & 255 ) - 32;
|
||||
|
||||
bmins[ 0 ] = bmins[ 1 ] = -x;
|
||||
bmaxs[ 0 ] = bmaxs[ 1 ] = x;
|
||||
bmins[ 2 ] = -zd;
|
||||
bmaxs[ 2 ] = zu;
|
||||
|
||||
if( i == cg_numSolidEntities )
|
||||
BG_FindBBoxForClass( ( ent->powerups >> 8 ) & 0xFF, bmins, bmaxs, NULL, NULL, NULL );
|
||||
|
||||
cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
|
||||
VectorCopy( vec3_origin, angles );
|
||||
VectorCopy( cent->lerpOrigin, origin );
|
||||
}
|
||||
|
||||
|
||||
if( collisionType == TT_CAPSULE )
|
||||
{
|
||||
trap_CM_TransformedCapsuleTrace ( &trace, start, end,
|
||||
mins, maxs, cmodel, mask, origin, angles );
|
||||
}
|
||||
else if( collisionType == TT_AABB )
|
||||
{
|
||||
trap_CM_TransformedBoxTrace ( &trace, start, end,
|
||||
mins, maxs, cmodel, mask, origin, angles );
|
||||
}
|
||||
else if( collisionType == TT_BISPHERE )
|
||||
{
|
||||
trap_CM_TransformedBiSphereTrace( &trace, start, end,
|
||||
mins[ 0 ], maxs[ 0 ], cmodel, mask, origin );
|
||||
}
|
||||
|
||||
if( trace.allsolid || trace.fraction < tr->fraction )
|
||||
{
|
||||
trace.entityNum = ent->number;
|
||||
|
||||
if( tr->lateralFraction < trace.lateralFraction )
|
||||
{
|
||||
float oldLateralFraction = tr->lateralFraction;
|
||||
*tr = trace;
|
||||
tr->lateralFraction = oldLateralFraction;
|
||||
}
|
||||
else
|
||||
*tr = trace;
|
||||
}
|
||||
else if( trace.startsolid )
|
||||
tr->startsolid = qtrue;
|
||||
|
||||
if( tr->allsolid )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_Trace
|
||||
================
|
||||
*/
|
||||
void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
|
||||
int skipNumber, int mask )
|
||||
{
|
||||
trace_t t;
|
||||
|
||||
trap_CM_BoxTrace( &t, start, end, mins, maxs, 0, mask );
|
||||
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
|
||||
// check all other solid models
|
||||
CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, &t, TT_AABB );
|
||||
|
||||
*result = t;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_CapTrace
|
||||
================
|
||||
*/
|
||||
void CG_CapTrace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
|
||||
int skipNumber, int mask )
|
||||
{
|
||||
trace_t t;
|
||||
|
||||
trap_CM_CapsuleTrace( &t, start, end, mins, maxs, 0, mask );
|
||||
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
|
||||
// check all other solid models
|
||||
CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, &t, TT_CAPSULE );
|
||||
|
||||
*result = t;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_BiSphereTrace
|
||||
================
|
||||
*/
|
||||
void CG_BiSphereTrace( trace_t *result, const vec3_t start, const vec3_t end,
|
||||
const float startRadius, const float endRadius, int skipNumber, int mask )
|
||||
{
|
||||
trace_t t;
|
||||
vec3_t mins, maxs;
|
||||
|
||||
mins[ 0 ] = startRadius;
|
||||
maxs[ 0 ] = endRadius;
|
||||
|
||||
trap_CM_BiSphereTrace( &t, start, end, startRadius, endRadius, 0, mask );
|
||||
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
|
||||
// check all other solid models
|
||||
CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, &t, TT_BISPHERE );
|
||||
|
||||
*result = t;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_PointContents
|
||||
================
|
||||
*/
|
||||
int CG_PointContents( const vec3_t point, int passEntityNum )
|
||||
{
|
||||
int i;
|
||||
entityState_t *ent;
|
||||
centity_t *cent;
|
||||
clipHandle_t cmodel;
|
||||
int contents;
|
||||
|
||||
contents = trap_CM_PointContents (point, 0);
|
||||
|
||||
for( i = 0; i < cg_numSolidEntities; i++ )
|
||||
{
|
||||
cent = cg_solidEntities[ i ];
|
||||
|
||||
ent = ¢->currentState;
|
||||
|
||||
if( ent->number == passEntityNum )
|
||||
continue;
|
||||
|
||||
if( ent->solid != SOLID_BMODEL ) // special value for bmodel
|
||||
continue;
|
||||
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
|
||||
if( !cmodel )
|
||||
continue;
|
||||
|
||||
contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_InterpolatePlayerState
|
||||
|
||||
Generates cg.predictedPlayerState by interpolating between
|
||||
cg.snap->player_state and cg.nextFrame->player_state
|
||||
========================
|
||||
*/
|
||||
static void CG_InterpolatePlayerState( qboolean grabAngles )
|
||||
{
|
||||
float f;
|
||||
int i;
|
||||
playerState_t *out;
|
||||
snapshot_t *prev, *next;
|
||||
|
||||
out = &cg.predictedPlayerState;
|
||||
prev = cg.snap;
|
||||
next = cg.nextSnap;
|
||||
|
||||
*out = cg.snap->ps;
|
||||
|
||||
// if we are still allowing local input, short circuit the view angles
|
||||
if( grabAngles )
|
||||
{
|
||||
usercmd_t cmd;
|
||||
int cmdNum;
|
||||
|
||||
cmdNum = trap_GetCurrentCmdNumber( );
|
||||
trap_GetUserCmd( cmdNum, &cmd );
|
||||
|
||||
PM_UpdateViewAngles( out, &cmd );
|
||||
}
|
||||
|
||||
// if the next frame is a teleport, we can't lerp to it
|
||||
if( cg.nextFrameTeleport )
|
||||
return;
|
||||
|
||||
if( !next || next->serverTime <= prev->serverTime )
|
||||
return;
|
||||
|
||||
f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
|
||||
|
||||
i = next->ps.bobCycle;
|
||||
if( i < prev->ps.bobCycle )
|
||||
i += 256; // handle wraparound
|
||||
|
||||
out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
out->origin[ i ] = prev->ps.origin[ i ] + f * ( next->ps.origin[ i ] - prev->ps.origin[ i ] );
|
||||
|
||||
if( !grabAngles )
|
||||
out->viewangles[ i ] = LerpAngle( prev->ps.viewangles[ i ], next->ps.viewangles[ i ], f );
|
||||
|
||||
out->velocity[ i ] = prev->ps.velocity[ i ] +
|
||||
f * (next->ps.velocity[ i ] - prev->ps.velocity[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================
|
||||
CG_TouchTriggerPrediction
|
||||
|
||||
Predict push triggers and items
|
||||
=========================
|
||||
*/
|
||||
static void CG_TouchTriggerPrediction( void )
|
||||
{
|
||||
int i;
|
||||
trace_t trace;
|
||||
entityState_t *ent;
|
||||
clipHandle_t cmodel;
|
||||
centity_t *cent;
|
||||
qboolean spectator;
|
||||
|
||||
// dead clients don't activate triggers
|
||||
if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
|
||||
return;
|
||||
|
||||
spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );
|
||||
|
||||
if( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator )
|
||||
return;
|
||||
|
||||
for( i = 0; i < cg_numTriggerEntities; i++ )
|
||||
{
|
||||
cent = cg_triggerEntities[ i ];
|
||||
ent = ¢->currentState;
|
||||
|
||||
if( ent->solid != SOLID_BMODEL )
|
||||
continue;
|
||||
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
if( !cmodel )
|
||||
continue;
|
||||
|
||||
trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin,
|
||||
cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
|
||||
|
||||
if( !trace.startsolid )
|
||||
continue;
|
||||
|
||||
if( ent->eType == ET_TELEPORT_TRIGGER )
|
||||
cg.hyperspace = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
static int CG_IsUnacceptableError( playerState_t *ps, playerState_t *pps )
|
||||
{
|
||||
vec3_t delta;
|
||||
int i;
|
||||
|
||||
if( pps->pm_type != ps->pm_type ||
|
||||
pps->pm_flags != ps->pm_flags ||
|
||||
pps->pm_time != ps->pm_time )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
VectorSubtract( pps->origin, ps->origin, delta );
|
||||
if( VectorLengthSquared( delta ) > 0.1f * 0.1f )
|
||||
{
|
||||
if( cg_showmiss.integer )
|
||||
{
|
||||
CG_Printf( "origin delta: %.2f ", VectorLength( delta ) );
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
VectorSubtract( pps->velocity, ps->velocity, delta );
|
||||
if( VectorLengthSquared( delta ) > 0.1f * 0.1f )
|
||||
{
|
||||
if( cg_showmiss.integer )
|
||||
{
|
||||
CG_Printf( "velocity delta: %.2f ", VectorLength( delta ) );
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
if( pps->weaponTime != ps->weaponTime ||
|
||||
pps->gravity != ps->gravity ||
|
||||
pps->speed != ps->speed ||
|
||||
pps->delta_angles[ 0 ] != ps->delta_angles[ 0 ] ||
|
||||
pps->delta_angles[ 1 ] != ps->delta_angles[ 1 ] ||
|
||||
pps->delta_angles[ 2 ] != ps->delta_angles[ 2 ] ||
|
||||
pps->groundEntityNum != ps->groundEntityNum )
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
if( pps->legsTimer != ps->legsTimer ||
|
||||
pps->legsAnim != ps->legsAnim ||
|
||||
pps->torsoTimer != ps->torsoTimer ||
|
||||
pps->torsoAnim != ps->torsoAnim ||
|
||||
pps->movementDir != ps->movementDir )
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
VectorSubtract( pps->grapplePoint, ps->grapplePoint, delta );
|
||||
if( VectorLengthSquared( delta ) > 0.1f * 0.1f )
|
||||
return 6;
|
||||
|
||||
if( pps->eFlags != ps->eFlags )
|
||||
return 7;
|
||||
|
||||
if( pps->eventSequence != ps->eventSequence )
|
||||
return 8;
|
||||
|
||||
for( i = 0; i < MAX_PS_EVENTS; i++ )
|
||||
{
|
||||
if ( pps->events[ i ] != ps->events[ i ] ||
|
||||
pps->eventParms[ i ] != ps->eventParms[ i ] )
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
|
||||
if( pps->externalEvent != ps->externalEvent ||
|
||||
pps->externalEventParm != ps->externalEventParm ||
|
||||
pps->externalEventTime != ps->externalEventTime )
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
if( pps->clientNum != ps->clientNum ||
|
||||
pps->weapon != ps->weapon ||
|
||||
pps->weaponstate != ps->weaponstate )
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
|
||||
if( fabs( AngleDelta( ps->viewangles[ 0 ], pps->viewangles[ 0 ] ) ) > 1.0f ||
|
||||
fabs( AngleDelta( ps->viewangles[ 1 ], pps->viewangles[ 1 ] ) ) > 1.0f ||
|
||||
fabs( AngleDelta( ps->viewangles[ 2 ], pps->viewangles[ 2 ] ) ) > 1.0f )
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
if( pps->viewheight != ps->viewheight )
|
||||
return 13;
|
||||
|
||||
if( pps->damageEvent != ps->damageEvent ||
|
||||
pps->damageYaw != ps->damageYaw ||
|
||||
pps->damagePitch != ps->damagePitch ||
|
||||
pps->damageCount != ps->damageCount )
|
||||
{
|
||||
return 14;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_STATS; i++ )
|
||||
{
|
||||
if( pps->stats[ i ] != ps->stats[ i ] )
|
||||
return 15;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_PERSISTANT; i++ )
|
||||
{
|
||||
if( pps->persistant[ i ] != ps->persistant[ i ] )
|
||||
return 16;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_WEAPONS; i++ )
|
||||
{
|
||||
if( pps->ammo[ i ] != ps->ammo[ i ] )
|
||||
return 18;
|
||||
}
|
||||
|
||||
if( pps->generic1 != ps->generic1 ||
|
||||
pps->loopSound != ps->loopSound )
|
||||
{
|
||||
return 19;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_PredictPlayerState
|
||||
|
||||
Generates cg.predictedPlayerState for the current cg.time
|
||||
cg.predictedPlayerState is guaranteed to be valid after exiting.
|
||||
|
||||
For demo playback, this will be an interpolation between two valid
|
||||
playerState_t.
|
||||
|
||||
For normal gameplay, it will be the result of predicted usercmd_t on
|
||||
top of the most recent playerState_t received from the server.
|
||||
|
||||
Each new snapshot will usually have one or more new usercmd over the last,
|
||||
but we simulate all unacknowledged commands each time, not just the new ones.
|
||||
This means that on an internet connection, quite a few pmoves may be issued
|
||||
each frame.
|
||||
|
||||
OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
|
||||
differs from the predicted one. Would require saving all intermediate
|
||||
playerState_t during prediction.
|
||||
|
||||
We detect prediction errors and allow them to be decayed off over several frames
|
||||
to ease the jerk.
|
||||
=================
|
||||
*/
|
||||
void CG_PredictPlayerState( void )
|
||||
{
|
||||
int cmdNum, current, i;
|
||||
playerState_t oldPlayerState;
|
||||
qboolean moved;
|
||||
usercmd_t oldestCmd;
|
||||
usercmd_t latestCmd;
|
||||
int stateIndex = 0, predictCmd = 0;
|
||||
|
||||
cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
|
||||
|
||||
// if this is the first frame we must guarantee
|
||||
// predictedPlayerState is valid even if there is some
|
||||
// other error condition
|
||||
if( !cg.validPPS )
|
||||
{
|
||||
cg.validPPS = qtrue;
|
||||
cg.predictedPlayerState = cg.snap->ps;
|
||||
}
|
||||
|
||||
|
||||
// demo playback just copies the moves
|
||||
if( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) )
|
||||
{
|
||||
CG_InterpolatePlayerState( qfalse );
|
||||
return;
|
||||
}
|
||||
|
||||
// non-predicting local movement will grab the latest angles
|
||||
if( cg_nopredict.integer || cg_synchronousClients.integer )
|
||||
{
|
||||
CG_InterpolatePlayerState( qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare for pmove
|
||||
cg_pmove.ps = &cg.predictedPlayerState;
|
||||
cg_pmove.pmext = &cg.pmext;
|
||||
cg_pmove.trace = CG_Trace;
|
||||
cg_pmove.pointcontents = CG_PointContents;
|
||||
cg_pmove.debugLevel = cg_debugMove.integer;
|
||||
|
||||
if( cg_pmove.ps->pm_type == PM_DEAD )
|
||||
cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
|
||||
else
|
||||
cg_pmove.tracemask = MASK_PLAYERSOLID;
|
||||
|
||||
if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
|
||||
cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies
|
||||
|
||||
cg_pmove.noFootsteps = 0;
|
||||
|
||||
// save the state before the pmove so we can detect transitions
|
||||
oldPlayerState = cg.predictedPlayerState;
|
||||
|
||||
current = trap_GetCurrentCmdNumber( );
|
||||
|
||||
// if we don't have the commands right after the snapshot, we
|
||||
// can't accurately predict a current position, so just freeze at
|
||||
// the last good position we had
|
||||
cmdNum = current - CMD_BACKUP + 1;
|
||||
trap_GetUserCmd( cmdNum, &oldestCmd );
|
||||
|
||||
if( oldestCmd.serverTime > cg.snap->ps.commandTime &&
|
||||
oldestCmd.serverTime < cg.time )
|
||||
{ // special check for map_restart
|
||||
if( cg_showmiss.integer )
|
||||
CG_Printf( "exceeded PACKET_BACKUP on commands\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// get the latest command so we can know which commands are from previous map_restarts
|
||||
trap_GetUserCmd( current, &latestCmd );
|
||||
|
||||
// get the most recent information we have, even if
|
||||
// the server time is beyond our current cg.time,
|
||||
// because predicted player positions are going to
|
||||
// be ahead of everything else anyway
|
||||
if( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport )
|
||||
{
|
||||
cg.predictedPlayerState = cg.nextSnap->ps;
|
||||
cg.physicsTime = cg.nextSnap->serverTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
cg.predictedPlayerState = cg.snap->ps;
|
||||
cg.physicsTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
if( pmove_msec.integer < 8 )
|
||||
trap_Cvar_Set( "pmove_msec", "8" );
|
||||
else if( pmove_msec.integer > 33 )
|
||||
trap_Cvar_Set( "pmove_msec", "33" );
|
||||
|
||||
cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
|
||||
cg_pmove.pmove_msec = pmove_msec.integer;
|
||||
|
||||
// Like the comments described above, a player's state is entirely
|
||||
// re-predicted from the last valid snapshot every client frame, which
|
||||
// can be really, really, really slow. Every old command has to be
|
||||
// run again. For every client frame that is *not* directly after a
|
||||
// snapshot, this is unnecessary, since we have no new information.
|
||||
// For those, we'll play back the predictions from the last frame and
|
||||
// predict only the newest commands. Essentially, we'll be doing
|
||||
// an incremental predict instead of a full predict.
|
||||
//
|
||||
// If we have a new snapshot, we can compare its player state's command
|
||||
// time to the command times in the queue to find a match. If we find
|
||||
// a matching state, and the predicted version has not deviated, we can
|
||||
// use the predicted state as a base - and also do an incremental predict.
|
||||
//
|
||||
// With this method, we get incremental predicts on every client frame
|
||||
// except a frame following a new snapshot in which there was a prediction
|
||||
// error. This yeilds anywhere from a 15% to 40% performance increase,
|
||||
// depending on how much of a bottleneck the CPU is.
|
||||
if( cg_optimizePrediction.integer )
|
||||
{
|
||||
if( cg.nextFrameTeleport || cg.thisFrameTeleport )
|
||||
{
|
||||
// do a full predict
|
||||
cg.lastPredictedCommand = 0;
|
||||
cg.stateTail = cg.stateHead;
|
||||
predictCmd = current - CMD_BACKUP + 1;
|
||||
}
|
||||
// cg.physicsTime is the current snapshot's serverTime if it's the same
|
||||
// as the last one
|
||||
else if( cg.physicsTime == cg.lastServerTime )
|
||||
{
|
||||
// we have no new information, so do an incremental predict
|
||||
predictCmd = cg.lastPredictedCommand + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a new snapshot
|
||||
int i;
|
||||
int errorcode;
|
||||
qboolean error = qtrue;
|
||||
|
||||
// loop through the saved states queue
|
||||
for( i = cg.stateHead; i != cg.stateTail;
|
||||
i = ( i + 1 ) % NUM_SAVED_STATES )
|
||||
{
|
||||
// if we find a predicted state whose commandTime matches the snapshot
|
||||
// player state's commandTime
|
||||
if( cg.savedPmoveStates[ i ].commandTime !=
|
||||
cg.predictedPlayerState.commandTime )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// make sure the state differences are acceptable
|
||||
errorcode = CG_IsUnacceptableError( &cg.predictedPlayerState,
|
||||
&cg.savedPmoveStates[ i ] );
|
||||
|
||||
if( errorcode )
|
||||
{
|
||||
if( cg_showmiss.integer )
|
||||
CG_Printf("errorcode %d at %d\n", errorcode, cg.time);
|
||||
break;
|
||||
}
|
||||
|
||||
// this one is almost exact, so we'll copy it in as the starting point
|
||||
*cg_pmove.ps = cg.savedPmoveStates[ i ];
|
||||
// advance the head
|
||||
cg.stateHead = ( i + 1 ) % NUM_SAVED_STATES;
|
||||
|
||||
// set the next command to predict
|
||||
predictCmd = cg.lastPredictedCommand + 1;
|
||||
|
||||
// a saved state matched, so flag it
|
||||
error = qfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
// if no saved states matched
|
||||
if( error )
|
||||
{
|
||||
// do a full predict
|
||||
cg.lastPredictedCommand = 0;
|
||||
cg.stateTail = cg.stateHead;
|
||||
predictCmd = current - CMD_BACKUP + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// keep track of the server time of the last snapshot so we
|
||||
// know when we're starting from a new one in future calls
|
||||
cg.lastServerTime = cg.physicsTime;
|
||||
stateIndex = cg.stateHead;
|
||||
}
|
||||
|
||||
// run cmds
|
||||
moved = qfalse;
|
||||
|
||||
for( cmdNum = current - CMD_BACKUP + 1; cmdNum <= current; cmdNum++ )
|
||||
{
|
||||
// get the command
|
||||
trap_GetUserCmd( cmdNum, &cg_pmove.cmd );
|
||||
|
||||
if( cg_pmove.pmove_fixed )
|
||||
PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
|
||||
|
||||
// don't do anything if the time is before the snapshot player time
|
||||
if( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime )
|
||||
continue;
|
||||
|
||||
// don't do anything if the command was from a previous map_restart
|
||||
if( cg_pmove.cmd.serverTime > latestCmd.serverTime )
|
||||
continue;
|
||||
|
||||
// check for a prediction error from last frame
|
||||
// on a lan, this will often be the exact value
|
||||
// from the snapshot, but on a wan we will have
|
||||
// to predict several commands to get to the point
|
||||
// we want to compare
|
||||
if( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime )
|
||||
{
|
||||
vec3_t delta;
|
||||
float len;
|
||||
|
||||
if( cg.thisFrameTeleport )
|
||||
{
|
||||
// a teleport will not cause an error decay
|
||||
VectorClear( cg.predictedError );
|
||||
|
||||
if( cg_showmiss.integer )
|
||||
CG_Printf( "PredictionTeleport\n" );
|
||||
|
||||
cg.thisFrameTeleport = qfalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3_t adjusted;
|
||||
CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
|
||||
cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
|
||||
|
||||
if( cg_showmiss.integer )
|
||||
{
|
||||
if( !VectorCompare( oldPlayerState.origin, adjusted ) )
|
||||
CG_Printf("prediction error\n");
|
||||
}
|
||||
|
||||
VectorSubtract( oldPlayerState.origin, adjusted, delta );
|
||||
len = VectorLength( delta );
|
||||
|
||||
if( len > 0.1 )
|
||||
{
|
||||
if( cg_showmiss.integer )
|
||||
CG_Printf( "Prediction miss: %f\n", len );
|
||||
|
||||
if( cg_errorDecay.integer )
|
||||
{
|
||||
int t;
|
||||
float f;
|
||||
|
||||
t = cg.time - cg.predictedErrorTime;
|
||||
f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
|
||||
|
||||
if( f < 0 )
|
||||
f = 0;
|
||||
|
||||
if( f > 0 && cg_showmiss.integer )
|
||||
CG_Printf( "Double prediction decay: %f\n", f );
|
||||
|
||||
VectorScale( cg.predictedError, f, cg.predictedError );
|
||||
}
|
||||
else
|
||||
VectorClear( cg.predictedError );
|
||||
|
||||
VectorAdd( delta, cg.predictedError, cg.predictedError );
|
||||
cg.predictedErrorTime = cg.oldTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't predict gauntlet firing, which is only supposed to happen
|
||||
// when it actually inflicts damage
|
||||
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
|
||||
cg_pmove.autoWeaponHit[ i ] = qfalse;
|
||||
|
||||
if( cg_pmove.pmove_fixed )
|
||||
cg_pmove.cmd.serverTime = ( ( cg_pmove.cmd.serverTime + pmove_msec.integer - 1 ) /
|
||||
pmove_msec.integer ) * pmove_msec.integer;
|
||||
|
||||
if( !cg_optimizePrediction.integer )
|
||||
{
|
||||
Pmove( &cg_pmove );
|
||||
}
|
||||
else if( cg_optimizePrediction.integer && ( cmdNum >= predictCmd ||
|
||||
( stateIndex + 1 ) % NUM_SAVED_STATES == cg.stateHead ) )
|
||||
{
|
||||
Pmove( &cg_pmove );
|
||||
// record the last predicted command
|
||||
cg.lastPredictedCommand = cmdNum;
|
||||
|
||||
// if we haven't run out of space in the saved states queue
|
||||
if( ( stateIndex + 1 ) % NUM_SAVED_STATES != cg.stateHead )
|
||||
{
|
||||
// save the state for the false case ( of cmdNum >= predictCmd )
|
||||
// in later calls to this function
|
||||
cg.savedPmoveStates[ stateIndex ] = *cg_pmove.ps;
|
||||
stateIndex = ( stateIndex + 1 ) % NUM_SAVED_STATES;
|
||||
cg.stateTail = stateIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*cg_pmove.ps = cg.savedPmoveStates[ stateIndex ];
|
||||
stateIndex = ( stateIndex + 1 ) % NUM_SAVED_STATES;
|
||||
}
|
||||
|
||||
moved = qtrue;
|
||||
|
||||
// add push trigger movement effects
|
||||
CG_TouchTriggerPrediction( );
|
||||
|
||||
// check for predictable events that changed from previous predictions
|
||||
//CG_CheckChangedPredictableEvents(&cg.predictedPlayerState);
|
||||
}
|
||||
|
||||
// adjust for the movement of the groundentity
|
||||
CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
|
||||
cg.predictedPlayerState.groundEntityNum,
|
||||
cg.physicsTime, cg.time, cg.predictedPlayerState.origin );
|
||||
|
||||
|
||||
// fire events and other transition triggered things
|
||||
CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_ptr.c -- post timeout restoration handling
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
#define PTRC_FILE "ptrc.cfg"
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_ReadPTRCode
|
||||
|
||||
Read a PTR code from disk
|
||||
===============
|
||||
*/
|
||||
int CG_ReadPTRCode( void )
|
||||
{
|
||||
int len;
|
||||
char text[ 16 ];
|
||||
fileHandle_t f;
|
||||
|
||||
// load the file
|
||||
len = trap_FS_FOpenFile( PTRC_FILE, &f, FS_READ );
|
||||
if( len <= 0 )
|
||||
return 0;
|
||||
|
||||
// should never happen - malformed write
|
||||
if( len >= sizeof( text ) - 1 )
|
||||
return 0;
|
||||
|
||||
trap_FS_Read( text, len, f );
|
||||
text[ len ] = 0;
|
||||
trap_FS_FCloseFile( f );
|
||||
|
||||
return atoi( text );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_WritePTRCode
|
||||
|
||||
Write a PTR code to disk
|
||||
===============
|
||||
*/
|
||||
void CG_WritePTRCode( int code )
|
||||
{
|
||||
char text[ 16 ];
|
||||
fileHandle_t f;
|
||||
|
||||
Com_sprintf( text, 16, "%d", code );
|
||||
|
||||
// open file
|
||||
if( trap_FS_FOpenFile( PTRC_FILE, &f, FS_WRITE ) < 0 )
|
||||
return;
|
||||
|
||||
// write the code
|
||||
trap_FS_Write( text, strlen( text ), f );
|
||||
|
||||
trap_FS_FCloseFile( f );
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define CMD_BACKUP 64
|
||||
#define CMD_MASK (CMD_BACKUP - 1)
|
||||
// allow a lot of command backups for very fast systems
|
||||
// multiple commands may be combined into a single packet, so this
|
||||
// needs to be larger than PACKET_BACKUP
|
||||
|
||||
|
||||
#define MAX_ENTITIES_IN_SNAPSHOT 256
|
||||
|
||||
// snapshots are a view of the server at a given time
|
||||
|
||||
// Snapshots are generated at regular time intervals by the server,
|
||||
// but they may not be sent if a client's rate level is exceeded, or
|
||||
// they may be dropped by the network.
|
||||
typedef struct
|
||||
{
|
||||
int snapFlags; // SNAPFLAG_RATE_DELAYED, etc
|
||||
int ping;
|
||||
|
||||
int serverTime; // server time the message is valid for (in msec)
|
||||
|
||||
byte areamask[ MAX_MAP_AREA_BYTES ]; // portalarea visibility bits
|
||||
|
||||
playerState_t ps; // complete information about the current player at this time
|
||||
|
||||
int numEntities; // all of the entities that need to be presented
|
||||
entityState_t entities[ MAX_ENTITIES_IN_SNAPSHOT ]; // at the time of this snapshot
|
||||
|
||||
int numServerCommands; // text based server commands to execute when this
|
||||
int serverCommandSequence; // snapshot becomes current
|
||||
} snapshot_t;
|
||||
|
||||
enum
|
||||
{
|
||||
CGAME_EVENT_NONE,
|
||||
CGAME_EVENT_TEAMMENU,
|
||||
CGAME_EVENT_SCOREBOARD,
|
||||
CGAME_EVENT_EDITHUD
|
||||
};
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
functions imported from the main executable
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
#define CGAME_IMPORT_API_VERSION 4
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_PRINT,
|
||||
CG_ERROR,
|
||||
CG_MILLISECONDS,
|
||||
CG_CVAR_REGISTER,
|
||||
CG_CVAR_UPDATE,
|
||||
CG_CVAR_SET,
|
||||
CG_CVAR_VARIABLESTRINGBUFFER,
|
||||
CG_ARGC,
|
||||
CG_ARGV,
|
||||
CG_ARGS,
|
||||
CG_FS_FOPENFILE,
|
||||
CG_FS_READ,
|
||||
CG_FS_WRITE,
|
||||
CG_FS_FCLOSEFILE,
|
||||
CG_SENDCONSOLECOMMAND,
|
||||
CG_ADDCOMMAND,
|
||||
CG_SENDCLIENTCOMMAND,
|
||||
CG_UPDATESCREEN,
|
||||
CG_CM_LOADMAP,
|
||||
CG_CM_NUMINLINEMODELS,
|
||||
CG_CM_INLINEMODEL,
|
||||
CG_CM_LOADMODEL,
|
||||
CG_CM_TEMPBOXMODEL,
|
||||
CG_CM_POINTCONTENTS,
|
||||
CG_CM_TRANSFORMEDPOINTCONTENTS,
|
||||
CG_CM_BOXTRACE,
|
||||
CG_CM_TRANSFORMEDBOXTRACE,
|
||||
CG_CM_MARKFRAGMENTS,
|
||||
CG_S_STARTSOUND,
|
||||
CG_S_STARTLOCALSOUND,
|
||||
CG_S_CLEARLOOPINGSOUNDS,
|
||||
CG_S_ADDLOOPINGSOUND,
|
||||
CG_S_UPDATEENTITYPOSITION,
|
||||
CG_S_RESPATIALIZE,
|
||||
CG_S_REGISTERSOUND,
|
||||
CG_S_STARTBACKGROUNDTRACK,
|
||||
CG_R_LOADWORLDMAP,
|
||||
CG_R_REGISTERMODEL,
|
||||
CG_R_REGISTERSKIN,
|
||||
CG_R_REGISTERSHADER,
|
||||
CG_R_CLEARSCENE,
|
||||
CG_R_ADDREFENTITYTOSCENE,
|
||||
CG_R_ADDPOLYTOSCENE,
|
||||
CG_R_ADDLIGHTTOSCENE,
|
||||
CG_R_RENDERSCENE,
|
||||
CG_R_SETCOLOR,
|
||||
CG_R_DRAWSTRETCHPIC,
|
||||
CG_R_MODELBOUNDS,
|
||||
CG_R_LERPTAG,
|
||||
CG_GETGLCONFIG,
|
||||
CG_GETGAMESTATE,
|
||||
CG_GETCURRENTSNAPSHOTNUMBER,
|
||||
CG_GETSNAPSHOT,
|
||||
CG_GETSERVERCOMMAND,
|
||||
CG_GETCURRENTCMDNUMBER,
|
||||
CG_GETUSERCMD,
|
||||
CG_SETUSERCMDVALUE,
|
||||
CG_R_REGISTERSHADERNOMIP,
|
||||
CG_MEMORY_REMAINING,
|
||||
CG_R_REGISTERFONT,
|
||||
CG_KEY_ISDOWN,
|
||||
CG_KEY_GETCATCHER,
|
||||
CG_KEY_SETCATCHER,
|
||||
CG_KEY_GETKEY,
|
||||
CG_S_STOPBACKGROUNDTRACK,
|
||||
CG_REAL_TIME,
|
||||
CG_SNAPVECTOR,
|
||||
CG_REMOVECOMMAND,
|
||||
CG_R_LIGHTFORPOINT,
|
||||
CG_CIN_PLAYCINEMATIC,
|
||||
CG_CIN_STOPCINEMATIC,
|
||||
CG_CIN_RUNCINEMATIC,
|
||||
CG_CIN_DRAWCINEMATIC,
|
||||
CG_CIN_SETEXTENTS,
|
||||
CG_R_REMAP_SHADER,
|
||||
CG_S_ADDREALLOOPINGSOUND,
|
||||
CG_S_STOPLOOPINGSOUND,
|
||||
|
||||
CG_CM_TEMPCAPSULEMODEL,
|
||||
CG_CM_CAPSULETRACE,
|
||||
CG_CM_TRANSFORMEDCAPSULETRACE,
|
||||
CG_R_ADDADDITIVELIGHTTOSCENE,
|
||||
CG_GET_ENTITY_TOKEN,
|
||||
CG_R_ADDPOLYSTOSCENE,
|
||||
CG_R_INPVS,
|
||||
CG_FS_SEEK,
|
||||
CG_FS_GETFILELIST,
|
||||
CG_LITERAL_ARGS,
|
||||
CG_CM_BISPHERETRACE,
|
||||
CG_CM_TRANSFORMEDBISPHERETRACE,
|
||||
CG_GETDEMOSTATE,
|
||||
CG_GETDEMOPOS,
|
||||
CG_GETDEMONAME,
|
||||
|
||||
CG_KEY_KEYNUMTOSTRINGBUF,
|
||||
CG_KEY_GETBINDINGBUF,
|
||||
CG_KEY_SETBINDING,
|
||||
|
||||
CG_PARSE_ADD_GLOBAL_DEFINE,
|
||||
CG_PARSE_LOAD_SOURCE,
|
||||
CG_PARSE_FREE_SOURCE,
|
||||
CG_PARSE_READ_TOKEN,
|
||||
CG_PARSE_SOURCE_FILE_AND_LINE,
|
||||
|
||||
CG_MEMSET = 200,
|
||||
CG_MEMCPY,
|
||||
CG_STRNCPY,
|
||||
CG_SIN,
|
||||
CG_COS,
|
||||
CG_ATAN2,
|
||||
CG_SQRT,
|
||||
CG_FLOOR,
|
||||
CG_CEIL,
|
||||
|
||||
CG_TESTPRINTINT,
|
||||
CG_TESTPRINTFLOAT,
|
||||
CG_ACOS
|
||||
} cgameImport_t;
|
||||
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
functions exported to the main executable
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CG_INIT,
|
||||
// void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
|
||||
// called when the level loads or when the renderer is restarted
|
||||
// all media should be registered at this time
|
||||
// cgame will display loading status by calling SCR_Update, which
|
||||
// will call CG_DrawInformation during the loading process
|
||||
// reliableCommandSequence will be 0 on fresh loads, but higher for
|
||||
// demos, tourney restarts, or vid_restarts
|
||||
|
||||
CG_SHUTDOWN,
|
||||
// void (*CG_Shutdown)( void );
|
||||
// oportunity to flush and close any open files
|
||||
|
||||
CG_CONSOLE_COMMAND,
|
||||
// qboolean (*CG_ConsoleCommand)( void );
|
||||
// a console command has been issued locally that is not recognized by the
|
||||
// main game system.
|
||||
// use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the
|
||||
// command is not known to the game
|
||||
|
||||
CG_DRAW_ACTIVE_FRAME,
|
||||
// void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback );
|
||||
// Generates and draws a game scene and status information at the given time.
|
||||
// If demoPlayback is set, local movement prediction will not be enabled
|
||||
|
||||
CG_CROSSHAIR_PLAYER,
|
||||
// int (*CG_CrosshairPlayer)( void );
|
||||
|
||||
CG_LAST_ATTACKER,
|
||||
// int (*CG_LastAttacker)( void );
|
||||
|
||||
CG_KEY_EVENT,
|
||||
// void (*CG_KeyEvent)( int key, qboolean down );
|
||||
|
||||
CG_MOUSE_EVENT,
|
||||
// void (*CG_MouseEvent)( int dx, int dy );
|
||||
CG_EVENT_HANDLING,
|
||||
// void (*CG_EventHandling)(int type);
|
||||
|
||||
CG_CONSOLE_TEXT
|
||||
// void (*CG_ConsoleText)( void );
|
||||
// pass text that has been printed to the console to cgame
|
||||
// use Cmd_Argc() / Cmd_Argv() to read it
|
||||
} cgameExport_t;
|
||||
|
||||
//----------------------------------------------
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
static entityPos_t entityPositions;
|
||||
|
||||
#define HUMAN_SCANNER_UPDATE_PERIOD 700
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_UpdateEntityPositions
|
||||
|
||||
Update this client's perception of entity positions
|
||||
=============
|
||||
*/
|
||||
void CG_UpdateEntityPositions( void )
|
||||
{
|
||||
centity_t *cent = NULL;
|
||||
int i;
|
||||
|
||||
if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
|
||||
{
|
||||
if( entityPositions.lastUpdateTime + HUMAN_SCANNER_UPDATE_PERIOD > cg.time )
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( cg.refdef.vieworg, entityPositions.origin );
|
||||
VectorCopy( cg.refdefViewAngles, entityPositions.vangles );
|
||||
entityPositions.lastUpdateTime = cg.time;
|
||||
|
||||
entityPositions.numAlienBuildables = 0;
|
||||
entityPositions.numHumanBuildables = 0;
|
||||
entityPositions.numAlienClients = 0;
|
||||
entityPositions.numHumanClients = 0;
|
||||
|
||||
for( i = 0; i < cg.snap->numEntities; i++ )
|
||||
{
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
|
||||
if( cent->currentState.eType == ET_BUILDABLE )
|
||||
{
|
||||
//TA: add to list of item positions (for creep)
|
||||
if( cent->currentState.modelindex2 == BIT_ALIENS )
|
||||
{
|
||||
VectorCopy( cent->lerpOrigin, entityPositions.alienBuildablePos[
|
||||
entityPositions.numAlienBuildables ] );
|
||||
entityPositions.alienBuildableTimes[
|
||||
entityPositions.numAlienBuildables ] = cent->miscTime;
|
||||
|
||||
if( entityPositions.numAlienBuildables < MAX_GENTITIES )
|
||||
entityPositions.numAlienBuildables++;
|
||||
}
|
||||
else if( cent->currentState.modelindex2 == BIT_HUMANS )
|
||||
{
|
||||
VectorCopy( cent->lerpOrigin, entityPositions.humanBuildablePos[
|
||||
entityPositions.numHumanBuildables ] );
|
||||
|
||||
if( entityPositions.numHumanBuildables < MAX_GENTITIES )
|
||||
entityPositions.numHumanBuildables++;
|
||||
}
|
||||
}
|
||||
else if( cent->currentState.eType == ET_PLAYER )
|
||||
{
|
||||
int team = cent->currentState.powerups & 0x00FF;
|
||||
|
||||
if( team == PTE_ALIENS )
|
||||
{
|
||||
VectorCopy( cent->lerpOrigin, entityPositions.alienClientPos[
|
||||
entityPositions.numAlienClients ] );
|
||||
|
||||
if( entityPositions.numAlienClients < MAX_CLIENTS )
|
||||
entityPositions.numAlienClients++;
|
||||
}
|
||||
else if( team == PTE_HUMANS )
|
||||
{
|
||||
VectorCopy( cent->lerpOrigin, entityPositions.humanClientPos[
|
||||
entityPositions.numHumanClients ] );
|
||||
|
||||
if( entityPositions.numHumanClients < MAX_CLIENTS )
|
||||
entityPositions.numHumanClients++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define STALKWIDTH 2.0f
|
||||
#define BLIPX 16.0f
|
||||
#define BLIPY 8.0f
|
||||
#define FAR_ALPHA 0.8f
|
||||
#define NEAR_ALPHA 1.2f
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_DrawBlips
|
||||
|
||||
Draw blips and stalks for the human scanner
|
||||
=============
|
||||
*/
|
||||
static void CG_DrawBlips( rectDef_t *rect, vec3_t origin, vec4_t colour )
|
||||
{
|
||||
vec3_t drawOrigin;
|
||||
vec3_t up = { 0, 0, 1 };
|
||||
float alphaMod = 1.0f;
|
||||
float timeFractionSinceRefresh = 1.0f -
|
||||
( (float)( cg.time - entityPositions.lastUpdateTime ) /
|
||||
(float)HUMAN_SCANNER_UPDATE_PERIOD );
|
||||
vec4_t localColour;
|
||||
|
||||
Vector4Copy( colour, localColour );
|
||||
|
||||
RotatePointAroundVector( drawOrigin, up, origin, -entityPositions.vangles[ 1 ] - 90 );
|
||||
drawOrigin[ 0 ] /= ( 2 * HELMET_RANGE / rect->w );
|
||||
drawOrigin[ 1 ] /= ( 2 * HELMET_RANGE / rect->h );
|
||||
drawOrigin[ 2 ] /= ( 2 * HELMET_RANGE / rect->w );
|
||||
|
||||
alphaMod = FAR_ALPHA +
|
||||
( ( drawOrigin[ 1 ] + ( rect->h / 2.0f ) ) / rect->h ) * ( NEAR_ALPHA - FAR_ALPHA );
|
||||
|
||||
localColour[ 3 ] *= alphaMod;
|
||||
localColour[ 3 ] *= ( 0.5f + ( timeFractionSinceRefresh * 0.5f ) );
|
||||
|
||||
if( localColour[ 3 ] > 1.0f )
|
||||
localColour[ 3 ] = 1.0f;
|
||||
else if( localColour[ 3 ] < 0.0f )
|
||||
localColour[ 3 ] = 0.0f;
|
||||
|
||||
trap_R_SetColor( localColour );
|
||||
|
||||
if( drawOrigin[ 2 ] > 0 )
|
||||
CG_DrawPic( rect->x + ( rect->w / 2 ) - ( STALKWIDTH / 2 ) - drawOrigin[ 0 ],
|
||||
rect->y + ( rect->h / 2 ) + drawOrigin[ 1 ] - drawOrigin[ 2 ],
|
||||
STALKWIDTH, drawOrigin[ 2 ], cgs.media.scannerLineShader );
|
||||
else
|
||||
CG_DrawPic( rect->x + ( rect->w / 2 ) - ( STALKWIDTH / 2 ) - drawOrigin[ 0 ],
|
||||
rect->y + ( rect->h / 2 ) + drawOrigin[ 1 ],
|
||||
STALKWIDTH, -drawOrigin[ 2 ], cgs.media.scannerLineShader );
|
||||
|
||||
CG_DrawPic( rect->x + ( rect->w / 2 ) - ( BLIPX / 2 ) - drawOrigin[ 0 ],
|
||||
rect->y + ( rect->h / 2 ) - ( BLIPY / 2 ) + drawOrigin[ 1 ] - drawOrigin[ 2 ],
|
||||
BLIPX, BLIPY, cgs.media.scannerBlipShader );
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
#define BLIPX2 24.0f
|
||||
#define BLIPY2 24.0f
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_DrawDir
|
||||
|
||||
Draw dot marking the direction to an enemy
|
||||
=============
|
||||
*/
|
||||
static void CG_DrawDir( rectDef_t *rect, vec3_t origin, vec4_t colour )
|
||||
{
|
||||
vec3_t drawOrigin;
|
||||
vec3_t noZOrigin;
|
||||
vec3_t normal, antinormal, normalDiff;
|
||||
vec3_t view, noZview;
|
||||
vec3_t up = { 0.0f, 0.0f, 1.0f };
|
||||
vec3_t top = { 0.0f, -1.0f, 0.0f };
|
||||
float angle;
|
||||
playerState_t *ps = &cg.snap->ps;
|
||||
|
||||
if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
|
||||
{
|
||||
if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
|
||||
VectorSet( normal, 0.0f, 0.0f, -1.0f );
|
||||
else
|
||||
VectorCopy( ps->grapplePoint, normal );
|
||||
}
|
||||
else
|
||||
VectorSet( normal, 0.0f, 0.0f, 1.0f );
|
||||
|
||||
AngleVectors( entityPositions.vangles, view, NULL, NULL );
|
||||
|
||||
ProjectPointOnPlane( noZOrigin, origin, normal );
|
||||
ProjectPointOnPlane( noZview, view, normal );
|
||||
VectorNormalize( noZOrigin );
|
||||
VectorNormalize( noZview );
|
||||
|
||||
//calculate the angle between the images of the blip and the view
|
||||
angle = RAD2DEG( acos( DotProduct( noZOrigin, noZview ) ) );
|
||||
CrossProduct( noZOrigin, noZview, antinormal );
|
||||
VectorNormalize( antinormal );
|
||||
|
||||
//decide which way to rotate
|
||||
VectorSubtract( normal, antinormal, normalDiff );
|
||||
if( VectorLength( normalDiff ) < 1.0f )
|
||||
angle = 360.0f - angle;
|
||||
|
||||
RotatePointAroundVector( drawOrigin, up, top, angle );
|
||||
|
||||
trap_R_SetColor( colour );
|
||||
CG_DrawPic( rect->x + ( rect->w / 2 ) - ( BLIPX2 / 2 ) - drawOrigin[ 0 ] * ( rect->w / 2 ),
|
||||
rect->y + ( rect->h / 2 ) - ( BLIPY2 / 2 ) + drawOrigin[ 1 ] * ( rect->h / 2 ),
|
||||
BLIPX2, BLIPY2, cgs.media.scannerBlipShader );
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_AlienSense
|
||||
=============
|
||||
*/
|
||||
void CG_AlienSense( rectDef_t *rect )
|
||||
{
|
||||
int i;
|
||||
vec3_t origin;
|
||||
vec3_t relOrigin;
|
||||
vec4_t buildable = { 1.0f, 0.0f, 0.0f, 0.7f };
|
||||
vec4_t client = { 0.0f, 0.0f, 1.0f, 0.7f };
|
||||
|
||||
VectorCopy( entityPositions.origin, origin );
|
||||
|
||||
//draw human buildables
|
||||
for( i = 0; i < entityPositions.numHumanBuildables; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
|
||||
CG_DrawDir( rect, relOrigin, buildable );
|
||||
}
|
||||
|
||||
//draw human clients
|
||||
for( i = 0; i < entityPositions.numHumanClients; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
|
||||
CG_DrawDir( rect, relOrigin, client );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_Scanner
|
||||
=============
|
||||
*/
|
||||
void CG_Scanner( rectDef_t *rect, qhandle_t shader, vec4_t color )
|
||||
{
|
||||
int i;
|
||||
vec3_t origin;
|
||||
vec3_t relOrigin;
|
||||
vec4_t hIabove;
|
||||
vec4_t hIbelow;
|
||||
vec4_t aIabove = { 1.0f, 0.0f, 0.0f, 0.75f };
|
||||
vec4_t aIbelow = { 1.0f, 0.0f, 0.0f, 0.5f };
|
||||
|
||||
Vector4Copy( color, hIabove );
|
||||
hIabove[ 3 ] *= 1.5f;
|
||||
Vector4Copy( color, hIbelow );
|
||||
|
||||
VectorCopy( entityPositions.origin, origin );
|
||||
|
||||
//draw human buildables below scanner plane
|
||||
for( i = 0; i < entityPositions.numHumanBuildables; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, hIbelow );
|
||||
}
|
||||
|
||||
//draw alien buildables below scanner plane
|
||||
for( i = 0; i < entityPositions.numAlienBuildables; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, aIbelow );
|
||||
}
|
||||
|
||||
//draw human clients below scanner plane
|
||||
for( i = 0; i < entityPositions.numHumanClients; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, hIbelow );
|
||||
}
|
||||
|
||||
//draw alien buildables below scanner plane
|
||||
for( i = 0; i < entityPositions.numAlienClients; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, aIbelow );
|
||||
}
|
||||
|
||||
if( !cg_disableScannerPlane.integer )
|
||||
{
|
||||
trap_R_SetColor( color );
|
||||
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
//draw human buildables above scanner plane
|
||||
for( i = 0; i < entityPositions.numHumanBuildables; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, hIabove );
|
||||
}
|
||||
|
||||
//draw alien buildables above scanner plane
|
||||
for( i = 0; i < entityPositions.numAlienBuildables; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, aIabove );
|
||||
}
|
||||
|
||||
//draw human clients above scanner plane
|
||||
for( i = 0; i < entityPositions.numHumanClients; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, hIabove );
|
||||
}
|
||||
|
||||
//draw alien clients above scanner plane
|
||||
for( i = 0; i < entityPositions.numAlienClients; i++ )
|
||||
{
|
||||
VectorClear( relOrigin );
|
||||
VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin );
|
||||
|
||||
if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
|
||||
CG_DrawBlips( rect, relOrigin, aIabove );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_snapshot.c -- things that happen on snapshot transition,
|
||||
// not necessarily every single rendered frame
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ResetEntity
|
||||
==================
|
||||
*/
|
||||
static void CG_ResetEntity( centity_t *cent )
|
||||
{
|
||||
// if the previous snapshot this entity was updated in is at least
|
||||
// an event window back in time then we can reset the previous event
|
||||
if( cent->snapShotTime < cg.time - EVENT_VALID_MSEC )
|
||||
cent->previousEvent = 0;
|
||||
|
||||
cent->trailTime = cg.snap->serverTime;
|
||||
|
||||
VectorCopy( cent->currentState.origin, cent->lerpOrigin );
|
||||
VectorCopy( cent->currentState.angles, cent->lerpAngles );
|
||||
|
||||
if( cent->currentState.eType == ET_PLAYER )
|
||||
CG_ResetPlayerEntity( cent );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionEntity
|
||||
|
||||
cent->nextState is moved to cent->currentState and events are fired
|
||||
===============
|
||||
*/
|
||||
static void CG_TransitionEntity( centity_t *cent )
|
||||
{
|
||||
cent->currentState = cent->nextState;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
// reset if the entity wasn't in the last frame or was teleported
|
||||
if( !cent->interpolate )
|
||||
CG_ResetEntity( cent );
|
||||
|
||||
// clear the next state. if will be set by the next CG_SetNextSnap
|
||||
cent->interpolate = qfalse;
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_SetInitialSnapshot
|
||||
|
||||
This will only happen on the very first snapshot, or
|
||||
on tourney restarts. All other times will use
|
||||
CG_TransitionSnapshot instead.
|
||||
|
||||
FIXME: Also called by map_restart?
|
||||
==================
|
||||
*/
|
||||
void CG_SetInitialSnapshot( snapshot_t *snap )
|
||||
{
|
||||
int i;
|
||||
centity_t *cent;
|
||||
entityState_t *state;
|
||||
|
||||
cg.snap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList( );
|
||||
|
||||
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
|
||||
|
||||
// set our local weapon selection pointer to
|
||||
// what the server has indicated the current weapon is
|
||||
CG_Respawn( );
|
||||
|
||||
for( i = 0; i < cg.snap->numEntities; i++ )
|
||||
{
|
||||
state = &cg.snap->entities[ i ];
|
||||
cent = &cg_entities[ state->number ];
|
||||
|
||||
memcpy( ¢->currentState, state, sizeof( entityState_t ) );
|
||||
//cent->currentState = *state;
|
||||
cent->interpolate = qfalse;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
CG_ResetEntity( cent );
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_TransitionSnapshot
|
||||
|
||||
The transition point from snap to nextSnap has passed
|
||||
===================
|
||||
*/
|
||||
static void CG_TransitionSnapshot( void )
|
||||
{
|
||||
centity_t *cent;
|
||||
snapshot_t *oldFrame;
|
||||
int i;
|
||||
|
||||
if( !cg.snap )
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
|
||||
|
||||
if( !cg.nextSnap )
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
|
||||
|
||||
// execute any server string commands before transitioning entities
|
||||
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
|
||||
|
||||
// if we had a map_restart, set everthing with initial
|
||||
if( !cg.snap ) { } //TA: ?
|
||||
|
||||
// clear the currentValid flag for all entities in the existing snapshot
|
||||
for( i = 0; i < cg.snap->numEntities; i++ )
|
||||
{
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
cent->currentValid = qfalse;
|
||||
}
|
||||
|
||||
// move nextSnap to snap and do the transitions
|
||||
oldFrame = cg.snap;
|
||||
cg.snap = cg.nextSnap;
|
||||
|
||||
BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
|
||||
|
||||
for( i = 0; i < cg.snap->numEntities; i++ )
|
||||
{
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
CG_TransitionEntity( cent );
|
||||
|
||||
// remember time of snapshot this entity was last updated in
|
||||
cent->snapShotTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
cg.nextSnap = NULL;
|
||||
|
||||
// check for playerstate transition events
|
||||
if( oldFrame )
|
||||
{
|
||||
playerState_t *ops, *ps;
|
||||
|
||||
ops = &oldFrame->ps;
|
||||
ps = &cg.snap->ps;
|
||||
// teleporting checks are irrespective of prediction
|
||||
if( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT )
|
||||
cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
|
||||
|
||||
// if we are not doing client side movement prediction for any
|
||||
// reason, then the client events and view changes will be issued now
|
||||
if( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ||
|
||||
cg_nopredict.integer || cg_synchronousClients.integer )
|
||||
CG_TransitionPlayerState( ps, ops );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_SetNextSnap
|
||||
|
||||
A new snapshot has just been read in from the client system.
|
||||
===================
|
||||
*/
|
||||
static void CG_SetNextSnap( snapshot_t *snap )
|
||||
{
|
||||
int num;
|
||||
entityState_t *es;
|
||||
centity_t *cent;
|
||||
|
||||
cg.nextSnap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
||||
|
||||
// check for extrapolation errors
|
||||
for( num = 0 ; num < snap->numEntities ; num++ )
|
||||
{
|
||||
es = &snap->entities[ num ];
|
||||
cent = &cg_entities[ es->number ];
|
||||
|
||||
memcpy( ¢->nextState, es, sizeof( entityState_t ) );
|
||||
//cent->nextState = *es;
|
||||
|
||||
// if this frame is a teleport, or the entity wasn't in the
|
||||
// previous frame, don't interpolate
|
||||
if( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) )
|
||||
cent->interpolate = qfalse;
|
||||
else
|
||||
cent->interpolate = qtrue;
|
||||
}
|
||||
|
||||
// if the next frame is a teleport for the playerstate, we
|
||||
// can't interpolate during demos
|
||||
if( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) )
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
else
|
||||
cg.nextFrameTeleport = qfalse;
|
||||
|
||||
// if changing follow mode, don't interpolate
|
||||
if( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum )
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
|
||||
// if changing server restarts, don't interpolate
|
||||
if( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT )
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList( );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_ReadNextSnapshot
|
||||
|
||||
This is the only place new snapshots are requested
|
||||
This may increment cgs.processedSnapshotNum multiple
|
||||
times if the client system fails to return a
|
||||
valid snapshot.
|
||||
========================
|
||||
*/
|
||||
static snapshot_t *CG_ReadNextSnapshot( void )
|
||||
{
|
||||
qboolean r;
|
||||
snapshot_t *dest;
|
||||
|
||||
if( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 )
|
||||
{
|
||||
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
|
||||
cg.latestSnapshotNum, cgs.processedSnapshotNum );
|
||||
}
|
||||
|
||||
while( cgs.processedSnapshotNum < cg.latestSnapshotNum )
|
||||
{
|
||||
// decide which of the two slots to load it into
|
||||
if( cg.snap == &cg.activeSnapshots[ 0 ] )
|
||||
dest = &cg.activeSnapshots[ 1 ];
|
||||
else
|
||||
dest = &cg.activeSnapshots[ 0 ];
|
||||
|
||||
// try to read the snapshot from the client system
|
||||
cgs.processedSnapshotNum++;
|
||||
r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
|
||||
|
||||
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
|
||||
if( cg.snap && r && dest->serverTime == cg.snap->serverTime )
|
||||
{
|
||||
//continue;
|
||||
}
|
||||
|
||||
// if it succeeded, return
|
||||
if( r )
|
||||
{
|
||||
CG_AddLagometerSnapshotInfo( dest );
|
||||
return dest;
|
||||
}
|
||||
|
||||
// a GetSnapshot will return failure if the snapshot
|
||||
// never arrived, or is so old that its entities
|
||||
// have been shoved off the end of the circular
|
||||
// buffer in the client system.
|
||||
|
||||
// record as a dropped packet
|
||||
CG_AddLagometerSnapshotInfo( NULL );
|
||||
|
||||
// If there are additional snapshots, continue trying to
|
||||
// read them.
|
||||
}
|
||||
|
||||
// nothing left to read
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
CG_ProcessSnapshots
|
||||
|
||||
We are trying to set up a renderable view, so determine
|
||||
what the simulated time is, and try to get snapshots
|
||||
both before and after that time if available.
|
||||
|
||||
If we don't have a valid cg.snap after exiting this function,
|
||||
then a 3D game view cannot be rendered. This should only happen
|
||||
right after the initial connection. After cg.snap has been valid
|
||||
once, it will never turn invalid.
|
||||
|
||||
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
|
||||
hasn't arrived yet (it becomes an extrapolating situation instead
|
||||
of an interpolating one)
|
||||
|
||||
============
|
||||
*/
|
||||
void CG_ProcessSnapshots( void )
|
||||
{
|
||||
snapshot_t *snap;
|
||||
int n;
|
||||
|
||||
// see what the latest snapshot the client system has is
|
||||
trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
|
||||
|
||||
if( n != cg.latestSnapshotNum )
|
||||
{
|
||||
if( n < cg.latestSnapshotNum )
|
||||
{
|
||||
// this should never happen
|
||||
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
|
||||
}
|
||||
|
||||
cg.latestSnapshotNum = n;
|
||||
}
|
||||
|
||||
// If we have yet to receive a snapshot, check for it.
|
||||
// Once we have gotten the first snapshot, cg.snap will
|
||||
// always have valid data for the rest of the game
|
||||
while( !cg.snap )
|
||||
{
|
||||
snap = CG_ReadNextSnapshot( );
|
||||
|
||||
if( !snap )
|
||||
{
|
||||
// we can't continue until we get a snapshot
|
||||
return;
|
||||
}
|
||||
|
||||
// set our weapon selection to what
|
||||
// the playerstate is currently using
|
||||
if( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) )
|
||||
CG_SetInitialSnapshot( snap );
|
||||
}
|
||||
|
||||
// loop until we either have a valid nextSnap with a serverTime
|
||||
// greater than cg.time to interpolate towards, or we run
|
||||
// out of available snapshots
|
||||
do
|
||||
{
|
||||
// if we don't have a nextframe, try and read a new one in
|
||||
if( !cg.nextSnap )
|
||||
{
|
||||
snap = CG_ReadNextSnapshot( );
|
||||
|
||||
// if we still don't have a nextframe, we will just have to
|
||||
// extrapolate
|
||||
if( !snap )
|
||||
break;
|
||||
|
||||
CG_SetNextSnap( snap );
|
||||
|
||||
// if time went backwards, we have a level restart
|
||||
if( cg.nextSnap->serverTime < cg.snap->serverTime )
|
||||
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
|
||||
}
|
||||
|
||||
// if our time is < nextFrame's, we have a nice interpolating state
|
||||
if( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime )
|
||||
break;
|
||||
|
||||
// we have passed the transition from nextFrame to frame
|
||||
CG_TransitionSnapshot( );
|
||||
} while( 1 );
|
||||
|
||||
// assert our valid conditions upon exiting
|
||||
if( cg.snap == NULL )
|
||||
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
|
||||
|
||||
if( cg.time < cg.snap->serverTime )
|
||||
{
|
||||
// this can happen right after a vid_restart
|
||||
cg.time = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
if( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time )
|
||||
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
code
|
||||
|
||||
equ trap_Print -1
|
||||
equ trap_Error -2
|
||||
equ trap_Milliseconds -3
|
||||
equ trap_Cvar_Register -4
|
||||
equ trap_Cvar_Update -5
|
||||
equ trap_Cvar_Set -6
|
||||
equ trap_Cvar_VariableStringBuffer -7
|
||||
equ trap_Argc -8
|
||||
equ trap_Argv -9
|
||||
equ trap_Args -10
|
||||
equ trap_FS_FOpenFile -11
|
||||
equ trap_FS_Read -12
|
||||
equ trap_FS_Write -13
|
||||
equ trap_FS_FCloseFile -14
|
||||
equ trap_SendConsoleCommand -15
|
||||
equ trap_AddCommand -16
|
||||
equ trap_SendClientCommand -17
|
||||
equ trap_UpdateScreen -18
|
||||
equ trap_CM_LoadMap -19
|
||||
equ trap_CM_NumInlineModels -20
|
||||
equ trap_CM_InlineModel -21
|
||||
equ trap_CM_LoadModel -22
|
||||
equ trap_CM_TempBoxModel -23
|
||||
equ trap_CM_PointContents -24
|
||||
equ trap_CM_TransformedPointContents -25
|
||||
equ trap_CM_BoxTrace -26
|
||||
equ trap_CM_TransformedBoxTrace -27
|
||||
equ trap_CM_MarkFragments -28
|
||||
equ trap_S_StartSound -29
|
||||
equ trap_S_StartLocalSound -30
|
||||
equ trap_S_ClearLoopingSounds -31
|
||||
equ trap_S_AddLoopingSound -32
|
||||
equ trap_S_UpdateEntityPosition -33
|
||||
equ trap_S_Respatialize -34
|
||||
equ trap_S_RegisterSound -35
|
||||
equ trap_S_StartBackgroundTrack -36
|
||||
equ trap_R_LoadWorldMap -37
|
||||
equ trap_R_RegisterModel -38
|
||||
equ trap_R_RegisterSkin -39
|
||||
equ trap_R_RegisterShader -40
|
||||
equ trap_R_ClearScene -41
|
||||
equ trap_R_AddRefEntityToScene -42
|
||||
equ trap_R_AddPolyToScene -43
|
||||
equ trap_R_AddLightToScene -44
|
||||
equ trap_R_RenderScene -45
|
||||
equ trap_R_SetColor -46
|
||||
equ trap_R_DrawStretchPic -47
|
||||
equ trap_R_ModelBounds -48
|
||||
equ trap_R_LerpTag -49
|
||||
equ trap_GetGlconfig -50
|
||||
equ trap_GetGameState -51
|
||||
equ trap_GetCurrentSnapshotNumber -52
|
||||
equ trap_GetSnapshot -53
|
||||
equ trap_GetServerCommand -54
|
||||
equ trap_GetCurrentCmdNumber -55
|
||||
equ trap_GetUserCmd -56
|
||||
equ trap_SetUserCmdValue -57
|
||||
equ trap_R_RegisterShaderNoMip -58
|
||||
equ trap_MemoryRemaining -59
|
||||
equ trap_R_RegisterFont -60
|
||||
equ trap_Key_IsDown -61
|
||||
equ trap_Key_GetCatcher -62
|
||||
equ trap_Key_SetCatcher -63
|
||||
equ trap_Key_GetKey -64
|
||||
equ trap_S_StopBackgroundTrack -65
|
||||
equ trap_RealTime -66
|
||||
equ trap_SnapVector -67
|
||||
equ trap_RemoveCommand -68
|
||||
equ trap_R_LightForPoint -69
|
||||
equ trap_CIN_PlayCinematic -70
|
||||
equ trap_CIN_StopCinematic -71
|
||||
equ trap_CIN_RunCinematic -72
|
||||
equ trap_CIN_DrawCinematic -73
|
||||
equ trap_CIN_SetExtents -74
|
||||
equ trap_R_RemapShader -75
|
||||
equ trap_S_AddRealLoopingSound -76
|
||||
equ trap_S_StopLoopingSound -77
|
||||
equ trap_CM_TempCapsuleModel -78
|
||||
equ trap_CM_CapsuleTrace -79
|
||||
equ trap_CM_TransformedCapsuleTrace -80
|
||||
equ trap_R_AddAdditiveLightToScene -81
|
||||
equ trap_GetEntityToken -82
|
||||
equ trap_R_AddPolysToScene -83
|
||||
equ trap_R_inPVS -84
|
||||
equ trap_FS_Seek -85
|
||||
equ trap_FS_GetFileList -86
|
||||
equ trap_LiteralArgs -87
|
||||
equ trap_CM_BiSphereTrace -88
|
||||
equ trap_CM_TransformedBiSphereTrace -89
|
||||
equ trap_GetDemoState -90
|
||||
equ trap_GetDemoPos -91
|
||||
equ trap_GetDemoName -92
|
||||
equ trap_Key_KeynumToStringBuf -93
|
||||
equ trap_Key_GetBindingBuf -94
|
||||
equ trap_Key_SetBinding -95
|
||||
|
||||
equ trap_Parse_AddGlobalDefine -96
|
||||
equ trap_Parse_LoadSource -97
|
||||
equ trap_Parse_FreeSource -98
|
||||
equ trap_Parse_ReadToken -99
|
||||
equ trap_Parse_SourceFileAndLine -100
|
||||
|
||||
equ memset -201
|
||||
equ memcpy -202
|
||||
equ strncpy -203
|
||||
equ sin -204
|
||||
equ cos -205
|
||||
equ atan2 -206
|
||||
equ sqrt -207
|
||||
equ floor -208
|
||||
equ ceil -209
|
||||
equ testPrintInt -210
|
||||
equ testPrintFloat -211
|
||||
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_syscalls.c -- this file is only included when building a dll
|
||||
// cg_syscalls.asm is included instead when building a qvm
|
||||
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
|
||||
|
||||
|
||||
void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) )
|
||||
{
|
||||
syscall = syscallptr;
|
||||
}
|
||||
|
||||
|
||||
int PASSFLOAT( float x )
|
||||
{
|
||||
float floatTemp;
|
||||
floatTemp = x;
|
||||
return *(int *)&floatTemp;
|
||||
}
|
||||
|
||||
void trap_Print( const char *fmt )
|
||||
{
|
||||
syscall( CG_PRINT, fmt );
|
||||
}
|
||||
|
||||
void trap_Error( const char *fmt )
|
||||
{
|
||||
syscall( CG_ERROR, fmt );
|
||||
}
|
||||
|
||||
int trap_Milliseconds( void )
|
||||
{
|
||||
return syscall( CG_MILLISECONDS );
|
||||
}
|
||||
|
||||
void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags )
|
||||
{
|
||||
syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags );
|
||||
}
|
||||
|
||||
void trap_Cvar_Update( vmCvar_t *vmCvar )
|
||||
{
|
||||
syscall( CG_CVAR_UPDATE, vmCvar );
|
||||
}
|
||||
|
||||
void trap_Cvar_Set( const char *var_name, const char *value )
|
||||
{
|
||||
syscall( CG_CVAR_SET, var_name, value );
|
||||
}
|
||||
|
||||
void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize )
|
||||
{
|
||||
syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize );
|
||||
}
|
||||
|
||||
int trap_Argc( void )
|
||||
{
|
||||
return syscall( CG_ARGC );
|
||||
}
|
||||
|
||||
void trap_Argv( int n, char *buffer, int bufferLength )
|
||||
{
|
||||
syscall( CG_ARGV, n, buffer, bufferLength );
|
||||
}
|
||||
|
||||
void trap_Args( char *buffer, int bufferLength )
|
||||
{
|
||||
syscall( CG_ARGS, buffer, bufferLength );
|
||||
}
|
||||
|
||||
void trap_LiteralArgs( char *buffer, int bufferLength )
|
||||
{
|
||||
syscall( CG_LITERAL_ARGS, buffer, bufferLength );
|
||||
}
|
||||
|
||||
int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode )
|
||||
{
|
||||
return syscall( CG_FS_FOPENFILE, qpath, f, mode );
|
||||
}
|
||||
|
||||
void trap_FS_Read( void *buffer, int len, fileHandle_t f )
|
||||
{
|
||||
syscall( CG_FS_READ, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_Write( const void *buffer, int len, fileHandle_t f )
|
||||
{
|
||||
syscall( CG_FS_WRITE, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_FCloseFile( fileHandle_t f )
|
||||
{
|
||||
syscall( CG_FS_FCLOSEFILE, f );
|
||||
}
|
||||
|
||||
void trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin )
|
||||
{
|
||||
syscall( CG_FS_SEEK, f, offset, origin );
|
||||
}
|
||||
|
||||
int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize )
|
||||
{
|
||||
return syscall( CG_FS_GETFILELIST, path, extension, listbuf, bufsize );
|
||||
}
|
||||
|
||||
void trap_SendConsoleCommand( const char *text )
|
||||
{
|
||||
syscall( CG_SENDCONSOLECOMMAND, text );
|
||||
}
|
||||
|
||||
void trap_AddCommand( const char *cmdName )
|
||||
{
|
||||
syscall( CG_ADDCOMMAND, cmdName );
|
||||
}
|
||||
|
||||
void trap_RemoveCommand( const char *cmdName )
|
||||
{
|
||||
syscall( CG_REMOVECOMMAND, cmdName );
|
||||
}
|
||||
|
||||
void trap_SendClientCommand( const char *s )
|
||||
{
|
||||
syscall( CG_SENDCLIENTCOMMAND, s );
|
||||
}
|
||||
|
||||
void trap_UpdateScreen( void )
|
||||
{
|
||||
syscall( CG_UPDATESCREEN );
|
||||
}
|
||||
|
||||
void trap_CM_LoadMap( const char *mapname )
|
||||
{
|
||||
syscall( CG_CM_LOADMAP, mapname );
|
||||
}
|
||||
|
||||
int trap_CM_NumInlineModels( void )
|
||||
{
|
||||
return syscall( CG_CM_NUMINLINEMODELS );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_InlineModel( int index )
|
||||
{
|
||||
return syscall( CG_CM_INLINEMODEL, index );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs )
|
||||
{
|
||||
return syscall( CG_CM_TEMPBOXMODEL, mins, maxs );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs )
|
||||
{
|
||||
return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs );
|
||||
}
|
||||
|
||||
int trap_CM_PointContents( const vec3_t p, clipHandle_t model )
|
||||
{
|
||||
return syscall( CG_CM_POINTCONTENTS, p, model );
|
||||
}
|
||||
|
||||
int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin,
|
||||
const vec3_t angles )
|
||||
{
|
||||
return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles );
|
||||
}
|
||||
|
||||
void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask )
|
||||
{
|
||||
syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask );
|
||||
}
|
||||
|
||||
void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask )
|
||||
{
|
||||
syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask );
|
||||
}
|
||||
|
||||
void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask,
|
||||
const vec3_t origin, const vec3_t angles )
|
||||
{
|
||||
syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
|
||||
}
|
||||
|
||||
void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask,
|
||||
const vec3_t origin, const vec3_t angles )
|
||||
{
|
||||
syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
|
||||
}
|
||||
|
||||
void trap_CM_BiSphereTrace( trace_t *results, const vec3_t start,
|
||||
const vec3_t end, float startRad, float endRad,
|
||||
clipHandle_t model, int mask )
|
||||
{
|
||||
syscall( CG_CM_BISPHERETRACE, results, start, end,
|
||||
PASSFLOAT( startRad ), PASSFLOAT( endRad ), model, mask );
|
||||
}
|
||||
|
||||
void trap_CM_TransformedBiSphereTrace( trace_t *results, const vec3_t start,
|
||||
const vec3_t end, float startRad, float endRad,
|
||||
clipHandle_t model, int mask,
|
||||
const vec3_t origin )
|
||||
{
|
||||
syscall( CG_CM_TRANSFORMEDBISPHERETRACE, results, start, end, PASSFLOAT( startRad ),
|
||||
PASSFLOAT( endRad ), model, mask, origin );
|
||||
}
|
||||
|
||||
int trap_CM_MarkFragments( int numPoints, const vec3_t *points,
|
||||
const vec3_t projection,
|
||||
int maxPoints, vec3_t pointBuffer,
|
||||
int maxFragments, markFragment_t *fragmentBuffer )
|
||||
{
|
||||
return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints,
|
||||
pointBuffer, maxFragments, fragmentBuffer );
|
||||
}
|
||||
|
||||
void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx )
|
||||
{
|
||||
syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx );
|
||||
}
|
||||
|
||||
void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum )
|
||||
{
|
||||
syscall( CG_S_STARTLOCALSOUND, sfx, channelNum );
|
||||
}
|
||||
|
||||
void trap_S_ClearLoopingSounds( qboolean killall )
|
||||
{
|
||||
syscall( CG_S_CLEARLOOPINGSOUNDS, killall );
|
||||
}
|
||||
|
||||
void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
|
||||
{
|
||||
syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx );
|
||||
}
|
||||
|
||||
void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
|
||||
{
|
||||
syscall( CG_S_ADDREALLOOPINGSOUND, entityNum, origin, velocity, sfx );
|
||||
}
|
||||
|
||||
void trap_S_StopLoopingSound( int entityNum )
|
||||
{
|
||||
syscall( CG_S_STOPLOOPINGSOUND, entityNum );
|
||||
}
|
||||
|
||||
void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin )
|
||||
{
|
||||
syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin );
|
||||
}
|
||||
|
||||
void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
|
||||
{
|
||||
syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater );
|
||||
}
|
||||
|
||||
sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed )
|
||||
{
|
||||
return syscall( CG_S_REGISTERSOUND, sample, compressed );
|
||||
}
|
||||
|
||||
void trap_S_StartBackgroundTrack( const char *intro, const char *loop )
|
||||
{
|
||||
syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop );
|
||||
}
|
||||
|
||||
void trap_R_LoadWorldMap( const char *mapname )
|
||||
{
|
||||
syscall( CG_R_LOADWORLDMAP, mapname );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterModel( const char *name )
|
||||
{
|
||||
return syscall( CG_R_REGISTERMODEL, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterSkin( const char *name )
|
||||
{
|
||||
return syscall( CG_R_REGISTERSKIN, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterShader( const char *name )
|
||||
{
|
||||
return syscall( CG_R_REGISTERSHADER, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterShaderNoMip( const char *name )
|
||||
{
|
||||
return syscall( CG_R_REGISTERSHADERNOMIP, name );
|
||||
}
|
||||
|
||||
void trap_R_RegisterFont( const char *fontName, int pointSize, fontInfo_t *font )
|
||||
{
|
||||
syscall(CG_R_REGISTERFONT, fontName, pointSize, font );
|
||||
}
|
||||
|
||||
void trap_R_ClearScene( void )
|
||||
{
|
||||
syscall( CG_R_CLEARSCENE );
|
||||
}
|
||||
|
||||
void trap_R_AddRefEntityToScene( const refEntity_t *re )
|
||||
{
|
||||
syscall( CG_R_ADDREFENTITYTOSCENE, re );
|
||||
}
|
||||
|
||||
void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts )
|
||||
{
|
||||
syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
|
||||
}
|
||||
|
||||
void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num )
|
||||
{
|
||||
syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num );
|
||||
}
|
||||
|
||||
int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir )
|
||||
{
|
||||
return syscall( CG_R_LIGHTFORPOINT, point, ambientLight, directedLight, lightDir );
|
||||
}
|
||||
|
||||
void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b )
|
||||
{
|
||||
syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
|
||||
}
|
||||
|
||||
void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b )
|
||||
{
|
||||
syscall( CG_R_ADDADDITIVELIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
|
||||
}
|
||||
|
||||
void trap_R_RenderScene( const refdef_t *fd )
|
||||
{
|
||||
syscall( CG_R_RENDERSCENE, fd );
|
||||
}
|
||||
|
||||
void trap_R_SetColor( const float *rgba )
|
||||
{
|
||||
syscall( CG_R_SETCOLOR, rgba );
|
||||
}
|
||||
|
||||
void trap_R_DrawStretchPic( float x, float y, float w, float h,
|
||||
float s1, float t1, float s2, float t2, qhandle_t hShader )
|
||||
{
|
||||
syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h),
|
||||
PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader );
|
||||
}
|
||||
|
||||
void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) {
|
||||
syscall( CG_R_MODELBOUNDS, model, mins, maxs );
|
||||
}
|
||||
|
||||
int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame,
|
||||
float frac, const char *tagName )
|
||||
{
|
||||
return syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName );
|
||||
}
|
||||
|
||||
void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset )
|
||||
{
|
||||
syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset );
|
||||
}
|
||||
|
||||
void trap_GetGlconfig( glconfig_t *glconfig )
|
||||
{
|
||||
syscall( CG_GETGLCONFIG, glconfig );
|
||||
}
|
||||
|
||||
void trap_GetGameState( gameState_t *gamestate )
|
||||
{
|
||||
syscall( CG_GETGAMESTATE, gamestate );
|
||||
}
|
||||
|
||||
void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime )
|
||||
{
|
||||
syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime );
|
||||
}
|
||||
|
||||
qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot )
|
||||
{
|
||||
return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot );
|
||||
}
|
||||
|
||||
qboolean trap_GetServerCommand( int serverCommandNumber )
|
||||
{
|
||||
return syscall( CG_GETSERVERCOMMAND, serverCommandNumber );
|
||||
}
|
||||
|
||||
int trap_GetCurrentCmdNumber( void )
|
||||
{
|
||||
return syscall( CG_GETCURRENTCMDNUMBER );
|
||||
}
|
||||
|
||||
qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd )
|
||||
{
|
||||
return syscall( CG_GETUSERCMD, cmdNumber, ucmd );
|
||||
}
|
||||
|
||||
void trap_SetUserCmdValue( int stateValue, float sensitivityScale )
|
||||
{
|
||||
syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT( sensitivityScale ) );
|
||||
}
|
||||
|
||||
void testPrintInt( char *string, int i )
|
||||
{
|
||||
syscall( CG_TESTPRINTINT, string, i );
|
||||
}
|
||||
|
||||
void testPrintFloat( char *string, float f )
|
||||
{
|
||||
syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT(f) );
|
||||
}
|
||||
|
||||
int trap_MemoryRemaining( void )
|
||||
{
|
||||
return syscall( CG_MEMORY_REMAINING );
|
||||
}
|
||||
|
||||
qboolean trap_Key_IsDown( int keynum )
|
||||
{
|
||||
return syscall( CG_KEY_ISDOWN, keynum );
|
||||
}
|
||||
|
||||
int trap_Key_GetCatcher( void )
|
||||
{
|
||||
return syscall( CG_KEY_GETCATCHER );
|
||||
}
|
||||
|
||||
void trap_Key_SetCatcher( int catcher )
|
||||
{
|
||||
syscall( CG_KEY_SETCATCHER, catcher );
|
||||
}
|
||||
|
||||
int trap_Key_GetKey( const char *binding )
|
||||
{
|
||||
return syscall( CG_KEY_GETKEY, binding );
|
||||
}
|
||||
|
||||
int trap_Parse_AddGlobalDefine( char *define )
|
||||
{
|
||||
return syscall( CG_PARSE_ADD_GLOBAL_DEFINE, define );
|
||||
}
|
||||
|
||||
int trap_Parse_LoadSource( const char *filename )
|
||||
{
|
||||
return syscall( CG_PARSE_LOAD_SOURCE, filename );
|
||||
}
|
||||
|
||||
int trap_Parse_FreeSource( int handle )
|
||||
{
|
||||
return syscall( CG_PARSE_FREE_SOURCE, handle );
|
||||
}
|
||||
|
||||
int trap_Parse_ReadToken( int handle, pc_token_t *pc_token )
|
||||
{
|
||||
return syscall( CG_PARSE_READ_TOKEN, handle, pc_token );
|
||||
}
|
||||
|
||||
int trap_Parse_SourceFileAndLine( int handle, char *filename, int *line )
|
||||
{
|
||||
return syscall( CG_PARSE_SOURCE_FILE_AND_LINE, handle, filename, line );
|
||||
}
|
||||
|
||||
void trap_S_StopBackgroundTrack( void )
|
||||
{
|
||||
syscall( CG_S_STOPBACKGROUNDTRACK );
|
||||
}
|
||||
|
||||
int trap_RealTime(qtime_t *qtime)
|
||||
{
|
||||
return syscall( CG_REAL_TIME, qtime );
|
||||
}
|
||||
|
||||
void trap_SnapVector( float *v )
|
||||
{
|
||||
syscall( CG_SNAPVECTOR, v );
|
||||
}
|
||||
|
||||
// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate)
|
||||
int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits )
|
||||
{
|
||||
return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits);
|
||||
}
|
||||
|
||||
// stops playing the cinematic and ends it. should always return FMV_EOF
|
||||
// cinematics must be stopped in reverse order of when they are started
|
||||
e_status trap_CIN_StopCinematic( int handle )
|
||||
{
|
||||
return syscall(CG_CIN_STOPCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached.
|
||||
e_status trap_CIN_RunCinematic( int handle )
|
||||
{
|
||||
return syscall(CG_CIN_RUNCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// draws the current frame
|
||||
void trap_CIN_DrawCinematic( int handle )
|
||||
{
|
||||
syscall(CG_CIN_DRAWCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// allows you to resize the animation dynamically
|
||||
void trap_CIN_SetExtents( int handle, int x, int y, int w, int h )
|
||||
{
|
||||
syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h);
|
||||
}
|
||||
|
||||
int trap_GetDemoState( void )
|
||||
{
|
||||
return syscall( CG_GETDEMOSTATE );
|
||||
}
|
||||
|
||||
int trap_GetDemoPos( void )
|
||||
{
|
||||
return syscall( CG_GETDEMOPOS );
|
||||
}
|
||||
|
||||
void trap_GetDemoName( char *buffer, int size )
|
||||
{
|
||||
syscall( CG_GETDEMONAME, buffer, size );
|
||||
}
|
||||
|
||||
void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) {
|
||||
syscall( CG_KEY_KEYNUMTOSTRINGBUF, keynum, buf, buflen );
|
||||
}
|
||||
|
||||
void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ) {
|
||||
syscall( CG_KEY_GETBINDINGBUF, keynum, buf, buflen );
|
||||
}
|
||||
|
||||
void trap_Key_SetBinding( int keynum, const char *binding ) {
|
||||
syscall( CG_KEY_SETBINDING, keynum, binding );
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Tremulous is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// cg_tutorial.c -- the tutorial system
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *command;
|
||||
char *humanName;
|
||||
keyNum_t keys[ 2 ];
|
||||
} bind_t;
|
||||
|
||||
static bind_t bindings[ ] =
|
||||
{
|
||||
{ "+button2", "Activate Upgrade", { -1, -1 } },
|
||||
{ "+speed", "Run/Walk", { -1, -1 } },
|
||||
{ "boost", "Sprint", { -1, -1 } },
|
||||
{ "+moveup", "Jump", { -1, -1 } },
|
||||
{ "+movedown", "Crouch", { -1, -1 } },
|
||||
{ "+attack", "Primary Attack", { -1, -1 } },
|
||||
{ "+button5", "Secondary Attack", { -1, -1 } },
|
||||
{ "reload", "Reload", { -1, -1 } },
|
||||
{ "buy ammo", "Buy Ammo", { -1, -1 } },
|
||||
{ "itemact medkit", "Use Medkit", { -1, -1 } },
|
||||
{ "+button7", "Use Structure/Evolve", { -1, -1 } },
|
||||
{ "deconstruct", "Deconstruct Structure", { -1, -1 } },
|
||||
{ "weapprev", "Previous Upgrade", { -1, -1 } },
|
||||
{ "weapnext", "Next Upgrade", { -1, -1 } }
|
||||
};
|
||||
|
||||
static const int numBindings = sizeof( bindings ) / sizeof( bind_t );
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_GetBindings
|
||||
=================
|
||||
*/
|
||||
static void CG_GetBindings( void )
|
||||
{
|
||||
int i, j, numKeys;
|
||||
char buffer[ MAX_STRING_CHARS ];
|
||||
|
||||
for( i = 0; i < numBindings; i++ )
|
||||
{
|
||||
bindings[ i ].keys[ 0 ] = bindings[ i ].keys[ 1 ] = K_NONE;
|
||||
numKeys = 0;
|
||||
|
||||
for( j = 0; j < K_LAST_KEY; j++ )
|
||||
{
|
||||
trap_Key_GetBindingBuf( j, buffer, MAX_STRING_CHARS );
|
||||
|
||||
if( buffer[ 0 ] == 0 )
|
||||
continue;
|
||||
|
||||
if( !Q_stricmp( buffer, bindings[ i ].command ) )
|
||||
{
|
||||
bindings[ i ].keys[ numKeys++ ] = j;
|
||||
|
||||
if( numKeys > 1 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_KeyNameForCommand
|
||||
===============
|
||||
*/
|
||||
static const char *CG_KeyNameForCommand( const char *command )
|
||||
{
|
||||
int i, j;
|
||||
static char buffer[ MAX_STRING_CHARS ];
|
||||
int firstKeyLength;
|
||||
|
||||
buffer[ 0 ] = '\0';
|
||||
|
||||
for( i = 0; i < numBindings; i++ )
|
||||
{
|
||||
if( !Q_stricmp( command, bindings[ i ].command ) )
|
||||
{
|
||||
if( bindings[ i ].keys[ 0 ] != K_NONE )
|
||||
{
|
||||
trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 0 ],
|
||||
buffer, MAX_STRING_CHARS );
|
||||
firstKeyLength = strlen( buffer );
|
||||
|
||||
for( j = 0; j < firstKeyLength; j++ )
|
||||
buffer[ j ] = toupper( buffer[ j ] );
|
||||
|
||||
if( bindings[ i ].keys[ 1 ] != K_NONE )
|
||||
{
|
||||
Q_strcat( buffer, MAX_STRING_CHARS, " or " );
|
||||
trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 1 ],
|
||||
buffer + strlen( buffer ), MAX_STRING_CHARS - strlen( buffer ) );
|
||||
|
||||
for( j = firstKeyLength + 4; j < strlen( buffer ); j++ )
|
||||
buffer[ j ] = toupper( buffer[ j ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpyz( buffer, va( "\"%s\"", bindings[ i ].humanName ),
|
||||
MAX_STRING_CHARS );
|
||||
Q_strcat( buffer, MAX_STRING_CHARS, " (unbound)" );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
#define MAX_TUTORIAL_TEXT 4096
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_BuildableInRange
|
||||
===============
|
||||
*/
|
||||
static entityState_t *CG_BuildableInRange( playerState_t *ps, float *healthFraction )
|
||||
{
|
||||
vec3_t view, point;
|
||||
trace_t trace;
|
||||
entityState_t *es;
|
||||
int health;
|
||||
|
||||
AngleVectors( cg.refdefViewAngles, view, NULL, NULL );
|
||||
VectorMA( cg.refdef.vieworg, 64, view, point );
|
||||
CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL,
|
||||
point, ps->clientNum, MASK_SHOT );
|
||||
|
||||
es = &cg_entities[ trace.entityNum ].currentState;
|
||||
|
||||
if( healthFraction )
|
||||
{
|
||||
health = es->generic1 & B_HEALTH_MASK;
|
||||
*healthFraction = (float)health / B_HEALTH_MASK;
|
||||
}
|
||||
|
||||
if( es->eType == ET_BUILDABLE &&
|
||||
ps->stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
|
||||
return es;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienBuilderText
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienBuilderText( char *text, playerState_t *ps )
|
||||
{
|
||||
buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
|
||||
entityState_t *es;
|
||||
|
||||
if( buildable > BA_NONE )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to place the %s\n",
|
||||
CG_KeyNameForCommand( "+attack" ),
|
||||
BG_FindHumanNameForBuildable( buildable ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to cancel placing the %s\n",
|
||||
CG_KeyNameForCommand( "+button5" ),
|
||||
BG_FindHumanNameForBuildable( buildable ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to build a structure\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( ( es = CG_BuildableInRange( ps, NULL ) ) )
|
||||
{
|
||||
if( cgs.markDeconstruct )
|
||||
{
|
||||
if( es->generic1 & B_MARKED_TOGGLEBIT )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to unmark this structure\n",
|
||||
CG_KeyNameForCommand( "deconstruct" ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to mark this structure\n",
|
||||
CG_KeyNameForCommand( "deconstruct" ) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to destroy this structure\n",
|
||||
CG_KeyNameForCommand( "deconstruct" ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG )
|
||||
{
|
||||
if( ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) == BA_NONE )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to swipe\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to launch a projectile\n",
|
||||
CG_KeyNameForCommand( "+button2" ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to walk on walls\n",
|
||||
CG_KeyNameForCommand( "+movedown" ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienLevel0Text
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienLevel0Text( char *text, playerState_t *ps )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
"Touch a human to damage it\n" );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to walk on walls\n",
|
||||
CG_KeyNameForCommand( "+movedown" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienLevel1Text
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienLevel1Text( char *text, playerState_t *ps )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
"Touch a human to grab it\n" );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to swipe\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL1_UPG )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to spray poisonous gas\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to walk on walls\n",
|
||||
CG_KeyNameForCommand( "+movedown" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienLevel2Text
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienLevel2Text( char *text, playerState_t *ps )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to bite\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL2_UPG )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to invoke an electrical attack\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold down %s then touch a wall to wall jump\n",
|
||||
CG_KeyNameForCommand( "+moveup" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienLevel3Text
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienLevel3Text( char *text, playerState_t *ps )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to bite\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL3_UPG )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to launch a projectile\n",
|
||||
CG_KeyNameForCommand( "+button2" ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold down and release %s to pounce\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_AlienLevel4Text
|
||||
===============
|
||||
*/
|
||||
static void CG_AlienLevel4Text( char *text, playerState_t *ps )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to swipe\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold down and release %s to charge\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_HumanCkitText
|
||||
===============
|
||||
*/
|
||||
static void CG_HumanCkitText( char *text, playerState_t *ps )
|
||||
{
|
||||
buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
|
||||
float health;
|
||||
|
||||
if( buildable > BA_NONE )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to place the %s\n",
|
||||
CG_KeyNameForCommand( "+attack" ),
|
||||
BG_FindHumanNameForBuildable( buildable ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to cancel placing the %s\n",
|
||||
CG_KeyNameForCommand( "+button5" ),
|
||||
BG_FindHumanNameForBuildable( buildable ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to build a structure\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( CG_BuildableInRange( ps, &health ) )
|
||||
{
|
||||
if( health < 1.0f )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold %s to repair this structure\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to destroy this structure\n",
|
||||
CG_KeyNameForCommand( "deconstruct" ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_HumanText
|
||||
===============
|
||||
*/
|
||||
static void CG_HumanText( char *text, playerState_t *ps )
|
||||
{
|
||||
char *name;
|
||||
int ammo, clips;
|
||||
upgrade_t upgrade = UP_NONE;
|
||||
|
||||
if( cg.weaponSelect <= 32 )
|
||||
name = cg_weapons[ cg.weaponSelect ].humanName;
|
||||
else if( cg.weaponSelect > 32 )
|
||||
{
|
||||
name = cg_upgrades[ cg.weaponSelect - 32 ].humanName;
|
||||
upgrade = cg.weaponSelect - 32;
|
||||
}
|
||||
|
||||
BG_UnpackAmmoArray( ps->weapon, ps->ammo, ps->powerups, &ammo, &clips );
|
||||
|
||||
if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( ps->weapon ) )
|
||||
{
|
||||
//no ammo
|
||||
switch( ps->weapon )
|
||||
{
|
||||
case WP_MACHINEGUN:
|
||||
case WP_CHAINGUN:
|
||||
case WP_SHOTGUN:
|
||||
case WP_FLAMER:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Find an Armoury and press %s for more ammo\n",
|
||||
CG_KeyNameForCommand( "buy ammo" ) ) );
|
||||
break;
|
||||
|
||||
case WP_LAS_GUN:
|
||||
case WP_PULSE_RIFLE:
|
||||
case WP_MASS_DRIVER:
|
||||
case WP_LUCIFER_CANNON:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Find a Reactor or Repeater and press %s for more ammo\n",
|
||||
CG_KeyNameForCommand( "buy ammo" ) ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( ps->weapon )
|
||||
{
|
||||
case WP_BLASTER:
|
||||
case WP_MACHINEGUN:
|
||||
case WP_SHOTGUN:
|
||||
case WP_LAS_GUN:
|
||||
case WP_CHAINGUN:
|
||||
case WP_PULSE_RIFLE:
|
||||
case WP_FLAMER:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to fire the %s\n",
|
||||
CG_KeyNameForCommand( "+attack" ),
|
||||
BG_FindHumanNameForWeapon( ps->weapon ) ) );
|
||||
break;
|
||||
|
||||
case WP_MASS_DRIVER:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to fire the %s\n",
|
||||
CG_KeyNameForCommand( "+attack" ),
|
||||
BG_FindHumanNameForWeapon( ps->weapon ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold %s to zoom\n",
|
||||
CG_KeyNameForCommand( "+button5" ) ) );
|
||||
break;
|
||||
|
||||
case WP_PAIN_SAW:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold %s to activate the %s\n",
|
||||
CG_KeyNameForCommand( "+attack" ),
|
||||
BG_FindHumanNameForWeapon( ps->weapon ) ) );
|
||||
break;
|
||||
|
||||
case WP_LUCIFER_CANNON:
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Hold and release %s to fire a charged shot\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to fire the %s\n",
|
||||
CG_KeyNameForCommand( "+button5" ),
|
||||
BG_FindHumanNameForWeapon( ps->weapon ) ) );
|
||||
break;
|
||||
|
||||
case WP_HBUILD:
|
||||
case WP_HBUILD2:
|
||||
CG_HumanCkitText( text, ps );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s and ",
|
||||
CG_KeyNameForCommand( "weapprev" ) ) );
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "%s to select an upgrade\n",
|
||||
CG_KeyNameForCommand( "weapnext" ) ) );
|
||||
|
||||
if( upgrade == UP_NONE ||
|
||||
( upgrade > UP_NONE && BG_FindUsableForUpgrade( upgrade ) ) )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to use the %s\n",
|
||||
CG_KeyNameForCommand( "+button2" ),
|
||||
name ) );
|
||||
}
|
||||
|
||||
if( ps->stats[ STAT_HEALTH ] <= 35 &&
|
||||
BG_InventoryContainsUpgrade( UP_MEDKIT, ps->stats ) )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to use your %s\n",
|
||||
CG_KeyNameForCommand( "itemact medkit" ),
|
||||
BG_FindHumanNameForUpgrade( UP_MEDKIT ) ) );
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to use a structure\n",
|
||||
CG_KeyNameForCommand( "+button7" ) ) );
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to sprint\n",
|
||||
CG_KeyNameForCommand( "boost" ) ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_SpectatorText
|
||||
===============
|
||||
*/
|
||||
static void CG_SpectatorText( char *text, playerState_t *ps )
|
||||
{
|
||||
if( ps->pm_flags & PMF_FOLLOW )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to return to free spectator mode\n",
|
||||
CG_KeyNameForCommand( "+button2" ) ) );
|
||||
|
||||
if( CG_PlayerCount( ) > 1 )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s or ",
|
||||
CG_KeyNameForCommand( "weapprev" ) ) );
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "%s to change player\n",
|
||||
CG_KeyNameForCommand( "weapnext" ) ) );
|
||||
}
|
||||
}
|
||||
else if( ps->pm_type == PM_SPECTATOR )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to join a team\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
|
||||
if( CG_PlayerCount( ) > 0 )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to enter spectator follow mode\n",
|
||||
CG_KeyNameForCommand( "+button2" ) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to spawn\n",
|
||||
CG_KeyNameForCommand( "+attack" ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TutorialText
|
||||
|
||||
Returns context help for the current class/weapon
|
||||
===============
|
||||
*/
|
||||
const char *CG_TutorialText( void )
|
||||
{
|
||||
playerState_t *ps;
|
||||
static char text[ MAX_TUTORIAL_TEXT ];
|
||||
|
||||
CG_GetBindings( );
|
||||
|
||||
text[ 0 ] = '\0';
|
||||
ps = &cg.snap->ps;
|
||||
|
||||
if( !cg.intermissionStarted && !cg.demoPlayback )
|
||||
{
|
||||
if( ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR ||
|
||||
ps->pm_flags & PMF_FOLLOW )
|
||||
{
|
||||
CG_SpectatorText( text, ps );
|
||||
}
|
||||
else if( ps->stats[ STAT_HEALTH ] > 0 )
|
||||
{
|
||||
switch( ps->stats[ STAT_PCLASS ] )
|
||||
{
|
||||
case PCL_ALIEN_BUILDER0:
|
||||
case PCL_ALIEN_BUILDER0_UPG:
|
||||
CG_AlienBuilderText( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_ALIEN_LEVEL0:
|
||||
CG_AlienLevel0Text( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_ALIEN_LEVEL1:
|
||||
case PCL_ALIEN_LEVEL1_UPG:
|
||||
CG_AlienLevel1Text( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_ALIEN_LEVEL2:
|
||||
case PCL_ALIEN_LEVEL2_UPG:
|
||||
CG_AlienLevel2Text( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_ALIEN_LEVEL3:
|
||||
case PCL_ALIEN_LEVEL3_UPG:
|
||||
CG_AlienLevel3Text( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_ALIEN_LEVEL4:
|
||||
CG_AlienLevel4Text( text, ps );
|
||||
break;
|
||||
|
||||
case PCL_HUMAN:
|
||||
CG_HumanText( text, ps );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( ps->stats[ STAT_PTEAM ] == PTE_ALIENS )
|
||||
{
|
||||
entityState_t *es = CG_BuildableInRange( ps, NULL );
|
||||
|
||||
if( ps->stats[ STAT_STATE ] & SS_HOVELING )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to exit the hovel\n",
|
||||
CG_KeyNameForCommand( "+button7" ) ) );
|
||||
}
|
||||
else if( es && es->modelindex == BA_A_HOVEL &&
|
||||
es->generic1 & B_SPAWNED_TOGGLEBIT &&
|
||||
( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 ||
|
||||
ps->stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ) )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to enter the hovel\n",
|
||||
CG_KeyNameForCommand( "+button7" ) ) );
|
||||
}
|
||||
else if( BG_UpgradeClassAvailable( ps ) )
|
||||
{
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT,
|
||||
va( "Press %s to evolve\n",
|
||||
CG_KeyNameForCommand( "+button7" ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_strcat( text, MAX_TUTORIAL_TEXT, "Press ESC for the menu" );
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
+1340
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user