First version.

This commit is contained in:
Paradox460 committed 2008-01-05 04:20:33 +00:00
commit ef1c2cc347
473 files changed
+305366

No files matched your search

+111
View File
@@ -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 );
}
+227
View File
@@ -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( &cent->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 = &cent->lerpFrame;
es = &cent->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( &cent->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 = &cent->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);
}
+404
View File
@@ -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
+325
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+378
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+1034
View File
File diff suppressed because it is too large. Load diff
+2071
View File
File diff suppressed because it is too large. Load diff
+1840
View File
File diff suppressed because it is too large. Load diff
+289
View File
@@ -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 );
}
}
+202
View File
@@ -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
+317
View File
@@ -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;
}
}
+878
View File
@@ -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 = &cent->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 = &cent->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( &cent->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 = &cent->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 = &cent->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 );
}
+81
View File
@@ -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 );
}
+252
View File
@@ -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;
//----------------------------------------------
+365
View File
@@ -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
+411
View File
@@ -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( &cent->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( &cent->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" );
}
+116
View File
@@ -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
+570
View File
@@ -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
+669
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff