summaryrefslogtreecommitdiff
path: root/src/cgame
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/cgame
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/cgame')
-rw-r--r--src/cgame/CMakeLists.txt83
-rw-r--r--src/cgame/binaryshader.h42
-rw-r--r--src/cgame/cg_animation.c13
-rw-r--r--src/cgame/cg_animmapobj.c15
-rw-r--r--src/cgame/cg_attachment.c9
-rw-r--r--src/cgame/cg_buildable.c410
-rw-r--r--src/cgame/cg_consolecmds.c293
-rw-r--r--src/cgame/cg_draw.c2941
-rw-r--r--src/cgame/cg_drawtools.c107
-rw-r--r--src/cgame/cg_ents.c226
-rw-r--r--src/cgame/cg_event.c474
-rw-r--r--src/cgame/cg_local.h463
-rw-r--r--src/cgame/cg_main.c837
-rw-r--r--src/cgame/cg_marks.c11
-rw-r--r--src/cgame/cg_mem.c202
-rw-r--r--src/cgame/cg_particles.c737
-rw-r--r--src/cgame/cg_players.c416
-rw-r--r--src/cgame/cg_playerstate.c34
-rw-r--r--src/cgame/cg_predict.c51
-rw-r--r--src/cgame/cg_ptr.c81
-rw-r--r--src/cgame/cg_public.h413
-rw-r--r--src/cgame/cg_rangemarker.c399
-rw-r--r--src/cgame/cg_scanner.c165
-rw-r--r--src/cgame/cg_servercmds.c1235
-rw-r--r--src/cgame/cg_snapshot.c20
-rw-r--r--src/cgame/cg_syscalls.asm4
-rw-r--r--src/cgame/cg_syscalls.c54
-rw-r--r--src/cgame/cg_syscalls_11.asm3
-rw-r--r--src/cgame/cg_trails.c61
-rw-r--r--src/cgame/cg_tutorial.c280
-rw-r--r--src/cgame/cg_view.c563
-rw-r--r--src/cgame/cg_weapons.c657
32 files changed, 6576 insertions, 4723 deletions
diff --git a/src/cgame/CMakeLists.txt b/src/cgame/CMakeLists.txt
new file mode 100644
index 0000000..026e6eb
--- /dev/null
+++ b/src/cgame/CMakeLists.txt
@@ -0,0 +1,83 @@
+#
+## ____ _
+## / ___|__ _ __ _ _ __ ___ ___ ___ ___ __| | ___
+##| | / _` |/ _` | '_ ` _ \ / _ \ / __/ _ \ / _` |/ _ \
+##| |__| (_| | (_| | | | | | | __/ | (_| (_) | (_| | __/
+## \____\__, |\__,_|_| |_| |_|\___| \___\___/ \__,_|\___|
+## |___/
+#
+
+set(CMAKE_INSTALL_NAME_DIR ${PROJECT_BINARY_DIR}/gpp)
+
+set(BG_SOURCE_DIR ../game)
+set(QC_SOURCE_DIR ../qcommon)
+set(UI_SOURCE_DIR ../ui)
+set(RC_SOURCE_DIR ../renderercommon)
+
+add_definitions( -DCGAME )
+
+set( CGAME_SOURCES
+ cg_main.c # Must be listed first
+ cg_public.h
+ cg_local.h
+ binaryshader.h
+ ${BG_SOURCE_DIR}/bg_lib.h
+ ${BG_SOURCE_DIR}/bg_public.h
+ ${BG_SOURCE_DIR}/bg_alloc.c
+ ${BG_SOURCE_DIR}/bg_lib.c
+ ${BG_SOURCE_DIR}/bg_misc.c
+ ${BG_SOURCE_DIR}/bg_voice.c
+ ${BG_SOURCE_DIR}/bg_pmove.c
+ ${BG_SOURCE_DIR}/bg_slidemove.c
+ cg_animation.c
+ cg_animmapobj.c
+ cg_attachment.c
+ cg_buildable.c
+ cg_consolecmds.c
+ cg_draw.c
+ cg_drawtools.c
+ cg_ents.c
+ cg_event.c
+ cg_marks.c
+ cg_particles.c
+ cg_players.c
+ cg_playerstate.c
+ cg_predict.c
+ cg_rangemarker.c
+ cg_scanner.c
+ cg_servercmds.c
+ cg_snapshot.c
+ cg_trails.c
+ cg_tutorial.c
+ cg_view.c
+ cg_weapons.c
+ #
+ ${UI_SOURCE_DIR}/ui_shared.h
+ ${UI_SOURCE_DIR}/ui_shared.c
+ #
+ ${QC_SOURCE_DIR}/q_shared.h
+ ${QC_SOURCE_DIR}/q_shared.c
+ ${QC_SOURCE_DIR}/q_math.c
+ #
+ ${RC_SOURCE_DIR}/tr_types.h
+ )
+
+add_library( cgame SHARED ${CGAME_SOURCES} cg_syscalls.c )
+
+target_include_directories(
+ cgame PUBLIC
+ ${BG_SOURCE_DIR}
+ ${QC_SOURCE_DIR}
+ ${RC_SOURCE_DIR}
+ ${UI_SOURCE_DIR}
+ )
+
+include( ${CMAKE_SOURCE_DIR}/cmake/AddQVM.cmake )
+add_qvm( cgame ${CGAME_SOURCES} cg_syscalls.asm )
+
+
+add_custom_command(
+ TARGET cgame POST_BUILD
+ COMMAND ${CMAKE_COMMAND}
+ ARGS -E copy ${CMAKE_CURRENT_BINARY_DIR}/libcgame${CMAKE_SHARED_LIBRARY_SUFFIX} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/gpp/cgame${CMAKE_SHARED_LIBRARY_SUFFIX}
+ )
diff --git a/src/cgame/binaryshader.h b/src/cgame/binaryshader.h
new file mode 100644
index 0000000..371f758
--- /dev/null
+++ b/src/cgame/binaryshader.h
@@ -0,0 +1,42 @@
+#ifndef BINARY_SHADER_H
+#define BINARY_SHADER_H 1
+
+typedef struct
+{
+ byte color[ 3 ];
+ qboolean drawIntersection;
+ qboolean drawFrontline;
+} cgBinaryShaderSetting_t;
+
+#define NUM_BINARY_SHADERS 256
+
+typedef struct
+{
+ qhandle_t f1;
+ qhandle_t f2;
+ qhandle_t f3;
+ qhandle_t b1;
+ qhandle_t b2;
+ qhandle_t b3;
+} cgMediaBinaryShader_t;
+
+typedef enum
+{
+ SHC_DARK_BLUE,
+ SHC_LIGHT_BLUE,
+ SHC_GREEN_CYAN,
+ SHC_VIOLET,
+ SHC_YELLOW,
+ SHC_ORANGE,
+ SHC_LIGHT_GREEN,
+ SHC_DARK_GREEN,
+ SHC_RED,
+ SHC_PINK,
+ SHC_GREY,
+ SHC_NUM_SHADER_COLORS
+} shaderColorEnum_t;
+
+extern const vec3_t cg_shaderColors[ SHC_NUM_SHADER_COLORS ];
+
+
+#endif
diff --git a/src/cgame/cg_animation.c b/src/cgame/cg_animation.c
index c370c53..78311b9 100644
--- a/src/cgame/cg_animation.c
+++ b/src/cgame/cg_animation.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -31,7 +32,7 @@ Sets cg.snap, cg.oldFrame, and cg.backlerp
cg.time should be between oldFrameTime and frameTime after exit
===============
*/
-void CG_RunLerpFrame( lerpFrame_t *lf )
+void CG_RunLerpFrame( lerpFrame_t *lf, float scale )
{
int f, numFrames;
animation_t *anim;
@@ -61,7 +62,9 @@ void CG_RunLerpFrame( lerpFrame_t *lf )
lf->frameTime = lf->oldFrameTime + anim->frameLerp;
f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
+ f *= scale;
numFrames = anim->numFrames;
+
if( anim->flipflop )
numFrames *= 2;
diff --git a/src/cgame/cg_animmapobj.c b/src/cgame/cg_animmapobj.c
index 1225314..9edbed4 100644
--- a/src/cgame/cg_animmapobj.c
+++ b/src/cgame/cg_animmapobj.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
#include "cg_local.h"
-
/*
===============
CG_DoorAnimation
@@ -31,7 +31,7 @@ CG_DoorAnimation
*/
static void CG_DoorAnimation( centity_t *cent, int *old, int *now, float *backLerp )
{
- CG_RunLerpFrame( &cent->lerpFrame );
+ CG_RunLerpFrame( &cent->lerpFrame, 1.0f );
*old = cent->lerpFrame.oldFrame;
*now = cent->lerpFrame.frame;
@@ -117,7 +117,7 @@ static void CG_AMOAnimation( centity_t *cent, int *old, int *now, float *backLer
cent->lerpFrame.frameTime += delta;
}
- CG_RunLerpFrame( &cent->lerpFrame );
+ CG_RunLerpFrame( &cent->lerpFrame, 1.0f );
cent->miscTime = cg.time;
}
@@ -147,7 +147,6 @@ void CG_AnimMapObj( centity_t *cent )
memset( &ent, 0, sizeof( ent ) );
- VectorCopy( es->angles, cent->lerpAngles );
AnglesToAxis( cent->lerpAngles, ent.axis );
ent.hModel = cgs.gameModels[ es->modelindex ];
diff --git a/src/cgame/cg_attachment.c b/src/cgame/cg_attachment.c
index 0d3c8ea..2c52c06 100644
--- a/src/cgame/cg_attachment.c
+++ b/src/cgame/cg_attachment.c
@@ -1,12 +1,13 @@
/*
===========================================================================
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -15,8 +16,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c
index e678ebd..7d7596b 100644
--- a/src/cgame/cg_buildable.c
+++ b/src/cgame/cg_buildable.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,12 +17,11 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
-
#include "cg_local.h"
char *cg_buildableSoundNames[ MAX_BUILDABLE_ANIMATIONS ] =
@@ -95,6 +95,7 @@ void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir )
#define CREEP_SIZE 64.0f
+#define CREEP_DISTANCE 64.0f
/*
==================
@@ -107,7 +108,7 @@ static void CG_Creep( centity_t *cent )
float size, frac;
trace_t tr;
vec3_t temp, origin;
- int scaleUpTime = BG_FindBuildTimeForBuildable( cent->currentState.modelindex );
+ int scaleUpTime = BG_Buildable( cent->currentState.modelindex )->buildTime;
int time;
time = cent->currentState.time;
@@ -121,7 +122,7 @@ static void CG_Creep( centity_t *cent )
else
frac = 1.0f;
}
- else
+ else if( time < 0 )
{
msec = cg.time + time;
if( msec >= 0 && msec < CREEP_SCALEDOWN_TIME )
@@ -131,7 +132,7 @@ static void CG_Creep( centity_t *cent )
}
VectorCopy( cent->currentState.origin2, temp );
- VectorScale( temp, -4096, temp );
+ VectorScale( temp, -CREEP_DISTANCE, temp );
VectorAdd( temp, cent->lerpOrigin, temp );
CG_Trace( &tr, cent->lerpOrigin, NULL, NULL, temp, cent->currentState.number, MASK_PLAYERSOLID );
@@ -140,7 +141,7 @@ static void CG_Creep( centity_t *cent )
size = CREEP_SIZE * frac;
- if( size > 0.0f )
+ if( size > 0.0f && tr.fraction < 1.0f )
CG_ImpactMark( cgs.media.creepShader, origin, cent->currentState.origin2,
0.0f, 1.0f, 1.0f, 1.0f, 1.0f, qfalse, size, qtrue );
}
@@ -168,12 +169,13 @@ static qboolean CG_ParseBuildableAnimationFile( const char *filename, buildable_
// load the file
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if( len <= 0 )
+ if( len < 0 )
return qfalse;
- if( len >= sizeof( text ) - 1 )
+ if( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( "File %s too long\n", filename );
+ trap_FS_FCloseFile( f );
+ CG_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
return qfalse;
}
@@ -258,12 +260,13 @@ static qboolean CG_ParseBuildableSoundFile( const char *filename, buildable_t bu
// load the file
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( len <= 0 )
+ if ( len < 0 )
return qfalse;
- if ( len >= sizeof( text ) - 1 )
+ if ( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( "File %s too long\n", filename );
+ trap_FS_FCloseFile( f );
+ CG_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
return qfalse;
}
@@ -335,7 +338,7 @@ void CG_InitBuildables( void )
for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ )
{
- buildableName = BG_FindNameForBuildable( i );
+ buildableName = BG_Buildable( i )->name;
//animation.cfg
Com_sprintf( filename, sizeof( filename ), "models/buildables/%s/animation.cfg", buildableName );
@@ -350,7 +353,8 @@ void CG_InitBuildables( void )
//models
for( j = 0; j <= 3; j++ )
{
- if( ( modelFile = BG_FindModelsForBuildable( i, j ) ) )
+ modelFile = BG_BuildableConfig( i )->models[ j ];
+ if( strlen( modelFile ) > 0 )
cg_buildables[ i ].models[ j ] = trap_R_RegisterModel( modelFile );
}
@@ -372,7 +376,7 @@ void CG_InitBuildables( void )
else
{
//file doesn't exist - use default
- if( BG_FindTeamForBuildable( i ) == BIT_ALIENS )
+ if( BG_Buildable( i )->team == TEAM_ALIENS )
cg_buildables[ i ].sounds[ j ].sound = defaultAlienSounds[ j ];
else
cg_buildables[ i ].sounds[ j ].sound = defaultHumanSounds[ j ];
@@ -426,24 +430,15 @@ cg.time should be between oldFrameTime and frameTime after exit
*/
static void CG_RunBuildableLerpFrame( centity_t *cent )
{
- int f, numFrames;
buildable_t buildable = cent->currentState.modelindex;
lerpFrame_t *lf = &cent->lerpFrame;
- animation_t *anim;
buildableAnimNumber_t newAnimation = cent->buildableAnim & ~( ANIM_TOGGLEBIT|ANIM_FORCEBIT );
- // debugging tool to get no animations
- if( cg_animSpeed.integer == 0 )
- {
- lf->oldFrame = lf->frame = lf->backlerp = 0;
- return;
- }
-
// see if the animation sequence is switching
if( newAnimation != lf->animationNumber || !lf->animation )
{
if( cg_debugRandom.integer )
- CG_Printf( "newAnimation: %d lf->animationNumber: %d lf->animation: %d\n",
+ CG_Printf( "newAnimation: %d lf->animationNumber: %d lf->animation: 0x%p\n",
newAnimation, lf->animationNumber, lf->animation );
CG_SetBuildableLerpFrameAnimation( buildable, lf, newAnimation );
@@ -453,7 +448,7 @@ static void CG_RunBuildableLerpFrame( centity_t *cent )
{
if( cg_debugRandom.integer )
CG_Printf( "Sound for animation %d for a %s\n",
- newAnimation, BG_FindHumanNameForBuildable( buildable ) );
+ newAnimation, BG_Buildable( buildable )->humanName );
trap_S_StartSound( cent->lerpOrigin, cent->currentState.number, CHAN_AUTO,
cg_buildables[ buildable ].sounds[ newAnimation ].sound );
@@ -465,72 +460,14 @@ static void CG_RunBuildableLerpFrame( centity_t *cent )
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
cg_buildables[ buildable ].sounds[ lf->animationNumber ].sound );
- // 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;
+ CG_RunLerpFrame( lf, 1.0f );
- // 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;
- cent->buildableAnim = cent->currentState.torsoAnim;
- }
- }
-
- 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");
- }
+ // animation ended
+ if( lf->frameTime == cg.time )
+ {
+ cent->buildableAnim = cent->currentState.torsoAnim;
+ cent->buildableIdleAnim = qtrue;
}
-
- 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 );
}
/*
@@ -544,10 +481,13 @@ static void CG_BuildableAnimation( centity_t *cent, int *old, int *now, float *b
//if no animation is set default to idle anim
if( cent->buildableAnim == BANIM_NONE )
+ {
cent->buildableAnim = es->torsoAnim;
+ cent->buildableIdleAnim = qtrue;
+ }
//display the first frame of the construction anim if not yet spawned
- if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) )
+ if( !( es->eFlags & EF_B_SPAWNED ) )
{
animation_t *anim = &cg_buildables[ es->modelindex ].animations[ BANIM_CONSTRUCT1 ];
@@ -572,12 +512,23 @@ static void CG_BuildableAnimation( centity_t *cent, int *old, int *now, float *b
CG_Printf( "%d->%d l:%d t:%d %s(%d)\n",
cent->oldBuildableAnim, cent->buildableAnim,
es->legsAnim, es->torsoAnim,
- BG_FindHumanNameForBuildable( es->modelindex ), es->number );
+ BG_Buildable( es->modelindex )->humanName, es->number );
if( cent->buildableAnim == es->torsoAnim || es->legsAnim & ANIM_FORCEBIT )
+ {
cent->buildableAnim = cent->oldBuildableAnim = es->legsAnim;
+ cent->buildableIdleAnim = qfalse;
+ }
else
+ {
cent->buildableAnim = cent->oldBuildableAnim = es->torsoAnim;
+ cent->buildableIdleAnim = qtrue;
+ }
+ }
+ else if( cent->buildableIdleAnim == qtrue &&
+ cent->buildableAnim != es->torsoAnim )
+ {
+ cent->buildableAnim = es->torsoAnim;
}
CG_RunBuildableLerpFrame( cent );
@@ -588,7 +539,7 @@ static void CG_BuildableAnimation( centity_t *cent, int *old, int *now, float *b
}
}
-#define TRACE_DEPTH 64.0f
+#define TRACE_DEPTH 15.0f
/*
===============
@@ -600,8 +551,9 @@ static void CG_PositionAndOrientateBuildable( const vec3_t angles, const vec3_t
const vec3_t mins, const vec3_t maxs,
vec3_t outAxis[ 3 ], vec3_t outOrigin )
{
- vec3_t forward, start, end;
+ vec3_t forward, end;
trace_t tr;
+ float fraction;
AngleVectors( angles, forward, NULL, NULL );
VectorCopy( normal, outAxis[ 2 ] );
@@ -620,16 +572,21 @@ static void CG_PositionAndOrientateBuildable( const vec3_t angles, const vec3_t
outAxis[ 1 ][ 2 ] = -outAxis[ 1 ][ 2 ];
VectorMA( inOrigin, -TRACE_DEPTH, normal, end );
- VectorMA( inOrigin, 1.0f, normal, start );
- CG_CapTrace( &tr, start, mins, maxs, end, skipNumber, MASK_PLAYERSOLID );
- if( tr.fraction == 1.0f )
+ CG_CapTrace( &tr, inOrigin, mins, maxs, end, skipNumber,
+ CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
+
+ fraction = tr.fraction;
+ if( tr.startsolid )
+ fraction = 0;
+ else if( tr.fraction == 1.0f )
{
- //erm we missed completely - try again with a box trace
- CG_Trace( &tr, start, mins, maxs, end, skipNumber, MASK_PLAYERSOLID );
+ // this is either too far off of the bbox to be useful for gameplay purposes
+ // or the model is positioned in thin air anyways.
+ fraction = 0;
}
- VectorMA( inOrigin, tr.fraction * -TRACE_DEPTH, normal, outOrigin );
+ VectorMA( inOrigin, fraction * -TRACE_DEPTH, normal, outOrigin );
}
/*
@@ -650,15 +607,18 @@ void CG_GhostBuildable( buildable_t buildable )
memset( &ent, 0, sizeof( ent ) );
- BG_FindBBoxForBuildable( buildable, mins, maxs );
+ BG_BuildableBoundingBox( buildable, mins, maxs );
BG_PositionBuildableRelativeToPlayer( ps, mins, maxs, CG_Trace, entity_origin, angles, &tr );
+ if( cg_rangeMarkerForBlueprint.integer && tr.entityNum != ENTITYNUM_NONE )
+ CG_GhostBuildableRangeMarker( buildable, entity_origin, tr.plane.normal );
+
CG_PositionAndOrientateBuildable( ps->viewangles, entity_origin, tr.plane.normal, ps->clientNum,
mins, maxs, ent.axis, ent.origin );
//offset on the Z axis if required
- VectorMA( ent.origin, BG_FindZOffsetForBuildable( buildable ), tr.plane.normal, ent.origin );
+ VectorMA( ent.origin, BG_BuildableConfig( buildable )->zOffset, tr.plane.normal, ent.origin );
VectorCopy( ent.origin, ent.lightingOrigin );
VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all
@@ -671,7 +631,7 @@ void CG_GhostBuildable( buildable_t buildable )
ent.customShader = cgs.media.redBuildShader;
//rescale the model
- scale = BG_FindModelScaleForBuildable( buildable );
+ scale = BG_BuildableConfig( buildable )->modelScale;
if( scale != 1.0f )
{
@@ -696,14 +656,14 @@ CG_BuildableParticleEffects
static void CG_BuildableParticleEffects( centity_t *cent )
{
entityState_t *es = &cent->currentState;
- buildableTeam_t team = BG_FindTeamForBuildable( es->modelindex );
- int health = es->generic1 & B_HEALTH_MASK;
- float healthFrac = (float)health / B_HEALTH_MASK;
+ team_t team = BG_Buildable( es->modelindex )->team;
+ int health = es->misc;
+ float healthFrac = (float)health / BG_Buildable( es->modelindex )->health;
- if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) )
+ if( !( es->eFlags & EF_B_SPAWNED ) )
return;
- if( team == BIT_HUMANS )
+ if( team == TEAM_HUMANS )
{
if( healthFrac < 0.33f && !CG_IsParticleSystemValid( &cent->buildablePS ) )
{
@@ -718,7 +678,7 @@ static void CG_BuildableParticleEffects( centity_t *cent )
else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( &cent->buildablePS ) )
CG_DestroyParticleSystem( &cent->buildablePS );
}
- else if( team == BIT_ALIENS )
+ else if( team == TEAM_ALIENS )
{
if( healthFrac < 0.33f && !CG_IsParticleSystemValid( &cent->buildablePS ) )
{
@@ -736,7 +696,11 @@ static void CG_BuildableParticleEffects( centity_t *cent )
}
}
-
+/*
+==================
+CG_BuildableStatusParse
+==================
+*/
void CG_BuildableStatusParse( const char *filename, buildStat_t *bs )
{
pc_token_t token;
@@ -866,10 +830,12 @@ void CG_BuildableStatusParse( const char *filename, buildStat_t *bs )
Com_Printf("CG_BuildableStatusParse: unknown token %s in %s\n",
token.string, filename );
bs->loaded = qfalse;
+ trap_Parse_FreeSource( handle );
return;
}
}
bs->loaded = qtrue;
+ trap_Parse_FreeSource( handle );
}
#define STATUS_FADE_TIME 200
@@ -899,23 +865,30 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
qboolean visible = qfalse;
vec3_t mins, maxs;
entityState_t *hit;
+ int anim;
- if( BG_FindTeamForBuildable( es->modelindex ) == BIT_ALIENS )
+ if( BG_Buildable( es->modelindex )->team == TEAM_ALIENS )
bs = &cgs.alienBuildStat;
else
bs = &cgs.humanBuildStat;
if( !bs->loaded )
return;
-
+
d = Distance( cent->lerpOrigin, cg.refdef.vieworg );
if( d > STATUS_MAX_VIEW_DIST )
return;
-
+
Vector4Copy( bs->foreColor, color );
- // trace for center point
- BG_FindBBoxForBuildable( es->modelindex, mins, maxs );
+ // trace for center point
+ BG_BuildableBoundingBox( es->modelindex, mins, maxs );
+
+ // hack for shrunken barricades
+ anim = es->torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT );
+ if( es->modelindex == BA_A_BARRICADE &&
+ ( anim == BANIM_DESTROYED || !( es->eFlags & EF_B_SPAWNED ) ) )
+ maxs[ 2 ] = (int)( maxs[ 2 ] * BARRICADE_SHRINKPROP );
VectorCopy( cent->lerpOrigin, origin );
@@ -960,8 +933,8 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
hit = &cg_entities[ tr.entityNum ].currentState;
if( tr.entityNum < MAX_CLIENTS || ( hit->eType == ET_BUILDABLE &&
- ( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ||
- BG_FindTransparentTestForBuildable( hit->modelindex ) ) ) )
+ ( !( es->eFlags & EF_B_SPAWNED ) ||
+ BG_Buildable( hit->modelindex )->transparentTest ) ) )
{
entNum = tr.entityNum;
VectorCopy( tr.endpos, trOrigin );
@@ -972,7 +945,7 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
}
// hack to make the kit obscure view
if( cg_drawGun.integer && visible &&
- cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS &&
+ cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS &&
CG_WorldToScreen( origin, &x, &y ) )
{
if( x > 450 && y > 290 )
@@ -1006,8 +979,8 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
return;
}
- health = es->generic1 & B_HEALTH_MASK;
- healthScale = (float)health / B_HEALTH_MASK;
+ health = es->misc;
+ healthScale = (float)health / BG_Buildable( es->modelindex )->health;
if( health > 0 && healthScale < 0.01f )
healthScale = 0.01f;
@@ -1024,13 +997,14 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
float picY = y;
float scale;
float subH, subY;
+ float clipX, clipY, clipW, clipH;
vec4_t frameColor;
// this is fudged to get the width/height in the cfg to be more realistic
scale = ( picH / d ) * 3;
- powered = es->generic1 & B_POWERED_TOGGLEBIT;
- marked = es->generic1 & B_MARKED_TOGGLEBIT;
+ powered = es->eFlags & EF_B_POWERED;
+ marked = es->eFlags & EF_B_MARKED;
picH *= scale;
picW *= scale;
@@ -1041,6 +1015,12 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
subH = picH - ( picH * bs->verticalMargin );
subY = picY + ( picH * 0.5f ) - ( subH * 0.5f );
+ clipW = ( 640.0f * cg_viewsize.integer ) / 100.0f;
+ clipH = ( 480.0f * cg_viewsize.integer ) / 100.0f;
+ clipX = 320.0f - ( clipW * 0.5f );
+ clipY = 240.0f - ( clipH * 0.5f );
+ CG_SetClipRegion( clipX, clipY, clipW, clipH );
+
if( bs->frameShader )
{
Vector4Copy( bs->backColor, frameColor );
@@ -1073,7 +1053,7 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
healthColor[ 3 ] = color[ 3 ];
trap_R_SetColor( healthColor );
-
+
CG_DrawPic( hX, hY, hW, hH, cgs.media.whiteShader );
trap_R_SetColor( NULL );
}
@@ -1089,7 +1069,7 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
oW *= scale;
oX -= ( oW * 0.5f );
oY -= ( oH * 0.5f );
-
+
trap_R_SetColor( frameColor );
CG_DrawPic( oX, oY, oW, oH, bs->overlayShader );
trap_R_SetColor( NULL );
@@ -1117,12 +1097,12 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
int healthMax;
int healthPoints;
- healthMax = BG_FindHealthForBuildable( es->modelindex );
+ healthMax = BG_Buildable( es->modelindex )->health;
healthPoints = (int)( healthScale * healthMax );
if( health > 0 && healthPoints < 1 )
healthPoints = 1;
- nX = picX + ( picW * 0.5f ) - 2.0f - ( ( subH * 4 ) * 0.5f );
-
+ nX = picX + ( picW * 0.5f ) - 2.0f - ( ( subH * 4 ) * 0.5f );
+
if( healthPoints > 999 )
nX -= 0.0f;
else if( healthPoints > 99 )
@@ -1131,14 +1111,21 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
nX -= subH * 1.0f;
else
nX -= subH * 1.5f;
-
+
CG_DrawField( nX, subY, 4, subH, subH, healthPoints );
}
+
trap_R_SetColor( NULL );
+ CG_ClearClipRegion( );
}
}
-static int QDECL CG_SortDistance( const void *a, const void *b )
+/*
+==================
+CG_SortDistance
+==================
+*/
+static int CG_SortDistance( const void *a, const void *b )
{
centity_t *aent, *bent;
float adist, bdist;
@@ -1157,6 +1144,48 @@ static int QDECL CG_SortDistance( const void *a, const void *b )
/*
==================
+CG_PlayerIsBuilder
+==================
+*/
+static qboolean CG_PlayerIsBuilder( buildable_t buildable )
+{
+ switch( cg.predictedPlayerState.weapon )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ return BG_Buildable( buildable )->team ==
+ BG_Weapon( cg.predictedPlayerState.weapon )->team;
+
+ default:
+ return qfalse;
+ }
+}
+
+/*
+==================
+CG_BuildableRemovalPending
+==================
+*/
+static qboolean CG_BuildableRemovalPending( int entityNum )
+{
+ int i;
+ playerState_t *ps = &cg.snap->ps;
+
+ if( !( ps->stats[ STAT_BUILDABLE ] & SB_VALID_TOGGLEBIT ) )
+ return qfalse;
+
+ for( i = 0; i < MAX_MISC; i++ )
+ {
+ if( ps->misc[ i ] == entityNum )
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+==================
CG_DrawBuildableStatus
==================
*/
@@ -1168,30 +1197,18 @@ void CG_DrawBuildableStatus( void )
int buildableList[ MAX_ENTITIES_IN_SNAPSHOT ];
int buildables = 0;
- switch( cg.predictedPlayerState.weapon )
+ for( i = 0; i < cg.snap->numEntities; i++ )
{
- case WP_ABUILD:
- case WP_ABUILD2:
- case WP_HBUILD:
- case WP_HBUILD2:
- for( i = 0; i < cg.snap->numEntities; i++ )
- {
- cent = &cg_entities[ cg.snap->entities[ i ].number ];
- es = &cent->currentState;
-
- if( es->eType == ET_BUILDABLE &&
- BG_FindTeamForBuildable( es->modelindex ) ==
- BG_FindTeamForWeapon( cg.predictedPlayerState.weapon ) )
- buildableList[ buildables++ ] = cg.snap->entities[ i ].number;
- }
- qsort( buildableList, buildables, sizeof( int ), CG_SortDistance );
- for( i = 0; i < buildables; i++ )
- CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ] );
- break;
+ cent = &cg_entities[ cg.snap->entities[ i ].number ];
+ es = &cent->currentState;
- default:
- break;
+ if( es->eType == ET_BUILDABLE && CG_PlayerIsBuilder( es->modelindex ) )
+ buildableList[ buildables++ ] = cg.snap->entities[ i ].number;
}
+
+ qsort( buildableList, buildables, sizeof( int ), CG_SortDistance );
+ for( i = 0; i < buildables; i++ )
+ CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ] );
}
#define BUILDABLE_SOUND_PERIOD 500
@@ -1205,17 +1222,15 @@ void CG_Buildable( centity_t *cent )
{
refEntity_t ent;
entityState_t *es = &cent->currentState;
- vec3_t angles;
vec3_t surfNormal, xNormal, mins, maxs;
vec3_t refNormal = { 0.0f, 0.0f, 1.0f };
float rotAngle;
- buildableTeam_t team = BG_FindTeamForBuildable( es->modelindex );
+ team_t team = BG_Buildable( es->modelindex )->team;
float scale;
int health;
- float healthScale;
//must be before EF_NODRAW check
- if( team == BIT_ALIENS )
+ if( team == TEAM_ALIENS )
CG_Creep( cent );
// if set to invisible, skip
@@ -1229,37 +1244,63 @@ void CG_Buildable( centity_t *cent )
memset ( &ent, 0, sizeof( ent ) );
- VectorCopy( cent->lerpOrigin, ent.origin );
- VectorCopy( cent->lerpOrigin, ent.oldorigin );
- VectorCopy( cent->lerpOrigin, ent.lightingOrigin );
-
VectorCopy( es->origin2, surfNormal );
- VectorCopy( es->angles, angles );
- BG_FindBBoxForBuildable( es->modelindex, mins, maxs );
+ BG_BuildableBoundingBox( es->modelindex, mins, maxs );
if( es->pos.trType == TR_STATIONARY )
- CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, es->number,
- mins, maxs, ent.axis, ent.origin );
+ {
+ // seeing as buildables rarely move, we cache the results and recalculate
+ // only if the buildable moves or changes orientation
+ if( VectorCompare( cent->buildableCache.cachedOrigin, cent->lerpOrigin ) &&
+ VectorCompare( cent->buildableCache.cachedAngles, cent->lerpAngles ) &&
+ VectorCompare( cent->buildableCache.cachedNormal, surfNormal ) &&
+ cent->buildableCache.cachedType == es->modelindex )
+ {
+ VectorCopy( cent->buildableCache.axis[ 0 ], ent.axis[ 0 ] );
+ VectorCopy( cent->buildableCache.axis[ 1 ], ent.axis[ 1 ] );
+ VectorCopy( cent->buildableCache.axis[ 2 ], ent.axis[ 2 ] );
+ VectorCopy( cent->buildableCache.origin, ent.origin );
+ }
+ else
+ {
+ CG_PositionAndOrientateBuildable( cent->lerpAngles, cent->lerpOrigin, surfNormal,
+ es->number, mins, maxs, ent.axis,
+ ent.origin );
+ VectorCopy( ent.axis[ 0 ], cent->buildableCache.axis[ 0 ] );
+ VectorCopy( ent.axis[ 1 ], cent->buildableCache.axis[ 1 ] );
+ VectorCopy( ent.axis[ 2 ], cent->buildableCache.axis[ 2 ] );
+ VectorCopy( ent.origin, cent->buildableCache.origin );
+ VectorCopy( cent->lerpOrigin, cent->buildableCache.cachedOrigin );
+ VectorCopy( cent->lerpAngles, cent->buildableCache.cachedAngles );
+ VectorCopy( surfNormal, cent->buildableCache.cachedNormal );
+ cent->buildableCache.cachedType = es->modelindex;
+ }
+ }
+ else
+ {
+ VectorCopy( cent->lerpOrigin, ent.origin );
+ AnglesToAxis( cent->lerpAngles, ent.axis );
+ }
//offset on the Z axis if required
- VectorMA( ent.origin, BG_FindZOffsetForBuildable( es->modelindex ), surfNormal, ent.origin );
+ VectorMA( ent.origin, BG_BuildableConfig( es->modelindex )->zOffset, surfNormal, ent.origin );
VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all
VectorCopy( ent.origin, ent.lightingOrigin );
ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ];
- if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) )
+ if( !( es->eFlags & EF_B_SPAWNED ) )
{
sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild;
- if( team == BIT_HUMANS )
+ if( team == TEAM_HUMANS )
{
ent.customShader = cgs.media.humanSpawningShader;
prebuildSound = cgs.media.humanBuildablePrebuild;
}
- else if( team == BIT_ALIENS )
+ else if( team == TEAM_ALIENS )
prebuildSound = cgs.media.alienBuildablePrebuild;
trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound );
@@ -1268,7 +1309,7 @@ void CG_Buildable( centity_t *cent )
CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp );
//rescale the model
- scale = BG_FindModelScaleForBuildable( es->modelindex );
+ scale = BG_BuildableConfig( es->modelindex )->modelScale;
if( scale != 1.0f )
{
@@ -1281,6 +1322,8 @@ void CG_Buildable( centity_t *cent )
else
ent.nonNormalizedAxes = qfalse;
+ if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) )
+ ent.customShader = cgs.media.redBuildShader;
//add to refresh list
trap_R_AddRefEntityToScene( &ent );
@@ -1324,6 +1367,9 @@ void CG_Buildable( centity_t *cent )
else
turretBarrel.nonNormalizedAxes = qfalse;
+ if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) )
+ turretBarrel.customShader = cgs.media.redBuildShader;
+
trap_R_AddRefEntityToScene( &turretBarrel );
}
@@ -1366,6 +1412,9 @@ void CG_Buildable( centity_t *cent )
else
turretTop.nonNormalizedAxes = qfalse;
+ if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) )
+ turretTop.customShader = cgs.media.redBuildShader;
+
trap_R_AddRefEntityToScene( &turretTop );
}
@@ -1375,7 +1424,7 @@ void CG_Buildable( centity_t *cent )
weaponInfo_t *weapon = &cg_weapons[ es->weapon ];
if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME ||
- BG_FindProjTypeForBuildable( es->modelindex ) == WP_TESLAGEN )
+ BG_Buildable( es->modelindex )->turretProjType == WP_TESLAGEN )
{
if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] ||
weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] ||
@@ -1397,27 +1446,34 @@ void CG_Buildable( centity_t *cent )
trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound );
}
- health = es->generic1 & B_HEALTH_MASK;
- healthScale = (float)health / B_HEALTH_MASK;
+ health = es->misc;
- if( healthScale < cent->lastBuildableHealthScale && ( es->generic1 & B_SPAWNED_TOGGLEBIT ) )
+ if( health < cent->lastBuildableHealth &&
+ ( es->eFlags & EF_B_SPAWNED ) )
{
if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time )
{
- if( team == BIT_HUMANS )
+ if( team == TEAM_HUMANS )
{
int i = rand( ) % 4;
trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] );
}
- else if( team == BIT_ALIENS )
+ else if( team == TEAM_ALIENS )
trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage );
cent->lastBuildableDamageSoundTime = cg.time;
}
}
- cent->lastBuildableHealthScale = healthScale;
+ cent->lastBuildableHealth = health;
//smoke etc for damaged buildables
CG_BuildableParticleEffects( cent );
+
+ if ( cg_rangeMarkerForBlueprint.integer )
+ {
+ // only light up the powered buildables.
+ if ( es->eFlags & EF_B_POWERED )
+ CG_GhostBuildableRangeMarker( es->modelindex, ent.origin, surfNormal );
+ }
}
diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c
index 68aab6c..90e2e2d 100644
--- a/src/cgame/cg_consolecmds.c
+++ b/src/cgame/cg_consolecmds.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,34 +17,16 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// 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
@@ -53,7 +36,7 @@ Keybinding command
*/
static void CG_SizeUp_f( void )
{
- trap_Cvar_Set( "cg_viewsize", va( "%i", (int)( cg_viewsize.integer + 10 ) ) );
+ trap_Cvar_Set( "cg_viewsize", va( "%i", MIN( cg_viewsize.integer + 10, 100 ) ) );
}
@@ -66,7 +49,7 @@ Keybinding command
*/
static void CG_SizeDown_f( void )
{
- trap_Cvar_Set( "cg_viewsize", va( "%i", (int)( cg_viewsize.integer - 10 ) ) );
+ trap_Cvar_Set( "cg_viewsize", va( "%i", MAX( cg_viewsize.integer - 10, 30 ) ) );
}
@@ -91,7 +74,6 @@ qboolean CG_RequestScores( void )
// 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;
@@ -127,26 +109,12 @@ static void CG_ScoresDown_f( void )
{
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;
- }
+ cg.showScores = qtrue;
}
else
{
- // show the cached contents even if they just pressed if it
- // is within two seconds
- cg.showScores = qtrue;
+ cg.showScores = qfalse;
+ cg.numScores = 0;
}
}
@@ -159,70 +127,111 @@ static void CG_ScoresUp_f( void )
}
}
-static void CG_TellTarget_f( void )
+void CG_ClientList_f( void )
{
- int clientNum;
- char command[ 128 ];
- char message[ 128 ];
+ clientInfo_t *ci;
+ int i;
+ int count = 0;
- clientNum = CG_CrosshairPlayer( );
- if( clientNum == -1 )
- return;
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+ ci = &cgs.clientinfo[ i ];
+ if( !ci->infoValid )
+ continue;
- trap_Args( message, 128 );
- Com_sprintf( command, 128, "tell %i %s", clientNum, message );
- trap_SendClientCommand( command );
+ switch( ci->team )
+ {
+ case TEAM_ALIENS:
+ Com_Printf( "%2d " S_COLOR_RED "A " S_COLOR_WHITE "%s\n", i,
+ ci->name );
+ break;
+
+ case TEAM_HUMANS:
+ Com_Printf( "%2d " S_COLOR_CYAN "H " S_COLOR_WHITE "%s\n", i,
+ ci->name );
+ break;
+
+ default:
+ case TEAM_NONE:
+ case NUM_TEAMS:
+ Com_Printf( "%2d S %s\n", i, ci->name );
+ break;
+ }
+
+ count++;
+ }
+
+ Com_Printf( "Listed %2d clients\n", count );
}
-static void CG_TellAttacker_f( void )
+static void CG_VoiceMenu_f( void )
{
- int clientNum;
- char command[ 128 ];
- char message[ 128 ];
-
- clientNum = CG_LastAttacker( );
- if( clientNum == -1 )
- return;
+ char cmd[sizeof("voicemenu3")];
+
+ trap_Argv(0, cmd, sizeof(cmd));
+
+ switch (cmd[9]) {
+ default:
+ case '\0':
+ trap_Cvar_Set("ui_voicemenu", "1");
+ break;
+ case '2':
+ trap_Cvar_Set("ui_voicemenu", "2");
+ break;
+ case '3':
+ trap_Cvar_Set("ui_voicemenu", "3");
+ break;
+ };
+
+ trap_SendConsoleCommand("menu tremulous_voicecmd\n");
+}
- trap_Args( message, 128 );
- Com_sprintf( command, 128, "tell %i %s", clientNum, message );
- trap_SendClientCommand( command );
+static void CG_UIMenu_f( void )
+{
+ trap_SendConsoleCommand( va( "menu \"%s\"\n", CG_Argv( 1 ) ) );
}
-typedef struct
+static void CG_KillMessage_f( void )
{
- char *cmd;
- void (*function)( void );
-} consoleCommand_t;
+ char msg1[ 33 * 3 + 1];
+ char msg2[ 33 * 3 + 1 ];
+ trap_Argv( 1, msg1, sizeof(msg1) );
+ trap_Argv( 2, msg2, sizeof(msg2) );
+ CG_AddToKillMsg(msg1, msg2, WP_GRENADE);
+}
static consoleCommand_t commands[ ] =
{
- { "testgun", CG_TestGun_f },
- { "testmodel", CG_TestModel_f },
+ { "+scores", CG_ScoresDown_f },
+ { "-scores", CG_ScoresUp_f },
+ { "cgame_memory", BG_MemoryInfo },
+ { "clientlist", CG_ClientList_f },
+ { "destroyTestPS", CG_DestroyTestPS_f },
+ { "destroyTestTS", CG_DestroyTestTS_f },
{ "nextframe", CG_TestModelNextFrame_f },
- { "prevframe", CG_TestModelPrevFrame_f },
{ "nextskin", CG_TestModelNextSkin_f },
+ { "prevframe", CG_TestModelPrevFrame_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 },
+ { "scoresUp", CG_scrollScoresUp_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 },
+ { "sizeup", CG_SizeUp_f },
+ { "testgun", CG_TestGun_f },
+ { "testmodel", CG_TestModel_f },
{ "testPS", CG_TestPS_f },
- { "destroyTestPS", CG_DestroyTestPS_f },
{ "testTS", CG_TestTS_f },
- { "destroyTestTS", CG_DestroyTestTS_f },
+ { "ui_menu", CG_UIMenu_f },
+ { "viewpos", CG_Viewpos_f },
+ { "voicemenu", CG_VoiceMenu_f },
+ { "voicemenu2", CG_VoiceMenu_f },
+ { "voicemenu3", CG_VoiceMenu_f },
+ { "weapnext", CG_NextWeapon_f },
+ { "weapon", CG_Weapon_f },
+ { "weapprev", CG_PrevWeapon_f },
+ { "zcp", CG_CenterPrint_f },
+ { "zkill", CG_KillMessage_f }
};
-
/*
=================
CG_ConsoleCommand
@@ -233,33 +242,81 @@ Cmd_Argc() / Cmd_Argv()
*/
qboolean CG_ConsoleCommand( void )
{
- const char *cmd;
- const char *arg1;
- int i;
+ consoleCommand_t *cmd;
- cmd = CG_Argv( 0 );
+ cmd = bsearch( CG_Argv( 0 ), commands,
+ ARRAY_LEN( commands ), sizeof( commands[ 0 ] ),
+ cmdcmp );
- //TA: ugly hacky special case
- if( !Q_stricmp( cmd, "ui_menu" ) )
- {
- arg1 = CG_Argv( 1 );
- trap_SendConsoleCommand( va( "menu %s\n", arg1 ) );
- return qtrue;
- }
+ if( !cmd )
+ return qfalse;
- for( i = 0; i < sizeof( commands ) / sizeof( commands[ 0 ] ); i++ )
- {
- if( !Q_stricmp( cmd, commands[ i ].cmd ) )
- {
- commands[ i ].function( );
- return qtrue;
- }
- }
+ cmd->function( );
+ return qtrue;
+}
- return qfalse;
+#ifndef MODULE_INTERFACE_11
+/*
+==================
+CG_CompleteCallVote_f
+==================
+*/
+void CG_CompleteCallVote_f( int argNum ) {
+ switch( argNum ) {
+ case 2:
+ trap_Field_CompleteList(
+ "["
+ "\"allowbuild\","
+ "\"cancel\","
+ "\"denybuild\","
+ "\"draw\","
+ "\"extend\","
+ "\"kick\","
+ "\"map\","
+ "\"map_restart\","
+ "\"mute\","
+ "\"nextmap\","
+ "\"poll\","
+ "\"sudden_death\","
+ "\"unmute\" ]" );
+ break;
+ }
}
+static consoleCommandCompletions_t commandCompletions[ ] =
+{
+ { "callvote", CG_CompleteCallVote_f }
+};
+
+/*
+=================
+CG_Console_CompleteArgument
+
+Try to complete the client command line argument given in
+argNum. Returns true if a completion function is found in CGAME,
+otherwise client tries another completion method.
+The string has been tokenized and can be retrieved with
+Cmd_Argc() / Cmd_Argv()
+=================
+*/
+qboolean CG_Console_CompleteArgument( int argNum )
+{
+ consoleCommandCompletions_t *cmd;
+
+ // Skip command prefix character
+ cmd = bsearch( CG_Argv( 0 ) + 1, commandCompletions,
+ ARRAY_LEN( commandCompletions ), sizeof( commandCompletions[ 0 ] ),
+ cmdcmp );
+
+ if( !cmd )
+ return qfalse;
+
+ cmd->function( argNum );
+ return qtrue;
+}
+#endif
+
/*
=================
CG_InitConsoleCommands
@@ -272,7 +329,7 @@ void CG_InitConsoleCommands( void )
{
int i;
- for( i = 0 ; i < sizeof( commands ) / sizeof( commands[ 0 ] ) ; i++ )
+ for( i = 0; i < ARRAY_LEN( commands ); i++ )
trap_AddCommand( commands[ i ].cmd );
//
@@ -280,31 +337,28 @@ void CG_InitConsoleCommands( void )
// forwarded to the server after they are not recognized locally
//
trap_AddCommand( "kill" );
+ trap_AddCommand( "ui_messagemode" );
+ trap_AddCommand( "ui_messagemode2" );
+ trap_AddCommand( "ui_messagemode3" );
+ trap_AddCommand( "ui_messagemode4" );
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( "vsay_local" );
+ trap_AddCommand( "m" );
+ trap_AddCommand( "mt" );
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" );
@@ -315,11 +369,6 @@ void CG_InitConsoleCommands( void )
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" );
+ trap_AddCommand( "ignore" );
+ trap_AddCommand( "unignore" );
}
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 1a3ecae..38c9578 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,284 +17,84 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_draw.c -- draw all of the graphical elements during
// active (after loading) gameplay
-
#include "cg_local.h"
-#include "../ui/ui_shared.h"
+#include "ui/ui_shared.h"
-// used for scoreboard
-extern displayContextDef_t cgDC;
menuDef_t *menuScoreboard = NULL;
-int drawTeamOverlayModificationCount = -1;
-
-int sortedTeamPlayers[ TEAM_MAXOVERLAY ];
-int numSortedTeamPlayers;
-
-//TA UI
-int CG_Text_Width( const char *text, float scale, int limit )
+static void CG_AlignText( rectDef_t *rect, const char *text, float scale,
+ float w, float h,
+ int align, int valign,
+ float *x, float *y )
{
- int count,len;
- float out;
- glyphInfo_t *glyph;
- float useScale;
-// FIXME: see ui_main.c, same problem
-// const unsigned char *s = text;
- const char *s = text;
- fontInfo_t *font = &cgDC.Assets.textFont;
+ float tx, ty;
- if( scale <= cg_smallFont.value )
- font = &cgDC.Assets.smallFont;
- else if( scale > cg_bigFont.value )
- font = &cgDC.Assets.bigFont;
-
- useScale = scale * font->glyphScale;
- out = 0;
-
- if( text )
+ if( scale > 0.0f )
{
- len = strlen( text );
- if( limit > 0 && len > limit )
- len = limit;
-
- count = 0;
- while( s && *s && count < len )
- {
- if( Q_IsColorString( s ) )
- {
- s += 2;
- continue;
- }
- else
- {
- glyph = &font->glyphs[ (int)*s ];
- //TTimo: FIXME: getting nasty warnings without the cast,
- //hopefully this doesn't break the VM build
- out += glyph->xSkip;
- s++;
- count++;
- }
- }
+ w = UI_Text_Width( text, scale );
+ h = UI_Text_Height( text, scale );
}
- return out * useScale;
-}
-
-int CG_Text_Height( const char *text, float scale, int limit )
-{
- int len, count;
- float max;
- glyphInfo_t *glyph;
- float useScale;
-// TTimo: FIXME
-// const unsigned char *s = text;
- const char *s = text;
- fontInfo_t *font = &cgDC.Assets.textFont;
-
- if( scale <= cg_smallFont.value )
- font = &cgDC.Assets.smallFont;
- else if( scale > cg_bigFont.value )
- font = &cgDC.Assets.bigFont;
-
- useScale = scale * font->glyphScale;
- max = 0;
-
- if( text )
+ switch( align )
{
- len = strlen( text );
- if( limit > 0 && len > limit )
- len = limit;
-
- count = 0;
- while( s && *s && count < len )
- {
- if( Q_IsColorString( s ) )
- {
- s += 2;
- continue;
- }
- else
- {
- glyph = &font->glyphs[ (int)*s ];
- //TTimo: FIXME: getting nasty warnings without the cast,
- //hopefully this doesn't break the VM build
- if( max < glyph->height )
- max = glyph->height;
-
- s++;
- count++;
- }
- }
- }
-
- return max * useScale;
-}
+ default:
+ case ALIGN_LEFT:
+ tx = 0.0f;
+ break;
-void CG_Text_PaintChar( float x, float y, float width, float height, float scale,
- float s, float t, float s2, float t2, qhandle_t hShader )
-{
- float w, h;
- w = width * scale;
- h = height * scale;
- CG_AdjustFrom640( &x, &y, &w, &h );
- trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
-}
+ case ALIGN_RIGHT:
+ tx = rect->w - w;
+ break;
-void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text,
- float adjust, int limit, int style )
-{
- int len, count;
- vec4_t newColor;
- glyphInfo_t *glyph;
- float useScale;
- fontInfo_t *font = &cgDC.Assets.textFont;
+ case ALIGN_CENTER:
+ tx = ( rect->w - w ) / 2.0f;
+ break;
- if( scale <= cg_smallFont.value )
- font = &cgDC.Assets.smallFont;
- else if( scale > cg_bigFont.value )
- font = &cgDC.Assets.bigFont;
+ case ALIGN_NONE:
+ tx = 0;
+ break;
+ }
- useScale = scale * font->glyphScale;
- if( text )
+ switch( valign )
{
-// TTimo: FIXME
-// const unsigned char *s = text;
- const char *s = text;
-
- trap_R_SetColor( color );
- memcpy( &newColor[ 0 ], &color[ 0 ], sizeof( vec4_t ) );
- len = strlen( text );
-
- if( limit > 0 && len > limit )
- len = limit;
-
- count = 0;
- while( s && *s && count < len )
- {
- glyph = &font->glyphs[ (int)*s ];
- //TTimo: FIXME: getting nasty warnings without the cast,
- //hopefully this doesn't break the VM build
+ default:
+ case VALIGN_BOTTOM:
+ ty = rect->h;
+ break;
- if( Q_IsColorString( s ) )
- {
- memcpy( newColor, g_color_table[ ColorIndex( *( s + 1 ) ) ], sizeof( newColor ) );
- newColor[ 3 ] = color[ 3 ];
- trap_R_SetColor( newColor );
- s += 2;
- continue;
- }
- else
- {
- float yadj = useScale * glyph->top;
- if( style == ITEM_TEXTSTYLE_SHADOWED ||
- style == ITEM_TEXTSTYLE_SHADOWEDMORE )
- {
- int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
- colorBlack[ 3 ] = newColor[ 3 ];
- trap_R_SetColor( colorBlack );
- CG_Text_PaintChar( x + ofs, y - yadj + ofs,
- glyph->imageWidth,
- glyph->imageHeight,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
-
- colorBlack[ 3 ] = 1.0;
- trap_R_SetColor( newColor );
- }
- else if( style == ITEM_TEXTSTYLE_NEON )
- {
- vec4_t glow, outer, inner, white;
-
- glow[ 0 ] = newColor[ 0 ] * 0.5;
- glow[ 1 ] = newColor[ 1 ] * 0.5;
- glow[ 2 ] = newColor[ 2 ] * 0.5;
- glow[ 3 ] = newColor[ 3 ] * 0.2;
-
- outer[ 0 ] = newColor[ 0 ];
- outer[ 1 ] = newColor[ 1 ];
- outer[ 2 ] = newColor[ 2 ];
- outer[ 3 ] = newColor[ 3 ];
-
- inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5;
- inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5;
- inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5;
- inner[ 3 ] = newColor[ 3 ];
-
- white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f;
-
- trap_R_SetColor( glow );
- CG_Text_PaintChar( x - 3, y - yadj - 3,
- glyph->imageWidth + 6,
- glyph->imageHeight + 6,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
-
- trap_R_SetColor( outer );
- CG_Text_PaintChar( x - 1, y - yadj - 1,
- glyph->imageWidth + 2,
- glyph->imageHeight + 2,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
-
- trap_R_SetColor( inner );
- CG_Text_PaintChar( x - 0.5, y - yadj - 0.5,
- glyph->imageWidth + 1,
- glyph->imageHeight + 1,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
-
- trap_R_SetColor( white );
- }
+ case VALIGN_TOP:
+ ty = h;
+ break;
+ case VALIGN_CENTER:
+ ty = h + ( ( rect->h - h ) / 2.0f );
+ break;
- CG_Text_PaintChar( x, y - yadj,
- glyph->imageWidth,
- glyph->imageHeight,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
+ case VALIGN_NONE:
+ ty = 0;
+ break;
+ }
- x += ( glyph->xSkip * useScale ) + adjust;
- s++;
- count++;
- }
- }
+ if( x )
+ *x = rect->x + tx;
- trap_R_SetColor( NULL );
- }
+ if( y )
+ *y = rect->y + ty;
}
/*
==============
CG_DrawFieldPadded
-Draws large numbers for status bar and powerups
+Draws large numbers for status bar
==============
*/
static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int value )
@@ -307,7 +108,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val
charWidth = CHAR_WIDTH;
if( !( charHeight = ch ) )
- charWidth = CHAR_HEIGHT;
+ charHeight = CHAR_HEIGHT;
if( width < 1 )
return;
@@ -344,7 +145,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val
orgL = l;
- x += 2;
+ x += ( 2.0f * cgDC.aspectScale );
ptr = num;
while( *ptr && l )
@@ -373,7 +174,7 @@ static void CG_DrawFieldPadded( int x, int y, int width, int cw, int ch, int val
==============
CG_DrawField
-Draws large numbers for status bar and powerups
+Draws large numbers for status bar
==============
*/
void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
@@ -387,7 +188,7 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
charWidth = CHAR_WIDTH;
if( !( charHeight = ch ) )
- charWidth = CHAR_HEIGHT;
+ charHeight = CHAR_HEIGHT;
if( width < 1 )
return;
@@ -422,7 +223,7 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
if( l > width )
l = width;
- x += 2 + charWidth * ( width - l );
+ x += ( 2.0f * cgDC.aspectScale ) + charWidth * ( width - l );
ptr = num;
while( *ptr && l )
@@ -440,18 +241,22 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int value )
}
static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special, float progress )
+ int align, int textalign, int textStyle,
+ float borderSize, float progress )
{
- float rimWidth = rect->h / 20.0f;
+ float rimWidth;
float doneWidth, leftWidth;
- float tx, ty, tw, th;
+ float tx, ty;
char textBuffer[ 8 ];
- if( rimWidth < 0.6f )
- rimWidth = 0.6f;
-
- if( special >= 0.0f )
- rimWidth = special;
+ if( borderSize >= 0.0f )
+ rimWidth = borderSize;
+ else
+ {
+ rimWidth = rect->h / 20.0f;
+ if( rimWidth < 0.6f )
+ rimWidth = 0.6f;
+ }
if( progress < 0.0f )
progress = 0.0f;
@@ -464,7 +269,7 @@ static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale,
trap_R_SetColor( color );
//draw rim and bar
- if( align == ITEM_ALIGN_RIGHT )
+ if( align == ALIGN_RIGHT )
{
CG_DrawPic( rect->x, rect->y, rimWidth, rect->h, cgs.media.whiteShader );
CG_DrawPic( rect->x + rimWidth, rect->y,
@@ -490,31 +295,9 @@ static void CG_DrawProgressBar( rectDef_t *rect, vec4_t color, float scale,
if( scale > 0.0 )
{
Com_sprintf( textBuffer, sizeof( textBuffer ), "%d%%", (int)( progress * 100 ) );
- tw = CG_Text_Width( textBuffer, scale, 0 );
- th = scale * 40.0f;
-
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x + ( rect->w / 10.0f );
- ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
- break;
-
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - ( rect->w / 10.0f ) - tw;
- ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
- break;
-
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
- ty = rect->y + ( rect->h / 2.0f ) + ( th / 2.0f );
- break;
+ CG_AlignText( rect, textBuffer, scale, 0.0f, 0.0f, textalign, VALIGN_CENTER, &tx, &ty );
- default:
- tx = ty = 0.0f;
- }
-
- CG_Text_Paint( tx, ty, scale, color, textBuffer, 0, 0, textStyle );
+ UI_Text_Paint( tx, ty, scale, color, textBuffer, 0, 0, textStyle );
}
}
@@ -539,14 +322,17 @@ static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean p
value = ps->persistant[ PERS_CREDIT ];
if( value > -1 )
{
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS &&
- !CG_AtHighestClass( ) )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
- if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
+ if( !BG_AlienCanEvolve( cg.predictedPlayerState.stats[ STAT_CLASS ],
+ value, cgs.alienStage ) &&
+ cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME &&
+ ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) & 1 )
{
- if( ( ( cg.time - cg.lastEvolveAttempt ) / 300 ) % 2 )
- color[ 3 ] = 0.0f;
+ color[ 3 ] = 0.0f;
}
+
+ value /= ALIEN_CREDITS_PER_KILL;
}
trap_R_SetColor( color );
@@ -560,135 +346,66 @@ static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean p
}
}
-static void CG_DrawPlayerBankValue( rectDef_t *rect, vec4_t color, qboolean padding )
+static void CG_DrawPlayerCreditsFraction( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
- int value;
- playerState_t *ps;
-
- ps = &cg.snap->ps;
-
- value = ps->persistant[ PERS_BANK ];
- if( value > -1 )
- {
- trap_R_SetColor( color );
-
- if( padding )
- CG_DrawFieldPadded( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
- else
- CG_DrawField( rect->x, rect->y, 1, rect->w, rect->h, value );
-
- trap_R_SetColor( NULL );
- }
-}
-
-#define HH_MIN_ALPHA 0.2f
-#define HH_MAX_ALPHA 0.8f
-#define HH_ALPHA_DIFF (HH_MAX_ALPHA-HH_MIN_ALPHA)
+ float fraction;
+ float height;
-#define AH_MIN_ALPHA 0.2f
-#define AH_MAX_ALPHA 0.8f
-#define AH_ALPHA_DIFF (AH_MAX_ALPHA-AH_MIN_ALPHA)
-
-/*
-==============
-CG_DrawPlayerStamina1
-==============
-*/
-static void CG_DrawPlayerStamina1( rectDef_t *rect, vec4_t color, qhandle_t shader )
-{
- playerState_t *ps = &cg.snap->ps;
- float stamina = ps->stats[ STAT_STAMINA ];
- float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
- float progress;
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] != TEAM_ALIENS )
+ return;
- stamina -= ( 2 * (int)maxStaminaBy3 );
- progress = stamina / maxStaminaBy3;
+ fraction = ((float)(cg.predictedPlayerState.persistant[ PERS_CREDIT ] %
+ ALIEN_CREDITS_PER_KILL)) / ALIEN_CREDITS_PER_KILL;
- if( progress > 1.0f )
- progress = 1.0f;
- else if( progress < 0.0f )
- progress = 0.0f;
-
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ CG_AdjustFrom640( &rect->x, &rect->y, &rect->w, &rect->h );
+ height = rect->h * fraction;
trap_R_SetColor( color );
- CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_DrawStretchPic( rect->x, rect->y - height + rect->h, rect->w,
+ height, 0.0f, 1.0f - fraction, 1.0f, 1.0f, shader );
trap_R_SetColor( NULL );
}
-/*
-==============
-CG_DrawPlayerStamina2
-==============
-*/
-static void CG_DrawPlayerStamina2( rectDef_t *rect, vec4_t color, qhandle_t shader )
-{
- playerState_t *ps = &cg.snap->ps;
- float stamina = ps->stats[ STAT_STAMINA ];
- float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
- float progress;
-
- stamina -= (int)maxStaminaBy3;
- progress = stamina / maxStaminaBy3;
-
- if( progress > 1.0f )
- progress = 1.0f;
- else if( progress < 0.0f )
- progress = 0.0f;
-
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
-
- trap_R_SetColor( color );
- CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
- trap_R_SetColor( NULL );
-}
/*
==============
-CG_DrawPlayerStamina3
+CG_DrawPlayerStamina
==============
*/
-static void CG_DrawPlayerStamina3( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerStamina( int ownerDraw, rectDef_t *rect,
+ vec4_t backColor, vec4_t foreColor,
+ qhandle_t shader )
{
playerState_t *ps = &cg.snap->ps;
float stamina = ps->stats[ STAT_STAMINA ];
- float maxStaminaBy3 = (float)MAX_STAMINA / 3.0f;
+ float maxStaminaBy3 = (float)STAMINA_MAX / 3.0f;
float progress;
+ vec4_t color;
+ switch( ownerDraw )
+ {
+ case CG_PLAYER_STAMINA_1:
+ progress = ( stamina - 2 * (int)maxStaminaBy3 ) / maxStaminaBy3;
+ break;
+ case CG_PLAYER_STAMINA_2:
+ progress = ( stamina - (int)maxStaminaBy3 ) / maxStaminaBy3;
+ break;
+ case CG_PLAYER_STAMINA_3:
progress = stamina / maxStaminaBy3;
+ break;
+ case CG_PLAYER_STAMINA_4:
+ progress = ( stamina + STAMINA_MAX ) / STAMINA_MAX;
+ break;
+ default:
+ return;
+ }
if( progress > 1.0f )
progress = 1.0f;
else if( progress < 0.0f )
progress = 0.0f;
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
-
- trap_R_SetColor( color );
- CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
- trap_R_SetColor( NULL );
-}
-
-/*
-==============
-CG_DrawPlayerStamina4
-==============
-*/
-static void CG_DrawPlayerStamina4( rectDef_t *rect, vec4_t color, qhandle_t shader )
-{
- playerState_t *ps = &cg.snap->ps;
- float stamina = ps->stats[ STAT_STAMINA ];
- float progress;
-
- stamina += (float)MAX_STAMINA;
- progress = stamina / (float)MAX_STAMINA;
-
- if( progress > 1.0f )
- progress = 1.0f;
- else if( progress < 0.0f )
- progress = 0.0f;
-
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ Vector4Lerp( progress, backColor, foreColor, color );
trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
@@ -700,15 +417,28 @@ static void CG_DrawPlayerStamina4( rectDef_t *rect, vec4_t color, qhandle_t shad
CG_DrawPlayerStaminaBolt
==============
*/
-static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t backColor,
+ vec4_t foreColor, qhandle_t shader )
{
- playerState_t *ps = &cg.snap->ps;
- float stamina = ps->stats[ STAT_STAMINA ];
+ float stamina = cg.snap->ps.stats[ STAT_STAMINA ];
+ vec4_t color;
- if( stamina < 0 )
- color[ 3 ] = HH_MIN_ALPHA;
+ if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_SPEEDBOOST )
+ {
+ if( stamina >= 0 )
+ Vector4Lerp( ( sin( cg.time / 150.0f ) + 1 ) / 2,
+ backColor, foreColor, color );
+ else
+ Vector4Lerp( ( sin( cg.time / 2000.0f ) + 1 ) / 2,
+ backColor, foreColor, color );
+ }
else
- color[ 3 ] = HH_MAX_ALPHA;
+ {
+ if( stamina < 0 )
+ Vector4Copy( backColor, color );
+ else
+ Vector4Copy( foreColor, color );
+ }
trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
@@ -720,40 +450,42 @@ static void CG_DrawPlayerStaminaBolt( rectDef_t *rect, vec4_t color, qhandle_t s
CG_DrawPlayerClipsRing
==============
*/
-static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t backColor,
+ vec4_t foreColor, qhandle_t shader )
{
playerState_t *ps = &cg.snap->ps;
centity_t *cent;
float buildTime = ps->stats[ STAT_MISC ];
float progress;
float maxDelay;
+ weapon_t weapon;
+ vec4_t color;
cent = &cg_entities[ cg.snap->ps.clientNum ];
+ weapon = BG_GetPlayerWeapon( ps );
- switch( cent->currentState.weapon )
+ switch( weapon )
{
case WP_ABUILD:
case WP_ABUILD2:
case WP_HBUILD:
- case WP_HBUILD2:
- maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );
-
- if( buildTime > maxDelay )
- buildTime = maxDelay;
+ if( buildTime > MAXIMUM_BUILD_TIME )
+ buildTime = MAXIMUM_BUILD_TIME;
+ progress = ( MAXIMUM_BUILD_TIME - buildTime ) / MAXIMUM_BUILD_TIME;
- progress = ( maxDelay - buildTime ) / maxDelay;
-
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ Vector4Lerp( progress, backColor, foreColor, color );
break;
default:
if( ps->weaponstate == WEAPON_RELOADING )
{
- maxDelay = (float)BG_FindReloadTimeForWeapon( cent->currentState.weapon );
+ maxDelay = (float)BG_Weapon( cent->currentState.weapon )->reloadTime;
progress = ( maxDelay - (float)ps->weaponTime ) / maxDelay;
- color[ 3 ] = HH_MIN_ALPHA + ( progress * HH_ALPHA_DIFF );
+ Vector4Lerp( progress, backColor, foreColor, color );
}
+ else
+ Com_Memcpy( color, foreColor, sizeof( color ) );
break;
}
@@ -767,24 +499,20 @@ static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t color, qhandle_t sha
CG_DrawPlayerBuildTimerRing
==============
*/
-static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t backColor,
+ vec4_t foreColor, qhandle_t shader )
{
playerState_t *ps = &cg.snap->ps;
- centity_t *cent;
float buildTime = ps->stats[ STAT_MISC ];
float progress;
- float maxDelay;
-
- cent = &cg_entities[ cg.snap->ps.clientNum ];
+ vec4_t color;
- maxDelay = (float)BG_FindBuildDelayForWeapon( cent->currentState.weapon );
+ if( buildTime > MAXIMUM_BUILD_TIME )
+ buildTime = MAXIMUM_BUILD_TIME;
- if( buildTime > maxDelay )
- buildTime = maxDelay;
+ progress = ( MAXIMUM_BUILD_TIME - buildTime ) / MAXIMUM_BUILD_TIME;
- progress = ( maxDelay - buildTime ) / maxDelay;
-
- color[ 3 ] = AH_MIN_ALPHA + ( progress * AH_ALPHA_DIFF );
+ Vector4Lerp( progress, backColor, foreColor, color );
trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
@@ -796,17 +524,14 @@ static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t color, qhandle_
CG_DrawPlayerBoosted
==============
*/
-static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t backColor,
+ vec4_t foreColor, qhandle_t shader )
{
- playerState_t *ps = &cg.snap->ps;
- qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;
-
- if( boosted )
- color[ 3 ] = AH_MAX_ALPHA;
+ if( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTED )
+ trap_R_SetColor( foreColor );
else
- color[ 3 ] = AH_MIN_ALPHA;
+ trap_R_SetColor( backColor );
- trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
trap_R_SetColor( NULL );
}
@@ -816,26 +541,20 @@ static void CG_DrawPlayerBoosted( rectDef_t *rect, vec4_t color, qhandle_t shade
CG_DrawPlayerBoosterBolt
==============
*/
-static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t backColor,
+ vec4_t foreColor, qhandle_t shader )
{
- playerState_t *ps = &cg.snap->ps;
- qboolean boosted = ps->stats[ STAT_STATE ] & SS_BOOSTED;
- vec4_t localColor;
-
- Vector4Copy( color, localColor );
-
- if( boosted )
- {
- if( ps->stats[ STAT_BOOSTTIME ] > BOOST_TIME - 3000 )
- {
- qboolean flash = ( ps->stats[ STAT_BOOSTTIME ] / 500 ) % 2;
+ vec4_t color;
- if( flash )
- localColor[ 3 ] = 1.0f;
- }
- }
+ // Flash bolts when the boost is almost out
+ if( ( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTED ) &&
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTEDWARNING ) )
+ Vector4Lerp( ( sin( cg.time / 100.0f ) + 1 ) / 2,
+ backColor, foreColor, color );
+ else
+ Vector4Copy( foreColor, color );
- trap_R_SetColor( localColor );
+ trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
trap_R_SetColor( NULL );
}
@@ -847,38 +566,49 @@ CG_DrawPlayerPoisonBarbs
*/
static void CG_DrawPlayerPoisonBarbs( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
- playerState_t *ps = &cg.snap->ps;
- int x = rect->x;
- int y = rect->y;
- int width = rect->w;
- int height = rect->h;
- qboolean vertical;
- int iconsize, numBarbs, i;
+ qboolean vertical;
+ float x = rect->x, y = rect->y;
+ float width = rect->w, height = rect->h;
+ float diff;
+ int iconsize, numBarbs, maxBarbs;
- numBarbs = ps->ammo;
+ maxBarbs = BG_Weapon( cg.snap->ps.weapon )->maxAmmo;
+ numBarbs = cg.snap->ps.ammo;
+ if( maxBarbs <= 0 || numBarbs <= 0 )
+ return;
+
+ // adjust these first to ensure the aspect ratio of the barb image is
+ // preserved
+ CG_AdjustFrom640( &x, &y, &width, &height );
if( height > width )
{
vertical = qtrue;
iconsize = width;
+ if( maxBarbs != 1 ) // avoid division by zero
+ diff = ( height - iconsize ) / (float)( maxBarbs - 1 );
+ else
+ diff = 0; // doesn't matter, won't be used
}
- else if( height <= width )
+ else
{
vertical = qfalse;
iconsize = height;
+ if( maxBarbs != 1 )
+ diff = ( width - iconsize ) / (float)( maxBarbs - 1 );
+ else
+ diff = 0;
}
- if( color[ 3 ] != 0.0 )
- trap_R_SetColor( color );
+ trap_R_SetColor( color );
- for( i = 0; i < numBarbs; i ++ )
+ for( ; numBarbs > 0; numBarbs-- )
{
+ trap_R_DrawStretchPic( x, y, iconsize, iconsize, 0, 0, 1, 1, shader );
if( vertical )
- y += iconsize;
+ y += diff;
else
- x += iconsize;
-
- CG_DrawPic( x, y, iconsize, iconsize, shader );
+ x += diff;
}
trap_R_SetColor( NULL );
@@ -889,70 +619,83 @@ static void CG_DrawPlayerPoisonBarbs( rectDef_t *rect, vec4_t color, qhandle_t s
CG_DrawPlayerWallclimbing
==============
*/
-static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t backColor, vec4_t foreColor, qhandle_t shader )
{
- playerState_t *ps = &cg.snap->ps;
- qboolean ww = ps->stats[ STAT_STATE ] & SS_WALLCLIMBING;
-
- if( ww )
- color[ 3 ] = AH_MAX_ALPHA;
+ if( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING )
+ trap_R_SetColor( foreColor );
else
- color[ 3 ] = AH_MIN_ALPHA;
+ trap_R_SetColor( backColor );
- trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
trap_R_SetColor( NULL );
}
-static void CG_DrawPlayerStamina( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
-{
- playerState_t *ps = &cg.snap->ps;
- int stamina = ps->stats[ STAT_STAMINA ];
- float progress = ( (float)stamina + (float)MAX_STAMINA ) / ( (float)MAX_STAMINA * 2.0f );
-
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, progress );
-}
-
static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color )
{
- int value;
- centity_t *cent;
- playerState_t *ps;
-
- cent = &cg_entities[ cg.snap->ps.clientNum ];
- ps = &cg.snap->ps;
+ int value;
+ int valueMarked = -1;
+ qboolean bp = qfalse;
- if( cent->currentState.weapon )
+ switch( cg.snap->ps.stats[ STAT_WEAPON ] )
{
- switch( cent->currentState.weapon )
- {
- case WP_ABUILD:
- case WP_ABUILD2:
- //percentage of BP remaining
- value = cgs.alienBuildPoints;
- break;
+ case WP_NONE:
+ case WP_BLASTER:
+ return;
- case WP_HBUILD:
- case WP_HBUILD2:
- //percentage of BP remaining
- value = cgs.humanBuildPoints;
- break;
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ value = cg.snap->ps.persistant[ PERS_BP ];
+ valueMarked = cg.snap->ps.persistant[ PERS_MARKEDBP ];
+ bp = qtrue;
+ break;
- default:
- value = ps->ammo;
- break;
- }
+ default:
+ value = cg.snap->ps.ammo;
+ break;
+ }
+
+ if( value > 999 )
+ value = 999;
+ if( valueMarked > 999 )
+ valueMarked = 999;
- if( value > 999 )
- value = 999;
+ if( value > -1 )
+ {
+ float tx, ty;
+ const char *text;
+ float scale;
+ int len;
- if( value > -1 )
+ trap_R_SetColor( color );
+ if( !bp )
{
- trap_R_SetColor( color );
- CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ CG_DrawField( rect->x - 5, rect->y, 4, rect->w / 4, rect->h, value );
trap_R_SetColor( NULL );
+ return;
}
+
+ if( valueMarked > 0 )
+ text = va( "%d+(%d)", value, valueMarked );
+ else
+ text = va( "%d", value );
+
+ len = strlen( text );
+
+ if( len <= 4 )
+ scale = 0.50;
+ else if( len <= 6 )
+ scale = 0.43;
+ else if( len == 7 )
+ scale = 0.36;
+ else if( len == 8 )
+ scale = 0.33;
+ else
+ scale = 0.31;
+
+ CG_AlignText( rect, text, scale, 0.0f, 0.0f, ALIGN_RIGHT, VALIGN_CENTER, &tx, &ty );
+ UI_Text_Paint( tx + 1, ty, scale, color, text, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ trap_R_SetColor( NULL );
}
}
@@ -964,7 +707,7 @@ CG_DrawAlienSense
*/
static void CG_DrawAlienSense( rectDef_t *rect )
{
- if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_PCLASS ], SCA_ALIENSENSE ) )
+ if( BG_ClassHasAbility( cg.snap->ps.stats[ STAT_CLASS ], SCA_ALIENSENSE ) )
CG_AlienSense( rect );
}
@@ -999,19 +742,25 @@ static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t co
es = &cg_entities[ trace.entityNum ].currentState;
- if( es->eType == ET_BUILDABLE && BG_FindUsableForBuildable( es->modelindex ) &&
- cg.predictedPlayerState.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
+ if( es->eType == ET_BUILDABLE && BG_Buildable( es->modelindex )->usable &&
+ cg.predictedPlayerState.stats[ STAT_TEAM ] == BG_Buildable( es->modelindex )->team )
{
//hack to prevent showing the usable buildable when you aren't carrying an energy weapon
if( ( es->modelindex == BA_H_REACTOR || es->modelindex == BA_H_REPEATER ) &&
- ( !BG_FindUsesEnergyForWeapon( cg.snap->ps.weapon ) ||
- BG_FindInfinteAmmoForWeapon( cg.snap->ps.weapon ) ) )
+ ( !BG_Weapon( cg.snap->ps.weapon )->usesEnergy ||
+ BG_Weapon( cg.snap->ps.weapon )->infiniteAmmo ) )
+ {
+ cg.nearUsableBuildable = BA_NONE;
return;
+ }
trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
trap_R_SetColor( NULL );
+ cg.nearUsableBuildable = es->modelindex;
}
+ else
+ cg.nearUsableBuildable = BA_NONE;
}
@@ -1019,221 +768,381 @@ static void CG_DrawUsableBuildable( rectDef_t *rect, qhandle_t shader, vec4_t co
static void CG_DrawPlayerBuildTimer( rectDef_t *rect, vec4_t color )
{
- float progress;
int index;
- centity_t *cent;
playerState_t *ps;
- cent = &cg_entities[ cg.snap->ps.clientNum ];
ps = &cg.snap->ps;
- if( cent->currentState.weapon )
- {
- switch( cent->currentState.weapon )
- {
- case WP_ABUILD:
- progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_BASE_DELAY;
- break;
-
- case WP_ABUILD2:
- progress = (float)ps->stats[ STAT_MISC ] / (float)ABUILDER_ADV_DELAY;
- break;
-
- case WP_HBUILD:
- progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD_DELAY;
- break;
-
- case WP_HBUILD2:
- progress = (float)ps->stats[ STAT_MISC ] / (float)HBUILD2_DELAY;
- break;
+ if( ps->stats[ STAT_MISC ] <= 0 )
+ return;
- default:
- return;
- break;
- }
+ switch( ps->stats[ STAT_WEAPON ] )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ break;
- if( !ps->stats[ STAT_MISC ] )
+ default:
return;
+ }
- index = (int)( progress * 8.0f );
-
- if( index > 7 )
- index = 7;
- else if( index < 0 )
- index = 0;
-
- if( cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME )
- {
- if( ( ( cg.time - cg.lastBuildAttempt ) / 300 ) % 2 )
- {
- color[ 0 ] = 1.0f;
- color[ 1 ] = color[ 2 ] = 0.0f;
- color[ 3 ] = 1.0f;
- }
- }
+ index = 8 * ( ps->stats[ STAT_MISC ] - 1 ) / MAXIMUM_BUILD_TIME;
+ if( index > 7 )
+ index = 7;
+ else if( index < 0 )
+ index = 0;
- trap_R_SetColor( color );
- CG_DrawPic( rect->x, rect->y, rect->w, rect->h,
- cgs.media.buildWeaponTimerPie[ index ] );
- trap_R_SetColor( NULL );
+ if( cg.time - cg.lastBuildAttempt <= BUILD_DELAY_TIME &&
+ ( ( cg.time - cg.lastBuildAttempt ) / 300 ) % 2 )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = color[ 2 ] = 0.0f;
+ color[ 3 ] = 1.0f;
}
+
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h,
+ cgs.media.buildWeaponTimerPie[ index ] );
+ trap_R_SetColor( NULL );
}
static void CG_DrawPlayerClipsValue( rectDef_t *rect, vec4_t color )
{
int value;
- centity_t *cent;
- playerState_t *ps;
-
- cent = &cg_entities[ cg.snap->ps.clientNum ];
- ps = &cg.snap->ps;
+ playerState_t *ps = &cg.snap->ps;
- if( cent->currentState.weapon )
+ switch( ps->stats[ STAT_WEAPON ] )
{
- switch( cent->currentState.weapon )
- {
- case WP_ABUILD:
- case WP_ABUILD2:
- case WP_HBUILD:
- case WP_HBUILD2:
- break;
+ case WP_NONE:
+ case WP_BLASTER:
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ return;
- default:
- value = ps->clips;
+ default:
+ value = ps->clips;
- if( value > -1 )
- {
- trap_R_SetColor( color );
- CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
- trap_R_SetColor( NULL );
- }
- break;
- }
+ if( value > -1 )
+ {
+ trap_R_SetColor( color );
+ CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ trap_R_SetColor( NULL );
+ }
+ break;
}
}
static void CG_DrawPlayerHealthValue( rectDef_t *rect, vec4_t color )
{
- playerState_t *ps;
- int value;
-
- ps = &cg.snap->ps;
-
- value = ps->stats[ STAT_HEALTH ];
-
trap_R_SetColor( color );
- CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h, value );
+ CG_DrawField( rect->x, rect->y, 4, rect->w / 4, rect->h,
+ cg.snap->ps.stats[ STAT_HEALTH ] );
trap_R_SetColor( NULL );
}
-static void CG_DrawPlayerHealthBar( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
-{
- playerState_t *ps;
- float total;
-
- ps = &cg.snap->ps;
-
- total = ( (float)ps->stats[ STAT_HEALTH ] / (float)ps->stats[ STAT_MAX_HEALTH ] );
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
-}
-
/*
==============
CG_DrawPlayerHealthCross
==============
*/
-static void CG_DrawPlayerHealthCross( rectDef_t *rect, vec4_t color, qhandle_t shader )
+static void CG_DrawPlayerHealthCross( rectDef_t *rect, vec4_t ref_color )
{
- playerState_t *ps = &cg.snap->ps;
- int health = ps->stats[ STAT_HEALTH ];
+ qhandle_t shader;
+ vec4_t color;
+ float ref_alpha;
+
+ // Pick the current icon
+ shader = cgs.media.healthCross;
+ if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_3X )
+ shader = cgs.media.healthCross3X;
+ else if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_2X )
+ {
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ shader = cgs.media.healthCross2X;
+ else
+ shader = cgs.media.healthCrossMedkit;
+ }
+ else if( cg.snap->ps.stats[ STAT_STATE ] & SS_POISONED )
+ shader = cgs.media.healthCrossPoisoned;
- if( health < 10 )
+ // Pick the alpha value
+ Vector4Copy( ref_color, color );
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS &&
+ cg.snap->ps.stats[ STAT_HEALTH ] < 10 )
{
color[ 0 ] = 1.0f;
color[ 1 ] = color[ 2 ] = 0.0f;
}
+ ref_alpha = ref_color[ 3 ];
+ if( cg.snap->ps.stats[ STAT_STATE ] & SS_HEALING_ACTIVE )
+ ref_alpha = 1.0f;
+
+ // Don't fade from nothing
+ if( !cg.lastHealthCross )
+ cg.lastHealthCross = shader;
+
+ // Fade the icon during transition
+ if( cg.lastHealthCross != shader )
+ {
+ cg.healthCrossFade += cg.frametime / 500.0f;
+ if( cg.healthCrossFade > 1.0f )
+ {
+ cg.healthCrossFade = 0.0f;
+ cg.lastHealthCross = shader;
+ }
+ else
+ {
+ // Fading between two icons
+ color[ 3 ] = ref_alpha * cg.healthCrossFade;
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ color[ 3 ] = ref_alpha * ( 1.0f - cg.healthCrossFade );
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg.lastHealthCross );
+ trap_R_SetColor( NULL );
+ return;
+ }
+ }
+ // Not fading, draw a single icon
+ color[ 3 ] = ref_alpha;
trap_R_SetColor( color );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
trap_R_SetColor( NULL );
}
-static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, const char *s, float fraction )
+static float CG_ChargeProgress( void )
{
- vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
- float tx, tw = CG_Text_Width( s, scale, 0 );
+ float progress;
+ int min = 0, max = 0;
- switch( align )
+ if( cg.snap->ps.weapon == WP_ALEVEL3 )
{
- case ITEM_ALIGN_LEFT:
- tx = 0.0f;
- break;
+ min = LEVEL3_POUNCE_TIME_MIN;
+ max = LEVEL3_POUNCE_TIME;
+ }
+ else if( cg.snap->ps.weapon == WP_ALEVEL3_UPG )
+ {
+ min = LEVEL3_POUNCE_TIME_MIN;
+ max = LEVEL3_POUNCE_TIME_UPG;
+ }
+ else if( cg.snap->ps.weapon == WP_ALEVEL4 )
+ {
+ if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_CHARGING )
+ {
+ min = 0;
+ max = LEVEL4_TRAMPLE_DURATION;
+ }
+ else
+ {
+ min = LEVEL4_TRAMPLE_CHARGE_MIN;
+ max = LEVEL4_TRAMPLE_CHARGE_MAX;
+ }
+ }
+ else if( cg.snap->ps.weapon == WP_LUCIFER_CANNON )
+ {
+ min = LCANNON_CHARGE_TIME_MIN;
+ max = LCANNON_CHARGE_TIME_MAX;
+ }
- case ITEM_ALIGN_RIGHT:
- tx = rect->w - tw;
- break;
+ if( max - min <= 0.0f )
+ return 0.0f;
- case ITEM_ALIGN_CENTER:
- tx = ( rect->w / 2.0f ) - ( tw / 2.0f );
- break;
+ progress = ( (float)cg.predictedPlayerState.stats[ STAT_MISC ] - min ) /
+ ( max - min );
- default:
- tx = 0.0f;
+ if( progress > 1.0f )
+ return 1.0f;
+
+ if( progress < 0.0f )
+ return 0.0f;
+
+ return progress;
+}
+
+#define CHARGE_BAR_FADE_RATE 0.002f
+
+static void CG_DrawPlayerChargeBarBG( rectDef_t *rect, vec4_t ref_color,
+ qhandle_t shader )
+{
+ vec4_t color;
+
+ if( !cg_drawChargeBar.integer || cg.chargeMeterAlpha <= 0.0f )
+ return;
+
+ color[ 0 ] = ref_color[ 0 ];
+ color[ 1 ] = ref_color[ 1 ];
+ color[ 2 ] = ref_color[ 2 ];
+ color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha;
+
+ // Draw meter background
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+// FIXME: This should come from the element info
+#define CHARGE_BAR_CAP_SIZE 3
+
+static void CG_DrawPlayerChargeBar( rectDef_t *rect, vec4_t ref_color,
+ qhandle_t shader )
+{
+ vec4_t color;
+ float x, y, width, height, cap_size, progress;
+
+ if( !cg_drawChargeBar.integer )
+ return;
+
+ // Get progress proportion and pump fade
+ progress = CG_ChargeProgress();
+ if( progress <= 0.0f )
+ {
+ cg.chargeMeterAlpha -= CHARGE_BAR_FADE_RATE * cg.frametime;
+ if( cg.chargeMeterAlpha <= 0.0f )
+ {
+ cg.chargeMeterAlpha = 0.0f;
+ return;
+ }
+ }
+ else
+ {
+ cg.chargeMeterValue = progress;
+ cg.chargeMeterAlpha += CHARGE_BAR_FADE_RATE * cg.frametime;
+ if( cg.chargeMeterAlpha > 1.0f )
+ cg.chargeMeterAlpha = 1.0f;
+ }
+
+ color[ 0 ] = ref_color[ 0 ];
+ color[ 1 ] = ref_color[ 1 ];
+ color[ 2 ] = ref_color[ 2 ];
+ color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha;
+
+ // Flash red for Lucifer Cannon warning
+ if( cg.snap->ps.weapon == WP_LUCIFER_CANNON &&
+ cg.snap->ps.stats[ STAT_MISC ] >= LCANNON_CHARGE_TIME_WARN &&
+ ( cg.time & 128 ) )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = 0.0f;
+ color[ 2 ] = 0.0f;
+ }
+
+ x = rect->x;
+ y = rect->y;
+
+ // Horizontal charge bar
+ if( rect->w >= rect->h )
+ {
+ width = ( rect->w - CHARGE_BAR_CAP_SIZE * 2 ) * cg.chargeMeterValue;
+ height = rect->h;
+ CG_AdjustFrom640( &x, &y, &width, &height );
+ cap_size = CHARGE_BAR_CAP_SIZE * cgs.screenXScale;
+
+ // Draw the meter
+ trap_R_SetColor( color );
+ trap_R_DrawStretchPic( x, y, cap_size, height, 0, 0, 1, 1, shader );
+ trap_R_DrawStretchPic( x + width + cap_size, y, cap_size, height,
+ 1, 0, 0, 1, shader );
+ trap_R_DrawStretchPic( x + cap_size, y, width, height, 1, 0, 1, 1, shader );
+ trap_R_SetColor( NULL );
}
+ // Vertical charge bar
+ else
+ {
+ y += rect->h;
+ width = rect->w;
+ height = ( rect->h - CHARGE_BAR_CAP_SIZE * 2 ) * cg.chargeMeterValue;
+ CG_AdjustFrom640( &x, &y, &width, &height );
+ cap_size = CHARGE_BAR_CAP_SIZE * cgs.screenYScale;
+
+ // Draw the meter
+ trap_R_SetColor( color );
+ trap_R_DrawStretchPic( x, y - cap_size, width, cap_size,
+ 0, 1, 1, 0, shader );
+ trap_R_DrawStretchPic( x, y - height - cap_size * 2, width,
+ cap_size, 0, 0, 1, 1, shader );
+ trap_R_DrawStretchPic( x, y - height - cap_size, width, height,
+ 0, 1, 1, 1, shader );
+ trap_R_SetColor( NULL );
+ }
+}
+
+static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int textalign, int textvalign,
+ const char *s, float fraction )
+{
+ vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float tx, ty;
+
+ CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty );
+
if( fraction < 1.0f )
- CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, white,
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, white,
s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
else
- CG_Text_Paint( rect->x + text_x + tx, rect->y + text_y, scale, color,
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, color,
s, 0, 0, ITEM_TEXTSTYLE_NEON );
}
static void CG_DrawMediaProgress( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
+ int align, int textalign, int textStyle,
+ float borderSize )
{
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.mediaFraction );
+ CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle,
+ borderSize, cg.mediaFraction );
}
static void CG_DrawMediaProgressLabel( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align )
+ vec4_t color, float scale, int textalign, int textvalign )
{
- CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Map and Textures", cg.mediaFraction );
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign,
+ "Map and Textures", cg.mediaFraction );
}
-static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
+static void CG_DrawBuildablesProgress( rectDef_t *rect, vec4_t color,
+ float scale, int align, int textalign,
+ int textStyle, float borderSize )
{
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.buildablesFraction );
+ CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle,
+ borderSize, cg.buildablesFraction );
}
static void CG_DrawBuildablesProgressLabel( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align )
+ vec4_t color, float scale, int textalign, int textvalign )
{
- CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Buildable Models", cg.buildablesFraction );
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign,
+ "Buildable Models", cg.buildablesFraction );
}
-static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
+static void CG_DrawCharModelProgress( rectDef_t *rect, vec4_t color,
+ float scale, int align, int textalign,
+ int textStyle, float borderSize )
{
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, cg.charModelFraction );
+ CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle,
+ borderSize, cg.charModelFraction );
}
static void CG_DrawCharModelProgressLabel( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align )
+ vec4_t color, float scale, int textalign, int textvalign )
{
- CG_DrawProgressLabel( rect, text_x, text_y, color, scale, align, "Character Models", cg.charModelFraction );
+ CG_DrawProgressLabel( rect, text_x, text_y, color, scale, textalign, textvalign,
+ "Character Models", cg.charModelFraction );
}
static void CG_DrawOverallProgress( rectDef_t *rect, vec4_t color, float scale,
- int align, int textStyle, int special )
+ int align, int textalign, int textStyle,
+ float borderSize )
{
float total;
- total = ( cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction ) / 3.0f;
- CG_DrawProgressBar( rect, color, scale, align, textStyle, special, total );
+ total = cg.charModelFraction + cg.buildablesFraction + cg.mediaFraction;
+ total /= 3.0f;
+
+ CG_DrawProgressBar( rect, color, scale, align, textalign, textStyle,
+ borderSize, total );
}
static void CG_DrawLevelShot( rectDef_t *rect )
@@ -1258,97 +1167,44 @@ static void CG_DrawLevelShot( rectDef_t *rect )
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, detail );
}
-static void CG_DrawLoadingString( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, int textStyle, const char *s )
-{
- float tw, th, tx;
- int pos, i;
- char buffer[ 1024 ];
- char *end;
-
- if( !s[ 0 ] )
- return;
-
- strcpy( buffer, s );
- tw = CG_Text_Width( s, scale, 0 );
- th = scale * 40.0f;
-
- pos = i = 0;
-
- while( pos < strlen( s ) )
- {
- strcpy( buffer, &s[ pos ] );
- tw = CG_Text_Width( buffer, scale, 0 );
-
- while( tw > rect->w )
- {
- end = strrchr( buffer, ' ' );
-
- if( end == NULL )
- break;
-
- *end = '\0';
- tw = CG_Text_Width( buffer, scale, 0 );
- }
-
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
-
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - tw;
- break;
-
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( tw / 2.0f );
- break;
-
- default:
- tx = 0.0f;
- }
-
- CG_Text_Paint( tx + text_x, rect->y + text_y + i * ( th + 3 ), scale, color,
- buffer, 0, 0, textStyle );
-
- pos += strlen( buffer ) + 1;
- i++;
- }
-}
-
static void CG_DrawLevelName( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align, int textStyle )
+ vec4_t color, float scale,
+ int textalign, int textvalign, int textStyle )
{
const char *s;
s = CG_ConfigString( CS_MESSAGE );
- CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, s );
}
static void CG_DrawMOTD( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align, int textStyle )
+ vec4_t color, float scale,
+ int textalign, int textvalign, int textStyle )
{
const char *s;
+ char parsed[ MAX_STRING_CHARS ];
s = CG_ConfigString( CS_MOTD );
- CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, s );
+ Q_ParseNewlines( parsed, s, sizeof( parsed ) );
+
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, parsed );
}
static void CG_DrawHostname( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align, int textStyle )
+ vec4_t color, float scale,
+ int textalign, int textvalign, int textStyle )
{
char buffer[ 1024 ];
const char *info;
info = CG_ConfigString( CS_SERVERINFO );
- Q_strncpyz( buffer, Info_ValueForKey( info, "sv_hostname" ), 1024 );
+ UI_EscapeEmoticons( buffer, Info_ValueForKey( info, "sv_hostname" ), sizeof( buffer ) );
Q_CleanStr( buffer );
- CG_DrawLoadingString( rect, text_x, text_y, color, scale, align, textStyle, buffer );
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, buffer );
}
/*
@@ -1409,30 +1265,29 @@ Draw all the status / pacifier stuff during level loading
*/
void CG_DrawLoadingScreen( void )
{
- Menu_Paint( Menus_FindByName( "Loading" ), qtrue );
+ menuDef_t *menu = Menus_FindByName( "Loading" );
+
+ Menu_Update( menu );
+ Menu_Paint( menu, qtrue );
}
float CG_GetValue( int ownerDraw )
{
- centity_t *cent;
playerState_t *ps;
+ weapon_t weapon;
- cent = &cg_entities[ cg.snap->ps.clientNum ];
ps = &cg.snap->ps;
+ weapon = BG_GetPlayerWeapon( ps );
switch( ownerDraw )
{
case CG_PLAYER_AMMO_VALUE:
- if( cent->currentState.weapon )
- {
+ if( weapon )
return ps->ammo;
- }
break;
case CG_PLAYER_CLIPS_VALUE:
- if( cent->currentState.weapon )
- {
+ if( weapon )
return ps->clips;
- }
break;
case CG_PLAYER_HEALTH:
return ps->stats[ STAT_HEALTH ];
@@ -1461,165 +1316,131 @@ static void CG_DrawKiller( rectDef_t *rect, float scale, vec4_t color,
if( cg.killerName[ 0 ] )
{
int x = rect->x + rect->w / 2;
- CG_Text_Paint( x - CG_Text_Width( CG_GetKillerText( ), scale, 0 ) / 2,
+ UI_Text_Paint( x - UI_Text_Width( CG_GetKillerText( ), scale ) / 2,
rect->y + rect->h, scale, color, CG_GetKillerText( ), 0, 0, textStyle );
}
}
-static void CG_Text_Paint_Limit( float *maxX, float x, float y, float scale,
- vec4_t color, const char* text, float adjust, int limit )
-{
- int len, count;
- vec4_t newColor;
- glyphInfo_t *glyph;
+#define SPECTATORS_PIXELS_PER_SECOND 30.0f
- if( text )
- {
-// TTimo: FIXME
-// const unsigned char *s = text; // bk001206 - unsigned
- const char *s = text;
- float max = *maxX;
- float useScale;
- fontInfo_t *font = &cgDC.Assets.textFont;
+/*
+==================
+CG_DrawTeamSpectators
+==================
+*/
+static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, int textvalign, vec4_t color, qhandle_t shader )
+{
+ float y;
+ char *text = cg.spectatorList;
+ float textWidth = UI_Text_Width( text, scale );
- if( scale <= cg_smallFont.value )
- font = &cgDC.Assets.smallFont;
- else if( scale > cg_bigFont.value )
- font = &cgDC.Assets.bigFont;
+ CG_AlignText( rect, text, scale, 0.0f, 0.0f, ALIGN_LEFT, textvalign, NULL, &y );
- useScale = scale * font->glyphScale;
- trap_R_SetColor( color );
- len = strlen( text );
+ if( textWidth > rect->w )
+ {
+ // The text is too wide to fit, so scroll it
+ int now = trap_Milliseconds( );
+ int delta = now - cg.spectatorTime;
- if( limit > 0 && len > limit )
- len = limit;
+ CG_SetClipRegion( rect->x, rect->y, rect->w, rect->h );
- count = 0;
+ UI_Text_Paint( rect->x - cg.spectatorOffset, y, scale, color, text, 0, 0, 0 );
+ UI_Text_Paint( rect->x + textWidth - cg.spectatorOffset, y, scale, color, text, 0, 0, 0 );
- while( s && *s && count < len )
- {
- glyph = &font->glyphs[ (int)*s ];
- //TTimo: FIXME: getting nasty warnings without the cast,
- //hopefully this doesn't break the VM build
+ CG_ClearClipRegion( );
- if( Q_IsColorString( s ) )
- {
- memcpy( newColor, g_color_table[ ColorIndex( *(s+1) ) ], sizeof( newColor ) );
- newColor[ 3 ] = color[ 3 ];
- trap_R_SetColor( newColor );
- s += 2;
- continue;
- }
- else
- {
- float yadj = useScale * glyph->top;
+ cg.spectatorOffset += ( delta / 1000.0f ) * SPECTATORS_PIXELS_PER_SECOND;
- if( CG_Text_Width( s, useScale, 1 ) + x > max )
- {
- *maxX = 0;
- break;
- }
+ while( cg.spectatorOffset > textWidth )
+ cg.spectatorOffset -= textWidth;
- CG_Text_PaintChar( x, y - yadj,
- glyph->imageWidth,
- glyph->imageHeight,
- useScale,
- glyph->s,
- glyph->t,
- glyph->s2,
- glyph->t2,
- glyph->glyph );
- x += ( glyph->xSkip * useScale ) + adjust;
- *maxX = x;
- count++;
- s++;
- }
- }
-
- trap_R_SetColor( NULL );
+ cg.spectatorTime = now;
+ }
+ else
+ {
+ UI_Text_Paint( rect->x, y, scale, color, text, 0, 0, 0 );
}
}
-static void CG_DrawTeamSpectators( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader )
+#define FOLLOWING_STRING "following "
+#define CHASING_STRING "chasing "
+
+/*
+==================
+CG_DrawFollow
+==================
+*/
+static void CG_DrawFollow( rectDef_t *rect, float text_x, float text_y,
+ vec4_t color, float scale, int textalign, int textvalign, int textStyle )
{
- if( cg.spectatorLen )
- {
- float maxX;
+ float tx, ty;
- if( cg.spectatorWidth == -1 )
- {
- cg.spectatorWidth = 0;
- cg.spectatorPaintX = rect->x + 1;
- cg.spectatorPaintX2 = -1;
- }
+ if( cg.snap && cg.snap->ps.pm_flags & PMF_FOLLOW )
+ {
+ char buffer[ MAX_STRING_CHARS ];
- if( cg.spectatorOffset > cg.spectatorLen )
- {
- cg.spectatorOffset = 0;
- cg.spectatorPaintX = rect->x + 1;
- cg.spectatorPaintX2 = -1;
- }
+ if( !cg.chaseFollow )
+ strcpy( buffer, FOLLOWING_STRING );
+ else
+ strcpy( buffer, CHASING_STRING );
- if( cg.time > cg.spectatorTime )
- {
- cg.spectatorTime = cg.time + 10;
+ strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name );
- if( cg.spectatorPaintX <= rect->x + 2 )
- {
- if( cg.spectatorOffset < cg.spectatorLen )
- {
- //TA: skip colour directives
- if( Q_IsColorString( &cg.spectatorList[ cg.spectatorOffset ] ) )
- cg.spectatorOffset += 2;
- else
- {
- cg.spectatorPaintX += CG_Text_Width( &cg.spectatorList[ cg.spectatorOffset ], scale, 1 ) - 1;
- cg.spectatorOffset++;
- }
- }
- else
- {
- cg.spectatorOffset = 0;
+ CG_AlignText( rect, buffer, scale, 0, 0, textalign, textvalign, &tx, &ty );
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, color, buffer, 0, 0,
+ textStyle );
+ }
+}
- if( cg.spectatorPaintX2 >= 0 )
- cg.spectatorPaintX = cg.spectatorPaintX2;
- else
- cg.spectatorPaintX = rect->x + rect->w - 2;
+/*
+==================
+CG_DrawTeamLabel
+==================
+*/
+static void CG_DrawTeamLabel( rectDef_t *rect, team_t team, float text_x, float text_y,
+ vec4_t color, float scale, int textalign, int textvalign, int textStyle )
+{
+ char *t;
+ char stage[ MAX_TOKEN_CHARS ];
+ const char *s;
+ float tx, ty;
- cg.spectatorPaintX2 = -1;
- }
- }
- else
- {
- cg.spectatorPaintX--;
+ stage[ 0 ] = '\0';
- if( cg.spectatorPaintX2 >= 0 )
- cg.spectatorPaintX2--;
- }
- }
+ switch( team )
+ {
+ case TEAM_ALIENS:
+ t = "Aliens";
+ if( cg.intermissionStarted )
+ Com_sprintf( stage, MAX_TOKEN_CHARS, "(Stage %d)", cgs.alienStage + 1 );
+ break;
- maxX = rect->x + rect->w - 2;
+ case TEAM_HUMANS:
+ t = "Humans";
+ if( cg.intermissionStarted )
+ Com_sprintf( stage, MAX_TOKEN_CHARS, "(Stage %d)", cgs.humanStage + 1 );
+ break;
- CG_Text_Paint_Limit( &maxX, cg.spectatorPaintX, rect->y + rect->h - 3, scale, color,
- &cg.spectatorList[ cg.spectatorOffset ], 0, 0 );
+ default:
+ t = "";
+ break;
+ }
- if( cg.spectatorPaintX2 >= 0 )
- {
- float maxX2 = rect->x + rect->w - 2;
- CG_Text_Paint_Limit( &maxX2, cg.spectatorPaintX2, rect->y + rect->h - 3, scale,
- color, cg.spectatorList, 0, cg.spectatorOffset );
- }
+ switch( textalign )
+ {
+ default:
+ case ALIGN_LEFT:
+ s = va( "%s %s", t, stage );
+ break;
- if( cg.spectatorOffset && maxX > 0 )
- {
- // if we have an offset ( we are skipping the first part of the string ) and we fit the string
- if( cg.spectatorPaintX2 == -1 )
- cg.spectatorPaintX2 = rect->x + rect->w - 2;
- }
- else
- cg.spectatorPaintX2 = -1;
+ case ALIGN_RIGHT:
+ s = va( "%s %s", stage, t );
+ break;
}
+
+ CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty );
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle );
}
/*
@@ -1628,70 +1449,52 @@ CG_DrawStageReport
==================
*/
static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y,
- vec4_t color, float scale, int align, int textStyle )
+ vec4_t color, float scale, int textalign, int textvalign, int textStyle )
{
char s[ MAX_TOKEN_CHARS ];
- int tx, w, kills;
+ float tx, ty;
- if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_NONE && !cg.intermissionStarted )
+ if( cg.intermissionStarted )
return;
- if( cg.intermissionStarted )
- {
- Com_sprintf( s, MAX_TOKEN_CHARS,
- "Stage %d" //PH34R MY MAD-LEET CODING SKILLZ
- " "
- "Stage %d",
- cgs.alienStage + 1, cgs.humanStage + 1 );
- }
- else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_NONE )
+ return;
+
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
- kills = cgs.alienNextStageThreshold - cgs.alienKills;
+ int kills = ceil( (float)(cgs.alienNextStageThreshold - cgs.alienCredits) / ALIEN_CREDITS_PER_KILL );
+ if( kills < 0 )
+ kills = 0;
if( cgs.alienNextStageThreshold < 0 )
Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.alienStage + 1 );
else if( kills == 1 )
- Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
- cgs.alienStage + 1, kills );
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, 1 frag for next stage",
+ cgs.alienStage + 1 );
else
- Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d frags for next stage",
cgs.alienStage + 1, kills );
}
- else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ else if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
- kills = cgs.humanNextStageThreshold - cgs.humanKills;
+ int credits = cgs.humanNextStageThreshold - cgs.humanCredits;
+
+ if( credits < 0 )
+ credits = 0;
if( cgs.humanNextStageThreshold < 0 )
Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d", cgs.humanStage + 1 );
- else if( kills == 1 )
- Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kill for next stage",
- cgs.humanStage + 1, kills );
+ else if( credits == 1 )
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, 1 credit for next stage",
+ cgs.humanStage + 1 );
else
- Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d kills for next stage",
- cgs.humanStage + 1, kills );
+ Com_sprintf( s, MAX_TOKEN_CHARS, "Stage %d, %d credits for next stage",
+ cgs.humanStage + 1, credits );
}
- w = CG_Text_Width( s, scale, 0 );
-
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
-
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - w;
- break;
-
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
- break;
-
- default:
- tx = 0.0f;
- }
+ CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty );
- CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle );
}
/*
@@ -1699,15 +1502,17 @@ static void CG_DrawStageReport( rectDef_t *rect, float text_x, float text_y,
CG_DrawFPS
==================
*/
-//TA: personally i think this should be longer - it should really be a cvar
#define FPS_FRAMES 20
#define FPS_STRING "fps"
static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
- float scale, vec4_t color, int align, int textStyle,
+ float scale, vec4_t color,
+ int textalign, int textvalign, int textStyle,
qboolean scalableText )
{
- char *s;
- int tx, w, totalWidth, strLength;
+ const char *s;
+ float tx, ty;
+ float w, h, totalWidth;
+ int strLength;
static int previousTimes[ FPS_FRAMES ];
static int index;
int i, total;
@@ -1741,27 +1546,12 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
fps = 1000 * FPS_FRAMES / total;
s = va( "%d", fps );
- w = CG_Text_Width( "0", scale, 0 );
+ w = UI_Text_Width( "0", scale );
+ h = UI_Text_Height( "0", scale );
strLength = CG_DrawStrlen( s );
- totalWidth = CG_Text_Width( FPS_STRING, scale, 0 ) + w * strLength;
+ totalWidth = UI_Text_Width( FPS_STRING, scale ) + w * strLength;
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
-
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - totalWidth;
- break;
-
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
- break;
-
- default:
- tx = 0.0f;
- }
+ CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty );
if( scalableText )
{
@@ -1772,8 +1562,10 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
c[ 0 ] = s[ i ];
c[ 1 ] = '\0';
- CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle );
}
+
+ UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, FPS_STRING, 0, 0, textStyle );
}
else
{
@@ -1781,9 +1573,6 @@ static void CG_DrawFPS( rectDef_t *rect, float text_x, float text_y,
CG_DrawField( rect->x, rect->y, 3, rect->w / 3, rect->h, fps );
trap_R_SetColor( NULL );
}
-
- if( scalableText )
- CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, FPS_STRING, 0, 0, textStyle );
}
}
@@ -1844,10 +1633,13 @@ CG_DrawTimer
=================
*/
static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y,
- float scale, vec4_t color, int align, int textStyle )
+ float scale, vec4_t color,
+ int textalign, int textvalign, int textStyle )
{
- char *s;
- int i, tx, w, totalWidth, strLength;
+ const char *s;
+ float tx, ty;
+ int i, strLength;
+ float w, h, totalWidth;
int mins, seconds, tens;
int msec;
@@ -1863,36 +1655,255 @@ static void CG_DrawTimer( rectDef_t *rect, float text_x, float text_y,
seconds -= tens * 10;
s = va( "%d:%d%d", mins, tens, seconds );
- w = CG_Text_Width( "0", scale, 0 );
+ w = UI_Text_Width( "0", scale );
+ h = UI_Text_Height( "0", scale );
strLength = CG_DrawStrlen( s );
totalWidth = w * strLength;
- switch( align )
+ CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty );
+
+ for( i = 0; i < strLength; i++ )
{
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
+ char c[ 2 ];
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - totalWidth;
- break;
+ c[ 0 ] = s[ i ];
+ c[ 1 ] = '\0';
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
- break;
+ UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle );
+ }
+}
- default:
- tx = 0.0f;
+/*
+=================
+CG_DrawTeamOverlay
+=================
+*/
+
+typedef enum
+{
+ TEAMOVERLAY_OFF,
+ TEAMOVERLAY_ALL,
+ TEAMOVERLAY_SUPPORT,
+ TEAMOVERLAY_NEARBY,
+} teamOverlayMode_t;
+
+typedef enum
+{
+ TEAMOVERLAY_SORT_NONE,
+ TEAMOVERLAY_SORT_SCORE,
+ TEAMOVERLAY_SORT_WEAPONCLASS,
+} teamOverlaySort_t;
+
+static int QDECL SortScore( const void *a, const void *b )
+{
+ int na = *(int *)a;
+ int nb = *(int *)b;
+
+ return( cgs.clientinfo[ nb ].score - cgs.clientinfo[ na ].score );
+}
+
+static int QDECL SortWeaponClass( const void *a, const void *b )
+{
+ int out;
+ clientInfo_t *ca = cgs.clientinfo + *(int *)a;
+ clientInfo_t *cb = cgs.clientinfo + *(int *)b;
+
+ out = cb->curWeaponClass - ca->curWeaponClass;
+
+ // We want grangers on top. ckits are already on top without the special case.
+ if( ca->team == TEAM_ALIENS )
+ {
+ if( ca->curWeaponClass == PCL_ALIEN_BUILDER0_UPG ||
+ cb->curWeaponClass == PCL_ALIEN_BUILDER0_UPG ||
+ ca->curWeaponClass == PCL_ALIEN_BUILDER0 ||
+ cb->curWeaponClass == PCL_ALIEN_BUILDER0 )
+ {
+ out = -out;
+ }
}
- for( i = 0; i < strLength; i++ )
+ return( out );
+}
+
+static void CG_DrawTeamOverlay( rectDef_t *rect, float scale, vec4_t color )
+{
+ const char *s;
+ int i;
+ float x = rect->x;
+ float y;
+ clientInfo_t *ci, *pci;
+ vec4_t tcolor;
+ float iconSize = rect->h / 8.0f;
+ float leftMargin = 4.0f;
+ float iconTopMargin = 2.0f;
+ float midSep = 2.0f;
+ float backgroundWidth = rect->w;
+ float fontScale = 0.30f;
+ float vPad = 0.0f;
+ float nameWidth = 0.5f * rect->w;
+ char name[ MAX_NAME_LENGTH + 2 ];
+ int maxDisplayCount = 0;
+ int displayCount = 0;
+ float nameMaxX, nameMaxXCp;
+ float maxX = rect->x + rect->w;
+ float maxXCp = maxX;
+ weapon_t curWeapon = WP_NONE;
+ teamOverlayMode_t mode = cg_drawTeamOverlay.integer;
+ teamOverlaySort_t sort = cg_teamOverlaySortMode.integer;
+ int displayClients[ MAX_CLIENTS ];
+
+ if( cg.predictedPlayerState.pm_type == PM_SPECTATOR )
+ return;
+
+ if( mode == TEAMOVERLAY_OFF || !cg_teamOverlayMaxPlayers.integer )
+ return;
+
+ if( !cgs.teaminfoReceievedTime )
+ return;
+
+ if( cg.showScores ||
+ cg.predictedPlayerState.pm_type == PM_INTERMISSION )
+ return;
+
+ pci = cgs.clientinfo + cg.snap->ps.clientNum;
+
+ if( mode == TEAMOVERLAY_ALL || mode == TEAMOVERLAY_SUPPORT )
{
- char c[ 2 ];
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+ ci = cgs.clientinfo + i;
+ if( ci->infoValid && pci != ci && ci->team == pci->team )
+ {
+ if( mode == TEAMOVERLAY_ALL )
+ displayClients[ maxDisplayCount++ ] = i;
+ else
+ {
+ if( ci->curWeaponClass == PCL_ALIEN_BUILDER0 ||
+ ci->curWeaponClass == PCL_ALIEN_BUILDER0_UPG ||
+ ci->curWeaponClass == PCL_ALIEN_LEVEL1 ||
+ ci->curWeaponClass == PCL_ALIEN_LEVEL1_UPG ||
+ ci->curWeaponClass == WP_HBUILD )
+ {
+ displayClients[ maxDisplayCount++ ] = i;
+ }
+ }
+ }
+ }
+ }
+ else // find nearby
+ {
+ for( i = 0; i < cg.snap->numEntities; i++ )
+ {
+ centity_t *cent = &cg_entities[ cg.snap->entities[ i ].number ];
+ vec3_t relOrigin = { 0.0f, 0.0f, 0.0f };
+ int team = cent->currentState.misc & 0x00FF;
- c[ 0 ] = s[ i ];
- c[ 1 ] = '\0';
+ if( cent->currentState.eType != ET_PLAYER ||
+ team != pci->team ||
+ cent->currentState.eFlags & EF_DEAD )
+ {
+ continue;
+ }
+
+ VectorSubtract( cent->lerpOrigin, cg.predictedPlayerState.origin, relOrigin );
+
+ if( VectorLength( relOrigin ) < HELMET_RANGE )
+ displayClients[ maxDisplayCount++ ] = cg.snap->entities[ i ].number;
+ }
+ }
- CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ // Sort
+ if( sort == TEAMOVERLAY_SORT_SCORE )
+ {
+ qsort( displayClients, maxDisplayCount,
+ sizeof( displayClients[ 0 ] ), SortScore );
+ }
+ else if( sort == TEAMOVERLAY_SORT_WEAPONCLASS )
+ {
+ qsort( displayClients, maxDisplayCount,
+ sizeof( displayClients[ 0 ] ), SortWeaponClass );
+ }
+
+ if( maxDisplayCount > cg_teamOverlayMaxPlayers.integer )
+ maxDisplayCount = cg_teamOverlayMaxPlayers.integer;
+
+ iconSize *= scale;
+ leftMargin *= scale;
+ iconTopMargin *= scale;
+ midSep *= scale;
+ backgroundWidth *= scale;
+ fontScale *= scale;
+ nameWidth *= scale;
+
+ vPad = ( rect->h - ( (float) maxDisplayCount * iconSize ) ) / 2.0f;
+ y = rect->y + vPad;
+
+ tcolor[ 0 ] = 1.0f;
+ tcolor[ 1 ] = 1.0f;
+ tcolor[ 2 ] = 1.0f;
+ tcolor[ 3 ] = color[ 3 ];
+
+ for( i = 0; i < MAX_CLIENTS && displayCount < maxDisplayCount; i++ )
+ {
+ ci = cgs.clientinfo + displayClients[ i ];
+
+ if( !ci->infoValid || pci == ci || ci->team != pci->team )
+ continue;
+
+ Com_sprintf( name, sizeof( name ), "%s^7", ci->name );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( x, y, backgroundWidth,
+ iconSize, cgs.media.teamOverlayShader );
+ trap_R_SetColor( tcolor );
+ if( ci->health <= 0 || !ci->curWeaponClass )
+ s = "";
+ else
+ {
+ if( ci->team == TEAM_HUMANS )
+ curWeapon = ci->curWeaponClass;
+ else if( ci->team == TEAM_ALIENS )
+ curWeapon = BG_Class( ci->curWeaponClass )->startWeapon;
+
+ CG_DrawPic( x + leftMargin, y, iconSize, iconSize,
+ cg_weapons[ curWeapon ].weaponIcon );
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ {
+ if( ci->upgrade != UP_NONE )
+ {
+ CG_DrawPic( x + iconSize + leftMargin, y, iconSize,
+ iconSize, cg_upgrades[ ci->upgrade ].upgradeIcon );
+ }
+ }
+ else
+ {
+ if( curWeapon == WP_ABUILD2 || curWeapon == WP_ALEVEL1_UPG ||
+ curWeapon == WP_ALEVEL2_UPG || curWeapon == WP_ALEVEL3_UPG )
+ {
+ CG_DrawPic( x + iconSize + leftMargin, y, iconSize,
+ iconSize, cgs.media.upgradeClassIconShader );
+ }
+ }
+
+ s = va( " [^%c%3d^7] ^7%s",
+ CG_GetColorCharForHealth( displayClients[ i ] ),
+ ci->health,
+ CG_ConfigString( CS_LOCATIONS + ci->location ) );
+ }
+
+ trap_R_SetColor( NULL );
+ nameMaxX = nameMaxXCp = x + 2.0f * iconSize +
+ leftMargin + midSep + nameWidth;
+ UI_Text_Paint_Limit( &nameMaxXCp, x + 2.0f * iconSize + leftMargin + midSep,
+ y + iconSize - iconTopMargin, fontScale, tcolor, name,
+ 0, 0 );
+
+ maxXCp = maxX;
+
+ UI_Text_Paint_Limit( &maxXCp, nameMaxX, y + iconSize - iconTopMargin,
+ fontScale, tcolor, s, 0, 0 );
+ y += iconSize;
+ displayCount++;
}
}
@@ -1902,17 +1913,19 @@ CG_DrawClock
=================
*/
static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y,
- float scale, vec4_t color, int align, int textStyle )
+ float scale, vec4_t color,
+ int textalign, int textvalign, int textStyle )
{
- char *s;
- int i, tx, w, totalWidth, strLength;
+ const char *s;
+ float tx, ty;
+ int i, strLength;
+ float w, h, totalWidth;
qtime_t qt;
- int t;
if( !cg_drawClock.integer )
return;
- t = trap_RealTime( &qt );
+ trap_RealTime( &qt );
if( cg_drawClock.integer == 2 )
{
@@ -1936,27 +1949,12 @@ static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y,
s = va( "%d%s%02d%s", h, ( qt.tm_sec % 2 ) ? ":" : " ", qt.tm_min, pm );
}
- w = CG_Text_Width( "0", scale, 0 );
+ w = UI_Text_Width( "0", scale );
+ h = UI_Text_Height( "0", scale );
strLength = CG_DrawStrlen( s );
totalWidth = w * strLength;
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
-
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - totalWidth;
- break;
-
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( totalWidth / 2.0f );
- break;
-
- default:
- tx = 0.0f;
- }
+ CG_AlignText( rect, s, 0.0f, totalWidth, h, textalign, textvalign, &tx, &ty );
for( i = 0; i < strLength; i++ )
{
@@ -1965,7 +1963,7 @@ static void CG_DrawClock( rectDef_t *rect, float text_x, float text_y,
c[ 0 ] = s[ i ];
c[ 1 ] = '\0';
- CG_Text_Paint( text_x + tx + i * w, rect->y + text_y, scale, color, c, 0, 0, textStyle );
+ UI_Text_Paint( text_x + tx + i * w, text_y + ty, scale, color, c, 0, 0, textStyle );
}
}
@@ -1975,39 +1973,90 @@ CG_DrawSnapshot
==================
*/
static void CG_DrawSnapshot( rectDef_t *rect, float text_x, float text_y,
- float scale, vec4_t color, int align, int textStyle )
+ float scale, vec4_t color,
+ int textalign, int textvalign, int textStyle )
{
- char *s;
- int w, tx;
+ const char *s;
+ float tx, ty;
if( !cg_drawSnapshot.integer )
return;
s = va( "time:%d snap:%d cmd:%d", cg.snap->serverTime,
cg.latestSnapshotNum, cgs.serverCommandSequence );
- w = CG_Text_Width( s, scale, 0 );
- switch( align )
- {
- case ITEM_ALIGN_LEFT:
- tx = rect->x;
- break;
+ CG_AlignText( rect, s, scale, 0.0f, 0.0f, textalign, textvalign, &tx, &ty );
- case ITEM_ALIGN_RIGHT:
- tx = rect->x + rect->w - w;
- break;
+ UI_Text_Paint( text_x + tx, text_y + ty, scale, color, s, 0, 0, textStyle );
+}
- case ITEM_ALIGN_CENTER:
- tx = rect->x + ( rect->w / 2.0f ) - ( w / 2.0f );
- break;
+/*
+===============================================================================
- default:
- tx = 0.0f;
- }
+KILLL MESSAGE
- CG_Text_Paint( text_x + tx, rect->y + text_y, scale, color, s, 0, 0, textStyle );
+===============================================================================
+*/
+
+/*
+==================
+CG_DrawKillMsg
+==================
+*/
+static void CG_DrawKillMsg( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t color,
+ int textalign, int textvalign, int textStyle )
+{
+ int i;
+ vec4_t hcolor;
+ int chatHeight;
+
+ if (cg_killMsgHeight.integer < TEAMCHAT_HEIGHT)
+ chatHeight = cg_killMsgHeight.integer;
+ else
+ chatHeight = TEAMCHAT_HEIGHT;
+
+ if (chatHeight <= 0)
+ return; // disabled
+
+ if (cgs.killMsgLastPos != cgs.killMsgPos)
+ {
+ if (cg.time - cgs.killMsgMsgTimes[cgs.killMsgLastPos % chatHeight] > cg_killMsgTime.integer)
+ cgs.killMsgLastPos++;
+
+ hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0f;
+
+ for ( i = cgs.killMsgPos - 1; i >= cgs.killMsgLastPos; i-- )
+ {
+ int x = 0, w;
+ int j = i % chatHeight;
+
+ w = UI_Text_Width( cgs.killMsgKillers[j], scale );
+ UI_Text_Paint( rect->x + TINYCHAR_WIDTH,
+ rect->y - (cgs.killMsgPos - i)*20,
+ scale, color, cgs.killMsgKillers[j],
+ 0, 0, textStyle );
+ x += w + 3;
+
+ if ( cg_weapons[cgs.killMsgWeapons[j]].weaponIcon != WP_NONE )
+ {
+ CG_DrawPic( rect->x + TINYCHAR_WIDTH + x,
+ rect->y - (cgs.killMsgPos - i)*20-15,
+ 16, 16,
+ cg_weapons[cgs.killMsgWeapons[j]].weaponIcon );
+ x += 16 + 2;
+
+ w = UI_Text_Width( cgs.killMsgVictims[j], scale );
+ UI_Text_Paint( rect->x + TINYCHAR_WIDTH + x,
+ rect->y - (cgs.killMsgPos - i)*20,
+ scale, color, cgs.killMsgVictims[j],
+ 0, 0, textStyle );
+ }
+ }
+ }
}
+
/*
===============================================================================
@@ -2080,7 +2129,7 @@ void CG_AddLagometerSnapshotInfo( snapshot_t *snap )
{
previousPings[ index++ ] = cg.snap->ping;
index = index % PING_FRAMES;
-
+
for( i = 0; i < PING_FRAMES; i++ )
{
cg.ping += previousPings[ i ];
@@ -2116,8 +2165,8 @@ static void CG_DrawDisconnect( void )
// also add text in center of screen
s = "Connection Interrupted";
- w = CG_Text_Width( s, 0.7f, 0 );
- CG_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ w = UI_Text_Width( s, 0.7f );
+ UI_Text_Paint( 320 - w / 2, 100, 0.7f, color, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
// blink the icon
if( ( cg.time >> 9 ) & 1 )
@@ -2147,7 +2196,7 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
int color;
vec4_t adjustedColor;
float vscale;
- vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
+ const char *ping;
if( cg.snap->ps.pm_type == PM_INTERMISSION )
return;
@@ -2270,81 +2319,194 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
trap_R_SetColor( NULL );
if( cg_nopredict.integer || cg_synchronousClients.integer )
- CG_Text_Paint( ax, ay, 0.5, white, "snc", 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ ping = "snc";
else
- {
- char *s;
-
- s = va( "%d", cg.ping );
- ax = rect->x + ( rect->w / 2.0f ) - ( CG_Text_Width( s, scale, 0 ) / 2.0f ) + text_x;
- ay = rect->y + ( rect->h / 2.0f ) + ( CG_Text_Height( s, scale, 0 ) / 2.0f ) + text_y;
+ ping = va( "%d", cg.ping );
+ ax = rect->x + ( rect->w / 2.0f ) -
+ ( UI_Text_Width( ping, scale ) / 2.0f ) + text_x;
+ ay = rect->y + ( rect->h / 2.0f ) +
+ ( UI_Text_Height( ping, scale ) / 2.0f ) + text_y;
- Vector4Copy( textColor, adjustedColor );
- adjustedColor[ 3 ] = 0.5f;
- CG_Text_Paint( ax, ay, scale, adjustedColor, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
- }
+ Vector4Copy( textColor, adjustedColor );
+ adjustedColor[ 3 ] = 0.5f;
+ UI_Text_Paint( ax, ay, scale, adjustedColor, ping, 0, 0,
+ ITEM_TEXTSTYLE_NORMAL );
CG_DrawDisconnect( );
}
+#define SPEEDOMETER_NUM_SAMPLES 4096
+#define SPEEDOMETER_NUM_DISPLAYED_SAMPLES 160
+#define SPEEDOMETER_DRAW_TEXT 0x1
+#define SPEEDOMETER_DRAW_GRAPH 0x2
+#define SPEEDOMETER_IGNORE_Z 0x4
+float speedSamples[ SPEEDOMETER_NUM_SAMPLES ];
+int speedSampleTimes[ SPEEDOMETER_NUM_SAMPLES ];
+// array indices
+int oldestSpeedSample = 0;
+int maxSpeedSample = 0;
+int maxSpeedSampleInWindow = 0;
+
/*
-==============
-CG_DrawTextBlock
-==============
+===================
+CG_AddSpeed
+
+append a speed to the sample history
+===================
*/
-static void CG_DrawTextBlock( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, int textStyle, const char *text,
- menuDef_t *parent, itemDef_t *textItem )
+void CG_AddSpeed( void )
{
- float x, y, w, h;
+ float speed;
+ vec3_t vel;
+ int windowTime;
+ qboolean newSpeedGteMaxSpeed, newSpeedGteMaxSpeedInWindow;
- //offset the text
- x = rect->x;
- y = rect->y;
- w = rect->w - ( 16 + ( 2 * text_x ) ); //16 to ensure text within frame
- h = rect->h;
+ VectorCopy( cg.snap->ps.velocity, vel );
- textItem->text = text;
+ if( cg_drawSpeed.integer & SPEEDOMETER_IGNORE_Z )
+ vel[ 2 ] = 0;
- textItem->parent = parent;
- memcpy( textItem->window.foreColor, color, sizeof( vec4_t ) );
- textItem->window.flags = 0;
+ speed = VectorLength( vel );
- switch( align )
+ windowTime = cg_maxSpeedTimeWindow.integer;
+ if( windowTime < 0 )
+ windowTime = 0;
+ else if( windowTime > SPEEDOMETER_NUM_SAMPLES * 1000 )
+ windowTime = SPEEDOMETER_NUM_SAMPLES * 1000;
+
+ if( ( newSpeedGteMaxSpeed = ( speed >= speedSamples[ maxSpeedSample ] ) ) )
+ maxSpeedSample = oldestSpeedSample;
+
+ if( ( newSpeedGteMaxSpeedInWindow = ( speed >= speedSamples[ maxSpeedSampleInWindow ] ) ) )
+ maxSpeedSampleInWindow = oldestSpeedSample;
+
+ speedSamples[ oldestSpeedSample ] = speed;
+ speedSampleTimes[ oldestSpeedSample ] = cg.time;
+
+ if( !newSpeedGteMaxSpeed && maxSpeedSample == oldestSpeedSample )
{
- case ITEM_ALIGN_LEFT:
- textItem->window.rect.x = x;
- break;
+ // if old max was overwritten find a new one
+ int i;
+ for( maxSpeedSample = 0, i = 1; i < SPEEDOMETER_NUM_SAMPLES; i++ )
+ {
+ if( speedSamples[ i ] > speedSamples[ maxSpeedSample ] )
+ maxSpeedSample = i;
+ }
+ }
- case ITEM_ALIGN_RIGHT:
- textItem->window.rect.x = x + w;
- break;
+ if( !newSpeedGteMaxSpeedInWindow && ( maxSpeedSampleInWindow == oldestSpeedSample ||
+ cg.time - speedSampleTimes[ maxSpeedSampleInWindow ] > windowTime ) )
+ {
+ int i;
+ do {
+ maxSpeedSampleInWindow = ( maxSpeedSampleInWindow + 1 ) % SPEEDOMETER_NUM_SAMPLES;
+ } while( cg.time - speedSampleTimes[ maxSpeedSampleInWindow ] > windowTime );
+ for( i = maxSpeedSampleInWindow; ; i = ( i + 1 ) % SPEEDOMETER_NUM_SAMPLES )
+ {
+ if( speedSamples[ i ] > speedSamples[ maxSpeedSampleInWindow ] )
+ maxSpeedSampleInWindow = i;
+ if( i == oldestSpeedSample )
+ break;
+ }
+ }
- case ITEM_ALIGN_CENTER:
- textItem->window.rect.x = x + ( w / 2 );
- break;
+ oldestSpeedSample = ( oldestSpeedSample + 1 ) % SPEEDOMETER_NUM_SAMPLES;
+}
- default:
- textItem->window.rect.x = x;
- break;
+#define SPEEDOMETER_MIN_RANGE 900
+#define SPEED_MED 1000.f
+#define SPEED_FAST 1600.f
+
+/*
+===================
+CG_DrawSpeedGraph
+===================
+*/
+static void CG_DrawSpeedGraph( rectDef_t *rect, vec4_t foreColor,
+ vec4_t backColor )
+{
+ int i;
+ float val, max, top;
+ // colour of graph is interpolated between these values
+ const vec3_t slow = { 0.0, 0.0, 1.0 };
+ const vec3_t medium = { 0.0, 1.0, 0.0 };
+ const vec3_t fast = { 1.0, 0.0, 0.0 };
+ vec4_t color;
+
+ max = speedSamples[ maxSpeedSample ];
+ if( max < SPEEDOMETER_MIN_RANGE )
+ max = SPEEDOMETER_MIN_RANGE;
+
+ trap_R_SetColor( backColor );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.whiteShader );
+
+ Vector4Copy( foreColor, color );
+
+ for( i = 1; i < SPEEDOMETER_NUM_DISPLAYED_SAMPLES; i++ )
+ {
+ val = speedSamples[ ( oldestSpeedSample + i + SPEEDOMETER_NUM_SAMPLES -
+ SPEEDOMETER_NUM_DISPLAYED_SAMPLES ) % SPEEDOMETER_NUM_SAMPLES ];
+ if( val < SPEED_MED )
+ VectorLerp2( val / SPEED_MED, slow, medium, color );
+ else if( val < SPEED_FAST )
+ VectorLerp2( ( val - SPEED_MED ) / ( SPEED_FAST - SPEED_MED ),
+ medium, fast, color );
+ else
+ VectorCopy( fast, color );
+ trap_R_SetColor( color );
+ top = rect->y + ( 1 - val / max ) * rect->h;
+ CG_DrawPic( rect->x + ( i / (float)SPEEDOMETER_NUM_DISPLAYED_SAMPLES ) * rect->w, top,
+ rect->w / (float)SPEEDOMETER_NUM_DISPLAYED_SAMPLES, val * rect->h / max,
+ cgs.media.whiteShader );
+ }
+ trap_R_SetColor( NULL );
+}
+
+/*
+===================
+CG_DrawSpeedText
+===================
+*/
+static void CG_DrawSpeedText( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t foreColor )
+{
+ char speedstr[ 16 ];
+ float val;
+ vec4_t color;
+
+ VectorCopy( foreColor, color );
+ color[ 3 ] = 1;
+ if( cg.predictedPlayerState.clientNum == cg.clientNum )
+ {
+ vec3_t vel;
+ VectorCopy( cg.predictedPlayerState.velocity, vel );
+ if( cg_drawSpeed.integer & SPEEDOMETER_IGNORE_Z )
+ vel[ 2 ] = 0;
+ val = VectorLength( vel );
}
+ else
+ val = speedSamples[ ( oldestSpeedSample - 1 + SPEEDOMETER_NUM_SAMPLES ) % SPEEDOMETER_NUM_SAMPLES ];
- textItem->window.rect.y = y;
- textItem->window.rect.w = w;
- textItem->window.rect.h = h;
- textItem->window.borderSize = 0;
- textItem->textRect.x = 0;
- textItem->textRect.y = 0;
- textItem->textRect.w = 0;
- textItem->textRect.h = 0;
- textItem->textalignment = align;
- textItem->textalignx = text_x;
- textItem->textaligny = text_y;
- textItem->textscale = scale;
- textItem->textStyle = textStyle;
+ Com_sprintf( speedstr, sizeof( speedstr ), "%d / %d", (int)val, (int)speedSamples[ maxSpeedSampleInWindow ] );
- //hack to utilise existing autowrap code
- Item_Text_AutoWrapped_Paint( textItem );
+ UI_Text_Paint(
+ rect->x + ( rect->w - UI_Text_Width( speedstr, scale ) ) / 2.0f,
+ rect->y + ( rect->h + UI_Text_Height( speedstr, scale ) ) / 2.0f,
+ scale, color, speedstr, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+}
+
+/*
+===================
+CG_DrawSpeed
+===================
+*/
+static void CG_DrawSpeed( rectDef_t *rect, float text_x, float text_y,
+ float scale, vec4_t foreColor, vec4_t backColor )
+{
+ if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_GRAPH )
+ CG_DrawSpeedGraph( rect, foreColor, backColor );
+ if( cg_drawSpeed.integer & SPEEDOMETER_DRAW_TEXT )
+ CG_DrawSpeedText( rect, text_x, text_y, scale, foreColor );
}
/*
@@ -2353,13 +2515,9 @@ CG_DrawConsole
===================
*/
static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, int textStyle )
+ float scale, int textalign, int textvalign, int textStyle )
{
- static menuDef_t dummyParent;
- static itemDef_t textItem;
-
- CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
- cg.consoleText, &dummyParent, &textItem );
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, cg.consoleText );
}
/*
@@ -2368,16 +2526,12 @@ CG_DrawTutorial
===================
*/
static void CG_DrawTutorial( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, int textStyle )
+ float scale, int textalign, int textvalign, int textStyle )
{
- static menuDef_t dummyParent;
- static itemDef_t textItem;
-
if( !cg_tutorial.integer )
return;
- CG_DrawTextBlock( rect, text_x, text_y, color, scale, align, textStyle,
- CG_TutorialText( ), &dummyParent, &textItem );
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale, textalign, textvalign, textStyle, CG_TutorialText( ) );
}
/*
@@ -2387,29 +2541,34 @@ CG_DrawWeaponIcon
*/
void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
{
- int ammo, clips, maxAmmo;
- centity_t *cent;
+ int maxAmmo;
playerState_t *ps;
+ weapon_t weapon;
- cent = &cg_entities[ cg.snap->ps.clientNum ];
ps = &cg.snap->ps;
+ weapon = BG_GetPlayerWeapon( ps );
- ammo = ps->ammo;
- clips = ps->clips;
- BG_FindAmmoForWeapon( cent->currentState.weapon, &maxAmmo, NULL );
+ maxAmmo = BG_Weapon( weapon )->maxAmmo;
// don't display if dead
if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
return;
- if( cent->currentState.weapon == 0 )
+ if( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS )
+ {
return;
+ }
- CG_RegisterWeapon( cent->currentState.weapon );
+ if( !cg_weapons[ weapon ].registered )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_DrawWeaponIcon: weapon %d (%s) "
+ "is not registered\n", weapon, BG_Weapon( weapon )->name );
+ return;
+ }
- if( clips == 0 && !BG_FindInfinteAmmoForWeapon( cent->currentState.weapon ) )
+ if( ps->clips == 0 && !BG_Weapon( weapon )->infiniteAmmo )
{
- float ammoPercent = (float)ammo / (float)maxAmmo;
+ float ammoPercent = (float)ps->ammo / (float)maxAmmo;
if( ammoPercent < 0.33f )
{
@@ -2418,7 +2577,9 @@ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
}
}
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS && CG_AtHighestClass( ) )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS &&
+ !BG_AlienCanEvolve( cg.predictedPlayerState.stats[ STAT_CLASS ],
+ ps->persistant[ PERS_CREDIT ], cgs.alienStage ) )
{
if( cg.time - cg.lastEvolveAttempt <= NO_CREDITS_TIME )
{
@@ -2428,7 +2589,8 @@ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
}
trap_R_SetColor( color );
- CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].weaponIcon );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h,
+ cg_weapons[ weapon ].weaponIcon );
trap_R_SetColor( NULL );
}
@@ -2443,50 +2605,65 @@ CROSSHAIR
*/
+
/*
=================
CG_DrawCrosshair
=================
*/
-static void CG_DrawCrosshair( void )
+static void CG_DrawCrosshair( rectDef_t *rect, vec4_t color )
{
float w, h;
qhandle_t hShader;
float x, y;
weaponInfo_t *wi;
+ weapon_t weapon;
+
+ weapon = BG_GetPlayerWeapon( &cg.snap->ps );
if( cg_drawCrosshair.integer == CROSSHAIR_ALWAYSOFF )
return;
if( cg_drawCrosshair.integer == CROSSHAIR_RANGEDONLY &&
- !BG_FindLongRangedForWeapon( cg.snap->ps.weapon ) )
- {
+ !BG_Weapon( weapon )->longRanged )
return;
- }
- if( ( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) ||
- ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) ||
- ( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) )
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
return;
if( cg.renderingThirdPerson )
return;
- wi = &cg_weapons[ cg.snap->ps.weapon ];
+ if( cg.snap->ps.pm_type == PM_INTERMISSION )
+ return;
- w = h = wi->crossHairSize;
+ wi = &cg_weapons[ weapon ];
- x = cg_crosshairX.integer;
- y = cg_crosshairY.integer;
- CG_AdjustFrom640( &x, &y, &w, &h );
+ w = h = wi->crossHairSize * cg_crosshairSize.value;
+ w *= cgDC.aspectScale;
+
+ //FIXME: this still ignores the width/height of the rect, but at least it's
+ //neater than cg_crosshairX/cg_crosshairY
+ x = rect->x + ( rect->w / 2 ) - ( w / 2 );
+ y = rect->y + ( rect->h / 2 ) - ( h / 2 );
hShader = wi->crossHair;
+ //aiming at a friendly player/buildable, dim the crosshair
+ if( cg.time == cg.crosshairClientTime || cg.crosshairBuildable >= 0 )
+ {
+ int i;
+ for( i = 0; i < 3; i++ )
+ color[i] *= .5f;
+
+ }
+
if( hShader != 0 )
{
- trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * ( cg.refdef.width - w ),
- y + cg.refdef.y + 0.5 * ( cg.refdef.height - h ),
- w, h, 0, 0, 1, 1, hShader );
+
+ trap_R_SetColor( color );
+ CG_DrawPic( x, y, w, h, hShader );
+ trap_R_SetColor( NULL );
}
}
@@ -2502,7 +2679,7 @@ static void CG_ScanForCrosshairEntity( void )
trace_t trace;
vec3_t start, end;
int content;
- pTeam_t team;
+ team_t team;
VectorCopy( cg.refdef.vieworg, start );
VectorMA( start, 131072, cg.refdef.viewaxis[ 0 ], end );
@@ -2510,20 +2687,29 @@ static void CG_ScanForCrosshairEntity( void )
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );
- if( trace.entityNum >= MAX_CLIENTS )
- return;
-
// if the player is in fog, don't show it
content = trap_CM_PointContents( trace.endpos, 0 );
if( content & CONTENTS_FOG )
return;
+ if( trace.entityNum >= MAX_CLIENTS )
+ {
+ entityState_t *s = &cg_entities[ trace.entityNum ].currentState;
+ if( s->eType == ET_BUILDABLE && BG_Buildable( s->modelindex )->team ==
+ cg.snap->ps.stats[ STAT_TEAM ] )
+ cg.crosshairBuildable = trace.entityNum;
+ else
+ cg.crosshairBuildable = -1;
+
+ return;
+ }
+
team = cgs.clientinfo[ trace.entityNum ].team;
- if( cg.snap->ps.persistant[ PERS_TEAM ] != TEAM_SPECTATOR )
+ if( cg.snap->ps.stats[ STAT_TEAM ] != TEAM_NONE )
{
//only display team names of those on the same team as this player
- if( team != cg.snap->ps.stats[ STAT_PTEAM ] )
+ if( team != cg.snap->ps.stats[ STAT_TEAM ] )
return;
}
@@ -2535,13 +2721,51 @@ static void CG_ScanForCrosshairEntity( void )
/*
=====================
+CG_DrawLocation
+=====================
+*/
+static void CG_DrawLocation( rectDef_t *rect, float scale, int textalign, vec4_t color )
+{
+ const char *location;
+ centity_t *locent;
+ float maxX;
+ float tx = rect->x, ty = rect->y;
+
+ if( cg.intermissionStarted )
+ return;
+
+ maxX = rect->x + rect->w;
+
+ locent = CG_GetPlayerLocation( );
+ if( locent )
+ location = CG_ConfigString( CS_LOCATIONS + locent->currentState.generic1 );
+ else
+ location = CG_ConfigString( CS_LOCATIONS );
+
+ // need to skip horiz. align if it's too long, but valign must be run either way
+ if( UI_Text_Width( location, scale ) < rect->w )
+ {
+ CG_AlignText( rect, location, scale, 0.0f, 0.0f, textalign, VALIGN_CENTER, &tx, &ty );
+ UI_Text_Paint( tx, ty, scale, color, location, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ }
+ else
+ {
+ CG_AlignText( rect, location, scale, 0.0f, 0.0f, ALIGN_NONE, VALIGN_CENTER, &tx, &ty );
+ UI_Text_Paint_Limit( &maxX, tx, ty, scale, color, location, 0, 0 );
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+=====================
CG_DrawCrosshairNames
=====================
*/
static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle )
{
float *color;
- char *name;
+ const char *name;
float w, x;
if( !cg_drawCrosshairNames.integer )
@@ -2554,21 +2778,31 @@ static void CG_DrawCrosshairNames( rectDef_t *rect, float scale, int textStyle )
CG_ScanForCrosshairEntity( );
// draw the name of the player being looked at
- color = CG_FadeColor( cg.crosshairClientTime, 1000 );
+ color = CG_FadeColor( cg.crosshairClientTime, CROSSHAIR_CLIENT_TIMEOUT );
if( !color )
{
trap_R_SetColor( NULL );
return;
}
+ // add health from overlay info to the crosshair client name
name = cgs.clientinfo[ cg.crosshairClientNum ].name;
- w = CG_Text_Width( name, scale, 0 );
- x = rect->x + rect->w / 2;
- CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
+ if( cg_teamOverlayUserinfo.integer &&
+ cg.snap->ps.stats[ STAT_TEAM ] != TEAM_NONE &&
+ cgs.teaminfoReceievedTime &&
+ cgs.clientinfo[ cg.crosshairClientNum ].health > 0 )
+ {
+ name = va( "%s ^7[^%c%d^7]", name,
+ CG_GetColorCharForHealth( cg.crosshairClientNum ),
+ cgs.clientinfo[ cg.crosshairClientNum ].health );
+ }
+
+ w = UI_Text_Width( name, scale );
+ x = rect->x + rect->w / 2.0f;
+ UI_Text_Paint( x - w / 2.0f, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
trap_R_SetColor( NULL );
}
-
/*
===============
CG_OwnerDraw
@@ -2578,14 +2812,12 @@ Draw an owner drawn item
*/
void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
float text_y, int ownerDraw, int ownerDrawFlags,
- int align, float special, float scale, vec4_t color,
+ int align, int textalign, int textvalign, float borderSize,
+ float scale, vec4_t foreColor, vec4_t backColor,
qhandle_t shader, int textStyle )
{
rectDef_t rect;
- if( cg_drawStatus.integer == 0 )
- return;
-
rect.x = x;
rect.y = y;
rect.w = w;
@@ -2594,100 +2826,107 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
switch( ownerDraw )
{
case CG_PLAYER_CREDITS_VALUE:
- CG_DrawPlayerCreditsValue( &rect, color, qtrue );
+ CG_DrawPlayerCreditsValue( &rect, foreColor, qtrue );
break;
- case CG_PLAYER_BANK_VALUE:
- CG_DrawPlayerBankValue( &rect, color, qtrue );
+ case CG_PLAYER_CREDITS_FRACTION:
+ CG_DrawPlayerCreditsFraction( &rect, foreColor, shader );
break;
case CG_PLAYER_CREDITS_VALUE_NOPAD:
- CG_DrawPlayerCreditsValue( &rect, color, qfalse );
- break;
- case CG_PLAYER_BANK_VALUE_NOPAD:
- CG_DrawPlayerBankValue( &rect, color, qfalse );
- break;
- case CG_PLAYER_STAMINA:
- CG_DrawPlayerStamina( &rect, color, scale, align, textStyle, special );
+ CG_DrawPlayerCreditsValue( &rect, foreColor, qfalse );
break;
case CG_PLAYER_STAMINA_1:
- CG_DrawPlayerStamina1( &rect, color, shader );
- break;
case CG_PLAYER_STAMINA_2:
- CG_DrawPlayerStamina2( &rect, color, shader );
- break;
case CG_PLAYER_STAMINA_3:
- CG_DrawPlayerStamina3( &rect, color, shader );
- break;
case CG_PLAYER_STAMINA_4:
- CG_DrawPlayerStamina4( &rect, color, shader );
+ CG_DrawPlayerStamina( ownerDraw, &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_STAMINA_BOLT:
- CG_DrawPlayerStaminaBolt( &rect, color, shader );
+ CG_DrawPlayerStaminaBolt( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_AMMO_VALUE:
- CG_DrawPlayerAmmoValue( &rect, color );
+ CG_DrawPlayerAmmoValue( &rect, foreColor );
break;
case CG_PLAYER_CLIPS_VALUE:
- CG_DrawPlayerClipsValue( &rect, color );
+ CG_DrawPlayerClipsValue( &rect, foreColor );
break;
case CG_PLAYER_BUILD_TIMER:
- CG_DrawPlayerBuildTimer( &rect, color );
+ CG_DrawPlayerBuildTimer( &rect, foreColor );
break;
case CG_PLAYER_HEALTH:
- CG_DrawPlayerHealthValue( &rect, color );
- break;
- case CG_PLAYER_HEALTH_BAR:
- CG_DrawPlayerHealthBar( &rect, color, scale, align, textStyle, special );
+ CG_DrawPlayerHealthValue( &rect, foreColor );
break;
case CG_PLAYER_HEALTH_CROSS:
- CG_DrawPlayerHealthCross( &rect, color, shader );
+ CG_DrawPlayerHealthCross( &rect, foreColor );
+ break;
+ case CG_PLAYER_CHARGE_BAR_BG:
+ CG_DrawPlayerChargeBarBG( &rect, foreColor, shader );
+ break;
+ case CG_PLAYER_CHARGE_BAR:
+ CG_DrawPlayerChargeBar( &rect, foreColor, shader );
break;
case CG_PLAYER_CLIPS_RING:
- CG_DrawPlayerClipsRing( &rect, color, shader );
+ CG_DrawPlayerClipsRing( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_BUILD_TIMER_RING:
- CG_DrawPlayerBuildTimerRing( &rect, color, shader );
+ CG_DrawPlayerBuildTimerRing( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_WALLCLIMBING:
- CG_DrawPlayerWallclimbing( &rect, color, shader );
+ CG_DrawPlayerWallclimbing( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_BOOSTED:
- CG_DrawPlayerBoosted( &rect, color, shader );
+ CG_DrawPlayerBoosted( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_BOOST_BOLT:
- CG_DrawPlayerBoosterBolt( &rect, color, shader );
+ CG_DrawPlayerBoosterBolt( &rect, backColor, foreColor, shader );
break;
case CG_PLAYER_POISON_BARBS:
- CG_DrawPlayerPoisonBarbs( &rect, color, shader );
+ CG_DrawPlayerPoisonBarbs( &rect, foreColor, shader );
break;
case CG_PLAYER_ALIEN_SENSE:
CG_DrawAlienSense( &rect );
break;
case CG_PLAYER_HUMAN_SCANNER:
- CG_DrawHumanScanner( &rect, shader, color );
+ CG_DrawHumanScanner( &rect, shader, foreColor );
break;
case CG_PLAYER_USABLE_BUILDABLE:
- CG_DrawUsableBuildable( &rect, shader, color );
+ CG_DrawUsableBuildable( &rect, shader, foreColor );
break;
case CG_KILLER:
- CG_DrawKiller( &rect, scale, color, shader, textStyle );
+ CG_DrawKiller( &rect, scale, foreColor, shader, textStyle );
break;
case CG_PLAYER_SELECT:
- CG_DrawItemSelect( &rect, color );
+ CG_DrawItemSelect( &rect, foreColor );
break;
case CG_PLAYER_WEAPONICON:
- CG_DrawWeaponIcon( &rect, color );
+ CG_DrawWeaponIcon( &rect, foreColor );
break;
case CG_PLAYER_SELECTTEXT:
CG_DrawItemSelectText( &rect, scale, textStyle );
break;
case CG_SPECTATORS:
- CG_DrawTeamSpectators( &rect, scale, color, shader );
+ CG_DrawTeamSpectators( &rect, scale, textvalign, foreColor, shader );
+ break;
+ case CG_PLAYER_LOCATION:
+ CG_DrawLocation( &rect, scale, textalign, foreColor );
+ break;
+ case CG_FOLLOW:
+ CG_DrawFollow( &rect, text_x, text_y, foreColor, scale,
+ textalign, textvalign, textStyle );
break;
case CG_PLAYER_CROSSHAIRNAMES:
CG_DrawCrosshairNames( &rect, scale, textStyle );
break;
+ case CG_PLAYER_CROSSHAIR:
+ CG_DrawCrosshair( &rect, foreColor );
+ break;
case CG_STAGE_REPORT_TEXT:
- CG_DrawStageReport( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawStageReport( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
+ break;
+ case CG_ALIENS_SCORE_LABEL:
+ CG_DrawTeamLabel( &rect, TEAM_ALIENS, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
+ break;
+ case CG_HUMANS_SCORE_LABEL:
+ CG_DrawTeamLabel( &rect, TEAM_HUMANS, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
break;
//loading screen
@@ -2695,74 +2934,92 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
CG_DrawLevelShot( &rect );
break;
case CG_LOAD_MEDIA:
- CG_DrawMediaProgress( &rect, color, scale, align, textStyle, special );
+ CG_DrawMediaProgress( &rect, foreColor, scale, align, textalign, textStyle,
+ borderSize );
break;
case CG_LOAD_MEDIA_LABEL:
- CG_DrawMediaProgressLabel( &rect, text_x, text_y, color, scale, align );
+ CG_DrawMediaProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign );
break;
case CG_LOAD_BUILDABLES:
- CG_DrawBuildablesProgress( &rect, color, scale, align, textStyle, special );
+ CG_DrawBuildablesProgress( &rect, foreColor, scale, align, textalign,
+ textStyle, borderSize );
break;
case CG_LOAD_BUILDABLES_LABEL:
- CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, color, scale, align );
+ CG_DrawBuildablesProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign );
break;
case CG_LOAD_CHARMODEL:
- CG_DrawCharModelProgress( &rect, color, scale, align, textStyle, special );
+ CG_DrawCharModelProgress( &rect, foreColor, scale, align, textalign,
+ textStyle, borderSize );
break;
case CG_LOAD_CHARMODEL_LABEL:
- CG_DrawCharModelProgressLabel( &rect, text_x, text_y, color, scale, align );
+ CG_DrawCharModelProgressLabel( &rect, text_x, text_y, foreColor, scale, textalign, textvalign );
break;
case CG_LOAD_OVERALL:
- CG_DrawOverallProgress( &rect, color, scale, align, textStyle, special );
+ CG_DrawOverallProgress( &rect, foreColor, scale, align, textalign, textStyle,
+ borderSize );
break;
case CG_LOAD_LEVELNAME:
- CG_DrawLevelName( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawLevelName( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
break;
case CG_LOAD_MOTD:
- CG_DrawMOTD( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawMOTD( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
break;
case CG_LOAD_HOSTNAME:
- CG_DrawHostname( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawHostname( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
break;
case CG_FPS:
- CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qtrue );
+ CG_DrawFPS( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle, qtrue );
break;
case CG_FPS_FIXED:
- CG_DrawFPS( &rect, text_x, text_y, scale, color, align, textStyle, qfalse );
+ CG_DrawFPS( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle, qfalse );
break;
case CG_TIMER:
- CG_DrawTimer( &rect, text_x, text_y, scale, color, align, textStyle );
+ CG_DrawTimer( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle );
break;
case CG_CLOCK:
- CG_DrawClock( &rect, text_x, text_y, scale, color, align, textStyle );
+ CG_DrawClock( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle );
break;
case CG_TIMER_MINS:
- CG_DrawTimerMins( &rect, color );
+ CG_DrawTimerMins( &rect, foreColor );
break;
case CG_TIMER_SECS:
- CG_DrawTimerSecs( &rect, color );
+ CG_DrawTimerSecs( &rect, foreColor );
break;
case CG_SNAPSHOT:
- CG_DrawSnapshot( &rect, text_x, text_y, scale, color, align, textStyle );
+ CG_DrawSnapshot( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle );
break;
case CG_LAGOMETER:
- CG_DrawLagometer( &rect, text_x, text_y, scale, color );
+ CG_DrawLagometer( &rect, text_x, text_y, scale, foreColor );
+ break;
+ case CG_TEAMOVERLAY:
+ CG_DrawTeamOverlay( &rect, scale, foreColor );
+ break;
+ case CG_SPEEDOMETER:
+ CG_DrawSpeed( &rect, text_x, text_y, scale, foreColor, backColor );
break;
case CG_DEMO_PLAYBACK:
- CG_DrawDemoPlayback( &rect, color, shader );
+ CG_DrawDemoPlayback( &rect, foreColor, shader );
break;
case CG_DEMO_RECORDING:
- CG_DrawDemoRecording( &rect, color, shader );
+ CG_DrawDemoRecording( &rect, foreColor, shader );
break;
case CG_CONSOLE:
- CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawConsole( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
break;
case CG_TUTORIAL:
- CG_DrawTutorial( &rect, text_x, text_y, color, scale, align, textStyle );
+ CG_DrawTutorial( &rect, text_x, text_y, foreColor, scale, textalign, textvalign, textStyle );
+ break;
+
+ case CG_KILLFEED:
+ CG_DrawKillMsg( &rect, text_x, text_y, scale, foreColor, textalign, textvalign, textStyle );
+ break;
+
+ case CG_PLAYER_THZ_SCANNER:
+ THZ_DrawScanner( &rect );
break;
default:
@@ -2827,17 +3084,17 @@ CG_ShowTeamMenus
*/
void CG_ShowTeamMenu( void )
{
- Menus_OpenByName( "teamMenu" );
+ Menus_ActivateByName( "teamMenu" );
}
/*
==================
CG_EventHandling
-==================
- type 0 - no event handling
- 1 - team menu
- 2 - hud editor
+type 0 - no event handling
+ 1 - team menu
+ 2 - hud editor
+==================
*/
void CG_EventHandling( int type )
{
@@ -2891,14 +3148,6 @@ int CG_ClientNumFromName( const char *p )
void CG_RunMenuScript( char **args )
{
}
-
-
-void CG_GetTeamColor( vec4_t *color )
-{
- (*color)[ 0 ] = (*color)[ 2 ] = 0.0f;
- (*color)[ 1 ] = 0.17f;
- (*color)[ 3 ] = 0.25f;
-}
//END TA UI
@@ -2910,13 +3159,9 @@ CG_DrawLighting
*/
static void CG_DrawLighting( void )
{
- centity_t *cent;
-
- cent = &cg_entities[ cg.snap->ps.clientNum ];
-
//fade to black if stamina is low
- if( ( cg.snap->ps.stats[ STAT_STAMINA ] < -800 ) &&
- ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) )
+ if( ( cg.snap->ps.stats[ STAT_STAMINA ] < STAMINA_BLACKOUT_LEVEL ) &&
+ ( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) )
{
vec4_t black = { 0, 0, 0, 0 };
black[ 3 ] = 1.0 - ( (float)( cg.snap->ps.stats[ STAT_STAMINA ] + 1000 ) / 200.0f );
@@ -2946,8 +3191,15 @@ for a few moments
void CG_CenterPrint( const char *str, int y, int charWidth )
{
char *s;
+ char newlineParsed[ MAX_STRING_CHARS ];
+ const char *wrapped;
+ static int maxWidth = (int)( ( 2.0f / 3.0f ) * (float)SCREEN_WIDTH );
+
+ Q_ParseNewlines( newlineParsed, str, sizeof( newlineParsed ) );
- Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) );
+ wrapped = Item_Text_Wrap( newlineParsed, 0.5f, maxWidth );
+
+ Q_strncpyz( cg.centerPrint, wrapped, sizeof( cg.centerPrint ) );
cg.centerPrintTime = cg.time;
cg.centerPrintY = y;
@@ -2994,9 +3246,9 @@ static void CG_DrawCenterString( void )
while( 1 )
{
- char linebuffer[ 1024 ];
+ char linebuffer[ MAX_STRING_CHARS ];
- for( l = 0; l < 50; l++ )
+ for( l = 0; l < sizeof(linebuffer) - 1; l++ )
{
if( !start[ l ] || start[ l ] == '\n' )
break;
@@ -3006,10 +3258,10 @@ static void CG_DrawCenterString( void )
linebuffer[ l ] = 0;
- w = CG_Text_Width( linebuffer, 0.5, 0 );
- h = CG_Text_Height( linebuffer, 0.5, 0 );
+ w = UI_Text_Width( linebuffer, 0.5 );
+ h = UI_Text_Height( linebuffer, 0.5 );
x = ( SCREEN_WIDTH - w ) / 2;
- CG_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE );
+ UI_Text_Paint( x, y + h, 0.5, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE );
y += h + 6;
while( *start && ( *start != '\n' ) )
@@ -3037,83 +3289,62 @@ static void CG_DrawCenterString( void )
CG_DrawVote
=================
*/
-static void CG_DrawVote( void )
+static void CG_DrawVote( team_t team )
{
- char *s;
+ const char *s;
int sec;
+ int offset = 0;
vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
- char yeskey[ 32 ], nokey[ 32 ];
+ char yeskey[ 32 ] = "", nokey[ 32 ] = "";
- if( !cgs.voteTime )
+ if( !cgs.voteTime[ team ] )
return;
// play a talk beep whenever it is modified
- if( cgs.voteModified )
+ if( cgs.voteModified[ team ] )
{
- cgs.voteModified = qfalse;
+ cgs.voteModified[ team ] = qfalse;
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
}
- sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
+ sec = ( VOTE_TIME - ( cg.time - cgs.voteTime[ team ] ) ) / 1000;
if( sec < 0 )
sec = 0;
- Q_strncpyz( yeskey, CG_KeyBinding( "vote yes" ), sizeof( yeskey ) );
- Q_strncpyz( nokey, CG_KeyBinding( "vote no" ), sizeof( nokey ) );
- s = va( "VOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec, cgs.voteString,
- yeskey, cgs.voteYes, nokey, cgs.voteNo );
- CG_Text_Paint( 8, 340, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
-}
-/*
-=================
-CG_DrawTeamVote
-=================
-*/
-static void CG_DrawTeamVote( void )
-{
- char *s;
- int sec, cs_offset;
- vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
- char yeskey[ 32 ], nokey[ 32 ];
+ if( cg_tutorial.integer )
+ {
+ Com_sprintf( yeskey, sizeof( yeskey ), "[%s]",
+ CG_KeyBinding( va( "%svote yes", team == TEAM_NONE ? "" : "team" ) ) );
+ Com_sprintf( nokey, sizeof( nokey ), "[%s]",
+ CG_KeyBinding( va( "%svote no", team == TEAM_NONE ? "" : "team" ) ) );
+ }
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
- cs_offset = 0;
- else if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
- cs_offset = 1;
- else
- return;
+ if( team != TEAM_NONE )
+ offset = 80;
- if( !cgs.teamVoteTime[ cs_offset ] )
- return;
+ s = va( "%sVOTE(%i): %s",
+ team == TEAM_NONE ? "" : "TEAM", sec, cgs.voteString[ team ] );
- // play a talk beep whenever it is modified
- if ( cgs.teamVoteModified[ cs_offset ] )
- {
- cgs.teamVoteModified[ cs_offset ] = qfalse;
- trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
- }
+ UI_Text_Paint( 8, 300 + offset, 0.3f, white, s, 0, 0,
+ ITEM_TEXTSTYLE_NORMAL );
- sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[ cs_offset ] ) ) / 1000;
+ s = va( " Called by: \"%s\"", cgs.voteCaller[ team ] );
- if( sec < 0 )
- sec = 0;
+ UI_Text_Paint( 8, 320 + offset, 0.3f, white, s, 0, 0,
+ ITEM_TEXTSTYLE_NORMAL );
- Q_strncpyz( yeskey, CG_KeyBinding( "teamvote yes" ), sizeof( yeskey ) );
- Q_strncpyz( nokey, CG_KeyBinding( "teamvote no" ), sizeof( nokey ) );
- s = va( "TEAMVOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec,
- cgs.teamVoteString[ cs_offset ],
- yeskey, cgs.teamVoteYes[cs_offset],
- nokey, cgs.teamVoteNo[ cs_offset ] );
+ s = va( " %sYes:%i %sNo:%i",
+ yeskey, cgs.voteYes[ team ], nokey, cgs.voteNo[ team ] );
- CG_Text_Paint( 8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL );
+ UI_Text_Paint( 8, 340 + offset, 0.3f, white, s, 0, 0,
+ ITEM_TEXTSTYLE_NORMAL );
}
static qboolean CG_DrawScoreboard( void )
{
static qboolean firstTime = qtrue;
- float fade, *fadeColor;
if( menuScoreboard )
menuScoreboard->window.flags &= ~WINDOW_FORCED;
@@ -3125,13 +3356,8 @@ static qboolean CG_DrawScoreboard( void )
return qfalse;
}
- if( cg.showScores ||
- cg.predictedPlayerState.pm_type == PM_INTERMISSION )
- {
- fade = 1.0;
- fadeColor = colorWhite;
- }
- else
+ if( !cg.showScores &&
+ cg.predictedPlayerState.pm_type != PM_INTERMISSION )
{
cg.deferredPlayerLoading = 0;
cg.killerName[ 0 ] = 0;
@@ -3139,6 +3365,7 @@ static qboolean CG_DrawScoreboard( void )
return qfalse;
}
+ CG_RequestScores( );
if( menuScoreboard == NULL )
menuScoreboard = Menus_FindByName( "teamscore_menu" );
@@ -3147,10 +3374,12 @@ static qboolean CG_DrawScoreboard( void )
{
if( firstTime )
{
+ cg.spectatorTime = trap_Milliseconds();
CG_SetScoreSelection( menuScoreboard );
firstTime = qfalse;
}
+ Menu_Update( menuScoreboard );
Menu_Paint( menuScoreboard, qtrue );
}
@@ -3164,27 +3393,28 @@ CG_DrawIntermission
*/
static void CG_DrawIntermission( void )
{
- if( cg_drawStatus.integer )
- Menu_Paint( Menus_FindByName( "default_hud" ), qtrue );
+ menuDef_t *menu = Menus_FindByName( "default_hud" );
+
+ Menu_Update( menu );
+ Menu_Paint( menu, qtrue );
cg.scoreFadeTime = cg.time;
cg.scoreBoardShowing = CG_DrawScoreboard( );
}
-#define FOLLOWING_STRING "following "
-
/*
=================
-CG_DrawFollow
+CG_DrawQueue
=================
*/
-static qboolean CG_DrawFollow( void )
+static qboolean CG_DrawQueue( void )
{
float w;
vec4_t color;
- char buffer[ MAX_STRING_CHARS ];
+ int position;
+ char *ordinal, buffer[ MAX_STRING_CHARS ];
- if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) )
return qfalse;
color[ 0 ] = 1;
@@ -3192,66 +3422,82 @@ static qboolean CG_DrawFollow( void )
color[ 2 ] = 1;
color[ 3 ] = 1;
- strcpy( buffer, FOLLOWING_STRING );
- strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name );
+ position = cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1;
+ if( position < 1 )
+ return qfalse;
- w = CG_Text_Width( buffer, 0.7f, 0 );
- CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ switch( position % 100 )
+ {
+ case 11:
+ case 12:
+ case 13:
+ ordinal = "th";
+ break;
+ default:
+ switch( position % 10 )
+ {
+ case 1: ordinal = "st"; break;
+ case 2: ordinal = "nd"; break;
+ case 3: ordinal = "rd"; break;
+ default: ordinal = "th"; break;
+ }
+ break;
+ }
+
+ Com_sprintf( buffer, MAX_STRING_CHARS, "You are %d%s in the spawn queue",
+ position, ordinal );
+
+ w = UI_Text_Width( buffer, 0.7f );
+ UI_Text_Paint( 320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+
+ if( cg.snap->ps.persistant[ PERS_SPAWNS ] == 0 )
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There are no spawns remaining" );
+ else if( cg.snap->ps.persistant[ PERS_SPAWNS ] == 1 )
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining" );
+ else
+ Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining",
+ cg.snap->ps.persistant[ PERS_SPAWNS ] );
+
+ w = UI_Text_Width( buffer, 0.7f );
+ UI_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
return qtrue;
}
+
/*
=================
-CG_DrawQueue
+CG_DrawWarmup
=================
*/
-static qboolean CG_DrawQueue( void )
+static void CG_DrawWarmup( void )
{
- float w;
- vec4_t color;
- char buffer[ MAX_STRING_CHARS ];
+ int sec = 0;
+ int w;
+ int h;
+ float size = 0.5f;
+ char text[ MAX_STRING_CHARS ] = "Warmup Time:";
- if( !( cg.snap->ps.pm_flags & PMF_QUEUED ) )
- return qfalse;
-
- color[ 0 ] = 1;
- color[ 1 ] = 1;
- color[ 2 ] = 1;
- color[ 3 ] = 1;
+ if( !cg.warmupTime )
+ return;
- Com_sprintf( buffer, MAX_STRING_CHARS, "You are in position %d of the spawn queue.",
- cg.snap->ps.persistant[ PERS_QUEUEPOS ] + 1 );
+ sec = ( cg.warmupTime - cg.time ) / 1000;
- w = CG_Text_Width( buffer, 0.7f, 0 );
- CG_Text_Paint( 320 - w / 2, 360, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ if( sec < 0 )
+ return;
- if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- if( cgs.numAlienSpawns == 1 )
- Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
- else
- Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
- cgs.numAlienSpawns );
- }
- else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- if( cgs.numHumanSpawns == 1 )
- Com_sprintf( buffer, MAX_STRING_CHARS, "There is 1 spawn remaining." );
- else
- Com_sprintf( buffer, MAX_STRING_CHARS, "There are %d spawns remaining.",
- cgs.numHumanSpawns );
- }
+ w = UI_Text_Width( text, size );
+ h = UI_Text_Height( text, size );
+ UI_Text_Paint( 320 - w / 2, 200, size, colorWhite, text, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
- w = CG_Text_Width( buffer, 0.7f, 0 );
- CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ Com_sprintf( text, sizeof( text ), "%s", sec ? va( "%d", sec ) : "FIGHT!" );
- return qtrue;
+ w = UI_Text_Width( text, size );
+ UI_Text_Paint( 320 - w / 2, 200 + 1.5f * h, size, colorWhite, text, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
}
//==================================================================================
-#define SPECTATOR_STRING "SPECTATOR"
/*
=================
CG_Draw2D
@@ -3259,15 +3505,11 @@ CG_Draw2D
*/
static void CG_Draw2D( void )
{
- vec4_t color;
- float w;
- menuDef_t *menu = NULL, *defaultMenu;
+ menuDef_t *menu = NULL;
- color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 1.0f;
-
- // if we are taking a levelshot for the menu, don't draw anything
- if( cg.levelShot )
- return;
+ // fading to black if stamina runs out
+ // (only 2D that can't be disabled)
+ CG_DrawLighting( );
if( cg_draw2D.integer == 0 )
return;
@@ -3278,36 +3520,30 @@ static void CG_Draw2D( void )
return;
}
- //TA: draw the lighting effects e.g. nvg
- CG_DrawLighting( );
-
-
- defaultMenu = Menus_FindByName( "default_hud" );
-
- if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ if ( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_NONE
+ || (cg.snap->ps.persistant[ PERS_SPECSTATE ] == SPECTATOR_NOT
+ && cg.snap->ps.stats[ STAT_HEALTH ] > 0 ))
{
- w = CG_Text_Width( SPECTATOR_STRING, 0.7f, 0 );
- CG_Text_Paint( 320 - w / 2, 440, 0.7f, color, SPECTATOR_STRING, 0, 0, ITEM_TEXTSTYLE_SHADOWED );
+ menu = Menus_FindByName( BG_ClassConfig(
+ cg.predictedPlayerState.stats[ STAT_CLASS ] )->hudName );
+
+ CG_DrawBuildableStatus( );
}
- else
- menu = Menus_FindByName( BG_FindHudNameForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] ) );
- if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) &&
- !( cg.snap->ps.stats[ STAT_STATE ] & SS_HOVELING ) && menu &&
- ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) )
+ if( !menu )
{
- CG_DrawBuildableStatus( );
- if( cg_drawStatus.integer )
- Menu_Paint( menu, qtrue );
+ menu = Menus_FindByName( "default_hud" );
- CG_DrawCrosshair( );
+ if( !menu ) // still couldn't find it
+ CG_Error( "Default HUD could not be found" );
}
- else if( cg_drawStatus.integer )
- Menu_Paint( defaultMenu, qtrue );
- CG_DrawVote( );
- CG_DrawTeamVote( );
- CG_DrawFollow( );
+ Menu_Update( menu );
+ Menu_Paint( menu, qtrue );
+
+ CG_DrawVote( TEAM_NONE );
+ CG_DrawVote( cg.predictedPlayerState.stats[ STAT_TEAM ] );
+ CG_DrawWarmup( );
CG_DrawQueue( );
// don't draw center string if scoreboard is up
@@ -3356,7 +3592,7 @@ static void CG_PainBlend( void )
float x, y, w, h;
float s1, t1, s2, t2;
- if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR || cg.intermissionStarted )
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT || cg.intermissionStarted )
return;
damage = cg.lastHealth - cg.snap->ps.stats[ STAT_HEALTH ];
@@ -3383,9 +3619,9 @@ static void CG_PainBlend( void )
return;
}
- if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
VectorSet( color, 0.43f, 0.8f, 0.37f );
- else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ else if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
VectorSet( color, 0.8f, 0.0f, 0.0f );
if( cg.painBlendValue > cg.painBlendTarget )
@@ -3455,6 +3691,66 @@ void CG_ResetPainBlend( void )
}
/*
+================
+CG_DrawBinaryShadersFinalPhases
+================
+*/
+static void CG_DrawBinaryShadersFinalPhases( void )
+{
+ float ss, f, l, u;
+ polyVert_t verts[ 4 ] = {
+ { { 0, 0, 0 }, { 0, 0 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 1, 0 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 1, 1 }, { 255, 255, 255, 255 } },
+ { { 0, 0, 0 }, { 0, 1 }, { 255, 255, 255, 255 } }
+ };
+ int i, j, k;
+
+ if( !cg.numBinaryShadersUsed )
+ return;
+
+ ss = cg_binaryShaderScreenScale.value;
+ if( ss <= 0.0f )
+ {
+ cg.numBinaryShadersUsed = 0;
+ return;
+ }
+ else if( ss > 1.0f )
+ ss = 1.0f;
+
+ ss = sqrt( ss );
+
+ f = 1.01f; // FIXME: is this a good choice to avoid near-clipping?
+ l = f * tan( DEG2RAD( cg.refdef.fov_x / 2 ) ) * ss;
+ u = f * tan( DEG2RAD( cg.refdef.fov_y / 2 ) ) * ss;
+
+ VectorMA( cg.refdef.vieworg, f, cg.refdef.viewaxis[ 0 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, l, cg.refdef.viewaxis[ 1 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, u, cg.refdef.viewaxis[ 2 ], verts[ 0 ].xyz );
+ VectorMA( verts[ 0 ].xyz, -2*l, cg.refdef.viewaxis[ 1 ], verts[ 1 ].xyz );
+ VectorMA( verts[ 1 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 2 ].xyz );
+ VectorMA( verts[ 0 ].xyz, -2*u, cg.refdef.viewaxis[ 2 ], verts[ 3 ].xyz );
+
+ trap_R_AddPolyToScene( cgs.media.binaryAlpha1Shader, 4, verts );
+
+ for( i = 0; i < cg.numBinaryShadersUsed; ++i )
+ {
+ for( j = 0; j < 4; ++j )
+ {
+ for( k = 0; k < 3; ++k )
+ verts[ j ].modulate[ k ] = cg.binaryShaderSettings[ i ].color[ k ];
+ }
+
+ if( cg.binaryShaderSettings[ i ].drawFrontline )
+ trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].f3, 4, verts );
+ if( cg.binaryShaderSettings[ i ].drawIntersection )
+ trap_R_AddPolyToScene( cgs.media.binaryShaders[ i ].b3, 4, verts );
+ }
+
+ cg.numBinaryShadersUsed = 0;
+}
+
+/*
=====================
CG_DrawActive
@@ -3496,6 +3792,8 @@ void CG_DrawActive( stereoFrame_t stereoView )
VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ],
cg.refdef.vieworg );
+ CG_DrawBinaryShadersFinalPhases( );
+
// draw 3D view
trap_R_RenderScene( &cg.refdef );
@@ -3510,6 +3808,3 @@ void CG_DrawActive( stereoFrame_t stereoView )
// draw status bar and other floating elements
CG_Draw2D( );
}
-
-
-
diff --git a/src/cgame/cg_drawtools.c b/src/cgame/cg_drawtools.c
index 06ae071..cc8533a 100644
--- a/src/cgame/cg_drawtools.c
+++ b/src/cgame/cg_drawtools.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc
-
#include "cg_local.h"
/*
@@ -126,10 +126,14 @@ Coords are virtual 640x480
*/
void CG_DrawSides( float x, float y, float w, float h, float size )
{
+ float sizeY;
+
CG_AdjustFrom640( &x, &y, &w, &h );
+ sizeY = size * cgs.screenYScale;
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 );
+
+ trap_R_DrawStretchPic( x, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, cgs.media.whiteShader );
+ trap_R_DrawStretchPic( x + w - size, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, cgs.media.whiteShader );
}
void CG_DrawTopBottom( float x, float y, float w, float h, float size )
@@ -172,7 +176,34 @@ void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
+/*
+================
+CG_SetClipRegion
+=================
+*/
+void CG_SetClipRegion( float x, float y, float w, float h )
+{
+ vec4_t clip;
+
+ CG_AdjustFrom640( &x, &y, &w, &h );
+ clip[ 0 ] = x;
+ clip[ 1 ] = y;
+ clip[ 2 ] = x + w;
+ clip[ 3 ] = y + h;
+
+ trap_R_SetClipRegion( clip );
+}
+
+/*
+================
+CG_ClearClipRegion
+=================
+*/
+void CG_ClearClipRegion( void )
+{
+ trap_R_SetClipRegion( NULL );
+}
/*
================
@@ -321,30 +352,30 @@ CG_WorldToScreen
*/
qboolean CG_WorldToScreen( vec3_t point, float *x, float *y )
{
- vec3_t trans;
- float xc, yc;
- float px, py;
- float z;
+ 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 );
+ px = tan( cg.refdef.fov_x * M_PI / 360.0f );
+ py = tan( cg.refdef.fov_y * M_PI / 360.0f );
- VectorSubtract( point, cg.refdef.vieworg, trans );
+ VectorSubtract( point, cg.refdef.vieworg, trans );
- xc = 640.0f / 2.0f;
- yc = 480.0f / 2.0f;
+ xc = ( 640.0f * cg_viewsize.integer ) / 200.0f;
+ yc = ( 480.0f * cg_viewsize.integer ) / 200.0f;
- z = DotProduct( trans, cg.refdef.viewaxis[ 0 ] );
- if( z <= 0.001f )
- return qfalse;
+ 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 );
+ *x = 320.0f - DotProduct( trans, cg.refdef.viewaxis[ 1 ] ) * xc / ( z * px );
if( y )
- *y = yc - DotProduct( trans, cg.refdef.viewaxis[ 2 ] ) * yc / ( z * py );
+ *y = 240.0f - DotProduct( trans, cg.refdef.viewaxis[ 2 ] ) * yc / ( z * py );
- return qtrue;
+ return qtrue;
}
/*
@@ -359,6 +390,7 @@ char *CG_KeyBinding( const char *bind )
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++ )
{
@@ -367,12 +399,41 @@ char *CG_KeyBinding( const char *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;
}
+
+/*
+=================
+CG_GetColorCharForHealth
+=================
+*/
+char CG_GetColorCharForHealth( int clientnum )
+{
+ char health_char = '2';
+ int healthPercent;
+ int maxHealth;
+ int curWeaponClass = cgs.clientinfo[ clientnum ].curWeaponClass;
+
+ if( cgs.clientinfo[ clientnum ].team == TEAM_ALIENS )
+ maxHealth = BG_Class( curWeaponClass )->health;
+ else
+ maxHealth = BG_Class( PCL_HUMAN )->health;
+
+ healthPercent = (int) ( 100.0f * (float) cgs.clientinfo[ clientnum ].health /
+ (float) maxHealth );
+
+ if( healthPercent < 33 )
+ health_char = '1';
+ else if( healthPercent < 67 )
+ health_char = '3';
+ return health_char;
+}
diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c
index 17f1a7d..4008e56 100644
--- a/src/cgame/cg_ents.c
+++ b/src/cgame/cg_ents.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_ents.c -- present snapshot entities, happens every single frame
-
#include "cg_local.h"
/*
@@ -251,7 +251,7 @@ static void CG_EntityEffects( centity_t *cent )
if( CG_IsTrailSystemValid( &cent->muzzleTS ) )
{
- //FIXME hack to prevent tesla trails reaching too far
+ //FIXME hack to prevent tesla trails reaching too far
if( cent->currentState.eType == ET_BUILDABLE )
{
vec3_t front, back;
@@ -311,6 +311,136 @@ static void CG_General( centity_t *cent )
/*
==================
+CG_WeaponDrop
+==================
+*/
+static void CG_WeaponDrop( centity_t *cent )
+{
+ refEntity_t ent;
+ entityState_t *es;
+ int msec;
+ float frac;
+ float scale;
+ weaponInfo_t *wi;
+
+ es = &cent->currentState;
+
+ if (BG_Weapon(es->modelindex) == WP_NONE)
+ {
+ CG_Printf("Bad weapon index %i on entity", es->modelindex);
+ return;
+ }
+
+ // if set to invisible, skip
+ if (es->eFlags & EF_NODRAW)
+ return;
+
+
+ // items bob up and down continuously
+ scale = 0.005 + cent->currentState.number * 0.00001;
+ cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
+
+ memset (&ent, 0, sizeof(ent));
+
+ // autorotate at one of two speeds
+ VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
+ AxisCopy( cg.autoAxisFast, ent.axis );
+// VectorCopy( cg.autoAngles, cent->lerpAngles );
+// AxisCopy( cg.autoAxis, ent.axis );
+
+ wi = &cg_weapons[ es->modelindex ];
+
+ // the weapons have their origin where they attatch to player
+ // models, so we need to offset them or they will rotate
+ // eccentricly
+
+ cent->lerpOrigin[0] -= wi->weaponMidpoint[0] * ent.axis[0][0]
+ + wi->weaponMidpoint[1] * ent.axis[1][0]
+ + wi->weaponMidpoint[2] * ent.axis[2][0];
+ cent->lerpOrigin[1] -= wi->weaponMidpoint[0] * ent.axis[0][1]
+ + wi->weaponMidpoint[1] * ent.axis[1][1]
+ + wi->weaponMidpoint[2] * ent.axis[2][1];
+ cent->lerpOrigin[2] -= wi->weaponMidpoint[0] * ent.axis[0][2]
+ + wi->weaponMidpoint[1] * ent.axis[1][2]
+ + wi->weaponMidpoint[2] * ent.axis[2][2];
+ cent->lerpOrigin[2] += 8; // an extra height boost
+
+#if 0
+ if( item->giType == IT_WEAPON && item->giTag == WP_RAILGUN ) {
+ clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum];
+ Byte4Copy( ci->c1RGBA, ent.shaderRGBA );
+ }
+#endif
+
+ ent.hModel = wi->weaponModel;
+
+ VectorCopy( cent->lerpOrigin, ent.origin);
+ VectorCopy( cent->lerpOrigin, ent.oldorigin);
+
+ ent.nonNormalizedAxes = qfalse;
+
+ // if just respawned, slowly scale up
+ msec = cg.time - cent->miscTime;
+ if ( msec >= 0 && msec < ITEM_SCALEUP_TIME )
+ {
+ frac = (float)msec / ITEM_SCALEUP_TIME;
+ VectorScale( ent.axis[0], frac, ent.axis[0] );
+ VectorScale( ent.axis[1], frac, ent.axis[1] );
+ VectorScale( ent.axis[2], frac, ent.axis[2] );
+ ent.nonNormalizedAxes = qtrue;
+ }
+ else
+ {
+ frac = 1.0;
+ }
+
+ // items without glow textures need to keep a minimum light value
+ // so they are always visible
+ ent.renderfx |= RF_MINLIGHT;
+
+ // increase the size of the weapons when they are presented as items
+ VectorScale( ent.axis[0], 1.5, ent.axis[0] );
+ VectorScale( ent.axis[1], 1.5, ent.axis[1] );
+ VectorScale( ent.axis[2], 1.5, ent.axis[2] );
+ ent.nonNormalizedAxes = qtrue;
+#ifdef MISSIONPACK
+ trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
+#endif
+
+ // add to refresh list
+ trap_R_AddRefEntityToScene(&ent);
+
+#if 0
+ if ( item->giType == IT_WEAPON && wi && wi->barrelModel )
+ {
+ refEntity_t barrel;
+ vec3_t angles;
+
+ memset( &barrel, 0, sizeof( barrel ) );
+
+ barrel.hModel = wi->barrelModel;
+
+ VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
+ barrel.shadowPlane = ent.shadowPlane;
+ barrel.renderfx = ent.renderfx;
+
+ angles[YAW] = 0;
+ angles[PITCH] = 0;
+ angles[ROLL] = 0;
+ AnglesToAxis( angles, barrel.axis );
+
+ CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
+
+ barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
+
+ trap_R_AddRefEntityToScene( &barrel );
+ }
+#endif
+}
+
+
+/*
+==================
CG_Speaker
Speaker entities can automatically play sounds
@@ -367,6 +497,7 @@ static void CG_LaunchMissile( centity_t *cent )
{
CG_SetAttachmentCent( &ps->attachment, cent );
CG_AttachToCent( &ps->attachment );
+ ps->charge = es->torsoAnim;
}
}
@@ -407,9 +538,6 @@ static void CG_Missile( centity_t *cent )
wim = &wi->wim[ weaponMode ];
- // calculate the axis
- VectorCopy( es->angles, cent->lerpAngles );
-
// add dynamic light
if( wim->missileDlight )
{
@@ -437,7 +565,8 @@ static void CG_Missile( centity_t *cent )
if( wim->usesSpriteMissle )
{
ent.reType = RT_SPRITE;
- ent.radius = wim->missileSpriteSize;
+ ent.radius = wim->missileSpriteSize +
+ wim->missileSpriteCharge * es->torsoAnim;
ent.rotation = 0;
ent.customShader = wim->missileSprite;
ent.shaderRGBA[ 0 ] = 0xFF;
@@ -498,6 +627,9 @@ static void CG_Mover( centity_t *cent )
s1 = &cent->currentState;
+ if( !s1->modelindex )
+ return;
+
// create the render entity
memset( &ent, 0, sizeof( ent ) );
VectorCopy( cent->lerpOrigin, ent.origin );
@@ -653,7 +785,7 @@ static void CG_LightFlare( centity_t *cent )
flare.renderfx |= RF_DEPTHHACK;
//bunch of geometry
- AngleVectors( es->angles, forward, NULL, NULL );
+ AngleVectors( cent->lerpAngles, forward, NULL, NULL );
VectorCopy( cent->lerpOrigin, flare.origin );
VectorSubtract( flare.origin, cg.refdef.vieworg, delta );
len = VectorLength( delta );
@@ -788,38 +920,28 @@ static void CG_Lev2ZapChain( centity_t *cent )
int i;
entityState_t *es;
centity_t *source = NULL, *target = NULL;
+ int entityNums[ LEVEL2_AREAZAP_MAX_TARGETS + 1 ];
+ int count;
es = &cent->currentState;
- for( i = 0; i <= 2; i++ )
+ count = BG_UnpackEntityNumbers( es, entityNums, LEVEL2_AREAZAP_MAX_TARGETS + 1 );
+
+ for( i = 1; i < count; i++ )
{
- switch( i )
+ if( i == 1 )
{
- case 0:
- if( es->time <= 0 )
- continue;
-
- source = &cg_entities[ es->misc ];
- target = &cg_entities[ es->time ];
- break;
-
- case 1:
- if( es->time2 <= 0 )
- continue;
-
- source = &cg_entities[ es->time ];
- target = &cg_entities[ es->time2 ];
- break;
-
- case 2:
- if( es->constantLight <= 0 )
- continue;
-
- source = &cg_entities[ es->time2 ];
- target = &cg_entities[ es->constantLight ];
- break;
+ // First entity is the attacker
+ source = &cg_entities[ entityNums[ 0 ] ];
+ }
+ else
+ {
+ // Subsequent zaps come from the first target
+ source = &cg_entities[ entityNums[ 1 ] ];
}
+ target = &cg_entities[ entityNums[ i ] ];
+
if( !CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
@@ -844,7 +966,7 @@ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int
{
centity_t *cent;
vec3_t oldOrigin, origin, deltaOrigin;
- vec3_t oldAngles, angles, deltaAngles;
+ vec3_t oldAngles, angles;
if( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL )
{
@@ -867,7 +989,6 @@ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int
BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );
VectorSubtract( origin, oldOrigin, deltaOrigin );
- VectorSubtract( angles, oldAngles, deltaAngles );
VectorAdd( in, deltaOrigin, out );
@@ -947,9 +1068,10 @@ static void CG_CalcEntityLerpPositions( centity_t *cent )
return;
}
- if( cg_projectileNudge.integer > 0 &&
- cent->currentState.eType == ET_MISSILE &&
- !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ if( cg_projectileNudge.integer &&
+ !cg.demoPlayback &&
+ cent->currentState.eType == ET_MISSILE &&
+ !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
timeshift = cg.ping;
}
@@ -972,7 +1094,7 @@ static void CG_CalcEntityLerpPositions( centity_t *cent )
// don't let the projectile go through the floor
if( tr.fraction < 1.0f )
- VectorLerp( tr.fraction, lastOrigin, cent->lerpOrigin, cent->lerpOrigin );
+ VectorLerp2( tr.fraction, lastOrigin, cent->lerpOrigin, cent->lerpOrigin );
}
// adjust for riding a mover if it wasn't rolled into the predicted
@@ -984,7 +1106,6 @@ static void CG_CalcEntityLerpPositions( centity_t *cent )
}
}
-
/*
===============
CG_CEntityPVSEnter
@@ -1003,6 +1124,10 @@ static void CG_CEntityPVSEnter( centity_t *cent )
case ET_MISSILE:
CG_LaunchMissile( cent );
break;
+
+ case ET_BUILDABLE:
+ cent->lastBuildableHealth = es->misc;
+ break;
}
//clear any particle systems from previous uses of this centity_t
@@ -1034,11 +1159,10 @@ static void CG_CEntityPVSLeave( centity_t *cent )
if( cg_debugPVS.integer )
CG_Printf( "Entity %d left PVS\n", cent->currentState.number );
-
switch( es->eType )
{
case ET_LEV2_ZAP_CHAIN:
- for( i = 0; i <= 2; i++ )
+ for( i = 0; i <= LEVEL2_AREAZAP_MAX_TARGETS; i++ )
{
if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
CG_DestroyTrailSystem( &cent->level2ZapTS[ i ] );
@@ -1069,18 +1193,23 @@ static void CG_AddCEntity( centity_t *cent )
switch( cent->currentState.eType )
{
default:
- CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
+ CG_Error( "Bad entity type: %i", cent->currentState.eType );
break;
case ET_INVISIBLE:
case ET_PUSH_TRIGGER:
case ET_TELEPORT_TRIGGER:
+ case ET_LOCATION:
break;
case ET_GENERAL:
CG_General( cent );
break;
+ case ET_WEAPON_DROP:
+ CG_WeaponDrop( cent );
+ break;
+
case ET_CORPSE:
CG_Corpse( cent );
break;
@@ -1093,6 +1222,10 @@ static void CG_AddCEntity( centity_t *cent )
CG_Buildable( cent );
break;
+ case ET_RANGE_MARKER:
+ CG_RangeMarker( cent );
+ break;
+
case ET_MISSILE:
CG_Missile( cent );
break;
@@ -1253,4 +1386,3 @@ void CG_AddPacketEntities( void )
}
}
}
-
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index 07bcea9..3ed0ac9 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,17 +17,130 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_event.c -- handle entity events at snapshot or playerstate transitions
-
#include "cg_local.h"
/*
+=======================
+CG_AddToKillMsg
+
+=======================
+*/
+void CG_AddToKillMsg( const char* killername, const char* victimname, int icon )
+{
+ int klen, vlen, index;
+ char *kls, *vls;
+ char *k, *v;
+ int lastcolor;
+ int chatHeight;
+
+ if( cg_killMsgHeight.integer < TEAMCHAT_HEIGHT )
+ chatHeight = cg_killMsgHeight.integer;
+ else
+ chatHeight = TEAMCHAT_HEIGHT;
+
+ if( chatHeight <= 0 || cg_killMsgTime.integer <= 0 ) {
+ cgs.killMsgPos = cgs.killMsgLastPos = 0;
+ return;
+ }
+
+ index = cgs.killMsgPos % chatHeight;
+ klen = vlen = 0;
+
+ k = cgs.killMsgKillers[ index ]; *k=0;
+ v = cgs.killMsgVictims[ index ]; *v=0;
+ cgs.killMsgWeapons[ index ] = icon;
+
+ memset( k, '\0', sizeof(cgs.killMsgKillers[index]));
+ memset( v, '\0', sizeof(cgs.killMsgVictims[index]));
+ kls = vls = NULL;
+
+ lastcolor = '7';
+
+ // Killers name
+ while( *killername )
+ {
+ if( klen > TEAMCHAT_WIDTH-1 ) {
+ if( kls ) {
+ killername -= ( k - kls );
+ killername ++;
+ k -= ( k - kls );
+ }
+ *k = 0;
+
+// cgs.killMsgMsgTimes[index] = cg.time;
+ k = cgs.killMsgKillers[index];
+ *k = 0;
+ *k++ = Q_COLOR_ESCAPE;
+ *k++ = lastcolor;
+ klen = 0;
+ kls = NULL;
+ }
+
+ if( Q_IsColorString( killername ) )
+ {
+ *k++ = *killername++;
+ lastcolor = *killername;
+ *k++ = *killername++;
+ continue;
+ }
+
+ if( *killername == ' ' )
+ kls = k;
+
+ *k++ = *killername++;
+ klen++;
+ }
+
+ // Victims name
+ if (victimname)
+ while( *victimname )
+ {
+ if( vlen > TEAMCHAT_WIDTH-1 ) {
+ if( vls ) {
+ victimname -= ( v - vls );
+ victimname ++;
+ v -= ( v - vls );
+ }
+ *v = 0;
+
+ v = cgs.killMsgVictims[index];
+ *v = 0;
+ *v++ = Q_COLOR_ESCAPE;
+ *v++ = lastcolor;
+ vlen = 0;
+ vls = NULL;
+ }
+
+ if( Q_IsColorString( victimname ) )
+ {
+ *v++ = *victimname++;
+ lastcolor = *victimname;
+ *v++ = *victimname++;
+ continue;
+ }
+
+ if( *victimname == ' ' )
+ vls = v;
+
+ *v++ = *victimname++;
+ vlen++;
+ }
+
+ cgs.killMsgMsgTimes[ index ] = cg.time;
+ cgs.killMsgPos++;
+
+ if( cgs.killMsgPos - cgs.killMsgLastPos > chatHeight )
+ cgs.killMsgLastPos = cgs.killMsgPos - chatHeight;
+}
+
+/*
=============
CG_Obituary
=============
@@ -39,12 +153,13 @@ static void CG_Obituary( entityState_t *ent )
char *message2;
const char *targetInfo;
const char *attackerInfo;
- char targetName[ 32 ];
- char attackerName[ 32 ];
+ char targetName[ MAX_NAME_LENGTH ];
+ char attackerName[ MAX_NAME_LENGTH ];
char className[ 64 ];
gender_t gender;
clientInfo_t *ci;
qboolean teamKill = qfalse;
+ int icon = WP_NONE;
target = ent->otherEntityNum;
attacker = ent->otherEntityNum2;
@@ -54,6 +169,7 @@ static void CG_Obituary( entityState_t *ent )
CG_Error( "CG_Obituary: target out of range" );
ci = &cgs.clientinfo[ target ];
+ gender = ci->gender;
if( attacker < 0 || attacker >= MAX_CLIENTS )
{
@@ -72,8 +188,7 @@ static void CG_Obituary( entityState_t *ent )
if( !targetInfo )
return;
- Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ) - 2 );
- strcat( targetName, S_COLOR_WHITE );
+ Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ));
message2 = "";
@@ -81,9 +196,6 @@ static void CG_Obituary( entityState_t *ent )
switch( mod )
{
- case MOD_SUICIDE:
- message = "suicides";
- break;
case MOD_FALLING:
message = "fell fowl to gravity";
break;
@@ -97,7 +209,7 @@ static void CG_Obituary( entityState_t *ent )
message = "melted";
break;
case MOD_LAVA:
- message = "does a back flip into the lava";
+ message = "did a back flip into the lava";
break;
case MOD_TARGET_LASER:
message = "saw the light";
@@ -137,9 +249,8 @@ static void CG_Obituary( entityState_t *ent )
break;
}
- if( attacker == target )
+ if( !message && attacker == target )
{
- gender = ci->gender;
switch( mod )
{
case MOD_FLAMER_SPLASH:
@@ -169,6 +280,24 @@ static void CG_Obituary( entityState_t *ent )
message = "blew himself up";
break;
+ case MOD_LEVEL3_BOUNCEBALL:
+ if( gender == GENDER_FEMALE )
+ message = "sniped herself";
+ else if( gender == GENDER_NEUTER )
+ message = "sniped itself";
+ else
+ message = "sniped himself";
+ break;
+
+ case MOD_PRIFLE:
+ if( gender == GENDER_FEMALE )
+ message = "pulse rifled herself";
+ else if( gender == GENDER_NEUTER )
+ message = "pulse rifled itself";
+ else
+ message = "pulse rifled himself";
+ break;
+
default:
if( gender == GENDER_FEMALE )
message = "killed herself";
@@ -178,12 +307,12 @@ static void CG_Obituary( entityState_t *ent )
message = "killed himself";
break;
}
- }
- if( message )
- {
- CG_Printf( "%s %s.\n", targetName, message );
- return;
+ if ( cg_killMsg.integer == 2)
+ {
+ CG_AddToKillMsg(va("%s ^7%s", targetName, message), NULL, WP_NONE);
+ return;
+ }
}
// check for double client messages
@@ -194,8 +323,7 @@ static void CG_Obituary( entityState_t *ent )
}
else
{
- Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ) - 2);
- strcat( attackerName, S_COLOR_WHITE );
+ Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ));
// check for kill messages about the current clientNum
if( target == cg.snap->ps.clientNum )
Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
@@ -205,119 +333,141 @@ static void CG_Obituary( entityState_t *ent )
{
switch( mod )
{
+ //
+ // HUMANS
+ //
case MOD_PAINSAW:
+ icon = WP_PAIN_SAW;
message = "was sawn by";
break;
case MOD_BLASTER:
+ icon = WP_BLASTER;
message = "was blasted by";
break;
case MOD_MACHINEGUN:
+ icon = WP_MACHINEGUN;
message = "was machinegunned by";
break;
case MOD_CHAINGUN:
+ icon = WP_CHAINGUN;
message = "was chaingunned by";
break;
case MOD_SHOTGUN:
+ icon = WP_SHOTGUN;
message = "was gunned down by";
break;
case MOD_PRIFLE:
+ icon = WP_PULSE_RIFLE;
message = "was pulse rifled by";
break;
case MOD_MDRIVER:
+ icon = WP_MASS_DRIVER;
message = "was mass driven by";
break;
case MOD_LASGUN:
+ icon = WP_LAS_GUN;
message = "was lasgunned by";
break;
case MOD_FLAMER:
- message = "was grilled by";
- message2 = "'s flamer";
- break;
case MOD_FLAMER_SPLASH:
- message = "was toasted by";
+ icon = WP_FLAMER;
+ message = "was grilled by";
message2 = "'s flamer";
break;
case MOD_LCANNON:
+ icon = WP_LUCIFER_CANNON;
message = "felt the full force of";
message2 = "'s lucifer cannon";
break;
case MOD_LCANNON_SPLASH:
+ icon = WP_LUCIFER_CANNON;
message = "was caught in the fallout of";
message2 = "'s lucifer cannon";
break;
case MOD_GRENADE:
+ icon = WP_GRENADE;
message = "couldn't escape";
message2 = "'s grenade";
break;
+ //
+ // ALIENS
+ //
case MOD_ABUILDER_CLAW:
+ icon = WP_ABUILD;
message = "should leave";
message2 = "'s buildings alone";
break;
case MOD_LEVEL0_BITE:
+ icon = WP_ALEVEL0;
message = "was bitten by";
break;
case MOD_LEVEL1_CLAW:
+ icon = WP_ALEVEL1;
message = "was swiped by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL1 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL1_PCLOUD:
+ icon = WP_ALEVEL1;
+ message = "was gassed by";
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
message2 = className;
break;
case MOD_LEVEL2_CLAW:
+ icon = WP_ALEVEL2;
message = "was clawed by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL2 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
message2 = className;
break;
case MOD_LEVEL2_ZAP:
+ icon = WP_ALEVEL2;
message = "was zapped by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL2 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
message2 = className;
break;
case MOD_LEVEL3_CLAW:
+ icon = WP_ALEVEL3;
message = "was chomped by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
message2 = className;
break;
case MOD_LEVEL3_POUNCE:
+ icon = WP_ALEVEL3;
message = "was pounced upon by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
message2 = className;
break;
case MOD_LEVEL3_BOUNCEBALL:
+ icon = WP_ALEVEL3;
message = "was sniped by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
message2 = className;
break;
case MOD_LEVEL4_CLAW:
+ icon = WP_ALEVEL4;
message = "was mauled by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
message2 = className;
break;
- case MOD_LEVEL4_CHARGE:
+ case MOD_LEVEL4_TRAMPLE:
+ icon = WP_ALEVEL4;
message = "should have gotten out of the way of";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
+ Com_sprintf( className, 64, "'s %s", BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
message2 = className;
break;
-
+ case MOD_LEVEL4_CRUSH:
+ message = "was crushed under";
+ message2 = "'s weight";
+ break;
case MOD_POISON:
message = "should have used a medkit against";
message2 = "'s poison";
break;
- case MOD_LEVEL1_PCLOUD:
- message = "was gassed by";
- Com_sprintf( className, 64, "'s %s",
- BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL1 ) );
- message2 = className;
- break;
-
+ //
+ // MISC..
+ //
case MOD_TELEFRAG:
message = "tried to invade";
message2 = "'s personal space";
@@ -327,24 +477,55 @@ static void CG_Obituary( entityState_t *ent )
break;
}
- if( message )
+ if ( cg_killMsg.integer == 1)
{
- CG_Printf( "%s %s %s%s%s\n",
- targetName, message,
- ( teamKill ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
- attackerName, message2 );
- if( teamKill && attacker == cg.clientNum )
+ char killMessage[80];
+ if( icon > WP_NONE )
{
- CG_CenterPrint( va ( "You killed " S_COLOR_RED "TEAMMATE "
- S_COLOR_WHITE "%s", targetName ),
- SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
+ Com_sprintf(killMessage, sizeof(killMessage), "%s%s",
+ teamKill ? S_COLOR_RED"TEAMMATE "S_COLOR_WHITE:"",
+ targetName);
+ CG_AddToKillMsg(attackerName, killMessage, icon);
}
- return;
}
+ else if( message )
+ {
+ CG_Printf( "%s" S_COLOR_WHITE " %s %s%s" S_COLOR_WHITE "%s\n",
+ targetName,
+ message,
+ teamKill ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
+ attackerName,
+ message2 );
+ }
+
+ if( attacker == cg.clientNum )
+ {
+ CG_CenterPrint(va("You killed %s%s", teamKill ? S_COLOR_RED"TEAMMATE "S_COLOR_WHITE:"",
+ targetName), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
+ }
+ if ( cg_killMsg.integer != 1)
+ return;
+ }
+
+#if 0
+ switch (mod) {
+ case MOD_SLAP:
+ message = "was slapped to death";
+ break;
+ default:
+ break;
}
+#endif
// we don't know what it was
- CG_Printf( "%s died.\n", targetName );
+ if ( cg_killMsg.integer )
+ {
+ CG_AddToKillMsg(va("%s ^7%s", targetName, message), NULL, WP_NONE);
+ }
+ else
+ {
+ CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message );
+ }
}
//==========================================================================
@@ -382,6 +563,60 @@ void CG_PainEvent( centity_t *cent, int health )
}
/*
+=========================
+CG_Level2Zap
+=========================
+*/
+static void CG_Level2Zap( entityState_t *es )
+{
+ int i;
+ centity_t *source = NULL, *target = NULL;
+
+ if( es->misc < 0 || es->misc >= MAX_CLIENTS )
+ return;
+
+ source = &cg_entities[ es->misc ];
+ for( i = 0; i <= 2; i++ )
+ {
+ switch( i )
+ {
+ case 0:
+ if( es->time <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time ];
+ break;
+
+ case 1:
+ if( es->time2 <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time2 ];
+ break;
+
+ case 2:
+ if( es->constantLight <= 0 )
+ continue;
+
+ target = &cg_entities[ es->constantLight ];
+ break;
+ }
+
+ if( !CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ source->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
+
+ if( CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ {
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->frontAttachment, source );
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->backAttachment, target );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->frontAttachment );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->backAttachment );
+ }
+ }
+ source->level2ZapTime = cg.time;
+}
+
+/*
==============
CG_EntityEvent
@@ -389,7 +624,6 @@ An entity has an event value
also called by CG_CheckPlayerstateEvents
==============
*/
-#define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");}
void CG_EntityEvent( centity_t *cent, vec3_t position )
{
entityState_t *es;
@@ -400,22 +634,20 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
clientInfo_t *ci;
int steptime;
- if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
steptime = 200;
else
- steptime = BG_FindSteptimeForClass( cg.snap->ps.stats[ STAT_PCLASS ] );
+ steptime = BG_Class( cg.snap->ps.stats[ STAT_CLASS ] )->steptime;
es = &cent->currentState;
event = es->event & ~EV_EVENT_BITS;
if( cg_debugEvents.integer )
- CG_Printf( "ent:%3i event:%3i ", es->number, event );
+ CG_Printf( "ent:%3i event:%3i %s\n", es->number, event,
+ BG_EventName( event ) );
if( !event )
- {
- DEBUGNAME("ZEROEVENT");
return;
- }
clientNum = es->clientNum;
if( clientNum < 0 || clientNum >= MAX_CLIENTS )
@@ -429,7 +661,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
// movement generated events
//
case EV_FOOTSTEP:
- DEBUGNAME( "EV_FOOTSTEP" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
if( ci->footsteps == FOOTSTEP_CUSTOM )
@@ -442,7 +673,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_FOOTSTEP_METAL:
- DEBUGNAME( "EV_FOOTSTEP_METAL" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
if( ci->footsteps == FOOTSTEP_CUSTOM )
@@ -455,7 +685,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_FOOTSTEP_SQUELCH:
- DEBUGNAME( "EV_FOOTSTEP_SQUELCH" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
trap_S_StartSound( NULL, es->number, CHAN_BODY,
@@ -464,7 +693,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_FOOTSPLASH:
- DEBUGNAME( "EV_FOOTSPLASH" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
trap_S_StartSound( NULL, es->number, CHAN_BODY,
@@ -473,7 +701,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_FOOTWADE:
- DEBUGNAME( "EV_FOOTWADE" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
trap_S_StartSound( NULL, es->number, CHAN_BODY,
@@ -482,7 +709,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_SWIM:
- DEBUGNAME( "EV_SWIM" );
if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
{
trap_S_StartSound( NULL, es->number, CHAN_BODY,
@@ -492,45 +718,41 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
case EV_FALL_SHORT:
- DEBUGNAME( "EV_FALL_SHORT" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound );
if( clientNum == cg.predictedPlayerState.clientNum )
{
// smooth landing z changes
- cg.landChange = -8;
+ cg.landChange = -1 * BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->landBob;
cg.landTime = cg.time;
}
break;
case EV_FALL_MEDIUM:
- DEBUGNAME( "EV_FALL_MEDIUM" );
// use normal pain sound
trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
if( clientNum == cg.predictedPlayerState.clientNum )
{
// smooth landing z changes
- cg.landChange = -16;
+ cg.landChange = -2 * BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->landBob;
cg.landTime = cg.time;
}
break;
case EV_FALL_FAR:
- DEBUGNAME( "EV_FALL_FAR" );
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
cent->pe.painTime = cg.time; // don't play a pain sound right after this
if( clientNum == cg.predictedPlayerState.clientNum )
{
// smooth landing z changes
- cg.landChange = -24;
+ cg.landChange = -3 * BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->landBob;
cg.landTime = cg.time;
}
break;
case EV_FALLING:
- DEBUGNAME( "EV_FALLING" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*falling1.wav" ) );
break;
@@ -542,7 +764,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
case EV_STEPDN_8:
case EV_STEPDN_12:
case EV_STEPDN_16: // smooth out step down transitions
- DEBUGNAME( "EV_STEP" );
{
float oldStep;
int delta;
@@ -586,10 +807,9 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
}
case EV_JUMP:
- DEBUGNAME( "EV_JUMP" );
trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
- if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_WALLJUMPER ) )
+ if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
{
vec3_t surfNormal, refNormal = { 0.0f, 0.0f, 1.0f };
vec3_t rotAxis;
@@ -617,44 +837,36 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_LEV1_GRAB:
- DEBUGNAME( "EV_LEV1_GRAB" );
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL1Grab );
break;
- case EV_LEV4_CHARGE_PREPARE:
- DEBUGNAME( "EV_LEV4_CHARGE_PREPARE" );
+ case EV_LEV4_TRAMPLE_PREPARE:
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargePrepare );
break;
- case EV_LEV4_CHARGE_START:
- DEBUGNAME( "EV_LEV4_CHARGE_START" );
+ case EV_LEV4_TRAMPLE_START:
//FIXME: stop cgs.media.alienL4ChargePrepare playing here
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargeStart );
break;
case EV_TAUNT:
- DEBUGNAME( "EV_TAUNT" );
if( !cg_noTaunt.integer )
trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
break;
case EV_WATER_TOUCH:
- DEBUGNAME( "EV_WATER_TOUCH" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
break;
case EV_WATER_LEAVE:
- DEBUGNAME( "EV_WATER_LEAVE" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
break;
case EV_WATER_UNDER:
- DEBUGNAME( "EV_WATER_UNDER" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
break;
case EV_WATER_CLEAR:
- DEBUGNAME( "EV_WATER_CLEAR" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
break;
@@ -662,28 +874,23 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
// weapon events
//
case EV_NOAMMO:
- DEBUGNAME( "EV_NOAMMO" );
- {
- }
+ trap_S_StartSound( NULL, es->number, CHAN_WEAPON,
+ cgs.media.weaponEmptyClick );
break;
case EV_CHANGE_WEAPON:
- DEBUGNAME( "EV_CHANGE_WEAPON" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
break;
case EV_FIRE_WEAPON:
- DEBUGNAME( "EV_FIRE_WEAPON" );
CG_FireWeapon( cent, WPM_PRIMARY );
break;
case EV_FIRE_WEAPON2:
- DEBUGNAME( "EV_FIRE_WEAPON2" );
CG_FireWeapon( cent, WPM_SECONDARY );
break;
case EV_FIRE_WEAPON3:
- DEBUGNAME( "EV_FIRE_WEAPON3" );
CG_FireWeapon( cent, WPM_TERTIARY );
break;
@@ -693,32 +900,26 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
// other events
//
case EV_PLAYER_TELEPORT_IN:
- DEBUGNAME( "EV_PLAYER_TELEPORT_IN" );
//deprecated
break;
case EV_PLAYER_TELEPORT_OUT:
- DEBUGNAME( "EV_PLAYER_TELEPORT_OUT" );
CG_PlayerDisconnect( position );
break;
case EV_BUILD_CONSTRUCT:
- DEBUGNAME( "EV_BUILD_CONSTRUCT" );
//do something useful here
break;
case EV_BUILD_DESTROY:
- DEBUGNAME( "EV_BUILD_DESTROY" );
//do something useful here
break;
case EV_RPTUSE_SOUND:
- DEBUGNAME( "EV_RPTUSE_SOUND" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.repeaterUseSound );
break;
case EV_GRENADE_BOUNCE:
- DEBUGNAME( "EV_GRENADE_BOUNCE" );
if( rand( ) & 1 )
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound1 );
else
@@ -729,40 +930,34 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
// missile impacts
//
case EV_MISSILE_HIT:
- DEBUGNAME( "EV_MISSILE_HIT" );
ByteToDir( es->eventParm, dir );
- CG_MissileHitPlayer( es->weapon, es->generic1, position, dir, es->otherEntityNum );
+ CG_MissileHitEntity( es->weapon, es->generic1, position, dir, es->otherEntityNum, es->torsoAnim );
break;
case EV_MISSILE_MISS:
- DEBUGNAME( "EV_MISSILE_MISS" );
ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_DEFAULT );
+ CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_DEFAULT, es->torsoAnim );
break;
case EV_MISSILE_MISS_METAL:
- DEBUGNAME( "EV_MISSILE_MISS_METAL" );
ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_METAL );
+ CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_METAL, es->torsoAnim );
break;
case EV_HUMAN_BUILDABLE_EXPLOSION:
- DEBUGNAME( "EV_HUMAN_BUILDABLE_EXPLOSION" );
ByteToDir( es->eventParm, dir );
CG_HumanBuildableExplosion( position, dir );
break;
case EV_ALIEN_BUILDABLE_EXPLOSION:
- DEBUGNAME( "EV_ALIEN_BUILDABLE_EXPLOSION" );
ByteToDir( es->eventParm, dir );
CG_AlienBuildableExplosion( position, dir );
break;
case EV_TESLATRAIL:
- DEBUGNAME( "EV_TESLATRAIL" );
cent->currentState.weapon = WP_TESLAGEN;
{
- centity_t *source = &cg_entities[ es->generic1 ];
+ centity_t *source = &cg_entities[ es->misc ];
centity_t *target = &cg_entities[ es->clientNum ];
vec3_t sourceOffset = { 0.0f, 0.0f, 28.0f };
@@ -785,23 +980,19 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_BULLET_HIT_WALL:
- DEBUGNAME( "EV_BULLET_HIT_WALL" );
ByteToDir( es->eventParm, dir );
CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD );
break;
case EV_BULLET_HIT_FLESH:
- DEBUGNAME( "EV_BULLET_HIT_FLESH" );
CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
break;
case EV_SHOTGUN:
- DEBUGNAME( "EV_SHOTGUN" );
CG_ShotgunFire( es );
break;
case EV_GENERAL_SOUND:
- DEBUGNAME( "EV_GENERAL_SOUND" );
if( cgs.gameSounds[ es->eventParm ] )
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] );
else
@@ -812,7 +1003,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes
- DEBUGNAME( "EV_GLOBAL_SOUND" );
if( cgs.gameSounds[ es->eventParm ] )
trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] );
else
@@ -825,7 +1015,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
case EV_PAIN:
// local player sounds are triggered in CG_CheckLocalSounds,
// so ignore events on the player
- DEBUGNAME( "EV_PAIN" );
if( cent->currentState.number != cg.snap->ps.clientNum )
CG_PainEvent( cent, es->eventParm );
break;
@@ -833,34 +1022,28 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
case EV_DEATH1:
case EV_DEATH2:
case EV_DEATH3:
- DEBUGNAME( "EV_DEATHx" );
trap_S_StartSound( NULL, es->number, CHAN_VOICE,
CG_CustomSound( es->number, va( "*death%i.wav", event - EV_DEATH1 + 1 ) ) );
break;
case EV_OBITUARY:
- DEBUGNAME( "EV_OBITUARY" );
CG_Obituary( es );
break;
case EV_GIB_PLAYER:
- DEBUGNAME( "EV_GIB_PLAYER" );
// no gibbing
break;
case EV_STOPLOOPINGSOUND:
- DEBUGNAME( "EV_STOPLOOPINGSOUND" );
trap_S_StopLoopingSound( es->number );
es->loopSound = 0;
break;
case EV_DEBUG_LINE:
- DEBUGNAME( "EV_DEBUG_LINE" );
CG_Beam( cent );
break;
case EV_BUILD_DELAY:
- DEBUGNAME( "EV_BUILD_DELAY" );
if( clientNum == cg.predictedPlayerState.clientNum )
{
trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
@@ -869,18 +1052,15 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_BUILD_REPAIR:
- DEBUGNAME( "EV_BUILD_REPAIR" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairSound );
break;
case EV_BUILD_REPAIRED:
- DEBUGNAME( "EV_BUILD_REPAIRED" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairedSound );
break;
case EV_OVERMIND_ATTACK:
- DEBUGNAME( "EV_OVERMIND_ATTACK" );
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
trap_S_StartLocalSound( cgs.media.alienOvermindAttack, CHAN_ANNOUNCER );
CG_CenterPrint( "The Overmind is under attack!", 200, GIANTCHAR_WIDTH * 4 );
@@ -888,8 +1068,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_OVERMIND_DYING:
- DEBUGNAME( "EV_OVERMIND_DYING" );
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
trap_S_StartLocalSound( cgs.media.alienOvermindDying, CHAN_ANNOUNCER );
CG_CenterPrint( "The Overmind is dying!", 200, GIANTCHAR_WIDTH * 4 );
@@ -897,17 +1076,19 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_DCC_ATTACK:
- DEBUGNAME( "EV_DCC_ATTACK" );
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
//trap_S_StartLocalSound( cgs.media.humanDCCAttack, CHAN_ANNOUNCER );
CG_CenterPrint( "Our base is under attack!", 200, GIANTCHAR_WIDTH * 4 );
}
break;
+ case EV_MGTURRET_SPINUP:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound );
+ break;
+
case EV_OVERMIND_SPAWNS:
- DEBUGNAME( "EV_OVERMIND_SPAWNS" );
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
trap_S_StartLocalSound( cgs.media.alienOvermindSpawns, CHAN_ANNOUNCER );
CG_CenterPrint( "The Overmind needs spawns!", 200, GIANTCHAR_WIDTH * 4 );
@@ -915,7 +1096,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_ALIEN_EVOLVE:
- DEBUGNAME( "EV_ALIEN_EVOLVE" );
trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );
{
particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS );
@@ -935,7 +1115,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_ALIEN_EVOLVE_FAILED:
- DEBUGNAME( "EV_ALIEN_EVOLVE_FAILED" );
if( clientNum == cg.predictedPlayerState.clientNum )
{
//FIXME: change to "negative" sound
@@ -945,7 +1124,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_ALIEN_ACIDTUBE:
- DEBUGNAME( "EV_ALIEN_ACIDTUBE" );
{
particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS );
@@ -960,18 +1138,19 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
break;
case EV_MEDKIT_USED:
- DEBUGNAME( "EV_MEDKIT_USED" );
trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.medkitUseSound );
break;
case EV_PLAYER_RESPAWN:
- DEBUGNAME( "EV_PLAYER_RESPAWN" );
if( es->number == cg.clientNum )
cg.spawnTime = cg.time;
break;
+ case EV_LEV2_ZAP:
+ CG_Level2Zap( es );
+ break;
+
default:
- DEBUGNAME( "UNKNOWN" );
CG_Error( "Unknown event: %i", event );
break;
}
@@ -1031,4 +1210,3 @@ void CG_CheckEvents( centity_t *cent )
if( oldEvent != EV_NONE )
cent->currentState.event = oldEvent;
}
-
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index ea1694b..ecdf02d 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,17 +17,18 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
-#include "../qcommon/q_shared.h"
-#include "../renderer/tr_types.h"
-#include "../game/bg_public.h"
+#include "qcommon/q_shared.h"
+#include "renderercommon/tr_types.h"
+#include "game/bg_public.h"
#include "cg_public.h"
-#include "../ui/ui_shared.h"
+#include "binaryshader.h"
+#include "ui/ui_shared.h"
// The entire cgame module is unloaded and reloaded on each level change,
// so there is NO persistant data between levels on the client side.
@@ -70,6 +72,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CHAR_HEIGHT 48
#define TEXT_ICON_SPACE 4
+#define TEAMCHAT_WIDTH 80
+#define TEAMCHAT_HEIGHT 8
+
// very large characters
#define GIANT_WIDTH 32
#define GIANT_HEIGHT 48
@@ -79,10 +84,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define TEAM_OVERLAY_MAXNAME_WIDTH 12
#define TEAM_OVERLAY_MAXLOCATION_WIDTH 16
-#define DEFAULT_MODEL "sarge"
-#define DEFAULT_TEAM_MODEL "sarge"
-#define DEFAULT_TEAM_HEAD "sarge"
-
typedef enum
{
FOOTSTEP_NORMAL,
@@ -207,7 +208,9 @@ typedef enum
PMT_STATIC_TRANSFORM,
PMT_TAG,
PMT_CENT_ANGLES,
- PMT_NORMAL
+ PMT_NORMAL,
+ PMT_LAST_NORMAL,
+ PMT_OPPORTUNISTIC_NORMAL
} pMoveType_t;
typedef enum
@@ -253,7 +256,7 @@ typedef struct pLerpValues_s
typedef struct baseParticle_s
{
vec3_t displacement;
- float randDisplacement;
+ vec3_t randDisplacement;
float normalDisplacement;
pMoveType_t velMoveType;
@@ -282,6 +285,7 @@ typedef struct baseParticle_s
float bounceSoundCountRandFrac;
pLerpValues_t radius;
+ int physicsRadius;
pLerpValues_t alpha;
pLerpValues_t rotation;
@@ -317,6 +321,8 @@ typedef struct baseParticle_s
qboolean overdrawProtection;
qboolean realLight;
qboolean cullOnStartSolid;
+
+ float scaleWithCharge;
} baseParticle_t;
@@ -358,6 +364,11 @@ typedef struct particleSystem_s
//for PMT_NORMAL
qboolean normalValid;
vec3_t normal;
+ //for PMT_LAST_NORMAL and PMT_OPPORTUNISTIC_NORMAL
+ qboolean lastNormalIsCurrent;
+ vec3_t lastNormal;
+
+ int charge;
} particleSystem_t;
@@ -383,6 +394,8 @@ typedef struct particle_s
baseParticle_t *class;
particleEjector_t *parent;
+ particleSystem_t *childParticleSystem;
+
int birthTime;
int lifeTime;
@@ -463,7 +476,7 @@ typedef struct baseTrailBeam_s
// the time it takes for a beam to fade out (double attached only)
int fadeOutTime;
-
+
char shaderName[ MAX_QPATH ];
qhandle_t shader;
@@ -492,6 +505,7 @@ typedef struct baseTrailSystem_s
baseTrailBeam_t *beams[ MAX_BEAMS_PER_SYSTEM ];
int numBeams;
+ int lifeTime;
qboolean thirdPersonOnly;
qboolean registered; //whether or not the assets for this trail have been loaded
} baseTrailSystem_t;
@@ -503,6 +517,7 @@ typedef struct trailSystem_s
attachment_t frontAttachment;
attachment_t backAttachment;
+ int birthTime;
int destroyTime;
qboolean valid;
} trailSystem_t;
@@ -551,7 +566,7 @@ typedef struct trailBeam_s
// because corpses after respawn are outside the normal
// client numbering range
-//TA: smoothing of view and model for WW transitions
+// smoothing of view and model for WW transitions
#define MAXSMOOTHS 32
typedef struct
@@ -566,7 +581,7 @@ typedef struct
typedef struct
{
- lerpFrame_t legs, torso, flag, nonseg;
+ lerpFrame_t legs, torso, nonseg, weapon;
int painTime;
int painDirection; // flip from 0 to 1
@@ -594,6 +609,16 @@ typedef struct buildableStatus_s
qboolean visible; // Status is visble?
} buildableStatus_t;
+typedef struct buildableCache_s
+{
+ vec3_t cachedOrigin; // If any of the values differ from their
+ vec3_t cachedAngles; // cached versions, then the cache is invalid
+ vec3_t cachedNormal;
+ buildable_t cachedType;
+ vec3_t axis[ 3 ];
+ vec3_t origin;
+} buildableCache_t;
+
//=================================================
// centity_t have a direct corespondence with gentity_t in the game, but
@@ -634,12 +659,13 @@ typedef struct centity_s
lerpFrame_t lerpFrame;
- //TA:
buildableAnimNumber_t buildableAnim; //persistant anim number
buildableAnimNumber_t oldBuildableAnim; //to detect when new anims are set
+ qboolean buildableIdleAnim; //to check if new idle anim
particleSystem_t *buildablePS;
buildableStatus_t buildableStatus;
- float lastBuildableHealthScale;
+ buildableCache_t buildableCache; // so we don't recalculate things
+ float lastBuildableHealth;
int lastBuildableDamageSoundTime;
lightFlareStatus_t lfs;
@@ -656,16 +682,20 @@ typedef struct centity_s
particleSystem_t *jetPackPS;
jetPackState_t jetPackState;
+ particleSystem_t *poisonCloudedPS;
+
particleSystem_t *entityPS;
qboolean entityPSMissing;
- trailSystem_t *level2ZapTS[ 3 ];
+ trailSystem_t *level2ZapTS[ LEVEL2_AREAZAP_MAX_TARGETS ];
+ int level2ZapTime;
trailSystem_t *muzzleTS; //used for the tesla and reactor
int muzzleTSDeathTime;
qboolean valid;
- qboolean oldValid;
+ qboolean oldValid;
+ struct centity_s *nextLocation;
} centity_t;
@@ -706,41 +736,20 @@ typedef struct
{
qboolean infoValid;
- char name[ MAX_QPATH ];
- pTeam_t team;
-
- int botSkill; // 0 = not bot, 1-5 = bot
-
- vec3_t color1;
- vec3_t color2;
+ char name[ MAX_NAME_LENGTH ];
+ team_t team;
int score; // updated by score servercmds
int location; // location index for team mode
int health; // you only get this info about your teammates
- int armor;
- int curWeapon;
-
- int handicap;
- int wins, losses; // in tourney mode
-
- int teamTask; // task in teamplay (offence/defence)
- qboolean teamLeader; // true when this is a team leader
-
- int powerups; // so can display quad/flag status
-
- int medkitUsageTime;
- int invulnerabilityStartTime;
- int invulnerabilityStopTime;
-
- int breathPuffTime;
+ int upgrade;
+ int curWeaponClass; // sends current weapon for H, current class for A
// when clientinfo is changed, the loading of models/skins/sounds
// can be deferred until you are dead, to prevent hitches in
// gameplay
char modelName[ MAX_QPATH ];
char skinName[ MAX_QPATH ];
- char headModelName[ MAX_QPATH ];
- char headSkinName[ MAX_QPATH ];
qboolean newAnims; // true if using the new mission pack animations
qboolean fixedlegs; // true if legs yaw is always the same as torso yaw
@@ -771,6 +780,9 @@ typedef struct
sfxHandle_t customFootsteps[ 4 ];
sfxHandle_t customMetalFootsteps[ 4 ];
+
+ char voice[ MAX_VOICE_NAME_LEN ];
+ int voiceTime;
} clientInfo_t;
@@ -789,6 +801,7 @@ typedef struct weaponInfoMode_s
qboolean usesSpriteMissle;
qhandle_t missileSprite;
int missileSpriteSize;
+ float missileSpriteCharge;
qhandle_t missileParticleSystem;
qhandle_t missileTrailSystem;
qboolean missileRotates;
@@ -799,7 +812,6 @@ typedef struct weaponInfoMode_s
int missileAnimLooping;
sfxHandle_t firingSound;
- qboolean loopFireSound;
qhandle_t muzzleParticleSystem;
@@ -824,6 +836,13 @@ typedef struct weaponInfo_s
qhandle_t barrelModel;
qhandle_t flashModel;
+ qhandle_t weaponModel3rdPerson;
+ qhandle_t barrelModel3rdPerson;
+ qhandle_t flashModel3rdPerson;
+
+ animation_t animations[ MAX_WEAPON_ANIMATIONS ];
+ qboolean noDrift;
+
vec3_t weaponMidpoint; // so it will rotate centered instead of by tag
qhandle_t weaponIcon;
@@ -869,7 +888,6 @@ typedef struct
//======================================================================
-//TA:
typedef struct
{
vec3_t alienBuildablePos[ MAX_GENTITIES ];
@@ -906,6 +924,9 @@ typedef struct
#define NUM_SAVED_STATES ( CMD_BACKUP + 2 )
+// After this many msec the crosshair name fades out completely
+#define CROSSHAIR_CLIENT_TIMEOUT 1000
+
typedef struct
{
int clientFrame; // incremented each frame
@@ -1000,19 +1021,14 @@ typedef struct
int scoreFadeTime;
char killerName[ MAX_NAME_LENGTH ];
char spectatorList[ MAX_STRING_CHARS ]; // list of names
- int spectatorLen; // length of list
- float spectatorWidth; // width in device units
int spectatorTime; // next time to offset
- int spectatorPaintX; // current paint x
- int spectatorPaintX2; // current paint x
- int spectatorOffset; // current offset from start
- int spectatorPaintLen; // current offset from start
+ float spectatorOffset; // current offset from start
// centerprinting
int centerPrintTime;
int centerPrintCharWidth;
int centerPrintY;
- char centerPrint[ 1024 ];
+ char centerPrint[ MAX_STRING_CHARS ];
int centerPrintLines;
// low ammo warning state
@@ -1022,6 +1038,7 @@ typedef struct
int lastKillTime;
// crosshair client ID
+ int crosshairBuildable;
int crosshairClientNum;
int crosshairClientTime;
@@ -1031,7 +1048,6 @@ typedef struct
// attacking player
int attackerTime;
- int voiceTime;
// reward medals
int rewardStack;
@@ -1052,8 +1068,7 @@ typedef struct
int voiceChatBufferOut;
// warmup countdown
- int warmup;
- int warmupCount;
+ int warmupTime;
//==========================
@@ -1083,8 +1098,7 @@ typedef struct
float v_dmg_pitch;
float v_dmg_roll;
- vec3_t kick_angles; // weapon kicks
- vec3_t kick_origin;
+ qboolean chaseFollow;
// temp working variables for player view
float bobfracsin;
@@ -1099,25 +1113,25 @@ typedef struct
char testModelBarrelName[MAX_QPATH];
qboolean testGun;
- int spawnTime; //TA: fovwarp
- int weapon1Time; //TA: time when BUTTON_ATTACK went t->f f->t
- int weapon2Time; //TA: time when BUTTON_ATTACK2 went t->f f->t
- int weapon3Time; //TA: time when BUTTON_USE_HOLDABLE went t->f f->t
+ int spawnTime; // fovwarp
+ int weapon1Time; // time when BUTTON_ATTACK went t->f f->t
+ int weapon2Time; // time when BUTTON_ATTACK2 went t->f f->t
+ int weapon3Time; // time when BUTTON_USE_HOLDABLE went t->f f->t
qboolean weapon1Firing;
qboolean weapon2Firing;
qboolean weapon3Firing;
int poisonedTime;
- vec3_t lastNormal; //TA: view smoothage
- vec3_t lastVangles; //TA: view smoothage
- smooth_t sList[ MAXSMOOTHS ]; //TA: WW smoothing
+ vec3_t lastNormal; // view smoothage
+ vec3_t lastVangles; // view smoothage
+ smooth_t sList[ MAXSMOOTHS ]; // WW smoothing
- int forwardMoveTime; //TA: for struggling
+ int forwardMoveTime; // for struggling
int rightMoveTime;
int upMoveTime;
- float charModelFraction; //TA: loading percentages
+ float charModelFraction; // loading percentages
float mediaFraction;
float buildablesFraction;
@@ -1129,23 +1143,32 @@ typedef struct
int numConsoleLines;
particleSystem_t *poisonCloudPS;
+ particleSystem_t *poisonCloudedPS;
float painBlendValue;
float painBlendTarget;
+ float healBlendValue;
int lastHealth;
+ qboolean wasDeadLastFrame;
int lastPredictedCommand;
int lastServerTime;
playerState_t savedPmoveStates[ NUM_SAVED_STATES ];
int stateHead, stateTail;
int ping;
+
+ float chargeMeterAlpha;
+ float chargeMeterValue;
+ qhandle_t lastHealthCross;
+ float healthCrossFade;
+ int nearUsableBuildable;
+
+ int nextWeaponClickTime;
+ // binary shaders - by /dev/humancontroller
+ int numBinaryShadersUsed;
+ cgBinaryShaderSetting_t binaryShaderSettings[ NUM_BINARY_SHADERS ];
} cg_t;
-
-// all of the model, shader, and sound references that are
-// loaded at gamestate time are stored in cgMedia_t
-// Other media that can be tied to clients, weapons, or items are
-// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t
typedef struct
{
qhandle_t charsetShader;
@@ -1168,6 +1191,7 @@ typedef struct
qhandle_t scannerBlipShader;
qhandle_t scannerLineShader;
+ qhandle_t teamOverlayShader;
qhandle_t numberShaders[ 11 ];
@@ -1179,12 +1203,22 @@ typedef struct
qhandle_t redBuildShader;
qhandle_t humanSpawningShader;
+ // binary shaders + range markers
+ qhandle_t sphereModel;
+ qhandle_t sphericalCone64Model;
+ qhandle_t sphericalCone240Model;
+
+ qhandle_t plainColorShader;
+ qhandle_t binaryAlpha1Shader;
+ cgMediaBinaryShader_t binaryShaders[ NUM_BINARY_SHADERS ];
+
// disconnect
qhandle_t disconnectPS;
qhandle_t disconnectSound;
// sounds
sfxHandle_t tracerSound;
+ sfxHandle_t weaponEmptyClick;
sfxHandle_t selectSound;
sfxHandle_t footsteps[ FOOTSTEP_TOTAL ][ 4 ];
sfxHandle_t talkSound;
@@ -1192,6 +1226,7 @@ typedef struct
sfxHandle_t humanTalkSound;
sfxHandle_t landSound;
sfxHandle_t fallSound;
+ sfxHandle_t turretSpinupSound;
sfxHandle_t hardBounceSound1;
sfxHandle_t hardBounceSound2;
@@ -1251,6 +1286,7 @@ typedef struct
sfxHandle_t buildableRepairedSound;
qhandle_t poisonCloudPS;
+ qhandle_t poisonCloudedPS;
qhandle_t alienEvolvePS;
qhandle_t alienAcidTubePS;
@@ -1263,13 +1299,22 @@ typedef struct
qhandle_t alienBleedPS;
qhandle_t humanBleedPS;
+ qhandle_t alienBuildableBleedPS;
+ qhandle_t humanBuildableBleedPS;
+
qhandle_t teslaZapTS;
sfxHandle_t lCannonWarningSound;
+ sfxHandle_t lCannonWarningSound2;
qhandle_t buildWeaponTimerPie[ 8 ];
qhandle_t upgradeClassIconShader;
+ qhandle_t healthCross;
+ qhandle_t healthCross2X;
+ qhandle_t healthCross3X;
+ qhandle_t healthCrossMedkit;
+ qhandle_t healthCrossPoisoned;
} cgMedia_t;
typedef struct
@@ -1319,17 +1364,12 @@ typedef struct
char mapname[ MAX_QPATH ];
qboolean markDeconstruct; // Whether or not buildables are marked
- int voteTime;
- int voteYes;
- int voteNo;
- qboolean voteModified; // beep whenever changed
- char voteString[ MAX_STRING_TOKENS ];
-
- int teamVoteTime[ 2 ];
- int teamVoteYes[ 2 ];
- int teamVoteNo[ 2 ];
- qboolean teamVoteModified[ 2 ]; // beep whenever changed
- char teamVoteString[ 2 ][ MAX_STRING_TOKENS ];
+ int voteTime[ NUM_TEAMS ];
+ int voteYes[ NUM_TEAMS ];
+ int voteNo[ NUM_TEAMS ];
+ char voteCaller[ NUM_TEAMS ][ MAX_NAME_LENGTH ];
+ qboolean voteModified[ NUM_TEAMS ];// beep whenever changed
+ char voteString[ NUM_TEAMS ][ MAX_STRING_TOKENS ];
int levelStartTime;
@@ -1337,22 +1377,13 @@ typedef struct
qboolean newHud;
- int alienBuildPoints;
- int alienBuildPointsTotal;
- int humanBuildPoints;
- int humanBuildPointsTotal;
- int humanBuildPointsPowered;
-
int alienStage;
int humanStage;
- int alienKills;
- int humanKills;
+ int alienCredits;
+ int humanCredits;
int alienNextStageThreshold;
int humanNextStageThreshold;
- int numAlienSpawns;
- int numHumanSpawns;
-
//
// locally derived information from gamestate
//
@@ -1367,7 +1398,9 @@ typedef struct
clientInfo_t clientinfo[ MAX_CLIENTS ];
- //TA: corpse info
+ int teaminfoReceievedTime;
+
+ // corpse info
clientInfo_t corpseinfo[ MAX_CLIENTS ];
int cursorX;
@@ -1383,57 +1416,69 @@ typedef struct
// media
cgMedia_t media;
+
+ voice_t *voices;
+ clientList_t ignoreList;
+
+ // Kill Message
+ char killMsgKillers[ TEAMCHAT_HEIGHT ][ 33*3+1 ];
+ char killMsgVictims[ TEAMCHAT_HEIGHT ][ 33*3+1 ];
+ int killMsgWeapons[ TEAMCHAT_HEIGHT ];
+ int killMsgMsgTimes[ TEAMCHAT_HEIGHT ];
+ int killMsgPos;
+ int killMsgLastPos;
} cgs_t;
+typedef struct
+{
+ char *cmd;
+ void ( *function )( void );
+} consoleCommand_t;
+
+typedef struct
+{
+ char *cmd;
+ void (*function)( int argNum );
+} consoleCommandCompletions_t;
+
//==============================================================================
extern cgs_t cgs;
extern cg_t cg;
extern centity_t cg_entities[ MAX_GENTITIES ];
+extern displayContextDef_t cgDC;
-//TA: weapon limit expanded:
-//extern weaponInfo_t cg_weapons[MAX_WEAPONS];
extern weaponInfo_t cg_weapons[ 32 ];
-//TA: upgrade infos:
extern upgradeInfo_t cg_upgrades[ 32 ];
-//TA: buildable infos:
extern buildableInfo_t cg_buildables[ BA_NUM_BUILDABLES ];
extern markPoly_t cg_markPolys[ MAX_MARK_POLYS ];
+extern vmCvar_t cg_teslaTrailTime;
extern vmCvar_t cg_centertime;
extern vmCvar_t cg_runpitch;
extern vmCvar_t cg_runroll;
-extern vmCvar_t cg_bobup;
-extern vmCvar_t cg_bobpitch;
-extern vmCvar_t cg_bobroll;
extern vmCvar_t cg_swingSpeed;
extern vmCvar_t cg_shadows;
-extern vmCvar_t cg_gibs;
extern vmCvar_t cg_drawTimer;
extern vmCvar_t cg_drawClock;
extern vmCvar_t cg_drawFPS;
extern vmCvar_t cg_drawDemoState;
extern vmCvar_t cg_drawSnapshot;
-extern vmCvar_t cg_draw3dIcons;
-extern vmCvar_t cg_drawIcons;
-extern vmCvar_t cg_drawAmmoWarning;
+extern vmCvar_t cg_drawChargeBar;
extern vmCvar_t cg_drawCrosshair;
extern vmCvar_t cg_drawCrosshairNames;
-extern vmCvar_t cg_drawRewards;
+extern vmCvar_t cg_crosshairSize;
extern vmCvar_t cg_drawTeamOverlay;
+extern vmCvar_t cg_teamOverlaySortMode;
+extern vmCvar_t cg_teamOverlayMaxPlayers;
extern vmCvar_t cg_teamOverlayUserinfo;
-extern vmCvar_t cg_crosshairX;
-extern vmCvar_t cg_crosshairY;
-extern vmCvar_t cg_drawStatus;
extern vmCvar_t cg_draw2D;
extern vmCvar_t cg_animSpeed;
extern vmCvar_t cg_debugAnim;
extern vmCvar_t cg_debugPosition;
extern vmCvar_t cg_debugEvents;
-extern vmCvar_t cg_teslaTrailTime;
-extern vmCvar_t cg_railTrailTime;
extern vmCvar_t cg_errorDecay;
extern vmCvar_t cg_nopredict;
extern vmCvar_t cg_debugMove;
@@ -1441,65 +1486,46 @@ extern vmCvar_t cg_noPlayerAnims;
extern vmCvar_t cg_showmiss;
extern vmCvar_t cg_footsteps;
extern vmCvar_t cg_addMarks;
-extern vmCvar_t cg_brassTime;
+extern vmCvar_t cg_viewsize;
+extern vmCvar_t cg_drawGun;
extern vmCvar_t cg_gun_frame;
extern vmCvar_t cg_gun_x;
extern vmCvar_t cg_gun_y;
extern vmCvar_t cg_gun_z;
-extern vmCvar_t cg_drawGun;
-extern vmCvar_t cg_viewsize;
extern vmCvar_t cg_tracerChance;
extern vmCvar_t cg_tracerWidth;
extern vmCvar_t cg_tracerLength;
-extern vmCvar_t cg_autoswitch;
-extern vmCvar_t cg_ignore;
-extern vmCvar_t cg_simpleItems;
-extern vmCvar_t cg_fov;
-extern vmCvar_t cg_zoomFov;
-extern vmCvar_t cg_thirdPersonRange;
-extern vmCvar_t cg_thirdPersonAngle;
extern vmCvar_t cg_thirdPerson;
+extern vmCvar_t cg_thirdPersonAngle;
+extern vmCvar_t cg_thirdPersonShoulderViewMode;
+extern vmCvar_t cg_staticDeathCam;
+extern vmCvar_t cg_thirdPersonPitchFollow;
+extern vmCvar_t cg_thirdPersonRange;
extern vmCvar_t cg_stereoSeparation;
extern vmCvar_t cg_lagometer;
-extern vmCvar_t cg_drawAttacker;
+extern vmCvar_t cg_drawSpeed;
+extern vmCvar_t cg_maxSpeedTimeWindow;
extern vmCvar_t cg_synchronousClients;
extern vmCvar_t cg_stats;
-extern vmCvar_t cg_forceModel;
-extern vmCvar_t cg_buildScript;
extern vmCvar_t cg_paused;
extern vmCvar_t cg_blood;
-extern vmCvar_t cg_predictItems;
-extern vmCvar_t cg_deferPlayers;
-extern vmCvar_t cg_drawFriend;
+extern vmCvar_t cg_teamOverlayUserinfo;
extern vmCvar_t cg_teamChatsOnly;
extern vmCvar_t cg_noVoiceChats;
extern vmCvar_t cg_noVoiceText;
-extern vmCvar_t cg_scorePlum;
+extern vmCvar_t cg_hudFiles;
extern vmCvar_t cg_smoothClients;
extern vmCvar_t pmove_fixed;
extern vmCvar_t pmove_msec;
-//extern vmCvar_t cg_pmove_fixed;
-extern vmCvar_t cg_cameraOrbit;
-extern vmCvar_t cg_cameraOrbitDelay;
+extern vmCvar_t cg_cameraMode;
extern vmCvar_t cg_timescaleFadeEnd;
extern vmCvar_t cg_timescaleFadeSpeed;
extern vmCvar_t cg_timescale;
-extern vmCvar_t cg_cameraMode;
-extern vmCvar_t cg_smallFont;
-extern vmCvar_t cg_bigFont;
extern vmCvar_t cg_noTaunt;
-extern vmCvar_t cg_noProjectileTrail;
-extern vmCvar_t cg_oldRail;
-extern vmCvar_t cg_oldRocket;
-extern vmCvar_t cg_oldPlasma;
-extern vmCvar_t cg_trueLightning;
-extern vmCvar_t cg_creepRes;
extern vmCvar_t cg_drawSurfNormal;
extern vmCvar_t cg_drawBBOX;
-extern vmCvar_t cg_debugAlloc;
extern vmCvar_t cg_wwSmoothTime;
-extern vmCvar_t cg_wwFollow;
-extern vmCvar_t cg_wwToggle;
+extern vmCvar_t cg_disableBlueprintErrors;
extern vmCvar_t cg_depthSortParticles;
extern vmCvar_t cg_bounceParticles;
extern vmCvar_t cg_consoleLatency;
@@ -1508,21 +1534,38 @@ extern vmCvar_t cg_debugParticles;
extern vmCvar_t cg_debugTrails;
extern vmCvar_t cg_debugPVS;
extern vmCvar_t cg_disableWarningDialogs;
+extern vmCvar_t cg_disableUpgradeDialogs;
+extern vmCvar_t cg_disableBuildDialogs;
+extern vmCvar_t cg_disableCommandDialogs;
extern vmCvar_t cg_disableScannerPlane;
extern vmCvar_t cg_tutorial;
+extern vmCvar_t cg_rangeMarkerDrawSurface;
+extern vmCvar_t cg_rangeMarkerDrawIntersection;
+extern vmCvar_t cg_rangeMarkerDrawFrontline;
+extern vmCvar_t cg_rangeMarkerSurfaceOpacity;
+extern vmCvar_t cg_rangeMarkerLineOpacity;
+extern vmCvar_t cg_rangeMarkerLineThickness;
+extern vmCvar_t cg_rangeMarkerForBlueprint;
+extern vmCvar_t cg_rangeMarkerBuildableTypes;
+extern vmCvar_t cg_binaryShaderScreenScale;
+
extern vmCvar_t cg_painBlendUpRate;
extern vmCvar_t cg_painBlendDownRate;
extern vmCvar_t cg_painBlendMax;
extern vmCvar_t cg_painBlendScale;
extern vmCvar_t cg_painBlendZoom;
-//TA: hack to get class an carriage through to UI module
+extern vmCvar_t cg_stickySpec;
+extern vmCvar_t cg_sprintToggle;
+extern vmCvar_t cg_unlagged;
+
+extern vmCvar_t cg_debugVoices;
+
extern vmCvar_t ui_currentClass;
extern vmCvar_t ui_carriage;
extern vmCvar_t ui_stages;
extern vmCvar_t ui_dialog;
-extern vmCvar_t ui_loading;
extern vmCvar_t ui_voteActive;
extern vmCvar_t ui_alienTeamVoteActive;
extern vmCvar_t ui_humanTeamVoteActive;
@@ -1531,7 +1574,22 @@ extern vmCvar_t cg_debugRandom;
extern vmCvar_t cg_optimizePrediction;
extern vmCvar_t cg_projectileNudge;
-extern vmCvar_t cg_unlagged;
+
+extern vmCvar_t cg_voice;
+
+extern vmCvar_t cg_emoticons;
+
+extern vmCvar_t cg_chatTeamPrefix;
+
+extern vmCvar_t cg_killMsg;
+extern vmCvar_t cg_killMsgTime;
+extern vmCvar_t cg_killMsgHeight;
+
+extern vmCvar_t cg_killMsgTime;
+extern vmCvar_t cg_killMsgHeight;
+
+extern vmCvar_t thz_radar;
+extern vmCvar_t thz_radarrange;
//
// cg_main.c
@@ -1539,8 +1597,8 @@ extern vmCvar_t cg_unlagged;
const char *CG_ConfigString( int index );
const char *CG_Argv( int arg );
-void QDECL CG_Printf( const char *msg, ... );
-void QDECL CG_Error( const char *msg, ... );
+void QDECL CG_Printf( const char *msg, ... ) __attribute__ ((format (printf, 1, 2)));
+void QDECL CG_Error( const char *msg, ... ) __attribute__ ((noreturn, format (printf, 1, 2)));
void CG_StartMusic( void );
int CG_PlayerCount( void );
@@ -1554,17 +1612,22 @@ void CG_KeyEvent( int key, qboolean down );
void CG_MouseEvent( int x, int y );
void CG_EventHandling( int type );
void CG_SetScoreSelection( void *menu );
+qboolean CG_ClientIsReady( int clientNum );
void CG_BuildSpectatorString( void );
-qboolean CG_FileExists( char *filename );
+qboolean CG_FileExists( const char *filename );
void CG_RemoveNotifyLine( void );
void CG_AddNotifyText( void );
+qboolean CG_GetRangeMarkerPreferences( qboolean *drawSurface, qboolean *drawIntersection,
+ qboolean *drawFrontline, float *surfaceOpacity,
+ float *lineOpacity, float *lineThickness );
+void CG_UpdateBuildableRangeMarkerMask( void );
//
// cg_view.c
//
-void CG_addSmoothOp( vec3_t rotAxis, float rotAngle, float timeMod ); //TA
+void CG_addSmoothOp( vec3_t rotAxis, float rotAngle, float timeMod );
void CG_TestModel_f( void );
void CG_TestGun_f( void );
void CG_TestModelNextFrame_f( void );
@@ -1573,6 +1636,9 @@ void CG_TestModelNextSkin_f( void );
void CG_TestModelPrevSkin_f( void );
void CG_AddBufferedSound( sfxHandle_t sfx );
void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback );
+void CG_OffsetFirstPersonView( void );
+void CG_OffsetThirdPersonView( void );
+void CG_OffsetShoulderView( void );
//
@@ -1584,37 +1650,41 @@ void CG_FillRect( float x, float y, float width, float height, const floa
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
void CG_DrawFadePic( float x, float y, float width, float height, vec4_t fcolor,
vec4_t tcolor, float amount, qhandle_t hShader );
+void CG_SetClipRegion( float x, float y, float w, float h );
+void CG_ClearClipRegion( void );
int CG_DrawStrlen( const char *str );
float *CG_FadeColor( int startMsec, int totalMsec );
void CG_TileClear( void );
void CG_ColorForHealth( vec4_t hcolor );
-void CG_GetColorForHealth( int health, int armor, vec4_t hcolor );
-
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color );
void CG_DrawSides(float x, float y, float w, float h, float size);
void CG_DrawTopBottom(float x, float y, float w, float h, float size);
qboolean CG_WorldToScreen( vec3_t point, float *x, float *y );
char *CG_KeyBinding( const char *bind );
-
+char CG_GetColorCharForHealth( int clientnum );
+void CG_DrawSphere( const vec3_t center, float radius, int customShader, const float *shaderRGBA );
+void CG_DrawSphericalCone( const vec3_t tip, const vec3_t rotation, float radius,
+ qboolean a240, int customShader, const float *shaderRGBA );
+void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range,
+ qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline,
+ const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness );
//
// cg_draw.c
//
-extern int sortedTeamPlayers[ TEAM_MAXOVERLAY ];
-extern int numSortedTeamPlayers;
void CG_AddLagometerFrameInfo( void );
void CG_AddLagometerSnapshotInfo( snapshot_t *snap );
+void CG_AddSpeed( void );
void CG_CenterPrint( const char *str, int y, int charWidth );
void CG_DrawActive( stereoFrame_t stereoView );
-void CG_OwnerDraw( float x, float y, float w, float h, float text_x, float text_y,
- int ownerDraw, int ownerDrawFlags, int align, float special,
- float scale, vec4_t color, qhandle_t shader, int textStyle);
-void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style );
-int CG_Text_Width( const char *text, float scale, int limit );
-int CG_Text_Height( const char *text, float scale, int limit );
+void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
+ float text_y, int ownerDraw, int ownerDrawFlags,
+ int align, int textalign, int textvalign,
+ float borderSize, float scale, vec4_t foreColor,
+ vec4_t backColor, qhandle_t shader, int textStyle );
float CG_GetValue(int ownerDraw);
void CG_RunMenuScript(char **args);
void CG_SetPrintString( int type, const char *p );
@@ -1633,13 +1703,12 @@ void CG_DrawField( float x, float y, int width, float cw, float ch, int v
void CG_Player( centity_t *cent );
void CG_Corpse( centity_t *cent );
void CG_ResetPlayerEntity( centity_t *cent );
-void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, int team );
void CG_NewClientInfo( int clientNum );
-void CG_PrecacheClientInfo( pClass_t class, char *model, char *skin );
+void CG_PrecacheClientInfo( class_t class, char *model, char *skin );
sfxHandle_t CG_CustomSound( int clientNum, const char *soundName );
void CG_PlayerDisconnect( vec3_t org );
void CG_Bleed( vec3_t origin, vec3_t normal, int entityNum );
-qboolean CG_AtHighestClass( void );
+centity_t *CG_GetPlayerLocation( void );
//
// cg_buildable.c
@@ -1651,11 +1720,12 @@ void CG_DrawBuildableStatus( void );
void CG_InitBuildables( void );
void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir );
void CG_AlienBuildableExplosion( vec3_t origin, vec3_t dir );
+qboolean CG_GetBuildableRangeMarkerProperties( buildable_t bType, rangeMarkerType_t *rmType, float *range, vec3_t rgb );
//
// cg_animation.c
//
-void CG_RunLerpFrame( lerpFrame_t *lf );
+void CG_RunLerpFrame( lerpFrame_t *lf, float scale );
//
// cg_animmapobj.c
@@ -1687,6 +1757,7 @@ void CG_CheckEvents( centity_t *cent );
void CG_EntityEvent( centity_t *cent, vec3_t position );
void CG_PainEvent( centity_t *cent, int health );
+void CG_AddToKillMsg( const char* killername, const char* victimname, int icon );
//
// cg_ents.c
@@ -1701,8 +1772,7 @@ void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *pare
qhandle_t parentModel, char *tagName );
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
qhandle_t parentModel, char *tagName );
-
-
+void CG_RangeMarker( centity_t *cent );
//
@@ -1719,8 +1789,9 @@ void CG_RegisterWeapon( int weaponNum );
void CG_FireWeapon( centity_t *cent, weaponMode_t weaponMode );
void CG_MissileHitWall( weapon_t weapon, weaponMode_t weaponMode, int clientNum,
- vec3_t origin, vec3_t dir, impactSound_t soundType );
-void CG_MissileHitPlayer( weapon_t weapon, weaponMode_t weaponMode, vec3_t origin, vec3_t dir, int entityNum );
+ vec3_t origin, vec3_t dir, impactSound_t soundType, int charge );
+void CG_MissileHitEntity( weapon_t weaponNum, weaponMode_t weaponMode,
+ vec3_t origin, vec3_t dir, int entityNum, int charge );
void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum );
void CG_ShotgunFire( entityState_t *es );
@@ -1736,6 +1807,7 @@ void CG_DrawItemSelectText( rectDef_t *rect, float scale, int textStyle )
void CG_UpdateEntityPositions( void );
void CG_Scanner( rectDef_t *rect, qhandle_t shader, vec4_t color );
void CG_AlienSense( rectDef_t *rect );
+void THZ_DrawScanner( rectDef_t *rect );
//
// cg_marks.c
@@ -1757,6 +1829,7 @@ void CG_ProcessSnapshots( void );
//
// cg_consolecmds.c
//
+qboolean CG_Console_CompleteArgument( int argNum );
qboolean CG_ConsoleCommand( void );
void CG_InitConsoleCommands( void );
qboolean CG_RequestScores( void );
@@ -1768,6 +1841,8 @@ void CG_ExecuteNewServerCommands( int latestSequence );
void CG_ParseServerinfo( void );
void CG_SetConfigValues( void );
void CG_ShaderStateChanged(void);
+void CG_UnregisterCommands( void );
+void CG_CenterPrint_f( void );
//
// cg_playerstate.c
@@ -1777,14 +1852,6 @@ void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops );
void CG_CheckChangedPredictableEvents( playerState_t *ps );
//
-// cg_mem.c
-//
-void CG_InitMemory( void );
-void *CG_Alloc( int size );
-void CG_Free( void *ptr );
-void CG_DefragmentMemory( void );
-
-//
// cg_attachment.c
//
qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v );
@@ -1820,6 +1887,7 @@ qboolean CG_IsParticleSystemInfinite( particleSystem_t *ps );
qboolean CG_IsParticleSystemValid( particleSystem_t **ps );
void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal );
+void CG_SetParticleSystemLastNormal( particleSystem_t *ps, const float *normal );
void CG_AddParticles( void );
@@ -1855,6 +1923,20 @@ void CG_WritePTRCode( int code );
//
const char *CG_TutorialText( void );
+// cg_main.c
+qboolean CG_GetRangeMarkerPreferences( qboolean *drawSurface, qboolean *drawIntersection,
+ qboolean *drawFrontline, float *surfaceOpacity,
+ float *lineOpacity, float *lineThickness );
+// cg_drawtools.c
+void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range,
+ qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline,
+ const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness );
+// cg_buildable.c
+qboolean CG_GetBuildableRangeMarkerProperties( buildable_t bType, rangeMarkerType_t *rmType, float *range, vec3_t rgb );
+void CG_GhostBuildableRangeMarker( buildable_t buildable, const vec3_t origin, const vec3_t normal );
+// cg_ents.c
+void CG_RangeMarker( centity_t *cent );
+
//
//===============================================
@@ -1868,7 +1950,7 @@ const char *CG_TutorialText( void );
void trap_Print( const char *fmt );
// abort the game
-void trap_Error( const char *fmt );
+void trap_Error( const char *fmt ) __attribute__((noreturn));
// milliseconds should only be used for performance tuning, never
// for anything game related. Get time from the CG_DrawActiveFrame parameter
@@ -1888,11 +1970,11 @@ void trap_LiteralArgs( char *buffer, int bufferLength );
// filesystem access
// returns length of file
-int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode );
+int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, enum FS_Mode mode );
void trap_FS_Read( void *buffer, int len, fileHandle_t f );
void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
void trap_FS_FCloseFile( fileHandle_t f );
-void trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ); // fsOrigin_t
+void trap_FS_Seek( fileHandle_t f, long offset, enum FS_Origin origin ); // fsOrigin_t
int trap_FS_GetFileList( const char *path, const char *extension,
char *listbuf, int bufsize );
@@ -1905,6 +1987,7 @@ void trap_SendConsoleCommand( const char *text );
// register a command name so the console can perform command completion.
// FIXME: replace this with a normal console command "defineCommand"?
void trap_AddCommand( const char *cmdName );
+void trap_RemoveCommand( const char *cmdName );
// send a string to the server over the network
void trap_SendClientCommand( const char *s );
@@ -1985,11 +2068,13 @@ void trap_R_AddRefEntityToScene( const refEntity_t *re );
// significant construction
void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts );
void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int numPolys );
+qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 );
void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b );
void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b );
int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir );
void trap_R_RenderScene( const refdef_t *fd );
void trap_R_SetColor( const float *rgba ); // NULL = 1,1,1,1
+void trap_R_SetClipRegion( const float *region );
void trap_R_DrawStretchPic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader );
void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs );
@@ -2180,6 +2265,8 @@ int trap_Key_GetKey( const char *binding );
void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen );
void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen );
void trap_Key_SetBinding( int keynum, const char *binding );
+void trap_Key_SetOverstrikeMode( qboolean state );
+qboolean trap_Key_GetOverstrikeMode( void );
int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits );
e_status trap_CIN_StopCinematic( int handle );
@@ -2199,9 +2286,19 @@ qboolean trap_GetEntityToken( char *buffer, int bufferSize );
int trap_GetDemoState( void );
int trap_GetDemoPos( void );
void trap_GetDemoName( char *buffer, int size );
+void trap_Field_CompleteList( char *listJson );
// cg_drawCrosshair settings
#define CROSSHAIR_ALWAYSOFF 0
#define CROSSHAIR_RANGEDONLY 1
#define CROSSHAIR_ALWAYSON 2
+// menu types for cg_disable*Dialogs
+typedef enum
+{
+ DT_INTERACTIVE, // team, class, armoury
+ DT_ARMOURYEVOLVE, // Insufficient funds et al
+ DT_BUILD, // build errors
+ DT_COMMAND, // You must be living/human/spec etc.
+
+} dialogType_t;
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index eb19dcb..7ec4ae9 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,25 +17,22 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_main.c -- initialization and primary entry point for cgame
-
#include "cg_local.h"
+#include "ui/ui_shared.h"
-#include "../ui/ui_shared.h"
// display context for new ui stuff
displayContextDef_t cgDC;
-int forceModelModificationCount = -1;
-
void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum );
void CG_Shutdown( void );
-char *CG_VoIPString( void );
+static char *CG_VoIPString( void );
/*
================
@@ -44,9 +42,7 @@ This is the only way control passes into the module.
This must be the very first function compiled into the .q3vm file
================
*/
-Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
- int arg4, int arg5, int arg6, int arg7,
- int arg8, int arg9, int arg10, int arg11 )
+Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2 )
{
switch( command )
{
@@ -80,9 +76,7 @@ Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
return 0;
case CG_MOUSE_EVENT:
- cgDC.cursorx = cgs.cursorX;
- cgDC.cursory = cgs.cursorY;
- CG_MouseEvent( arg0, arg1 );
+ // cgame doesn't care where the cursor is
return 0;
case CG_EVENT_HANDLING:
@@ -92,6 +86,9 @@ Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
#ifndef MODULE_INTERFACE_11
case CG_VOIP_STRING:
return (intptr_t)CG_VoIPString( );
+
+ case CG_CONSOLE_COMPLETARGUMENT:
+ return CG_Console_CompleteArgument( arg0 );
#endif
default:
@@ -107,39 +104,27 @@ cg_t cg;
cgs_t cgs;
centity_t cg_entities[ MAX_GENTITIES ];
-//TA: weapons limit expanded:
-//weaponInfo_t cg_weapons[MAX_WEAPONS];
weaponInfo_t cg_weapons[ 32 ];
upgradeInfo_t cg_upgrades[ 32 ];
buildableInfo_t cg_buildables[ BA_NUM_BUILDABLES ];
vmCvar_t cg_teslaTrailTime;
-vmCvar_t cg_railTrailTime;
vmCvar_t cg_centertime;
vmCvar_t cg_runpitch;
vmCvar_t cg_runroll;
-vmCvar_t cg_bobup;
-vmCvar_t cg_bobpitch;
-vmCvar_t cg_bobroll;
vmCvar_t cg_swingSpeed;
vmCvar_t cg_shadows;
-vmCvar_t cg_gibs;
vmCvar_t cg_drawTimer;
vmCvar_t cg_drawClock;
vmCvar_t cg_drawFPS;
vmCvar_t cg_drawDemoState;
vmCvar_t cg_drawSnapshot;
-vmCvar_t cg_draw3dIcons;
-vmCvar_t cg_drawIcons;
-vmCvar_t cg_drawAmmoWarning;
+vmCvar_t cg_drawChargeBar;
vmCvar_t cg_drawCrosshair;
vmCvar_t cg_drawCrosshairNames;
-vmCvar_t cg_drawRewards;
-vmCvar_t cg_crosshairX;
-vmCvar_t cg_crosshairY;
+vmCvar_t cg_crosshairSize;
vmCvar_t cg_draw2D;
-vmCvar_t cg_drawStatus;
vmCvar_t cg_animSpeed;
vmCvar_t cg_debugAnim;
vmCvar_t cg_debugPosition;
@@ -151,7 +136,6 @@ vmCvar_t cg_noPlayerAnims;
vmCvar_t cg_showmiss;
vmCvar_t cg_footsteps;
vmCvar_t cg_addMarks;
-vmCvar_t cg_brassTime;
vmCvar_t cg_viewsize;
vmCvar_t cg_drawGun;
vmCvar_t cg_gun_frame;
@@ -161,59 +145,41 @@ vmCvar_t cg_gun_z;
vmCvar_t cg_tracerChance;
vmCvar_t cg_tracerWidth;
vmCvar_t cg_tracerLength;
-vmCvar_t cg_autoswitch;
-vmCvar_t cg_ignore;
-vmCvar_t cg_simpleItems;
-vmCvar_t cg_fov;
-vmCvar_t cg_zoomFov;
vmCvar_t cg_thirdPerson;
-vmCvar_t cg_thirdPersonRange;
vmCvar_t cg_thirdPersonAngle;
+vmCvar_t cg_thirdPersonShoulderViewMode;
+vmCvar_t cg_staticDeathCam;
+vmCvar_t cg_thirdPersonPitchFollow;
+vmCvar_t cg_thirdPersonRange;
vmCvar_t cg_stereoSeparation;
vmCvar_t cg_lagometer;
-vmCvar_t cg_drawAttacker;
+vmCvar_t cg_drawSpeed;
+vmCvar_t cg_maxSpeedTimeWindow;
vmCvar_t cg_synchronousClients;
vmCvar_t cg_stats;
-vmCvar_t cg_buildScript;
-vmCvar_t cg_forceModel;
vmCvar_t cg_paused;
vmCvar_t cg_blood;
-vmCvar_t cg_predictItems;
-vmCvar_t cg_deferPlayers;
+vmCvar_t cg_teamChatsOnly;
vmCvar_t cg_drawTeamOverlay;
+vmCvar_t cg_teamOverlaySortMode;
+vmCvar_t cg_teamOverlayMaxPlayers;
vmCvar_t cg_teamOverlayUserinfo;
-vmCvar_t cg_drawFriend;
-vmCvar_t cg_teamChatsOnly;
+vmCvar_t cg_noPrintDuplicate;
vmCvar_t cg_noVoiceChats;
vmCvar_t cg_noVoiceText;
vmCvar_t cg_hudFiles;
-vmCvar_t cg_scorePlum;
vmCvar_t cg_smoothClients;
vmCvar_t pmove_fixed;
-//vmCvar_t cg_pmove_fixed;
vmCvar_t pmove_msec;
-vmCvar_t cg_pmove_msec;
vmCvar_t cg_cameraMode;
-vmCvar_t cg_cameraOrbit;
-vmCvar_t cg_cameraOrbitDelay;
vmCvar_t cg_timescaleFadeEnd;
vmCvar_t cg_timescaleFadeSpeed;
vmCvar_t cg_timescale;
-vmCvar_t cg_smallFont;
-vmCvar_t cg_bigFont;
vmCvar_t cg_noTaunt;
-vmCvar_t cg_noProjectileTrail;
-vmCvar_t cg_oldRail;
-vmCvar_t cg_oldRocket;
-vmCvar_t cg_oldPlasma;
-vmCvar_t cg_trueLightning;
-vmCvar_t cg_creepRes;
vmCvar_t cg_drawSurfNormal;
vmCvar_t cg_drawBBOX;
-vmCvar_t cg_debugAlloc;
vmCvar_t cg_wwSmoothTime;
-vmCvar_t cg_wwFollow;
-vmCvar_t cg_wwToggle;
+vmCvar_t cg_disableBlueprintErrors;
vmCvar_t cg_depthSortParticles;
vmCvar_t cg_bounceParticles;
vmCvar_t cg_consoleLatency;
@@ -222,21 +188,38 @@ vmCvar_t cg_debugParticles;
vmCvar_t cg_debugTrails;
vmCvar_t cg_debugPVS;
vmCvar_t cg_disableWarningDialogs;
+vmCvar_t cg_disableUpgradeDialogs;
+vmCvar_t cg_disableBuildDialogs;
+vmCvar_t cg_disableCommandDialogs;
vmCvar_t cg_disableScannerPlane;
vmCvar_t cg_tutorial;
+vmCvar_t cg_rangeMarkerDrawSurface;
+vmCvar_t cg_rangeMarkerDrawIntersection;
+vmCvar_t cg_rangeMarkerDrawFrontline;
+vmCvar_t cg_rangeMarkerSurfaceOpacity;
+vmCvar_t cg_rangeMarkerLineOpacity;
+vmCvar_t cg_rangeMarkerLineThickness;
+vmCvar_t cg_rangeMarkerForBlueprint;
+vmCvar_t cg_rangeMarkerBuildableTypes;
+vmCvar_t cg_binaryShaderScreenScale;
+
vmCvar_t cg_painBlendUpRate;
vmCvar_t cg_painBlendDownRate;
vmCvar_t cg_painBlendMax;
vmCvar_t cg_painBlendScale;
vmCvar_t cg_painBlendZoom;
-//TA: hack to get class and carriage through to UI module
+vmCvar_t cg_stickySpec;
+vmCvar_t cg_sprintToggle;
+vmCvar_t cg_unlagged;
+
+vmCvar_t cg_debugVoices;
+
vmCvar_t ui_currentClass;
vmCvar_t ui_carriage;
vmCvar_t ui_stages;
vmCvar_t ui_dialog;
-vmCvar_t ui_loading;
vmCvar_t ui_voteActive;
vmCvar_t ui_alienTeamVoteActive;
vmCvar_t ui_humanTeamVoteActive;
@@ -245,8 +228,19 @@ vmCvar_t cg_debugRandom;
vmCvar_t cg_optimizePrediction;
vmCvar_t cg_projectileNudge;
-vmCvar_t cg_unlagged;
+vmCvar_t cg_voice;
+
+vmCvar_t cg_emoticons;
+
+vmCvar_t cg_chatTeamPrefix;
+
+vmCvar_t cg_killMsg;
+vmCvar_t cg_killMsgTime;
+vmCvar_t cg_killMsgHeight;
+
+vmCvar_t thz_radar;
+vmCvar_t thz_radarrange;
typedef struct
{
@@ -258,46 +252,31 @@ typedef struct
static cvarTable_t cvarTable[ ] =
{
- { &cg_ignore, "cg_ignore", "0", 0 }, // used for debugging
- { &cg_autoswitch, "cg_autoswitch", "1", CVAR_ARCHIVE },
{ &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE },
- { &cg_zoomFov, "cg_zoomfov", "22.5", CVAR_ARCHIVE },
- { &cg_fov, "cg_fov", "90", CVAR_ARCHIVE },
{ &cg_viewsize, "cg_viewsize", "100", CVAR_ARCHIVE },
{ &cg_stereoSeparation, "cg_stereoSeparation", "0.4", CVAR_ARCHIVE },
{ &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE },
- { &cg_gibs, "cg_gibs", "1", CVAR_ARCHIVE },
{ &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE },
- { &cg_drawStatus, "cg_drawStatus", "1", CVAR_ARCHIVE },
{ &cg_drawTimer, "cg_drawTimer", "1", CVAR_ARCHIVE },
{ &cg_drawClock, "cg_drawClock", "0", CVAR_ARCHIVE },
{ &cg_drawFPS, "cg_drawFPS", "1", CVAR_ARCHIVE },
{ &cg_drawDemoState, "cg_drawDemoState", "1", CVAR_ARCHIVE },
{ &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE },
- { &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE },
- { &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE },
- { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE },
- { &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE },
- { &cg_drawCrosshair, "cg_drawCrosshair", "1", CVAR_ARCHIVE },
+ { &cg_drawChargeBar, "cg_drawChargeBar", "1", CVAR_ARCHIVE },
+ { &cg_drawCrosshair, "cg_drawCrosshair", "2", CVAR_ARCHIVE },
{ &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE },
- { &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE },
- { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE },
- { &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE },
- { &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE },
- { &cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE },
+ { &cg_crosshairSize, "cg_crosshairSize", "1", CVAR_ARCHIVE },
{ &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE },
{ &cg_lagometer, "cg_lagometer", "0", CVAR_ARCHIVE },
+ { &cg_drawSpeed, "cg_drawSpeed", "0", CVAR_ARCHIVE },
+ { &cg_maxSpeedTimeWindow, "cg_maxSpeedTimeWindow", "2000", CVAR_ARCHIVE },
{ &cg_teslaTrailTime, "cg_teslaTrailTime", "250", CVAR_ARCHIVE },
- { &cg_railTrailTime, "cg_railTrailTime", "400", CVAR_ARCHIVE },
{ &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT },
{ &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT },
{ &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT },
{ &cg_centertime, "cg_centertime", "3", CVAR_CHEAT },
{ &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE},
{ &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE },
- { &cg_bobup , "cg_bobup", "0.005", CVAR_CHEAT },
- { &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE },
- { &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE },
{ &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT },
{ &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT },
{ &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT },
@@ -312,27 +291,31 @@ static cvarTable_t cvarTable[ ] =
{ &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT },
{ &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT },
{ &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT },
- { &cg_thirdPersonRange, "cg_thirdPersonRange", "40", CVAR_CHEAT },
- { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT },
+ { &cg_thirdPersonRange, "cg_thirdPersonRange", "75", CVAR_ARCHIVE },
{ &cg_thirdPerson, "cg_thirdPerson", "0", CVAR_CHEAT },
- { &cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE },
- { &cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE },
- { &cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE },
- { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE },
- { &cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO },
+ { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT },
+ { &cg_thirdPersonPitchFollow, "cg_thirdPersonPitchFollow", "0", 0 },
+ { &cg_thirdPersonShoulderViewMode, "cg_thirdPersonShoulderViewMode", "1", CVAR_ARCHIVE },
+ { &cg_staticDeathCam, "cg_staticDeathCam", "0", CVAR_ARCHIVE },
{ &cg_stats, "cg_stats", "0", 0 },
- { &cg_drawFriend, "cg_drawFriend", "1", CVAR_ARCHIVE },
+ { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "1", CVAR_ARCHIVE },
+ { &cg_teamOverlaySortMode, "cg_teamOverlaySortMode", "1", CVAR_ARCHIVE },
+ { &cg_teamOverlayMaxPlayers, "cg_teamOverlayMaxPlayers", "8", CVAR_ARCHIVE },
+ { &cg_teamOverlayUserinfo, "teamoverlay", "1", CVAR_ARCHIVE|CVAR_USERINFO },
{ &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE },
+ { &cg_noPrintDuplicate, "cg_noPrintDuplicate", "0", CVAR_ARCHIVE },
{ &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE },
{ &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE },
- { &cg_creepRes, "cg_creepRes", "16", CVAR_ARCHIVE },
{ &cg_drawSurfNormal, "cg_drawSurfNormal", "0", CVAR_CHEAT },
{ &cg_drawBBOX, "cg_drawBBOX", "0", CVAR_CHEAT },
- { &cg_debugAlloc, "cg_debugAlloc", "0", 0 },
{ &cg_wwSmoothTime, "cg_wwSmoothTime", "300", CVAR_ARCHIVE },
- { &cg_wwFollow, "cg_wwFollow", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_wwToggle, "cg_wwToggle", "1", CVAR_ARCHIVE|CVAR_USERINFO },
+ { NULL, "cg_wwFollow", "1", CVAR_ARCHIVE|CVAR_USERINFO },
+ { NULL, "cg_wwToggle", "1", CVAR_ARCHIVE|CVAR_USERINFO },
+ { NULL, "cg_disableBlueprintErrors", "0", CVAR_ARCHIVE|CVAR_USERINFO },
+ { &cg_stickySpec, "cg_stickySpec", "1", CVAR_ARCHIVE|CVAR_USERINFO },
+ { &cg_sprintToggle, "cg_sprintToggle", "0", CVAR_ARCHIVE|CVAR_USERINFO },
{ &cg_unlagged, "cg_unlagged", "1", CVAR_ARCHIVE|CVAR_USERINFO },
+ { NULL, "cg_flySpeed", "600", CVAR_ARCHIVE|CVAR_USERINFO },
{ &cg_depthSortParticles, "cg_depthSortParticles", "1", CVAR_ARCHIVE },
{ &cg_bounceParticles, "cg_bounceParticles", "0", CVAR_ARCHIVE },
{ &cg_consoleLatency, "cg_consoleLatency", "3000", CVAR_ARCHIVE },
@@ -341,24 +324,44 @@ static cvarTable_t cvarTable[ ] =
{ &cg_debugTrails, "cg_debugTrails", "0", CVAR_CHEAT },
{ &cg_debugPVS, "cg_debugPVS", "0", CVAR_CHEAT },
{ &cg_disableWarningDialogs, "cg_disableWarningDialogs", "0", CVAR_ARCHIVE },
- { &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE },
+ { &cg_disableUpgradeDialogs, "cg_disableUpgradeDialogs", "0", CVAR_ARCHIVE },
+ { &cg_disableBuildDialogs, "cg_disableBuildDialogs", "0", CVAR_ARCHIVE },
+ { &cg_disableCommandDialogs, "cg_disableCommandDialogs", "0", CVAR_ARCHIVE },
+ { &cg_disableScannerPlane, "cg_disableScannerPlane", "1", CVAR_ARCHIVE },
{ &cg_tutorial, "cg_tutorial", "1", CVAR_ARCHIVE },
+
+ { &cg_rangeMarkerDrawSurface, "cg_rangeMarkerDrawSurface", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerDrawIntersection, "cg_rangeMarkerDrawIntersection", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerDrawFrontline, "cg_rangeMarkerDrawFrontline", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerSurfaceOpacity, "cg_rangeMarkerSurfaceOpacity", "0.08", CVAR_ARCHIVE },
+ { &cg_rangeMarkerLineOpacity, "cg_rangeMarkerLineOpacity", "0.4", CVAR_ARCHIVE },
+ { &cg_rangeMarkerLineThickness, "cg_rangeMarkerLineThickness", "4.0", CVAR_ARCHIVE },
+ { &cg_rangeMarkerForBlueprint, "cg_rangeMarkerForBlueprint", "1", CVAR_ARCHIVE },
+ { &cg_rangeMarkerBuildableTypes, "cg_rangeMarkerBuildableTypes", "support", CVAR_ARCHIVE },
+ { NULL, "cg_buildableRangeMarkerMask", "", CVAR_USERINFO },
+ { &cg_binaryShaderScreenScale, "cg_binaryShaderScreenScale", "1.0", CVAR_ARCHIVE },
+
{ &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
+ { NULL, "cg_alienConfig", "", CVAR_ARCHIVE },
+ { NULL, "cg_humanConfig", "", CVAR_ARCHIVE },
+ { NULL, "cg_spectatorConfig", "", CVAR_ARCHIVE },
{ &cg_painBlendUpRate, "cg_painBlendUpRate", "10.0", 0 },
{ &cg_painBlendDownRate, "cg_painBlendDownRate", "0.5", 0 },
{ &cg_painBlendMax, "cg_painBlendMax", "0.7", 0 },
{ &cg_painBlendScale, "cg_painBlendScale", "7.0", 0 },
{ &cg_painBlendZoom, "cg_painBlendZoom", "0.65", 0 },
+
+ { &cg_debugVoices, "cg_debugVoices", "0", 0 },
- { &ui_currentClass, "ui_currentClass", "0", 0 },
- { &ui_carriage, "ui_carriage", "", 0 },
- { &ui_stages, "ui_stages", "0 0", 0 },
- { &ui_dialog, "ui_dialog", "Text not set", 0 },
- { &ui_loading, "ui_loading", "0", 0 },
- { &ui_voteActive, "ui_voteActive", "0", 0 },
- { &ui_humanTeamVoteActive, "ui_humanTeamVoteActive", "0", 0 },
- { &ui_alienTeamVoteActive, "ui_alienTeamVoteActive", "0", 0 },
+ // communication cvars set by the cgame to be read by ui
+ { &ui_currentClass, "ui_currentClass", "0", CVAR_ROM },
+ { &ui_carriage, "ui_carriage", "", CVAR_ROM },
+ { &ui_stages, "ui_stages", "0 0", CVAR_ROM },
+ { &ui_dialog, "ui_dialog", "Text not set", CVAR_ROM },
+ { &ui_voteActive, "ui_voteActive", "0", CVAR_ROM },
+ { &ui_humanTeamVoteActive, "ui_humanTeamVoteActive", "0", CVAR_ROM },
+ { &ui_alienTeamVoteActive, "ui_alienTeamVoteActive", "0", CVAR_ROM },
{ &cg_debugRandom, "cg_debugRandom", "0", 0 },
@@ -368,33 +371,36 @@ static cvarTable_t cvarTable[ ] =
// the following variables are created in other parts of the system,
// but we also reference them here
- { &cg_buildScript, "com_buildScript", "0", 0 }, // force loading of all possible data amd error on failures
{ &cg_paused, "cl_paused", "0", CVAR_ROM },
- { &cg_blood, "com_blood", "1", CVAR_ARCHIVE },
+ { &cg_blood, "cg_blood", "1", CVAR_ARCHIVE },
{ &cg_synchronousClients, "g_synchronousClients", "0", 0 }, // communicated by systeminfo
- { &cg_cameraOrbit, "cg_cameraOrbit", "0", CVAR_CHEAT},
- { &cg_cameraOrbitDelay, "cg_cameraOrbitDelay", "50", CVAR_ARCHIVE},
- { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", 0},
- { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0},
+ { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", CVAR_CHEAT },
+ { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", CVAR_CHEAT },
{ &cg_timescale, "timescale", "1", 0},
- { &cg_scorePlum, "cg_scorePlums", "1", CVAR_USERINFO | CVAR_ARCHIVE},
{ &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE},
{ &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT},
{ &pmove_fixed, "pmove_fixed", "0", 0},
{ &pmove_msec, "pmove_msec", "8", 0},
{ &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE},
- { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE},
- { &cg_smallFont, "ui_smallFont", "0.2", CVAR_ARCHIVE},
- { &cg_bigFont, "ui_bigFont", "0.5", CVAR_ARCHIVE},
- { &cg_oldRail, "cg_oldRail", "1", CVAR_ARCHIVE},
- { &cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE},
- { &cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE},
- { &cg_trueLightning, "cg_trueLightning", "0.0", CVAR_ARCHIVE}
-// { &cg_pmove_fixed, "cg_pmove_fixed", "0", CVAR_USERINFO | CVAR_ARCHIVE }
+
+ { &cg_voice, "voice", "default", CVAR_USERINFO|CVAR_ARCHIVE},
+
+ { &cg_emoticons, "cg_emoticons", "1", CVAR_LATCH|CVAR_ARCHIVE},
+
+ { &cg_chatTeamPrefix, "cg_chatTeamPrefix", "1", CVAR_ARCHIVE},
+
+ { &cg_killMsg, "cg_killMsg", "1", CVAR_ARCHIVE },
+ { &cg_killMsgTime, "cg_killMsgTime", "4000", CVAR_ARCHIVE },
+ { &cg_killMsgHeight, "cg_killMsgHeight", "7", CVAR_ARCHIVE },
+
+ // Old school thz stuff
+ { &thz_radar, "thz_radar", "0", CVAR_CHEAT},
+ { &thz_radarrange, "thz_radarrange", "600", CVAR_ARCHIVE},
+
};
-static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] );
+static size_t cvarTableSize = ARRAY_LEN( cvarTable );
/*
=================
@@ -413,263 +419,98 @@ void CG_RegisterCvars( void )
cv->defaultString, cv->cvarFlags );
}
- //repress standard Q3 console
- trap_Cvar_Set( "con_notifytime", "-2" );
-
// see if we are also running the server on this machine
trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) );
cgs.localServer = atoi( var );
- forceModelModificationCount = cg_forceModel.modificationCount;
-
- trap_Cvar_Register( NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
- trap_Cvar_Register( NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
- trap_Cvar_Register( NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE );
- trap_Cvar_Register( NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE );
}
/*
-===================
-CG_ForceModelChange
-===================
-*/
-static void CG_ForceModelChange( void )
-{
- int i;
-
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- const char *clientInfo;
-
- clientInfo = CG_ConfigString( CS_PLAYERS + i );
-
- if( !clientInfo[ 0 ] )
- continue;
-
- CG_NewClientInfo( i );
- }
-}
-
-/*
===============
-CG_SetPVars
+CG_SetUIVars
-Set the p_* cvars
+Set some cvars used by the UI
===============
*/
-static void CG_SetPVars( void )
+static void CG_SetUIVars( void )
{
- playerState_t *ps;
+ int i;
+ char carriageCvar[ MAX_TOKEN_CHARS ];
if( !cg.snap )
return;
- ps = &cg.snap->ps;
-
- trap_Cvar_Set( "player_hp", va( "%d", ps->stats[ STAT_HEALTH ] ) );
- trap_Cvar_Set( "player_maxhp", va( "%d", ps->stats[ STAT_MAX_HEALTH ] ) );
- switch( ps->stats[ STAT_PTEAM ] )
- {
- case PTE_NONE:
- trap_Cvar_Set( "player_team", "spectator" );
- trap_Cvar_Set( "player_stage", "0" );
- trap_Cvar_Set( "player_spawns","0" );
- trap_Cvar_Set( "player_kns", "0" );
- trap_Cvar_Set( "player_bp", "0" );
- trap_Cvar_Set( "player_maxbp", "0" );
- break;
-
- case PTE_ALIENS:
- trap_Cvar_Set( "player_team", "alien" );
- trap_Cvar_Set( "player_stage", va( "%d", cgs.alienStage+1 ) );
- trap_Cvar_Set( "player_spawns",va( "%d", cgs.numAlienSpawns ));
- trap_Cvar_Set( "player_kns", va( "%d",((cgs.alienStage==2)?0:abs(cgs.alienNextStageThreshold-cgs.alienKills))));
- trap_Cvar_Set( "player_stage", va( "%d", cgs.alienStage+1 ) );
- trap_Cvar_Set( "player_bp", va( "%d", cgs.alienBuildPoints ));
- trap_Cvar_Set( "player_maxbp", va( "%d", cgs.alienBuildPointsTotal ));
- break;
-
- case PTE_HUMANS:
- trap_Cvar_Set( "player_team", "human" );
- trap_Cvar_Set( "player_stage", va( "%d", cgs.humanStage+1 ) );
- trap_Cvar_Set( "player_spawns",va( "%d", cgs.numHumanSpawns ));
- trap_Cvar_Set( "player_kns", va( "%d",((cgs.humanStage==2)?0:abs(cgs.humanNextStageThreshold-cgs.humanKills))));
- trap_Cvar_Set( "player_bp", va( "%d", cgs.humanBuildPoints ));
- trap_Cvar_Set( "player_maxbp", va( "%d", cgs.humanBuildPointsTotal ));
- break;
- }
+ *carriageCvar = 0;
- trap_Cvar_Set( "player_credits", va( "%d", ps->persistant[ PERS_CREDIT ] ) );
- trap_Cvar_Set( "player_score", va( "%d", ps->persistant[ PERS_SCORE ] ) );
- trap_Cvar_Set( "player_deaths", va( "%d", ps->persistant[ PERS_KILLED ] ) );
+ //determine what the player is carrying
+ if( BG_Weapon( cg.snap->ps.stats[ STAT_WEAPON ] )->purchasable )
+ strcat( carriageCvar, va( "W%d ", cg.snap->ps.stats[ STAT_WEAPON ] ) );
- if ( CG_LastAttacker( ) != -1 )
+ for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
{
- trap_Cvar_Set( "player_attacker", cgs.clientinfo[ CG_LastAttacker( ) ].name );
- trap_Cvar_Set( "player_attacker_hp", va( "%d", cgs.clientinfo[ CG_LastAttacker( ) ].health));
+ if( BG_InventoryContainsUpgrade( i, cg.snap->ps.stats ) &&
+ BG_Upgrade( i )->purchasable )
+ strcat( carriageCvar, va( "U%d ", i ) );
}
- else
- {
- trap_Cvar_Set( "player_attacker", "" );
- trap_Cvar_Set( "player_attacker_hp", "" );
- }
-
+ strcat( carriageCvar, "$" );
- if ( CG_CrosshairPlayer( ) != -1 )
- {
- trap_Cvar_Set( "player_crosshair", cgs.clientinfo[ CG_CrosshairPlayer( ) ].name );
- //XXX hax required
- //trap_Cvar_Set( "player_crosshair_credits", va("%d",cgs.clientinfo[CG_CrosshairPlayer( )].credits));
- }
- else
- {
- trap_Cvar_Set( "player_crosshair", "" );
- //trap_Cvar_Set( "player_crosshair_credits", "" );
- }
-
- // stages
- trap_Cvar_Set( "alien_stage", va( "%d", cgs.alienStage+1 ) );
- trap_Cvar_Set( "human_stage", va( "%d", cgs.humanStage+1 ) );
-
- // alien kills to next stage
- if( cgs.alienStage == 2 )
- trap_Cvar_Set( "alien_kns", va( "%d", 0 ) );
- else
- trap_Cvar_Set( "alien_kns", va( "%d", abs(cgs.alienNextStageThreshold - cgs.alienKills)) );
-
- // human kills to next stage
- if( cgs.humanStage == 2 )
- trap_Cvar_Set( "human_kns", va( "%d", 0 ) );
- else
- trap_Cvar_Set( "human_kns", va( "%d", abs(cgs.humanNextStageThreshold - cgs.humanKills)) );
-
- // General score information
- trap_Cvar_Set( "alien_score", va( "%d", cgs.alienKills ) );
- trap_Cvar_Set( "human_score", va( "%d", cgs.humanKills ) );
-
- // class type
- switch ( ps->stats[ STAT_PCLASS ] )
- {
- case PCL_ALIEN_BUILDER0:
- trap_Cvar_Set( "player_class", "Granger" );
- trap_Cvar_Set( "player_weapon", "Granger" );
- break;
-
- case PCL_ALIEN_BUILDER0_UPG:
- trap_Cvar_Set( "player_class", "Advanced Granger" );
- trap_Cvar_Set( "player_weapon", "Advanced Granger" );
- break;
-
- case PCL_ALIEN_LEVEL0:
- trap_Cvar_Set( "player_class", "Dretch" );
- trap_Cvar_Set( "player_weapon", "Dretch" );
- break;
-
- case PCL_ALIEN_LEVEL1:
- trap_Cvar_Set( "player_class", "Basilisk" );
- trap_Cvar_Set( "player_weapon", "Basilisk" );
- break;
-
- case PCL_ALIEN_LEVEL1_UPG:
- trap_Cvar_Set( "player_class", "Advanced Basilisk" );
- trap_Cvar_Set( "player_weapon", "Advanced Basilisk" );
- break;
-
- case PCL_ALIEN_LEVEL2:
- trap_Cvar_Set( "player_class", "Marauder" );
- trap_Cvar_Set( "player_weapon", "Marauder" );
- break;
-
- case PCL_ALIEN_LEVEL2_UPG:
- trap_Cvar_Set( "player_class", "Advanced Marauder" );
- trap_Cvar_Set( "player_weapon", "Advanced Maruder" );
- break;
-
- case PCL_ALIEN_LEVEL3:
- trap_Cvar_Set( "player_class", "Dragoon" );
- trap_Cvar_Set( "player_weapon", "Dragoon" );
- break;
-
- case PCL_ALIEN_LEVEL3_UPG:
- trap_Cvar_Set( "player_class", "Advanced Dragoon" );
- trap_Cvar_Set( "player_weapon", "Advanced Dragoon" );
- break;
-
- case PCL_ALIEN_LEVEL4:
- trap_Cvar_Set( "player_class", "Tyrant" );
- trap_Cvar_Set( "player_weapon", "Tyrant" );
- break;
-
- case PCL_HUMAN:
- trap_Cvar_Set( "player_class", "Human" );
- break;
-
- case PCL_HUMAN_BSUIT:
- trap_Cvar_Set( "player_class", "Battlesuit" );
- break;
-
- default:
- trap_Cvar_Set( "player_class", "Unknown" );
- }
-
- // weapons
- switch ( ps->weapon )
- {
- case WP_HBUILD:
- trap_Cvar_Set( "player_weapon", "Construction Kit" );
- break;
+ trap_Cvar_Set( "ui_carriage", carriageCvar );
- case WP_HBUILD2:
- trap_Cvar_Set( "player_weapon", "Advanced Construction Kit" );
- break;
-
- case WP_BLASTER:
- trap_Cvar_Set( "player_weapon", "Blaster" );
- break;
-
- case WP_MACHINEGUN:
- trap_Cvar_Set( "player_weapon", "Machine Gun" );
- break;
-
- case WP_PAIN_SAW:
- trap_Cvar_Set( "player_weapon", "Painsaw" );
- break;
+ trap_Cvar_Set( "ui_stages", va( "%d %d", cgs.alienStage, cgs.humanStage ) );
+}
- case WP_SHOTGUN:
- trap_Cvar_Set( "player_weapon", "Shotgun" );
- break;
+/*
+=================
+CG_SetPVars
+=================
+*/
+static void CG_SetPVars( void )
+{
+ playerState_t *ps;
- case WP_LAS_GUN:
- trap_Cvar_Set( "player_weapon", "Laser Gun" );
- break;
+ if( !cg.snap ) return;
+ ps = &cg.snap->ps;
- case WP_MASS_DRIVER:
- trap_Cvar_Set( "player_weapon", "Mass Driver" );
- break;
+ trap_Cvar_Set( "player_hp", va( "%d", ps->stats[ STAT_HEALTH ] ));
+ trap_Cvar_Set( "player_maxhp",va( "%d", ps->stats[ STAT_MAX_HEALTH ] ));
- case WP_CHAINGUN:
- trap_Cvar_Set( "player_weapon", "Chain Gun" );
+ switch( ps->stats[ STAT_TEAM ] )
+ {
+ case TEAM_NONE:
+ trap_Cvar_Set( "team_bp", "0" );
+ trap_Cvar_Set( "team_kns", "0" );
+ trap_Cvar_Set( "team_teamname", "spectator" );
+ trap_Cvar_Set( "team_stage", "0" );
break;
- case WP_PULSE_RIFLE:
- trap_Cvar_Set( "player_weapon", "Pulse Rifle" );
+ case TEAM_ALIENS:
+ //trap_Cvar_Set( "team_bp", va( "%d", cgs.alienBuildPoints ));
+ trap_Cvar_Set( "team_kns", va("%d", cgs.alienNextStageThreshold) );
+ trap_Cvar_Set( "team_teamname", "aliens" );
+ trap_Cvar_Set( "team_stage", va( "%d", cgs.alienStage+1 ) );
break;
- case WP_FLAMER:
- trap_Cvar_Set( "player_weapon", "Flame Thrower" );
+ case TEAM_HUMANS:
+ //trap_Cvar_Set( "team_bp", va("%d",cgs.humanBuildPoints) );
+ trap_Cvar_Set( "team_kns", va("%d",cgs.humanNextStageThreshold) );
+ trap_Cvar_Set( "team_teamname", "humans" );
+ trap_Cvar_Set( "team_stage", va( "%d", cgs.humanStage+1 ) );
break;
+ }
+
+ trap_Cvar_Set( "player_credits", va( "%d", cg.snap->ps.persistant[ PERS_CREDIT ] ) );
+ trap_Cvar_Set( "player_score", va( "%d", cg.snap->ps.persistant[ PERS_SCORE ] ) );
- case WP_LUCIFER_CANNON:
- trap_Cvar_Set( "player_weapon", "Lucifier cannon" );
- break;
+ if ( CG_LastAttacker( ) != -1 )
+ trap_Cvar_Set( "player_attackername", cgs.clientinfo[ CG_LastAttacker( ) ].name );
+ else
+ trap_Cvar_Set( "player_attackername", "" );
- case WP_GRENADE:
- trap_Cvar_Set( "player_weapon", "Grenade" );
- break;
+ if ( CG_CrosshairPlayer( ) != -1 )
+ trap_Cvar_Set( "player_crosshairname", cgs.clientinfo[ CG_CrosshairPlayer( ) ].name );
+ else
+ trap_Cvar_Set( "player_crosshairname", "" );
- default:
- trap_Cvar_Set( "player_weapon", "Unknown" );
- }
}
/*
@@ -682,19 +523,16 @@ void CG_UpdateCvars( void )
int i;
cvarTable_t *cv;
- CG_SetPVars();
+ CG_SetPVars( );
for( i = 0, cv = cvarTable; i < cvarTableSize; i++, cv++ )
- trap_Cvar_Update( cv->vmCvar );
+ if( cv->vmCvar )
+ trap_Cvar_Update( cv->vmCvar );
// check for modications here
- // if force model changed
- if( forceModelModificationCount != cg_forceModel.modificationCount )
- {
- forceModelModificationCount = cg_forceModel.modificationCount;
- CG_ForceModelChange( );
- }
+ CG_SetUIVars( );
+ CG_UpdateBuildableRangeMarkerMask();
}
@@ -715,6 +553,7 @@ int CG_LastAttacker( void )
return cg.snap->ps.persistant[ PERS_ATTACKER ];
}
+
/*
=================
CG_RemoveNotifyLine
@@ -735,10 +574,9 @@ void CG_RemoveNotifyLine( void )
cg.consoleText[ i ] = cg.consoleText[ i + offset ];
//pop up the first consoleLine
+ cg.numConsoleLines--;
for( i = 0; i < cg.numConsoleLines; i++ )
cg.consoleLines[ i ] = cg.consoleLines[ i + 1 ];
-
- cg.numConsoleLines--;
}
/*
@@ -749,6 +587,7 @@ CG_AddNotifyText
void CG_AddNotifyText( void )
{
char buffer[ BIG_INFO_STRING ];
+ int bufferLen, textLen;
trap_LiteralArgs( buffer, BIG_INFO_STRING );
@@ -759,12 +598,24 @@ void CG_AddNotifyText( void )
return;
}
+ bufferLen = strlen( buffer );
+ textLen = strlen( cg.consoleText );
+
+ // Ignore console messages that were just printed
+ if( cg_noPrintDuplicate.integer && textLen >= bufferLen &&
+ !strcmp( cg.consoleText + textLen - bufferLen, buffer ) )
+ return;
+
if( cg.numConsoleLines == MAX_CONSOLE_LINES )
+ {
CG_RemoveNotifyLine( );
+ textLen = strlen( cg.consoleText );
+ }
- Q_strcat( cg.consoleText, MAX_CONSOLE_TEXT, buffer );
+ Q_strncpyz( cg.consoleText + textLen, buffer, MAX_CONSOLE_TEXT - textLen );
cg.consoleLines[ cg.numConsoleLines ].time = cg.time;
- cg.consoleLines[ cg.numConsoleLines ].length = strlen( buffer );
+ cg.consoleLines[ cg.numConsoleLines ].length =
+ MIN( bufferLen, MAX_CONSOLE_TEXT - textLen - 1 );
cg.numConsoleLines++;
}
@@ -774,7 +625,7 @@ void QDECL CG_Printf( const char *msg, ... )
char text[ 1024 ];
va_start( argptr, msg );
- vsprintf( text, msg, argptr );
+ Q_vsnprintf( text, sizeof( text ), msg, argptr );
va_end( argptr );
trap_Print( text );
@@ -786,7 +637,7 @@ void QDECL CG_Error( const char *msg, ... )
char text[ 1024 ];
va_start( argptr, msg );
- vsprintf( text, msg, argptr );
+ Q_vsnprintf( text, sizeof( text ), msg, argptr );
va_end( argptr );
trap_Error( text );
@@ -798,7 +649,7 @@ void QDECL Com_Error( int level, const char *error, ... )
char text[1024];
va_start( argptr, error );
- vsprintf( text, error, argptr );
+ Q_vsnprintf( text, sizeof( text ), error, argptr );
va_end( argptr );
CG_Error( "%s", text );
@@ -808,9 +659,9 @@ void QDECL Com_Printf( const char *msg, ... ) {
va_list argptr;
char text[1024];
- va_start (argptr, msg);
- vsprintf (text, msg, argptr);
- va_end (argptr);
+ va_start( argptr, msg );
+ Q_vsnprintf( text, sizeof( text ), msg, argptr );
+ va_end( argptr );
CG_Printf ("%s", text);
}
@@ -841,19 +692,9 @@ CG_FileExists
Test if a specific file exists or not
=================
*/
-qboolean CG_FileExists( char *filename )
+qboolean CG_FileExists( const char *filename )
{
- fileHandle_t f;
-
- if( trap_FS_FOpenFile( filename, &f, FS_READ ) > 0 )
- {
- //file exists so close it
- trap_FS_FCloseFile( f );
-
- return qtrue;
- }
- else
- return qfalse;
+ return trap_FS_FOpenFile( filename, NULL, FS_READ );
}
/*
@@ -882,6 +723,8 @@ static void CG_RegisterSounds( void )
cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/tracer.wav", qfalse );
cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse );
+ cgs.media.turretSpinupSound = trap_S_RegisterSound( "sound/buildables/mgturret/spinup.wav", qfalse );
+ cgs.media.weaponEmptyClick = trap_S_RegisterSound( "sound/weapons/click.wav", qfalse );
cgs.media.talkSound = trap_S_RegisterSound( "sound/misc/talk.wav", qfalse );
cgs.media.alienTalkSound = trap_S_RegisterSound( "sound/misc/alien_talk.wav", qfalse );
@@ -950,6 +793,7 @@ static void CG_RegisterSounds( void )
cgs.media.buildableRepairedSound = trap_S_RegisterSound( "sound/buildables/human/repaired.wav", qfalse );
cgs.media.lCannonWarningSound = trap_S_RegisterSound( "models/weapons/lcannon/warning.wav", qfalse );
+ cgs.media.lCannonWarningSound2 = trap_S_RegisterSound( "models/weapons/lcannon/warning2.wav", qfalse );
}
@@ -1011,12 +855,14 @@ static void CG_RegisterGraphics( void )
cgs.media.scannerBlipShader = trap_R_RegisterShader( "gfx/2d/blip" );
cgs.media.scannerLineShader = trap_R_RegisterShader( "gfx/2d/stalk" );
+ cgs.media.teamOverlayShader = trap_R_RegisterShader( "gfx/2d/teamoverlay" );
+
cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" );
cgs.media.backTileShader = trap_R_RegisterShader( "console" );
- //TA: building shaders
+ // building shaders
cgs.media.greenBuildShader = trap_R_RegisterShader("gfx/misc/greenbuild" );
cgs.media.redBuildShader = trap_R_RegisterShader("gfx/misc/redbuild" );
cgs.media.humanSpawningShader = trap_R_RegisterShader("models/buildables/telenode/rep_cyl" );
@@ -1024,8 +870,15 @@ static void CG_RegisterGraphics( void )
for( i = 0; i < 8; i++ )
cgs.media.buildWeaponTimerPie[ i ] = trap_R_RegisterShader( buildWeaponTimerPieShaders[ i ] );
+ // player health cross shaders
+ cgs.media.healthCross = trap_R_RegisterShader( "ui/assets/neutral/cross.tga" );
+ cgs.media.healthCross2X = trap_R_RegisterShader( "ui/assets/neutral/cross2.tga" );
+ cgs.media.healthCross3X = trap_R_RegisterShader( "ui/assets/neutral/cross3.tga" );
+ cgs.media.healthCrossMedkit = trap_R_RegisterShader( "ui/assets/neutral/cross_medkit.tga" );
+ cgs.media.healthCrossPoisoned = trap_R_RegisterShader( "ui/assets/neutral/cross_poison.tga" );
+
cgs.media.upgradeClassIconShader = trap_R_RegisterShader( "icons/icona_upgrade.tga" );
-
+
cgs.media.balloonShader = trap_R_RegisterShader( "gfx/sprites/chatballoon" );
cgs.media.disconnectPS = CG_RegisterParticleSystem( "disconnectPS" );
@@ -1039,6 +892,7 @@ static void CG_RegisterGraphics( void )
cgs.media.wakeMarkShader = trap_R_RegisterShader( "gfx/marks/wake" );
cgs.media.poisonCloudPS = CG_RegisterParticleSystem( "firstPersonPoisonCloudPS" );
+ cgs.media.poisonCloudedPS = CG_RegisterParticleSystem( "poisonCloudedPS" );
cgs.media.alienEvolvePS = CG_RegisterParticleSystem( "alienEvolvePS" );
cgs.media.alienAcidTubePS = CG_RegisterParticleSystem( "alienAcidTubePS" );
@@ -1051,9 +905,29 @@ static void CG_RegisterGraphics( void )
cgs.media.humanBuildableDestroyedPS = CG_RegisterParticleSystem( "humanBuildableDestroyedPS" );
cgs.media.alienBuildableDestroyedPS = CG_RegisterParticleSystem( "alienBuildableDestroyedPS" );
+ cgs.media.humanBuildableBleedPS = CG_RegisterParticleSystem( "humanBuildableBleedPS");
+ cgs.media.alienBuildableBleedPS = CG_RegisterParticleSystem( "alienBuildableBleedPS" );
+
cgs.media.alienBleedPS = CG_RegisterParticleSystem( "alienBleedPS" );
cgs.media.humanBleedPS = CG_RegisterParticleSystem( "humanBleedPS" );
+ cgs.media.sphereModel = trap_R_RegisterModel( "models/generic/sphere" );
+ cgs.media.sphericalCone64Model = trap_R_RegisterModel( "models/generic/sphericalCone64" );
+ cgs.media.sphericalCone240Model = trap_R_RegisterModel( "models/generic/sphericalCone240" );
+
+ cgs.media.plainColorShader = trap_R_RegisterShader( "gfx/plainColor" );
+ cgs.media.binaryAlpha1Shader = trap_R_RegisterShader( "gfx/binary/alpha1" );
+
+ for( i = 0; i < NUM_BINARY_SHADERS; ++i )
+ {
+ cgs.media.binaryShaders[ i ].f1 = trap_R_RegisterShader( va( "gfx/binary/%03i_F1", i ) );
+ cgs.media.binaryShaders[ i ].f2 = trap_R_RegisterShader( va( "gfx/binary/%03i_F2", i ) );
+ cgs.media.binaryShaders[ i ].f3 = trap_R_RegisterShader( va( "gfx/binary/%03i_F3", i ) );
+ cgs.media.binaryShaders[ i ].b1 = trap_R_RegisterShader( va( "gfx/binary/%03i_B1", i ) );
+ cgs.media.binaryShaders[ i ].b2 = trap_R_RegisterShader( va( "gfx/binary/%03i_B2", i ) );
+ cgs.media.binaryShaders[ i ].b3 = trap_R_RegisterShader( va( "gfx/binary/%03i_B3", i ) );
+ }
+
CG_BuildableStatusParse( "ui/assets/human/buildstat.cfg", &cgs.humanBuildStat );
CG_BuildableStatusParse( "ui/assets/alien/buildstat.cfg", &cgs.alienBuildStat );
@@ -1134,16 +1008,11 @@ void CG_BuildSpectatorString( void )
for( i = 0; i < MAX_CLIENTS; i++ )
{
- if( cgs.clientinfo[ i ].infoValid && cgs.clientinfo[ i ].team == PTE_NONE )
- Q_strcat( cg.spectatorList, sizeof( cg.spectatorList ), va( "%s " S_COLOR_WHITE, cgs.clientinfo[ i ].name ) );
- }
-
- i = strlen( cg.spectatorList );
-
- if( i != cg.spectatorLen )
- {
- cg.spectatorLen = i;
- cg.spectatorWidth = -1;
+ if( cgs.clientinfo[ i ].infoValid && cgs.clientinfo[ i ].team == TEAM_NONE )
+ {
+ Q_strcat( cg.spectatorList, sizeof( cg.spectatorList ),
+ va( S_COLOR_WHITE "%s ", cgs.clientinfo[ i ].name ) );
+ }
}
}
@@ -1164,8 +1033,8 @@ static void CG_RegisterClients( void )
//precache all the models/sounds/etc
for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
{
- CG_PrecacheClientInfo( i, BG_FindModelNameForClass( i ),
- BG_FindSkinNameForClass( i ) );
+ CG_PrecacheClientInfo( i, BG_ClassConfig( i )->modelName,
+ BG_ClassConfig( i )->skinName );
cg.charModelFraction = (float)i / (float)PCL_NUM_CLASSES;
trap_UpdateScreen( );
@@ -1246,8 +1115,8 @@ int CG_PlayerCount( void )
for( i = 0; i < cg.numScores; i++ )
{
- if( cg.scores[ i ].team == PTE_ALIENS ||
- cg.scores[ i ].team == PTE_HUMANS )
+ if( cg.scores[ i ].team == TEAM_ALIENS ||
+ cg.scores[ i ].team == TEAM_HUMANS )
count++;
}
@@ -1452,7 +1321,7 @@ qboolean CG_Asset_Parse( int handle )
}
}
- return qfalse; // bk001204 - why not?
+ return qfalse;
}
void CG_ParseMenu( const char *menuFile )
@@ -1545,11 +1414,11 @@ void CG_LoadMenus( const char *menuFile )
if( !f )
{
- trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) );
+ trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default", menuFile ) );
len = trap_FS_FOpenFile( "ui/hud.txt", &f, FS_READ );
if( !f )
- trap_Error( va( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!\n" ) );
+ trap_Error( va( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!" ) );
}
if( len >= MAX_MENUDEFFILE )
@@ -1594,13 +1463,13 @@ void CG_LoadMenus( const char *menuFile )
-static qboolean CG_OwnerDrawHandleKey( int ownerDraw, int flags, float *special, int key )
+static qboolean CG_OwnerDrawHandleKey( int ownerDraw, int key )
{
return qfalse;
}
-static int CG_FeederCount( float feederID )
+static int CG_FeederCount( int feederID )
{
int i, count = 0;
@@ -1608,7 +1477,7 @@ static int CG_FeederCount( float feederID )
{
for( i = 0; i < cg.numScores; i++ )
{
- if( cg.scores[ i ].team == PTE_ALIENS )
+ if( cg.scores[ i ].team == TEAM_ALIENS )
count++;
}
}
@@ -1616,7 +1485,7 @@ static int CG_FeederCount( float feederID )
{
for( i = 0; i < cg.numScores; i++ )
{
- if( cg.scores[ i ].team == PTE_HUMANS )
+ if( cg.scores[ i ].team == TEAM_HUMANS )
count++;
}
}
@@ -1636,9 +1505,9 @@ void CG_SetScoreSelection( void *p )
for( i = 0; i < cg.numScores; i++ )
{
- if( cg.scores[ i ].team == PTE_ALIENS )
+ if( cg.scores[ i ].team == TEAM_ALIENS )
alien++;
- else if( cg.scores[ i ].team == PTE_HUMANS )
+ else if( cg.scores[ i ].team == TEAM_HUMANS )
human++;
if( ps->clientNum == cg.scores[ i ].client )
@@ -1652,7 +1521,7 @@ void CG_SetScoreSelection( void *p )
feeder = FEEDER_ALIENTEAM_LIST;
i = alien;
- if( cg.scores[ cg.selectedScore ].team == PTE_HUMANS )
+ if( cg.scores[ cg.selectedScore ].team == TEAM_HUMANS )
{
feeder = FEEDER_HUMANTEAM_LIST;
i = human;
@@ -1684,7 +1553,16 @@ static clientInfo_t * CG_InfoFromScoreIndex( int index, int team, int *scoreInde
return &cgs.clientinfo[ cg.scores[ index ].client ];
}
-static const char *CG_FeederItemText( float feederID, int index, int column, qhandle_t *handle )
+qboolean CG_ClientIsReady( int clientNum )
+{
+ clientList_t ready;
+
+ Com_ClientListParse( &ready, CG_ConfigString( CS_CLIENTS_READY ) );
+
+ return Com_ClientListContains( &ready, clientNum );
+}
+
+static const char *CG_FeederItemText( int feederID, int index, int column, qhandle_t *handle )
{
int scoreIndex = 0;
clientInfo_t *info = NULL;
@@ -1695,19 +1573,23 @@ static const char *CG_FeederItemText( float feederID, int index, int column, qha
*handle = -1;
if( feederID == FEEDER_ALIENTEAM_LIST )
- team = PTE_ALIENS;
+ team = TEAM_ALIENS;
else if( feederID == FEEDER_HUMANTEAM_LIST )
- team = PTE_HUMANS;
+ team = TEAM_HUMANS;
info = CG_InfoFromScoreIndex( index, team, &scoreIndex );
sp = &cg.scores[ scoreIndex ];
- if( ( atoi( CG_ConfigString( CS_CLIENTS_READY ) ) & ( 1 << sp->client ) ) &&
- cg.intermissionStarted )
+ if( cg.intermissionStarted && CG_ClientIsReady( sp->client ) )
showIcons = qfalse;
- else if( cg.snap->ps.pm_type == PM_SPECTATOR || cg.snap->ps.pm_flags & PMF_FOLLOW ||
- team == cg.snap->ps.stats[ STAT_PTEAM ] || cg.intermissionStarted )
+ else if( cg.snap->ps.pm_type == PM_SPECTATOR ||
+ cg.snap->ps.pm_type == PM_NOCLIP ||
+ cg.snap->ps.pm_flags & PMF_FOLLOW ||
+ team == cg.snap->ps.stats[ STAT_TEAM ] ||
+ cg.intermissionStarted )
+ {
showIcons = qtrue;
+ }
if( info && info->infoValid )
{
@@ -1724,9 +1606,9 @@ static const char *CG_FeederItemText( float feederID, int index, int column, qha
case 1:
if( showIcons )
{
- if( sp->team == PTE_HUMANS && sp->upgrade != UP_NONE )
+ if( sp->team == TEAM_HUMANS && sp->upgrade != UP_NONE )
*handle = cg_upgrades[ sp->upgrade ].upgradeIcon;
- else if( sp->team == PTE_ALIENS )
+ else if( sp->team == TEAM_ALIENS )
{
switch( sp->weapon )
{
@@ -1745,17 +1627,16 @@ static const char *CG_FeederItemText( float feederID, int index, int column, qha
break;
case 2:
- if( ( atoi( CG_ConfigString( CS_CLIENTS_READY ) ) & ( 1 << sp->client ) ) &&
- cg.intermissionStarted )
+ if( cg.intermissionStarted && CG_ClientIsReady( sp->client ) )
return "Ready";
break;
case 3:
- return info->name;
+ return va( S_COLOR_WHITE "%s", info->name );
break;
case 4:
- return va( "%d", info->score );
+ return va( "%d", sp->score );
break;
case 5:
@@ -1764,7 +1645,7 @@ static const char *CG_FeederItemText( float feederID, int index, int column, qha
case 6:
if( sp->ping == -1 )
- return "connecting";
+ return "";
return va( "%4d", sp->ping );
break;
@@ -1774,15 +1655,15 @@ static const char *CG_FeederItemText( float feederID, int index, int column, qha
return "";
}
-static qhandle_t CG_FeederItemImage( float feederID, int index )
+static qhandle_t CG_FeederItemImage( int feederID, int index )
{
return 0;
}
-static void CG_FeederSelection( float feederID, int index )
+static void CG_FeederSelection( int feederID, int index )
{
int i, count;
- int team = ( feederID == FEEDER_ALIENTEAM_LIST ) ? PTE_ALIENS : PTE_HUMANS;
+ int team = ( feederID == FEEDER_ALIENTEAM_LIST ) ? TEAM_ALIENS : TEAM_HUMANS;
count = 0;
for( i = 0; i < cg.numScores; i++ )
@@ -1809,7 +1690,7 @@ static float CG_Cvar_Get( const char *cvar )
void CG_Text_PaintWithCursor( float x, float y, float scale, vec4_t color, const char *text,
int cursorPos, char cursor, int limit, int style )
{
- CG_Text_Paint( x, y, scale, color, text, 0, limit, style );
+ UI_Text_Paint( x, y, scale, color, text, 0, limit, style );
}
static int CG_OwnerDrawWidth( int ownerDraw, float scale )
@@ -1817,7 +1698,7 @@ static int CG_OwnerDrawWidth( int ownerDraw, float scale )
switch( ownerDraw )
{
case CG_KILLER:
- return CG_Text_Width( CG_GetKillerText( ), scale, 0 );
+ return UI_Text_Width( CG_GetKillerText( ), scale );
break;
}
@@ -1845,7 +1726,7 @@ static void CG_RunCinematicFrame( int handle )
trap_CIN_RunCinematic( handle );
}
-//TA: hack to prevent warning
+// hack to prevent warning
static qboolean CG_OwnerDrawVisible( int parameter )
{
return qfalse;
@@ -1861,13 +1742,18 @@ void CG_LoadHudMenu( void )
char buff[ 1024 ];
const char *hudSet;
+ cgDC.aspectScale = ( ( 640.0f * cgs.glconfig.vidHeight ) /
+ ( 480.0f * cgs.glconfig.vidWidth ) );
+ cgDC.xscale = cgs.glconfig.vidWidth / 640.0f;
+ cgDC.yscale = cgs.glconfig.vidHeight / 480.0f;
+
+ cgDC.smallFontScale = CG_Cvar_Get( "ui_smallFont" );
+ cgDC.bigFontScale = CG_Cvar_Get( "ui_bigFont" );
+
cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
cgDC.setColor = &trap_R_SetColor;
cgDC.drawHandlePic = &CG_DrawPic;
cgDC.drawStretchPic = &trap_R_DrawStretchPic;
- cgDC.drawText = &CG_Text_Paint;
- cgDC.textWidth = &CG_Text_Width;
- cgDC.textHeight = &CG_Text_Height;
cgDC.registerModel = &trap_R_RegisterModel;
cgDC.modelBounds = &trap_R_ModelBounds;
cgDC.fillRect = &CG_FillRect;
@@ -1882,13 +1768,11 @@ void CG_LoadHudMenu( void )
cgDC.getValue = &CG_GetValue;
cgDC.ownerDrawVisible = &CG_OwnerDrawVisible;
cgDC.runScript = &CG_RunMenuScript;
- cgDC.getTeamColor = &CG_GetTeamColor;
cgDC.setCVar = trap_Cvar_Set;
cgDC.getCVarString = trap_Cvar_VariableStringBuffer;
cgDC.getCVarValue = CG_Cvar_Get;
- cgDC.drawTextWithCursor = &CG_Text_PaintWithCursor;
- //cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
- //cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
+ cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
+ cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
cgDC.startLocalSound = &trap_S_StartLocalSound;
cgDC.ownerDrawHandleKey = &CG_OwnerDrawHandleKey;
cgDC.feederCount = &CG_FeederCount;
@@ -1902,6 +1786,7 @@ void CG_LoadHudMenu( void )
cgDC.Error = &Com_Error;
cgDC.Print = &Com_Printf;
cgDC.ownerDrawWidth = &CG_OwnerDrawWidth;
+ //cgDC.ownerDrawText = &CG_OwnerDrawText;
//cgDC.Pause = &CG_Pause;
cgDC.registerSound = &trap_S_RegisterSound;
cgDC.startBackgroundTrack = &trap_S_StartBackgroundTrack;
@@ -1926,6 +1811,8 @@ void CG_LoadHudMenu( void )
void CG_AssetCache( void )
{
+ int i;
+
cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR );
cgDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR );
cgDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN );
@@ -1935,6 +1822,21 @@ void CG_AssetCache( void )
cgDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB );
cgDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR );
cgDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB );
+
+ if( cg_emoticons.integer )
+ {
+ cgDC.Assets.emoticonCount = BG_LoadEmoticons( cgDC.Assets.emoticons,
+ MAX_EMOTICONS );
+ }
+ else
+ cgDC.Assets.emoticonCount = 0;
+
+ for( i = 0; i < cgDC.Assets.emoticonCount; i++ )
+ {
+ cgDC.Assets.emoticons[ i ].shader = trap_R_RegisterShaderNoMip(
+ va( "emoticons/%s_%dx1.tga", cgDC.Assets.emoticons[ i ].name,
+ cgDC.Assets.emoticons[ i ].width ) );
+ }
}
/*
@@ -1952,7 +1854,6 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
// clear everything
memset( &cgs, 0, sizeof( cgs ) );
memset( &cg, 0, sizeof( cg ) );
- memset( &cg.pmext, 0, sizeof( cg.pmext ) );
memset( cg_entities, 0, sizeof( cg_entities ) );
cg.clientNum = clientNum;
@@ -1960,45 +1861,52 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
cgs.processedSnapshotNum = serverMessageNum;
cgs.serverCommandSequence = serverCommandSequence;
+ // get the rendering configuration from the client system
+ trap_GetGlconfig( &cgs.glconfig );
+ cgs.screenXScale = cgs.glconfig.vidWidth / 640.0f;
+ cgs.screenYScale = cgs.glconfig.vidHeight / 480.0f;
+
// load a few needed things before we do any screen updates
cgs.media.whiteShader = trap_R_RegisterShader( "white" );
cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/bigchars" );
cgs.media.outlineShader = trap_R_RegisterShader( "outline" );
- //inform UI to repress cursor whilst loading
- trap_Cvar_Set( "ui_loading", "1" );
-
- //TA: load overrides
- BG_InitClassOverrides( );
- BG_InitBuildableOverrides( );
+ // load overrides
+ BG_InitClassConfigs( );
+ BG_InitBuildableConfigs( );
BG_InitAllowedGameElements( );
- //TA: dyn memory
- CG_InitMemory( );
+ // Dynamic memory
+ BG_InitMemory( );
CG_RegisterCvars( );
CG_InitConsoleCommands( );
- //TA: moved up for LoadHudMenu
String_Init( );
- //TA: TA UI
CG_AssetCache( );
- CG_LoadHudMenu( ); // load new hud stuff
+ CG_LoadHudMenu( );
cg.weaponSelect = WP_NONE;
// old servers
- // get the rendering configuration from the client system
- trap_GetGlconfig( &cgs.glconfig );
- cgs.screenXScale = cgs.glconfig.vidWidth / 640.0;
- cgs.screenYScale = cgs.glconfig.vidHeight / 480.0;
-
// get the gamestate from the client system
trap_GetGameState( &cgs.gameState );
+ // copy vote display strings so they don't show up blank if we see
+ // the same one directly after connecting
+ Q_strncpyz( cgs.voteString[ TEAM_NONE ],
+ CG_ConfigString( CS_VOTE_STRING + TEAM_NONE ),
+ sizeof( cgs.voteString ) );
+ Q_strncpyz( cgs.voteString[ TEAM_ALIENS ],
+ CG_ConfigString( CS_VOTE_STRING + TEAM_ALIENS ),
+ sizeof( cgs.voteString[ TEAM_ALIENS ] ) );
+ Q_strncpyz( cgs.voteString[ TEAM_HUMANS ],
+ CG_ConfigString( CS_VOTE_STRING + TEAM_ALIENS ),
+ sizeof( cgs.voteString[ TEAM_HUMANS ] ) );
+
// check version
s = CG_ConfigString( CS_GAME_VERSION );
@@ -2033,8 +1941,10 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
CG_InitUpgrades( );
CG_UpdateMediaFraction( 1.0f );
- //TA:
CG_InitBuildables( );
+
+ cgs.voices = BG_VoiceInit( );
+ BG_PrintVoices( cgs.voices, cg_debugVoices.integer );
CG_RegisterClients( ); // if low on memory, some clients will be deferred
@@ -2053,8 +1963,6 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
CG_ShaderStateChanged( );
trap_S_ClearLoopingSounds( qtrue );
-
- trap_Cvar_Set( "ui_loading", "0" );
}
/*
@@ -2066,8 +1974,7 @@ Called before every level change or subsystem restart
*/
void CG_Shutdown( void )
{
- // some mods may need to do cleanup work here,
- // like closing files or archiving session data
+ CG_UnregisterCommands( );
}
/*
@@ -2075,7 +1982,7 @@ void CG_Shutdown( void )
CG_VoIPString
================
*/
-char *CG_VoIPString( void )
+static char *CG_VoIPString( void )
{
// a generous overestimate of the space needed for 0,1,2...61,62,63
static char voipString[ MAX_CLIENTS * 4 ];
@@ -2086,7 +1993,7 @@ char *CG_VoIPString( void )
if( Q_stricmp( voipSendTarget, "team" ) == 0 )
{
- int i, slen;
+ int i, slen, nlen;
for( slen = i = 0; i < cgs.maxclients; i++ )
{
if( !cgs.clientinfo[ i ].infoValid || i == cg.clientNum )
@@ -2094,14 +2001,15 @@ char *CG_VoIPString( void )
if( cgs.clientinfo[ i ].team != cgs.clientinfo[ cg.clientNum ].team )
continue;
- Com_sprintf( &voipString[ slen ], sizeof( voipString ) - slen,
- "%s%d", ( slen > 0 ) ? "," : "", i );
- slen = strlen( voipString );
- if( slen + 1 >= sizeof( voipString ) )
+ nlen = Q_snprintf( &voipString[ slen ], sizeof( voipString ) - slen,
+ "%s%d", ( slen > 0 ) ? "," : "", i );
+ if( slen + nlen + 1 >= sizeof( voipString ) )
{
CG_Printf( S_COLOR_YELLOW "WARNING: voipString overflowed\n" );
break;
}
+
+ slen += nlen;
}
// Notice that if the snprintf was truncated, slen was not updated
@@ -2119,3 +2027,26 @@ char *CG_VoIPString( void )
return voipString;
}
+
+#ifdef MODULE_INTERFACE_11
+int trap_S_SoundDuration( sfxHandle_t handle )
+{
+ return 1;
+}
+
+void trap_R_SetClipRegion( const float *region )
+{
+}
+
+static qboolean keyOverstrikeMode = qfalse;
+
+void trap_Key_SetOverstrikeMode( qboolean state )
+{
+ keyOverstrikeMode = state;
+}
+
+qboolean trap_Key_GetOverstrikeMode( void )
+{
+ return keyOverstrikeMode;
+}
+#endif
diff --git a/src/cgame/cg_marks.c b/src/cgame/cg_marks.c
index 380f1f0..a5e2351 100644
--- a/src/cgame/cg_marks.c
+++ b/src/cgame/cg_marks.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_marks.c -- wall marks
-
#include "cg_local.h"
/*
@@ -286,4 +286,3 @@ void CG_AddMarks( void )
trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts );
}
}
-
diff --git a/src/cgame/cg_mem.c b/src/cgame/cg_mem.c
deleted file mode 100644
index 6cf5ddd..0000000
--- a/src/cgame/cg_mem.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-===========================================================================
-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
- }
-}
diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c
index 80c4b23..eca9de2 100644
--- a/src/cgame/cg_particles.c
+++ b/src/cgame/cg_particles.c
@@ -1,12 +1,13 @@
/*
===========================================================================
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -15,14 +16,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_particles.c -- the particle system
-
#include "cg_local.h"
static baseParticleSystem_t baseParticleSystems[ MAX_BASEPARTICLE_SYSTEMS ];
@@ -157,6 +157,8 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
p->radius.delay = (int)CG_RandomiseValue( (float)bp->radius.delay, bp->radius.delayRandFrac );
p->radius.initial = CG_RandomiseValue( bp->radius.initial, bp->radius.initialRandFrac );
p->radius.final = CG_RandomiseValue( bp->radius.final, bp->radius.finalRandFrac );
+
+ p->radius.initial += bp->scaleWithCharge * pe->parent->charge;
p->alpha.delay = (int)CG_RandomiseValue( (float)bp->alpha.delay, bp->alpha.delayRandFrac );
p->alpha.initial = CG_RandomiseValue( bp->alpha.initial, bp->alpha.initialRandFrac );
@@ -208,7 +210,7 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
VectorAdd( p->origin, bp->displacement, p->origin );
for( j = 0; j <= 2; j++ )
- p->origin[ j ] += ( crandom( ) * bp->randDisplacement );
+ p->origin[ j ] += ( crandom( ) * bp->randDisplacement[ j ] );
switch( bp->velMoveType )
{
@@ -259,6 +261,21 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
VectorNormalize( p->velocity );
VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin );
break;
+
+ case PMT_LAST_NORMAL:
+ VectorCopy( ps->lastNormal, p->velocity );
+ VectorNormalize( p->velocity );
+ VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin );
+ break;
+
+ case PMT_OPPORTUNISTIC_NORMAL:
+ if( ps->lastNormalIsCurrent )
+ {
+ VectorCopy( ps->lastNormal, p->velocity );
+ VectorNormalize( p->velocity );
+ VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin );
+ }
+ break;
}
VectorNormalize( p->velocity );
@@ -281,12 +298,18 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
//this particle has a child particle system attached
if( bp->childSystemName[ 0 ] != '\0' )
{
- particleSystem_t *ps = CG_SpawnNewParticleSystem( bp->childSystemHandle );
+ particleSystem_t *chps = CG_SpawnNewParticleSystem( bp->childSystemHandle );
- if( CG_IsParticleSystemValid( &ps ) )
+ if( CG_IsParticleSystemValid( &chps ) )
{
- CG_SetAttachmentParticle( &ps->attachment, p );
- CG_AttachToParticle( &ps->attachment );
+ CG_SetAttachmentParticle( &chps->attachment, p );
+ CG_AttachToParticle( &chps->attachment );
+ p->childParticleSystem = chps;
+
+ if( ps->lastNormalIsCurrent )
+ CG_SetParticleSystemLastNormal( chps, ps->lastNormal );
+ else
+ VectorCopy( ps->lastNormal, chps->lastNormal );
}
}
@@ -465,6 +488,9 @@ particleSystem_t *CG_SpawnNewParticleSystem( qhandle_t psHandle )
ps->valid = qtrue;
ps->lazyRemove = qfalse;
+ // use "up" as an arbitrary (non-null) "last" normal
+ VectorSet( ps->lastNormal, 0, 0, 1 );
+
for( j = 0; j < bps->numEjectors; j++ )
CG_SpawnNewParticleEjector( bps->ejectors[ j ], ps );
@@ -567,13 +593,11 @@ Parse a value and its random variance
static void CG_ParseValueAndVariance( char *token, float *value, float *variance, qboolean allowNegative )
{
char valueBuffer[ 16 ];
- char varianceBuffer[ 16 ];
char *variancePtr = NULL, *varEndPointer = NULL;
float localValue = 0.0f;
float localVariance = 0.0f;
Q_strncpyz( valueBuffer, token, sizeof( valueBuffer ) );
- Q_strncpyz( varianceBuffer, token, sizeof( varianceBuffer ) );
variancePtr = strchr( valueBuffer, '~' );
@@ -623,13 +647,96 @@ static qboolean CG_ParseColor( byte *c, char **text_p )
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
-
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
return qfalse;
c[ i ] = (int)( (float)0xFF * atof_neg( token, qfalse ) );
}
+ token = COM_Parse( text_p );
+ if( strcmp( token, "}" ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+CG_ParseParticle helpers
+*/
+static void CG_CopyLine( int *i, char *toks, size_t num, size_t size, char **text_p )
+{
+ char *token;
+
+ while( *i < num )
+ {
+ token = COM_ParseExt( text_p, qfalse );
+ if( !*token )
+ break;
+
+ Q_strncpyz( toks, token, size );
+ ( *i )++;
+
+ toks += size;
+ }
+}
+
+static qboolean CG_ParseType( pMoveType_t *pmt, char **text_p )
+{
+ char *token = COM_Parse( text_p );
+ if( !*token )
+ return qfalse;
+
+ if( !Q_stricmp( token, "static" ) )
+ *pmt = PMT_STATIC;
+ else if( !Q_stricmp( token, "static_transform" ) )
+ *pmt = PMT_STATIC_TRANSFORM;
+ else if( !Q_stricmp( token, "tag" ) )
+ *pmt = PMT_TAG;
+ else if( !Q_stricmp( token, "cent" ) )
+ *pmt = PMT_CENT_ANGLES;
+ else if( !Q_stricmp( token, "normal" ) )
+ *pmt = PMT_NORMAL;
+ else if( !Q_stricmp( token, "last_normal" ) )
+ *pmt = PMT_LAST_NORMAL;
+ else if( !Q_stricmp( token, "opportunistic_normal" ) )
+ *pmt = PMT_OPPORTUNISTIC_NORMAL;
+
+ return qtrue;
+}
+
+static qboolean CG_ParseDir( pMoveValues_t *pmv, char **text_p )
+{
+ char *token = COM_Parse( text_p );
+ if( !*token )
+ return qfalse;
+
+ if( !Q_stricmp( token, "linear" ) )
+ pmv->dirType = PMD_LINEAR;
+ else if( !Q_stricmp( token, "point" ) )
+ pmv->dirType = PMD_POINT;
+
+ return qtrue;
+}
+
+static qboolean CG_ParseFinal( pLerpValues_t *plv, char **text_p )
+{
+ char *token = COM_Parse( text_p );
+ if( !*token )
+ return qfalse;
+
+ if( !Q_stricmp( token, "-" ) )
+ {
+ plv->final = PARTICLES_SAME_AS_INITIAL;
+ plv->finalRandFrac = 0.0f;
+ }
+ else
+ {
+ CG_ParseValueAndVariance( token, &plv->final, &plv->finalRandFrac, qfalse );
+ }
+
return qtrue;
}
@@ -650,17 +757,13 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
while( 1 )
{
token = COM_Parse( text_p );
-
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
return qfalse;
if( !Q_stricmp( token, "bounce" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
if( !Q_stricmp( token, "cull" ) )
@@ -672,13 +775,9 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
}
else
{
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceFrac = number;
- bp->bounceFracRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->bounceFrac,
+ &bp->bounceFracRandFrac, qfalse );
}
-
- continue;
}
else if( !Q_stricmp( token, "bounceMark" ) )
{
@@ -686,27 +785,21 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceMarkCount = number;
- bp->bounceMarkCountRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->bounceMarkCount,
+ &bp->bounceMarkCountRandFrac, qfalse );
token = COM_Parse( text_p );
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceMarkRadius = number;
- bp->bounceMarkRadiusRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->bounceMarkRadius,
+ &bp->bounceMarkRadiusRandFrac, qfalse );
token = COM_ParseExt( text_p, qfalse );
if( !*token )
break;
Q_strncpyz( bp->bounceMarkName, token, MAX_QPATH );
-
- continue;
}
else if( !Q_stricmp( token, "bounceSound" ) )
{
@@ -714,30 +807,26 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceSoundCount = number;
- bp->bounceSoundCountRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->bounceSoundCount,
+ &bp->bounceSoundCountRandFrac, qfalse );
token = COM_Parse( text_p );
if( !*token )
break;
Q_strncpyz( bp->bounceSoundName, token, MAX_QPATH );
-
- continue;
}
else if( !Q_stricmp( token, "shader" ) )
{
if( bp->numModels > 0 )
{
CG_Printf( S_COLOR_RED "ERROR: 'shader' not allowed in "
- "conjunction with 'model'\n", token );
+ "conjunction with 'model'\n" );
break;
}
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
if( !Q_stricmp( token, "sync" ) )
@@ -745,38 +834,20 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
else
bp->framerate = atof_neg( token, qfalse );
- token = COM_ParseExt( text_p, qfalse );
- if( !*token )
- break;
-
- while( *token && bp->numFrames < MAX_PS_SHADER_FRAMES )
- {
- Q_strncpyz( bp->shaderNames[ bp->numFrames++ ], token, MAX_QPATH );
- token = COM_ParseExt( text_p, qfalse );
- }
-
- continue;
+ CG_CopyLine( &bp->numFrames, bp->shaderNames[ 0 ],
+ ARRAY_LEN( bp->shaderNames ), MAX_QPATH, text_p );
}
else if( !Q_stricmp( token, "model" ) )
{
if( bp->numFrames > 0 )
{
CG_Printf( S_COLOR_RED "ERROR: 'model' not allowed in "
- "conjunction with 'shader'\n", token );
+ "conjunction with 'shader'\n" );
break;
}
- token = COM_ParseExt( text_p, qfalse );
- if( !*token )
- break;
-
- while( *token && bp->numModels < MAX_PS_MODELS )
- {
- Q_strncpyz( bp->modelNames[ bp->numModels++ ], token, MAX_QPATH );
- token = COM_ParseExt( text_p, qfalse );
- }
-
- continue;
+ CG_CopyLine( &bp->numModels, bp->modelNames[ 0 ],
+ ARRAY_LEN( bp->modelNames ), MAX_QPATH, text_p );
}
else if( !Q_stricmp( token, "modelAnimation" ) )
{
@@ -823,200 +894,130 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
if( fps == 0.0f )
fps = 1.0f;
- bp->modelAnimation.frameLerp = 1000 / fps;
- bp->modelAnimation.initialLerp = 1000 / fps;
+ bp->modelAnimation.frameLerp = bp->modelAnimation.initialLerp =
+ 1000 / fps;
}
-
- continue;
}
///
else if( !Q_stricmp( token, "velocityType" ) )
{
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseType( &bp->velMoveType, text_p ) )
break;
-
- if( !Q_stricmp( token, "static" ) )
- bp->velMoveType = PMT_STATIC;
- else if( !Q_stricmp( token, "static_transform" ) )
- bp->velMoveType = PMT_STATIC_TRANSFORM;
- else if( !Q_stricmp( token, "tag" ) )
- bp->velMoveType = PMT_TAG;
- else if( !Q_stricmp( token, "cent" ) )
- bp->velMoveType = PMT_CENT_ANGLES;
- else if( !Q_stricmp( token, "normal" ) )
- bp->velMoveType = PMT_NORMAL;
-
- continue;
}
else if( !Q_stricmp( token, "velocityDir" ) )
{
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseDir( &bp->velMoveValues, text_p ) )
break;
-
- if( !Q_stricmp( token, "linear" ) )
- bp->velMoveValues.dirType = PMD_LINEAR;
- else if( !Q_stricmp( token, "point" ) )
- bp->velMoveValues.dirType = PMD_POINT;
-
- continue;
}
else if( !Q_stricmp( token, "velocityMagnitude" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->velMoveValues.mag = number;
- bp->velMoveValues.magRandFrac = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, &bp->velMoveValues.mag,
+ &bp->velMoveValues.magRandFrac, qtrue );
}
else if( !Q_stricmp( token, "parentVelocityFraction" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->velMoveValues.parentVelFrac = number;
- bp->velMoveValues.parentVelFracRandFrac = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, &bp->velMoveValues.parentVelFrac,
+ &bp->velMoveValues.parentVelFracRandFrac, qfalse );
}
else if( !Q_stricmp( token, "velocity" ) )
{
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bp->velMoveValues.dir[ i ] = atof_neg( token, qtrue );
}
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->velMoveValues.dirRandAngle = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, NULL, &bp->velMoveValues.dirRandAngle,
+ qfalse );
}
else if( !Q_stricmp( token, "velocityPoint" ) )
{
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bp->velMoveValues.point[ i ] = atof_neg( token, qtrue );
}
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->velMoveValues.pointRandAngle = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, NULL, &bp->velMoveValues.pointRandAngle,
+ qfalse );
}
///
else if( !Q_stricmp( token, "accelerationType" ) )
{
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseType( &bp->accMoveType, text_p ) )
break;
-
- if( !Q_stricmp( token, "static" ) )
- bp->accMoveType = PMT_STATIC;
- else if( !Q_stricmp( token, "static_transform" ) )
- bp->accMoveType = PMT_STATIC_TRANSFORM;
- else if( !Q_stricmp( token, "tag" ) )
- bp->accMoveType = PMT_TAG;
- else if( !Q_stricmp( token, "cent" ) )
- bp->accMoveType = PMT_CENT_ANGLES;
- else if( !Q_stricmp( token, "normal" ) )
- bp->accMoveType = PMT_NORMAL;
-
- continue;
}
else if( !Q_stricmp( token, "accelerationDir" ) )
{
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseDir( &bp->accMoveValues, text_p ) )
break;
-
- if( !Q_stricmp( token, "linear" ) )
- bp->accMoveValues.dirType = PMD_LINEAR;
- else if( !Q_stricmp( token, "point" ) )
- bp->accMoveValues.dirType = PMD_POINT;
-
- continue;
}
else if( !Q_stricmp( token, "accelerationMagnitude" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->accMoveValues.mag = number;
- bp->accMoveValues.magRandFrac = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, &bp->accMoveValues.mag,
+ &bp->accMoveValues.magRandFrac, qtrue );
}
else if( !Q_stricmp( token, "acceleration" ) )
{
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bp->accMoveValues.dir[ i ] = atof_neg( token, qtrue );
}
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->accMoveValues.dirRandAngle = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, NULL, &bp->accMoveValues.dirRandAngle,
+ qfalse );
}
else if( !Q_stricmp( token, "accelerationPoint" ) )
{
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bp->accMoveValues.point[ i ] = atof_neg( token, qtrue );
}
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->accMoveValues.pointRandAngle = randFrac;
-
- continue;
+ CG_ParseValueAndVariance( token, NULL, &bp->accMoveValues.pointRandAngle,
+ qfalse );
}
///
else if( !Q_stricmp( token, "displacement" ) )
@@ -1024,43 +1025,45 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
for( i = 0; i <= 2; i++ )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- bp->displacement[ i ] = atof_neg( token, qtrue );
+ CG_ParseValueAndVariance( token, &bp->displacement[ i ],
+ &bp->randDisplacement[ i ], qtrue );
}
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
+ // if there is another token on the same line interpret it as an
+ // additional displacement in all three directions, for compatibility
+ // with the old scripts where this was the only option
+ randFrac = 0;
+ token = COM_ParseExt( text_p, qfalse );
+ if( token )
+ CG_ParseValueAndVariance( token, NULL, &randFrac, qtrue );
- bp->randDisplacement = randFrac;
+ for( i = 0; i < 3; i++ )
+ {
+ // convert randDisplacement from proportions to absolute values
+ if( bp->displacement[ i ] != 0 )
+ bp->randDisplacement[ i ] *= bp->displacement[ i ];
- continue;
+ bp->randDisplacement[ i ] += randFrac;
+ }
}
else if( !Q_stricmp( token, "normalDisplacement" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bp->normalDisplacement = atof_neg( token, qtrue );
-
- continue;
}
else if( !Q_stricmp( token, "overdrawProtection" ) )
{
bp->overdrawProtection = qtrue;
-
- continue;
}
else if( !Q_stricmp( token, "realLight" ) )
{
bp->realLight = qtrue;
-
- continue;
}
else if( !Q_stricmp( token, "dynamicLight" ) )
{
@@ -1070,37 +1073,20 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->dLightRadius.delayRandFrac,
+ qfalse );
bp->dLightRadius.delay = (int)number;
- bp->dLightRadius.delayRandFrac = randFrac;
token = COM_Parse( text_p );
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
+ CG_ParseValueAndVariance( token, &bp->dLightRadius.initial,
+ &bp->dLightRadius.initialRandFrac, qfalse );
- bp->dLightRadius.initial = number;
- bp->dLightRadius.initialRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
+ if( !CG_ParseFinal( &bp->dLightRadius, text_p ) )
break;
- if( !Q_stricmp( token, "-" ) )
- {
- bp->dLightRadius.final = PARTICLES_SAME_AS_INITIAL;
- bp->dLightRadius.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->dLightRadius.final = number;
- bp->dLightRadius.finalRandFrac = randFrac;
- }
-
token = COM_Parse( text_p );
if( !*token )
break;
@@ -1109,100 +1095,59 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
{
if( !CG_ParseColor( bp->dLightColor, text_p ) )
break;
-
- token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
}
-
- continue;
}
else if( !Q_stricmp( token, "cullOnStartSolid" ) )
{
bp->cullOnStartSolid = qtrue;
-
- continue;
}
else if( !Q_stricmp( token, "radius" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->radius.delayRandFrac,
+ qfalse );
bp->radius.delay = (int)number;
- bp->radius.delayRandFrac = randFrac;
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->radius.initial = number;
- bp->radius.initialRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->radius.initial,
+ &bp->radius.initialRandFrac, qfalse );
+ if( !CG_ParseFinal( &bp->radius, text_p ) )
+ break;
+ }
+ else if( !Q_stricmp( token, "physicsRadius" ) )
+ {
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->radius.final = PARTICLES_SAME_AS_INITIAL;
- bp->radius.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->radius.final = number;
- bp->radius.finalRandFrac = randFrac;
- }
-
- continue;
+
+ bp->physicsRadius = atoi( token );
}
else if( !Q_stricmp( token, "alpha" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->alpha.delayRandFrac,
+ qfalse );
bp->alpha.delay = (int)number;
- bp->alpha.delayRandFrac = randFrac;
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->alpha.initial = number;
- bp->alpha.initialRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->alpha.initial,
+ &bp->alpha.initialRandFrac, qfalse );
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseFinal( &bp->alpha, text_p ) )
break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->alpha.final = PARTICLES_SAME_AS_INITIAL;
- bp->alpha.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->alpha.final = number;
- bp->alpha.finalRandFrac = randFrac;
- }
-
- continue;
}
else if( !Q_stricmp( token, "color" ) )
{
@@ -1210,10 +1155,9 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->colorDelayRandFrac,
+ qfalse );
bp->colorDelay = (int)number;
- bp->colorDelayRandFrac = randFrac;
token = COM_Parse( text_p );
if( !*token )
@@ -1225,33 +1169,17 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
break;
token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
-
- token = COM_Parse( text_p );
if( !*token )
break;
if( !Q_stricmp( token, "-" ) )
{
- bp->finalColor[ 0 ] = bp->initialColor[ 0 ];
- bp->finalColor[ 1 ] = bp->initialColor[ 1 ];
- bp->finalColor[ 2 ] = bp->initialColor[ 2 ];
+ memcpy( bp->finalColor, bp->initialColor, sizeof( bp->finalColor ) );
}
else if( !Q_stricmp( token, "{" ) )
{
if( !CG_ParseColor( bp->finalColor, text_p ) )
break;
-
- token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
}
else
{
@@ -1264,90 +1192,69 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" );
break;
}
-
- continue;
}
else if( !Q_stricmp( token, "rotation" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->rotation.delayRandFrac,
+ qfalse );
bp->rotation.delay = (int)number;
- bp->rotation.delayRandFrac = randFrac;
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qtrue );
-
- bp->rotation.initial = number;
- bp->rotation.initialRandFrac = randFrac;
+ CG_ParseValueAndVariance( token, &bp->rotation.initial,
+ &bp->rotation.initialRandFrac, qtrue );
- token = COM_Parse( text_p );
- if( !token )
+ if( !CG_ParseFinal( &bp->rotation, text_p ) )
break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->rotation.final = PARTICLES_SAME_AS_INITIAL;
- bp->rotation.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qtrue );
-
- bp->rotation.final = number;
- bp->rotation.finalRandFrac = randFrac;
- }
-
- continue;
}
else if( !Q_stricmp( token, "lifeTime" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bp->lifeTimeRandFrac, qfalse );
bp->lifeTime = (int)number;
- bp->lifeTimeRandFrac = randFrac;
continue;
}
else if( !Q_stricmp( token, "childSystem" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
Q_strncpyz( bp->childSystemName, token, MAX_QPATH );
-
- continue;
}
else if( !Q_stricmp( token, "onDeathSystem" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
Q_strncpyz( bp->onDeathSystemName, token, MAX_QPATH );
-
- continue;
}
else if( !Q_stricmp( token, "childTrailSystem" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
Q_strncpyz( bp->childTrailSystemName, token, MAX_QPATH );
+ }
+ else if( !Q_stricmp( token, "scaleWithCharge" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !*token )
+ break;
- continue;
+ bp->scaleWithCharge = atof( token );
}
else if( !Q_stricmp( token, "}" ) )
return qtrue; //reached the end of this particle
@@ -1384,17 +1291,14 @@ Parse a particle ejector section
static qboolean CG_ParseParticleEjector( baseParticleEjector_t *bpe, char **text_p )
{
char *token;
- float number, randFrac;
+ float number;
// read optional parameters
while( 1 )
{
token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
return qfalse;
if( !Q_stricmp( token, "{" ) )
@@ -1412,43 +1316,38 @@ static qboolean CG_ParseParticleEjector( baseParticleEjector_t *bpe, char **text
CG_Printf( S_COLOR_RED "ERROR: ejector has > %d particles\n", MAX_PARTICLES_PER_EJECTOR );
return qfalse;
}
- else if( numBaseParticles == MAX_BASEPARTICLES )
+
+ if( numBaseParticles == MAX_BASEPARTICLES )
{
CG_Printf( S_COLOR_RED "ERROR: maximum number of particles (%d) reached\n", MAX_BASEPARTICLES );
return qfalse;
}
- else
- {
- //start parsing particles again
- bpe->particles[ bpe->numParticles ] = &baseParticles[ numBaseParticles ];
- bpe->numParticles++;
- numBaseParticles++;
- }
- continue;
+
+ //start parsing particles again
+ bpe->particles[ bpe->numParticles ] = &baseParticles[ numBaseParticles ];
+ bpe->numParticles++;
+ numBaseParticles++;
}
else if( !Q_stricmp( token, "delay" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bpe->eject.delayRandFrac,
+ qfalse );
bpe->eject.delay = (int)number;
- bpe->eject.delayRandFrac = randFrac;
-
- continue;
}
else if( !Q_stricmp( token, "period" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
bpe->eject.initial = atoi_neg( token, qfalse );
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
if( !Q_stricmp( token, "-" ) )
@@ -1457,17 +1356,15 @@ static qboolean CG_ParseParticleEjector( baseParticleEjector_t *bpe, char **text
bpe->eject.final = atoi_neg( token, qfalse );
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
CG_ParseValueAndVariance( token, NULL, &bpe->eject.randFrac, qfalse );
-
- continue;
}
else if( !Q_stricmp( token, "count" ) )
{
token = COM_Parse( text_p );
- if( !token )
+ if( !*token )
break;
if( !Q_stricmp( token, "infinite" ) )
@@ -1477,13 +1374,10 @@ static qboolean CG_ParseParticleEjector( baseParticleEjector_t *bpe, char **text
}
else
{
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
+ CG_ParseValueAndVariance( token, &number, &bpe->totalParticlesRandFrac,
+ qfalse );
bpe->totalParticles = (int)number;
- bpe->totalParticlesRandFrac = randFrac;
}
-
- continue;
}
else if( !Q_stricmp( token, "particle" ) ) //acceptable text
continue;
@@ -1517,10 +1411,7 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p
{
token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
return qfalse;
if( !Q_stricmp( token, "{" ) )
@@ -1546,20 +1437,18 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p
CG_Printf( S_COLOR_RED "ERROR: particle system has > %d ejectors\n", MAX_EJECTORS_PER_SYSTEM );
return qfalse;
}
- else if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS )
+
+ if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS )
{
CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n",
MAX_BASEPARTICLE_EJECTORS );
return qfalse;
}
- else
- {
- //start parsing ejectors again
- bps->ejectors[ bps->numEjectors ] = &baseParticleEjectors[ numBaseParticleEjectors ];
- bps->numEjectors++;
- numBaseParticleEjectors++;
- }
- continue;
+
+ //start parsing ejectors again
+ bps->ejectors[ bps->numEjectors ] = &baseParticleEjectors[ numBaseParticleEjectors ];
+ bps->numEjectors++;
+ numBaseParticleEjectors++;
}
else if( !Q_stricmp( token, "thirdPersonOnly" ) )
bps->thirdPersonOnly = qtrue;
@@ -1602,12 +1491,14 @@ static qboolean CG_ParseParticleFile( const char *fileName )
// load the file
len = trap_FS_FOpenFile( fileName, &f, FS_READ );
- if( len <= 0 )
+ if( len < 0 )
return qfalse;
- if( len >= sizeof( text ) - 1 )
+ if( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( S_COLOR_RED "ERROR: particle file %s too long\n", fileName );
+ trap_FS_FCloseFile( f );
+ CG_Printf( S_COLOR_RED "ERROR: particle file %s is %s\n", fileName,
+ len == 0 ? "empty" : "too long" );
return qfalse;
}
@@ -1623,23 +1514,13 @@ static qboolean CG_ParseParticleFile( const char *fileName )
{
token = COM_Parse( &text_p );
- if( !Q_stricmp( token, "" ) )
+ if( !*token )
break;
if( !Q_stricmp( token, "{" ) )
{
if( psNameSet )
{
- //check for name space clashes
- for( i = 0; i < numBaseParticleSystems; i++ )
- {
- if( !Q_stricmp( baseParticleSystems[ i ].name, psName ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle system is already named %s\n", psName );
- return qfalse;
- }
- }
-
Q_strncpyz( baseParticleSystems[ numBaseParticleSystems ].name, psName, MAX_QPATH );
if( !CG_ParseParticleSystem( &baseParticleSystems[ numBaseParticleSystems ], &text_p, psName ) )
@@ -1657,10 +1538,8 @@ static qboolean CG_ParseParticleFile( const char *fileName )
MAX_BASEPARTICLE_SYSTEMS );
return qfalse;
}
- else
- numBaseParticleSystems++;
- continue;
+ numBaseParticleSystems++;
}
else
{
@@ -1668,10 +1547,25 @@ static qboolean CG_ParseParticleFile( const char *fileName )
return qfalse;
}
}
-
- if( !psNameSet )
+ else if( !psNameSet )
{
Q_strncpyz( psName, token, sizeof( psName ) );
+
+ //check for name space clashes
+ for( i = 0; i < numBaseParticleSystems; i++ )
+ {
+ if( !Q_stricmp( baseParticleSystems[ i ].name, psName ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: a particle system is already named %s\n", psName );
+ break;
+ }
+ }
+ if( i < numBaseParticleSystems )
+ {
+ SkipBracedSection( &text_p, 0 );
+ continue;
+ }
+
psNameSet = qtrue;
}
else
@@ -1810,6 +1704,31 @@ void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal )
ps->normalValid = qtrue;
VectorCopy( normal, ps->normal );
VectorNormalize( ps->normal );
+
+ CG_SetParticleSystemLastNormal( ps, normal );
+}
+
+/*
+===============
+CG_SetParticleSystemLastNormal
+===============
+*/
+void CG_SetParticleSystemLastNormal( particleSystem_t *ps, const float *normal )
+{
+ if( ps == NULL || !ps->valid )
+ {
+ CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
+ return;
+ }
+
+ if( normal )
+ {
+ ps->lastNormalIsCurrent = qtrue;
+ VectorCopy( normal, ps->lastNormal );
+ VectorNormalize( ps->lastNormal );
+ }
+ else
+ ps->lastNormalIsCurrent = qfalse;
}
@@ -2056,6 +1975,17 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
VectorCopy( ps->normal, acceleration );
break;
+
+ case PMT_LAST_NORMAL:
+ VectorCopy( ps->lastNormal, acceleration );
+ break;
+
+ case PMT_OPPORTUNISTIC_NORMAL:
+ if( ps->lastNormalIsCurrent )
+ VectorCopy( ps->lastNormal, acceleration );
+ else
+ VectorClear( acceleration );
+ break;
}
#define MAX_ACC_RADIUS 1000.0f
@@ -2086,11 +2016,13 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
acceleration );
}
- radius = CG_LerpValues( p->radius.initial,
- p->radius.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->radius.delay ) );
+ // Some particles have a visual radius that differs from their collision radius
+ if( bp->physicsRadius )
+ radius = bp->physicsRadius;
+ else
+ radius = CG_LerpValues( p->radius.initial, p->radius.final,
+ CG_CalculateTimeFrac( p->birthTime, p->lifeTime,
+ p->radius.delay ) );
VectorSet( mins, -radius, -radius, -radius );
VectorSet( maxs, radius, radius, radius );
@@ -2121,6 +2053,8 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
if( trace.fraction == 1.0f || bounce == 0.0f )
{
VectorCopy( newOrigin, p->origin );
+ if( CG_IsParticleSystemValid( &p->childParticleSystem ) )
+ CG_SetParticleSystemLastNormal( p->childParticleSystem, NULL );
return;
}
@@ -2162,6 +2096,12 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
}
VectorCopy( trace.endpos, p->origin );
+
+ if( !trace.allsolid )
+ {
+ if( CG_IsParticleSystemValid( &p->childParticleSystem ) )
+ CG_SetParticleSystemLastNormal( p->childParticleSystem, trace.plane.normal );
+ }
}
@@ -2394,7 +2334,7 @@ static void CG_RenderParticle( particle_t *p )
p->lf.animation = &bp->modelAnimation;
//run animation
- CG_RunLerpFrame( &p->lf );
+ CG_RunLerpFrame( &p->lf, 1.0f );
re.oldframe = p->lf.oldFrame;
re.frame = p->lf.frame;
@@ -2505,9 +2445,8 @@ void CG_ParticleSystemEntity( centity_t *cent )
if( CG_IsParticleSystemValid( &cent->entityPS ) )
{
- CG_SetAttachmentPoint( &cent->entityPS->attachment, cent->lerpOrigin );
CG_SetAttachmentCent( &cent->entityPS->attachment, cent );
- CG_AttachToPoint( &cent->entityPS->attachment );
+ CG_AttachToCent( &cent->entityPS->attachment );
}
else
cent->entityPSMissing = qtrue;
diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c
index 3bd0e65..16779cb 100644
--- a/src/cgame/cg_players.c
+++ b/src/cgame/cg_players.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_players.c -- handle the media and animation for player entities
-
#include "cg_local.h"
char *cg_customSoundNames[ MAX_CUSTOM_SOUNDS ] =
@@ -107,12 +107,12 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci )
// load the file
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if( len <= 0 )
+ if( len < 0 )
return qfalse;
- if( len >= sizeof( text ) - 1 )
+ if( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( "File %s too long\n", filename );
+ CG_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
trap_FS_FCloseFile( f );
return qfalse;
}
@@ -480,51 +480,17 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
// if any skins failed to load, return failure
if( !CG_RegisterClientSkin( ci, modelName, skinName ) )
{
- Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
- return qfalse;
+ Com_Printf( "Failed to load skin file: %s : %s. Loading default\n", modelName, skinName );
+ if( !CG_RegisterClientSkin( ci, modelName, "default" ) )
+ {
+ Com_Printf( S_COLOR_RED "Failed to load default skin file!\n" );
+ return qfalse;
+ }
}
- //FIXME: skins do not load without icon present. do we want icons anyway?
-/* Com_sprintf( filename, sizeof( filename ), "models/players/%s/icon_%s.tga", modelName, skinName );
- ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
- if( !ci->modelIcon )
- {
- Com_Printf( "Failed to load icon file: %s\n", filename );
- return qfalse;
- }*/
-
return qtrue;
}
-/*
-====================
-CG_ColorFromString
-====================
-*/
-static void CG_ColorFromString( const char *v, vec3_t color )
-{
- int val;
-
- VectorClear( color );
-
- val = atoi( v );
-
- if( val < 1 || val > 7 )
- {
- VectorSet( color, 1, 1, 1 );
- return;
- }
-
- if( val & 1 )
- color[ 2 ] = 1.0f;
-
- if( val & 2 )
- color[ 1 ] = 1.0f;
-
- if( val & 4 )
- color[ 0 ] = 1.0f;
-}
-
/*
===================
@@ -535,24 +501,16 @@ Load it now, taking the disk hits
*/
static void CG_LoadClientInfo( clientInfo_t *ci )
{
- const char *dir, *fallback;
+ const char *dir;
int i;
const char *s;
int clientNum;
if( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName ) )
- {
- if( cg_buildScript.integer )
- CG_Error( "CG_RegisterClientModelname( %s, %s ) failed", ci->modelName, ci->skinName );
-
- // fall back
- if( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default" ) )
- CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
- }
+ CG_Error( "CG_RegisterClientModelname( %s, %s ) failed", ci->modelName, ci->skinName );
// sounds
dir = ci->modelName;
- fallback = DEFAULT_MODEL;
for( i = 0; i < MAX_CUSTOM_SOUNDS; i++ )
{
@@ -578,15 +536,11 @@ static void CG_LoadClientInfo( clientInfo_t *ci )
s = cg_customSoundNames[ 0 ]; //death1
ci->sounds[ i ] = trap_S_RegisterSound( va( "sound/player/%s/%s", dir, s + 1 ), qfalse );
- if( !ci->sounds[ i ] )
- ci->sounds[ i ] = trap_S_RegisterSound( va( "sound/player/%s/%s", fallback, s + 1 ), qfalse );
}
}
else
{
ci->sounds[ i ] = trap_S_RegisterSound( va( "sound/player/%s/%s", dir, s + 1 ), qfalse );
- if( !ci->sounds[ i ] )
- ci->sounds[ i ] = trap_S_RegisterSound( va( "sound/player/%s/%s", fallback, s + 1 ), qfalse );
}
}
@@ -649,15 +603,15 @@ static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to )
CG_GetCorpseNum
======================
*/
-static int CG_GetCorpseNum( pClass_t class )
+static int CG_GetCorpseNum( class_t class )
{
int i;
clientInfo_t *match;
char *modelName;
char *skinName;
- modelName = BG_FindModelNameForClass( class );
- skinName = BG_FindSkinNameForClass( class );
+ modelName = BG_ClassConfig( class )->modelName;
+ skinName = BG_ClassConfig( class )->skinName;
for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
{
@@ -666,10 +620,10 @@ static int CG_GetCorpseNum( pClass_t class )
if( !match->infoValid )
continue;
- if( !Q_stricmp( modelName, match->modelName )
- && !Q_stricmp( skinName, match->skinName ) )
+ if( !Q_stricmp( modelName, match->modelName ) &&
+ !Q_stricmp( skinName, match->skinName ) )
{
- // this clientinfo is identical, so use it's handles
+ // this clientinfo is identical, so use its handles
return i;
}
}
@@ -716,7 +670,7 @@ static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci )
CG_PrecacheClientInfo
======================
*/
-void CG_PrecacheClientInfo( pClass_t class, char *model, char *skin )
+void CG_PrecacheClientInfo( class_t class, char *model, char *skin )
{
clientInfo_t *ci;
clientInfo_t newInfo;
@@ -728,19 +682,12 @@ void CG_PrecacheClientInfo( pClass_t class, char *model, char *skin )
// model
Q_strncpyz( newInfo.modelName, model, sizeof( newInfo.modelName ) );
- Q_strncpyz( newInfo.headModelName, model, sizeof( newInfo.headModelName ) );
- // modelName didn not include a skin name
+ // modelName did not include a skin name
if( !skin )
- {
Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
- Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
- }
else
- {
Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) );
- Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) );
- }
newInfo.infoValid = qtrue;
@@ -749,6 +696,34 @@ void CG_PrecacheClientInfo( pClass_t class, char *model, char *skin )
CG_LoadClientInfo( ci );
}
+/*
+=============
+CG_StatusMessages
+
+Print messages for player status changes
+=============
+*/
+static void CG_StatusMessages( clientInfo_t *new, clientInfo_t *old )
+{
+ if( !old->infoValid )
+ return;
+
+ if( strcmp( new->name, old->name ) )
+ CG_Printf( "%s" S_COLOR_WHITE " renamed to %s\n", old->name, new->name );
+
+ if( old->team != new->team )
+ {
+ if( new->team == TEAM_NONE )
+ CG_Printf( "%s" S_COLOR_WHITE " left the %ss\n", new->name,
+ BG_TeamName( old->team ) );
+ else if( old->team == TEAM_NONE )
+ CG_Printf( "%s" S_COLOR_WHITE " joined the %ss\n", new->name,
+ BG_TeamName( new->team ) );
+ else
+ CG_Printf( "%s" S_COLOR_WHITE " left the %ss and joined the %ss\n",
+ new->name, BG_TeamName( old->team ), BG_TeamName( new->team ) );
+ }
+}
/*
======================
@@ -774,45 +749,35 @@ void CG_NewClientInfo( int clientNum )
// the old value
memset( &newInfo, 0, sizeof( newInfo ) );
-
+
// isolate the player's name
v = Info_ValueForKey( configstring, "n" );
Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
- // colors
- v = Info_ValueForKey( configstring, "c1" );
- CG_ColorFromString( v, newInfo.color1 );
-
- v = Info_ValueForKey( configstring, "c2" );
- CG_ColorFromString( v, newInfo.color2 );
-
- // bot skill
- v = Info_ValueForKey( configstring, "skill" );
- newInfo.botSkill = atoi( v );
-
- // handicap
- v = Info_ValueForKey( configstring, "hc" );
- newInfo.handicap = atoi( v );
-
- // wins
- v = Info_ValueForKey( configstring, "w" );
- newInfo.wins = atoi( v );
-
- // losses
- v = Info_ValueForKey( configstring, "l" );
- newInfo.losses = atoi( v );
-
// team
v = Info_ValueForKey( configstring, "t" );
newInfo.team = atoi( v );
- // team task
- v = Info_ValueForKey( configstring, "tt" );
- newInfo.teamTask = atoi( v );
+ // if this is us, execute team-specific config files
+ // the spectator config is a little unreliable because it's easy to get on
+ // to the spectator team without joining it - e.g. when a new game starts.
+ // It's not a big deal because the spec config is the least important
+ // slash used anyway.
+ // I guess it's possible for someone to change teams during a restart and
+ // for that to then be missed here. But that's rare enough that people can
+ // just exec the configs manually, I think.
+ if( clientNum == cg.clientNum && ci->infoValid &&
+ ci->team != newInfo.team )
+ {
+ char config[ MAX_CVAR_VALUE_STRING ];
- // team leader
- v = Info_ValueForKey( configstring, "tl" );
- newInfo.teamLeader = atoi( v );
+ trap_Cvar_VariableStringBuffer(
+ va( "cg_%sConfig", BG_TeamName( newInfo.team ) ),
+ config, sizeof( config ) );
+
+ if( config[ 0 ] )
+ trap_SendConsoleCommand( va( "exec \"%s\"\n", config ) );
+ }
// model
v = Info_ValueForKey( configstring, "model" );
@@ -832,25 +797,11 @@ void CG_NewClientInfo( int clientNum )
*slash = 0;
}
- //CG_Printf( "NCI: %s\n", v );
-
- // head model
- v = Info_ValueForKey( configstring, "hmodel" );
- Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) );
+ // voice
+ v = Info_ValueForKey( configstring, "v" );
+ Q_strncpyz( newInfo.voice, v, sizeof( newInfo.voice ) );
- slash = strchr( newInfo.headModelName, '/' );
-
- if( !slash )
- {
- // modelName didn not include a skin name
- Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
- }
- else
- {
- Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
- // truncate modelName
- *slash = 0;
- }
+ CG_StatusMessages( &newInfo, ci );
// replace whatever was there with the new one
newInfo.infoValid = qtrue;
@@ -909,90 +860,11 @@ cg.time should be between oldFrameTime and frameTime after exit
*/
static void CG_RunPlayerLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale )
{
- 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;
- }
-
// see if the animation sequence is switching
if( newAnimation != lf->animationNumber || !lf->animation )
- {
CG_SetLerpFrameAnimation( ci, lf, newAnimation );
- }
-
- // 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;
- f *= speedScale; // adjust for haste, etc
- 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 );
+ CG_RunLerpFrame( lf, speedScale );
}
@@ -1735,13 +1607,6 @@ static void CG_PlayerSprites( centity_t *cent )
CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
return;
}
-
- if( cent->currentState.eFlags & EF_TALK )
- {
- // the masses have decreed this to be wrong
-/* CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
- return;*/
- }
}
/*
@@ -1754,7 +1619,7 @@ Returns the Z component of the surface being shadowed
===============
*/
#define SHADOW_DISTANCE 128
-static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane, pClass_t class )
+static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane, class_t class )
{
vec3_t end, mins, maxs;
trace_t trace;
@@ -1762,7 +1627,7 @@ static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane, pClass_t c
entityState_t *es = &cent->currentState;
vec3_t surfNormal = { 0.0f, 0.0f, 1.0f };
- BG_FindBBoxForClass( class, mins, maxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( class, mins, maxs, NULL, NULL, NULL );
mins[ 2 ] = 0.0f;
maxs[ 2 ] = 2.0f;
@@ -1808,7 +1673,7 @@ static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane, pClass_t c
// without taking a spot in the cg_marks array
CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal,
cent->pe.legs.yawAngle, 0.0f, 0.0f, 0.0f, alpha, qfalse,
- 24.0f * BG_FindShadowScaleForClass( class ), qtrue );
+ 24.0f * BG_ClassConfig( class )->shadowScale, qtrue );
return qtrue;
}
@@ -1821,7 +1686,7 @@ CG_PlayerSplash
Draw a mark at the water surface
===============
*/
-static void CG_PlayerSplash( centity_t *cent, pClass_t class )
+static void CG_PlayerSplash( centity_t *cent, class_t class )
{
vec3_t start, end;
vec3_t mins, maxs;
@@ -1831,7 +1696,7 @@ static void CG_PlayerSplash( centity_t *cent, pClass_t class )
if( !cg_shadows.integer )
return;
- BG_FindBBoxForClass( class, mins, maxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( class, mins, maxs, NULL, NULL, NULL );
VectorCopy( cent->lerpOrigin, end );
end[ 2 ] += mins[ 2 ];
@@ -1861,7 +1726,7 @@ static void CG_PlayerSplash( centity_t *cent, pClass_t class )
CG_ImpactMark( cgs.media.wakeMarkShader, trace.endpos, trace.plane.normal,
cent->pe.legs.yawAngle, 1.0f, 1.0f, 1.0f, 1.0f, qfalse,
- 32.0f * BG_FindShadowScaleForClass( class ), qtrue );
+ 32.0f * BG_ClassConfig( class )->shadowScale, qtrue );
}
@@ -2012,7 +1877,7 @@ void CG_Player( centity_t *cent )
qboolean shadow = qfalse;
float shadowPlane = 0.0f;
entityState_t *es = &cent->currentState;
- pClass_t class = ( es->misc >> 8 ) & 0xFF;
+ class_t class = ( es->misc >> 8 ) & 0xFF;
float scale;
vec3_t tempAxis[ 3 ], tempAxis2[ 3 ];
vec3_t angles;
@@ -2051,7 +1916,7 @@ void CG_Player( centity_t *cent )
{
vec3_t mins, maxs;
- BG_FindBBoxForClass( class, mins, maxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( class, mins, maxs, NULL, NULL, NULL );
CG_DrawBoundingBox( cent->lerpOrigin, mins, maxs );
}
@@ -2150,7 +2015,7 @@ void CG_Player( centity_t *cent )
else
VectorCopy( es->angles2, surfNormal );
- BG_FindBBoxForClass( class, mins, maxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( class, mins, maxs, NULL, NULL, NULL );
VectorMA( legs.origin, -TRACE_DEPTH, surfNormal, end );
VectorMA( legs.origin, 1.0f, surfNormal, start );
@@ -2166,7 +2031,7 @@ void CG_Player( centity_t *cent )
}
//rescale the model
- scale = BG_FindModelScaleForClass( class );
+ scale = BG_ClassConfig( class )->modelScale;
if( scale != 1.0f )
{
@@ -2178,7 +2043,7 @@ void CG_Player( centity_t *cent )
}
//offset on the Z axis if required
- VectorMA( legs.origin, BG_FindZOffsetForClass( class ), surfNormal, legs.origin );
+ VectorMA( legs.origin, BG_ClassConfig( class )->zOffset, surfNormal, legs.origin );
VectorCopy( legs.origin, legs.lightingOrigin );
VectorCopy( legs.origin, legs.oldorigin ); // don't positionally lerp at all
@@ -2233,6 +2098,21 @@ void CG_Player( centity_t *cent )
head.renderfx = renderfx;
trap_R_AddRefEntityToScene( &head );
+
+ // if this player has been hit with poison cloud, add an effect PS
+ if( ( es->eFlags & EF_POISONCLOUDED ) &&
+ ( es->number != cg.snap->ps.clientNum || cg.renderingThirdPerson ) )
+ {
+ if( !CG_IsParticleSystemValid( &cent->poisonCloudedPS ) )
+ cent->poisonCloudedPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudedPS );
+
+ CG_SetAttachmentTag( &cent->poisonCloudedPS->attachment,
+ head, head.hModel, "tag_head" );
+ CG_SetAttachmentCent( &cent->poisonCloudedPS->attachment, cent );
+ CG_AttachToTag( &cent->poisonCloudedPS->attachment );
+ }
+ else if( CG_IsParticleSystemValid( &cent->poisonCloudedPS ) )
+ CG_DestroyParticleSystem( &cent->poisonCloudedPS );
}
//
@@ -2247,7 +2127,7 @@ void CG_Player( centity_t *cent )
}
CG_PlayerUpgrades( cent, &torso );
-
+
//sanity check that particle systems are stopped when dead
if( es->eFlags & EF_DEAD )
{
@@ -2277,7 +2157,7 @@ void CG_Corpse( centity_t *cent )
int renderfx;
qboolean shadow = qfalse;
float shadowPlane;
- vec3_t origin, liveZ, deadZ;
+ vec3_t origin, aliveZ, deadZ;
float scale;
corpseNum = CG_GetCorpseNum( es->clientNum );
@@ -2297,10 +2177,8 @@ void CG_Corpse( centity_t *cent )
memset( &head, 0, sizeof( head ) );
VectorCopy( cent->lerpOrigin, origin );
- BG_FindBBoxForClass( es->clientNum, liveZ, NULL, NULL, deadZ, NULL );
- origin[ 2 ] -= ( liveZ[ 2 ] - deadZ[ 2 ] );
-
- VectorCopy( es->angles, cent->lerpAngles );
+ BG_ClassBoundingBox( es->clientNum, aliveZ, NULL, NULL, deadZ, NULL );
+ origin[ 2 ] -= ( aliveZ[ 2 ] - deadZ[ 2 ] );
// get the rotation information
if( !ci->nonsegmented )
@@ -2364,11 +2242,11 @@ void CG_Corpse( centity_t *cent )
VectorCopy( origin, legs.lightingOrigin );
legs.shadowPlane = shadowPlane;
legs.renderfx = renderfx;
- legs.origin[ 2 ] += BG_FindZOffsetForClass( es->clientNum );
+ legs.origin[ 2 ] += BG_ClassConfig( es->clientNum )->zOffset;
VectorCopy( legs.origin, legs.oldorigin ); // don't positionally lerp at all
//rescale the model
- scale = BG_FindModelScaleForClass( es->clientNum );
+ scale = BG_ClassConfig( es->clientNum )->modelScale;
if( scale != 1.0f )
{
@@ -2379,7 +2257,6 @@ void CG_Corpse( centity_t *cent )
legs.nonNormalizedAxes = qtrue;
}
- //CG_AddRefEntityWithPowerups( &legs, es->misc, ci->team );
trap_R_AddRefEntityToScene( &legs );
// if the model failed, allow the default nullmodel to be displayed
@@ -2404,7 +2281,6 @@ void CG_Corpse( centity_t *cent )
torso.shadowPlane = shadowPlane;
torso.renderfx = renderfx;
- //CG_AddRefEntityWithPowerups( &torso, es->misc, ci->team );
trap_R_AddRefEntityToScene( &torso );
//
@@ -2423,7 +2299,6 @@ void CG_Corpse( centity_t *cent )
head.shadowPlane = shadowPlane;
head.renderfx = renderfx;
- //CG_AddRefEntityWithPowerups( &head, es->misc, ci->team );
trap_R_AddRefEntityToScene( &head );
}
}
@@ -2475,7 +2350,7 @@ void CG_ResetPlayerEntity( centity_t *cent )
cent->pe.nonseg.pitching = qfalse;
if( cg_debugPosition.integer )
- CG_Printf( "%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle );
+ CG_Printf( "%i ResetPlayerEntity yaw=%f\n", cent->currentState.number, cent->pe.torso.yawAngle );
}
/*
@@ -2500,66 +2375,35 @@ void CG_PlayerDisconnect( vec3_t org )
}
}
-/*
-=================
-CG_Bleed
-
-This is the spurt of blood when a character gets hit
-=================
-*/
-void CG_Bleed( vec3_t origin, vec3_t normal, int entityNum )
+centity_t *CG_GetPlayerLocation( void )
{
- pTeam_t team = cgs.clientinfo[ entityNum ].team;
- qhandle_t bleedPS;
- particleSystem_t *ps;
-
- if( !cg_blood.integer )
- return;
+ int i;
+ centity_t *eloc, *best;
+ float bestlen, len;
+ vec3_t origin;
- if( team == PTE_ALIENS )
- bleedPS = cgs.media.alienBleedPS;
- else if( team == PTE_HUMANS )
- bleedPS = cgs.media.humanBleedPS;
- else
- return;
+ best = NULL;
+ bestlen = 0.0f;
- ps = CG_SpawnNewParticleSystem( bleedPS );
+ VectorCopy( cg.predictedPlayerState.origin, origin );
- if( CG_IsParticleSystemValid( &ps ) )
+ for( i = MAX_CLIENTS; i < MAX_GENTITIES; i++ )
{
- CG_SetAttachmentPoint( &ps->attachment, origin );
- CG_SetAttachmentCent( &ps->attachment, &cg_entities[ entityNum ] );
- CG_AttachToPoint( &ps->attachment );
+ eloc = &cg_entities[ i ];
+ if( !eloc->valid || eloc->currentState.eType != ET_LOCATION )
+ continue;
- CG_SetParticleSystemNormal( ps, normal );
- }
-}
+ len = DistanceSquared(origin, eloc->lerpOrigin);
-/*
-===============
-CG_AtHighestClass
+ if( best != NULL && len > bestlen )
+ continue;
-Is the local client at the highest class possible?
-===============
-*/
-qboolean CG_AtHighestClass( void )
-{
- int i;
- qboolean superiorClasses = qfalse;
+ if( !trap_R_inPVS( origin, eloc->lerpOrigin ) )
+ continue;
- for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
- {
- if( BG_ClassCanEvolveFromTo(
- cg.predictedPlayerState.stats[ STAT_PCLASS ], i,
- ALIEN_MAX_KILLS, 0 ) >= 0 &&
- BG_FindStagesForClass( i, cgs.alienStage ) &&
- BG_ClassIsAllowed( i ) )
- {
- superiorClasses = qtrue;
- break;
- }
+ bestlen = len;
+ best = eloc;
}
- return !superiorClasses;
+ return best;
}
-
diff --git a/src/cgame/cg_playerstate.c b/src/cgame/cg_playerstate.c
index e1bcb09..8ff9be4 100644
--- a/src/cgame/cg_playerstate.c
+++ b/src/cgame/cg_playerstate.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -26,7 +27,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// 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"
/*
@@ -245,10 +245,8 @@ 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 ] )
+ // don't play the sounds if the player just spawned
+ if( ps->persistant[ PERS_SPECSTATE ] != ops->persistant[ PERS_SPECSTATE ] )
return;
// health changes of more than -1 should make pain sounds
@@ -257,14 +255,6 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops )
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;
}
@@ -301,7 +291,7 @@ void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops )
}
if( cg.snap->ps.pm_type != PM_INTERMISSION &&
- ps->persistant[ PERS_TEAM ] != TEAM_SPECTATOR )
+ ps->persistant[ PERS_SPECSTATE ] == SPECTATOR_NOT )
CG_CheckLocalSounds( ps, ops );
// run events
@@ -313,5 +303,11 @@ void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops )
cg.duckChange = ps->viewheight - ops->viewheight;
cg.duckTime = cg.time;
}
+
+ // changed team
+ if( ps->stats[ STAT_TEAM ] != ops->stats[ STAT_TEAM ] )
+ {
+ cg.lastHealthCross = 0;
+ cg.chargeMeterAlpha = 0.0f;
+ }
}
-
diff --git a/src/cgame/cg_predict.c b/src/cgame/cg_predict.c
index 03442ed..a791303 100644
--- a/src/cgame/cg_predict.c
+++ b/src/cgame/cg_predict.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -26,7 +27,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// 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;
@@ -138,7 +138,7 @@ static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins,
bmaxs[ 2 ] = zu;
if( i == cg_numSolidEntities )
- BG_FindBBoxForClass( ( ent->misc >> 8 ) & 0xFF, bmins, bmaxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( ( ent->misc >> 8 ) & 0xFF, bmins, bmaxs, NULL, NULL, NULL );
cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
VectorCopy( vec3_origin, angles );
@@ -273,7 +273,7 @@ int CG_PointContents( const vec3_t point, int passEntityNum )
if( !cmodel )
continue;
- contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
+ contents |= trap_CM_TransformedPointContents( point, cmodel, cent->lerpOrigin, cent->lerpAngles );
}
return contents;
@@ -476,13 +476,13 @@ static int CG_IsUnacceptableError( playerState_t *ps, playerState_t *pps )
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 )
+ fabs( AngleDelta( ps->viewangles[ 2 ], pps->viewangles[ 2 ] ) ) > 1.0f )
{
return 12;
}
if( pps->viewheight != ps->viewheight )
- return 13;
+ return 13;
if( pps->damageEvent != ps->damageEvent ||
pps->damageYaw != ps->damageYaw ||
@@ -504,13 +504,6 @@ static int CG_IsUnacceptableError( playerState_t *ps, playerState_t *pps )
return 16;
}
- for( i = 0; i < MAX_WEAPONS; i++ )
- {
- // GH FIXME
- if( pps->ammo != ps->ammo || pps->clips != ps->clips )
- return 18;
- }
-
if( pps->generic1 != ps->generic1 ||
pps->loopSound != ps->loopSound )
{
@@ -551,7 +544,6 @@ void CG_PredictPlayerState( void )
{
int cmdNum, current, i;
playerState_t oldPlayerState;
- qboolean moved;
usercmd_t oldestCmd;
usercmd_t latestCmd;
int stateIndex = 0, predictCmd = 0;
@@ -590,12 +582,12 @@ void CG_PredictPlayerState( void )
cg_pmove.debugLevel = cg_debugMove.integer;
if( cg_pmove.ps->pm_type == PM_DEAD )
- cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
+ cg_pmove.tracemask = MASK_DEADSOLID;
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
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
+ cg_pmove.tracemask = MASK_DEADSOLID; // spectators can fly through bodies
cg_pmove.noFootsteps = 0;
@@ -638,9 +630,15 @@ void CG_PredictPlayerState( void )
}
if( pmove_msec.integer < 8 )
+ {
trap_Cvar_Set( "pmove_msec", "8" );
+ trap_Cvar_Update(&pmove_msec);
+ }
else if( pmove_msec.integer > 33 )
+ {
trap_Cvar_Set( "pmove_msec", "33" );
+ trap_Cvar_Update(&pmove_msec);
+ }
cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
cg_pmove.pmove_msec = pmove_msec.integer;
@@ -700,22 +698,22 @@ void CG_PredictPlayerState( void )
// 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;
@@ -737,9 +735,6 @@ void CG_PredictPlayerState( void )
stateIndex = cg.stateHead;
}
- // run cmds
- moved = qfalse;
-
for( cmdNum = current - CMD_BACKUP + 1; cmdNum <= current; cmdNum++ )
{
// get the command
@@ -857,8 +852,6 @@ void CG_PredictPlayerState( void )
stateIndex = ( stateIndex + 1 ) % NUM_SAVED_STATES;
}
- moved = qtrue;
-
// add push trigger movement effects
CG_TouchTriggerPrediction( );
diff --git a/src/cgame/cg_ptr.c b/src/cgame/cg_ptr.c
deleted file mode 100644
index 1881087..0000000
--- a/src/cgame/cg_ptr.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-===========================================================================
-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 );
-}
diff --git a/src/cgame/cg_public.h b/src/cgame/cg_public.h
index 543a222..eef8d40 100644
--- a/src/cgame/cg_public.h
+++ b/src/cgame/cg_public.h
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,51 +17,47 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
+#ifndef CGAME_PUBLIC_H
+#define CGAME_PUBLIC_H
+
+#include "qcommon/q_shared.h"
-#define CMD_BACKUP 64
-#define CMD_MASK (CMD_BACKUP - 1)
+#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
+#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;
+typedef struct {
+ int snapFlags; // SNAPFLAG_RATE_DELAYED, etc
+ int ping;
- int serverTime; // server time the message is valid for (in msec)
+ int serverTime; // server time the message is valid for (in msec)
- byte areamask[ MAX_MAP_AREA_BYTES ]; // portalarea visibility bits
+ byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
- playerState_t ps; // complete information about the current player at this time
+ 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 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
+ 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
-};
+enum { CGAME_EVENT_NONE, CGAME_EVENT_TEAMMENU, CGAME_EVENT_SCOREBOARD, CGAME_EVENT_EDITHUD };
/*
==================================================================
@@ -70,147 +67,146 @@ 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,
+#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,
#ifndef MODULE_INTERFACE_11
- CG_R_SETCLIPREGION,
+ CG_R_SETCLIPREGION,
#endif
- 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_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,
#ifdef MODULE_INTERFACE_11
- CG_PARSE_ADD_GLOBAL_DEFINE,
- CG_PARSE_LOAD_SOURCE,
- CG_PARSE_FREE_SOURCE,
- CG_PARSE_READ_TOKEN,
- CG_PARSE_SOURCE_FILE_AND_LINE,
+ CG_PARSE_ADD_GLOBAL_DEFINE,
+ CG_PARSE_LOAD_SOURCE,
+ CG_PARSE_FREE_SOURCE,
+ CG_PARSE_READ_TOKEN,
+ CG_PARSE_SOURCE_FILE_AND_LINE,
#endif
- 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_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,
#ifndef MODULE_INTERFACE_11
- CG_PARSE_ADD_GLOBAL_DEFINE,
- CG_PARSE_LOAD_SOURCE,
- CG_PARSE_FREE_SOURCE,
- CG_PARSE_READ_TOKEN,
- CG_PARSE_SOURCE_FILE_AND_LINE,
+ CG_PARSE_ADD_GLOBAL_DEFINE,
+ CG_PARSE_LOAD_SOURCE,
+ CG_PARSE_FREE_SOURCE,
+ CG_PARSE_READ_TOKEN,
+ CG_PARSE_SOURCE_FILE_AND_LINE,
- CG_KEY_SETOVERSTRIKEMODE,
- CG_KEY_GETOVERSTRIKEMODE,
+ CG_KEY_SETOVERSTRIKEMODE,
+ CG_KEY_GETOVERSTRIKEMODE,
- CG_S_SOUNDDURATION,
+ CG_S_SOUNDDURATION,
+ CG_FIELD_COMPLETELIST,
#endif
- 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
+ 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;
-
/*
==================================================================
@@ -219,55 +215,62 @@ 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
-
- CG_VOIP_STRING
- // char *(*CG_VoIPString)( void );
- // returns a string of comma-delimited clientnums based on cl_voipSendTarget
+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
+
+ CG_VOIP_STRING,
+ // char *(*CG_VoIPString)( void );
+ // returns a string of comma-delimited clientnums based on cl_voipSendTarget
+
+ CG_CONSOLE_COMPLETARGUMENT
+ // qboolean (*CG_Console_CompleteArgument)( int argNum )
+ // Requests CGAME to try to complete the command line
+ // argument. argNum indicates which argument we're completing. CGAME
+ // uses trap_Argv and trap_Argc to read the command line
+ // contents. Returns true if a completion function is found in
+ // CGAME, otherwise client tries another completion method.
} cgameExport_t;
-//----------------------------------------------
+#endif
diff --git a/src/cgame/cg_rangemarker.c b/src/cgame/cg_rangemarker.c
new file mode 100644
index 0000000..2810b90
--- /dev/null
+++ b/src/cgame/cg_rangemarker.c
@@ -0,0 +1,399 @@
+#include "cg_local.h"
+
+const vec3_t cg_shaderColors[ SHC_NUM_SHADER_COLORS ] =
+{
+ { 0.0f, 0.0f, 0.75f }, // dark blue
+ { 0.3f, 0.35f, 0.625f }, // light blue
+ { 0.0f, 0.625f, 0.563f }, // green-cyan
+ { 0.313f, 0.0f, 0.625f }, // violet
+ { 0.625f, 0.625f, 0.0f }, // yellow
+ { 0.875f, 0.313f, 0.0f }, // orange
+ { 0.375f, 0.625f, 0.375f }, // light green
+ { 0.0f, 0.438f, 0.0f }, // dark green
+ { 1.0f, 0.0f, 0.0f }, // red
+ { 0.625f, 0.375f, 0.4f }, // pink
+ { 0.313f, 0.313f, 0.313f } // grey
+};
+
+/*
+================
+CG_RangeMarkerPreferences
+================
+*/
+qboolean CG_GetRangeMarkerPreferences( qboolean *drawSurface, qboolean *drawIntersection,
+ qboolean *drawFrontline, float *surfaceOpacity,
+ float *lineOpacity, float *lineThickness )
+{
+ *drawSurface = !!cg_rangeMarkerDrawSurface.integer;
+ *drawIntersection = !!cg_rangeMarkerDrawIntersection.integer;
+ *drawFrontline = !!cg_rangeMarkerDrawFrontline.integer;
+ *surfaceOpacity = cg_rangeMarkerSurfaceOpacity.value;
+ *lineOpacity = cg_rangeMarkerLineOpacity.value;
+ *lineThickness = cg_rangeMarkerLineThickness.value;
+
+ if( ( *drawSurface && *surfaceOpacity > 0.0f ) ||
+ ( ( *drawIntersection || *drawFrontline ) && *lineOpacity > 0.0f &&
+ *lineThickness > 0.0f && cg_binaryShaderScreenScale.value > 0.0f ) )
+ {
+ if( *surfaceOpacity > 1.0f )
+ *surfaceOpacity = 1.0f;
+ if( *lineOpacity > 1.0f )
+ *lineOpacity = 1.0f;
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+================
+CG_UpdateBuildableRangeMarkerMask
+================
+*/
+void CG_UpdateBuildableRangeMarkerMask( void )
+{
+ static int mc = 0;
+
+ if( cg_rangeMarkerBuildableTypes.modificationCount != mc )
+ {
+ int brmMask;
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ char *p, *q;
+ buildable_t buildable;
+
+ brmMask = 0;
+
+ if( !cg_rangeMarkerBuildableTypes.string[ 0 ] )
+ goto empty;
+
+ Q_strncpyz( buffer, cg_rangeMarkerBuildableTypes.string, sizeof( buffer ) );
+ p = &buffer[ 0 ];
+
+ for(;;)
+ {
+ q = strchr( p, ',' );
+ if( q )
+ *q = '\0';
+
+ while( *p == ' ' )
+ ++p;
+
+ buildable = BG_BuildableByName( p )->number;
+
+ if( buildable != BA_NONE )
+ {
+ brmMask |= 1 << buildable;
+ }
+ else if( !Q_stricmp( p, "all" ) )
+ {
+ brmMask |= ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE ) |
+ ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN );
+ }
+ else
+ {
+ char *pp;
+ int only;
+
+ if( !Q_stricmpn( p, "alien", 5 ) )
+ {
+ pp = p + 5;
+ only = ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE );
+ }
+ else if( !Q_stricmpn( p, "human", 5 ) )
+ {
+ pp = p + 5;
+ only = ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN );
+ }
+ else
+ {
+ pp = p;
+ only = ~0;
+ }
+
+ if( pp != p && !*pp )
+ {
+ brmMask |= only;
+ }
+ else if( !Q_stricmp( pp, "support" ) )
+ {
+ brmMask |= only & ( ( 1 << BA_A_OVERMIND ) | ( 1 << BA_A_SPAWN ) |
+ ( 1 << BA_H_REACTOR ) | ( 1 << BA_H_REPEATER ) | ( 1 << BA_H_DCC ) );
+ }
+ else if( !Q_stricmp( pp, "offensive" ) )
+ {
+ brmMask |= only & ( ( 1 << BA_A_ACIDTUBE ) | ( 1 << BA_A_TRAPPER ) | ( 1 << BA_A_HIVE ) |
+ ( 1 << BA_H_MGTURRET ) | ( 1 << BA_H_TESLAGEN ) );
+ }
+ else
+ Com_Printf( S_COLOR_YELLOW "WARNING: unknown buildable or group: %s\n", p );
+ }
+
+ if( q )
+ p = q + 1;
+ else
+ break;
+ }
+
+ empty:
+ trap_Cvar_Set( "cg_buildableRangeMarkerMask", va( "%i", brmMask ) );
+
+ mc = cg_rangeMarkerBuildableTypes.modificationCount;
+ }
+}
+
+// cg_drawtools.c
+//
+/*
+================
+CG_DrawSphere
+================
+*/
+void CG_DrawSphere( const vec3_t center, float radius, int customShader, const float *shaderRGBA )
+{
+ refEntity_t re;
+ memset( &re, 0, sizeof( re ) );
+
+ re.reType = RT_MODEL;
+ re.hModel = cgs.media.sphereModel;
+ re.customShader = customShader;
+ re.renderfx = RF_NOSHADOW;
+ if( shaderRGBA != NULL )
+ {
+ int i;
+ for( i = 0; i < 4; ++i )
+ re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ];
+ }
+
+ VectorCopy( center, re.origin );
+
+ radius *= 0.01f;
+ VectorSet( re.axis[ 0 ], radius, 0, 0 );
+ VectorSet( re.axis[ 1 ], 0, radius, 0 );
+ VectorSet( re.axis[ 2 ], 0, 0, radius );
+ re.nonNormalizedAxes = qtrue;
+
+ trap_R_AddRefEntityToScene( &re );
+}
+
+/*
+================
+CG_DrawSphericalCone
+================
+*/
+void CG_DrawSphericalCone( const vec3_t tip, const vec3_t rotation, float radius,
+ qboolean a240, int customShader, const float *shaderRGBA )
+{
+ refEntity_t re;
+ memset( &re, 0, sizeof( re ) );
+
+ re.reType = RT_MODEL;
+ re.hModel = a240 ? cgs.media.sphericalCone240Model : cgs.media.sphericalCone64Model;
+ re.customShader = customShader;
+ re.renderfx = RF_NOSHADOW;
+ if( shaderRGBA != NULL )
+ {
+ int i;
+ for( i = 0; i < 4; ++i )
+ re.shaderRGBA[ i ] = 255 * shaderRGBA[ i ];
+ }
+
+ VectorCopy( tip, re.origin );
+
+ radius *= 0.01f;
+ AnglesToAxis( rotation, re.axis );
+ VectorScale( re.axis[ 0 ], radius, re.axis[ 0 ] );
+ VectorScale( re.axis[ 1 ], radius, re.axis[ 1 ] );
+ VectorScale( re.axis[ 2 ], radius, re.axis[ 2 ] );
+ re.nonNormalizedAxes = qtrue;
+
+ trap_R_AddRefEntityToScene( &re );
+}
+
+
+/*
+================
+CG_DrawRangeMarker
+================
+*/
+void CG_DrawRangeMarker( rangeMarkerType_t rmType, const vec3_t origin, const float *angles, float range,
+ qboolean drawSurface, qboolean drawIntersection, qboolean drawFrontline,
+ const vec3_t rgb, float surfaceOpacity, float lineOpacity, float lineThickness )
+{
+ if( drawSurface )
+ {
+ qhandle_t pcsh;
+ vec4_t rgba;
+
+ pcsh = cgs.media.plainColorShader;
+ VectorCopy( rgb, rgba );
+ rgba[ 3 ] = surfaceOpacity;
+
+ switch( rmType )
+ {
+ case RMT_SPHERE:
+ CG_DrawSphere( origin, range, pcsh, rgba );
+ break;
+ case RMT_SPHERICAL_CONE_64:
+ CG_DrawSphericalCone( origin, angles, range, qfalse, pcsh, rgba );
+ break;
+ case RMT_SPHERICAL_CONE_240:
+ CG_DrawSphericalCone( origin, angles, range, qtrue, pcsh, rgba );
+ break;
+ }
+ }
+
+ if( drawIntersection || drawFrontline )
+ {
+ const cgMediaBinaryShader_t *mbsh;
+ cgBinaryShaderSetting_t *bshs;
+ int i;
+
+ if( cg.numBinaryShadersUsed >= NUM_BINARY_SHADERS )
+ return;
+ mbsh = &cgs.media.binaryShaders[ cg.numBinaryShadersUsed ];
+
+ if( rmType == RMT_SPHERE )
+ {
+ if( range > lineThickness / 2 )
+ {
+ if( drawIntersection )
+ CG_DrawSphere( origin, range - lineThickness / 2, mbsh->b1, NULL );
+ CG_DrawSphere( origin, range - lineThickness / 2, mbsh->f2, NULL );
+ }
+
+ if( drawIntersection )
+ CG_DrawSphere( origin, range + lineThickness / 2, mbsh->b2, NULL );
+ CG_DrawSphere( origin, range + lineThickness / 2, mbsh->f1, NULL );
+ }
+ else if( rmType == RMT_SPHERICAL_CONE_64 || rmType == RMT_SPHERICAL_CONE_240 )
+ {
+ qboolean a240;
+ float f, r;
+ vec3_t forward, tip;
+
+ a240 = ( rmType == RMT_SPHERICAL_CONE_240 );
+ f = lineThickness * ( a240 ? 0.26f : 0.8f );
+ r = f + lineThickness * ( a240 ? 0.23f : 0.43f );
+ AngleVectors( angles, forward, NULL, NULL );
+
+ if( range > r )
+ {
+ VectorMA( origin, f, forward, tip );
+ if( drawIntersection )
+ CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->b1, NULL );
+ CG_DrawSphericalCone( tip, angles, range - r, a240, mbsh->f2, NULL );
+ }
+
+ VectorMA( origin, -f, forward, tip );
+ if( drawIntersection )
+ CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->b2, NULL );
+ CG_DrawSphericalCone( tip, angles, range + r, a240, mbsh->f1, NULL );
+ }
+
+ bshs = &cg.binaryShaderSettings[ cg.numBinaryShadersUsed ];
+
+ for( i = 0; i < 3; ++i )
+ bshs->color[ i ] = 255 * lineOpacity * rgb[ i ];
+ bshs->drawIntersection = drawIntersection;
+ bshs->drawFrontline = drawFrontline;
+
+ ++cg.numBinaryShadersUsed;
+ }
+}
+
+// cg_buildable.c
+
+/*
+================
+CG_BuildableRangeMarkerProperties
+================
+*/
+qboolean CG_GetBuildableRangeMarkerProperties( buildable_t bType, rangeMarkerType_t *rmType, float *range, vec3_t rgb )
+{
+ shaderColorEnum_t shc;
+
+ switch( bType )
+ {
+ case BA_A_SPAWN: *range = CREEP_BASESIZE; shc = SHC_LIGHT_GREEN; break;
+ case BA_A_OVERMIND: *range = CREEP_BASESIZE; shc = SHC_DARK_GREEN; break;
+ case BA_A_ACIDTUBE: *range = ACIDTUBE_RANGE; shc = SHC_RED; break;
+ case BA_A_TRAPPER: *range = TRAPPER_RANGE; shc = SHC_PINK; break;
+ case BA_A_HIVE: *range = HIVE_SENSE_RANGE; shc = SHC_YELLOW; break;
+ case BA_H_MGTURRET: *range = MGTURRET_RANGE; shc = SHC_ORANGE; break;
+ case BA_H_TESLAGEN: *range = TESLAGEN_RANGE; shc = SHC_VIOLET; break;
+ case BA_H_DCC: *range = DC_RANGE; shc = SHC_GREEN_CYAN; break;
+ case BA_H_REACTOR: *range = REACTOR_BASESIZE; shc = SHC_DARK_BLUE; break;
+ case BA_H_REPEATER: *range = REPEATER_BASESIZE; shc = SHC_LIGHT_BLUE; break;
+ default: return qfalse;
+ }
+
+ if( bType == BA_A_TRAPPER )
+ *rmType = RMT_SPHERICAL_CONE_64;
+ else if( bType == BA_H_MGTURRET )
+ *rmType = RMT_SPHERICAL_CONE_240;
+ else
+ *rmType = RMT_SPHERE;
+
+ VectorCopy( cg_shaderColors[ shc ], rgb );
+
+ return qtrue;
+}
+
+/*
+================
+CG_GhostBuildableRangeMarker
+================
+*/
+void CG_GhostBuildableRangeMarker( buildable_t buildable, const vec3_t origin, const vec3_t normal )
+{
+ qboolean drawS, drawI, drawF;
+ float so, lo, th;
+ rangeMarkerType_t rmType;
+ float range;
+ vec3_t rgb;
+
+ if( CG_GetRangeMarkerPreferences( &drawS, &drawI, &drawF, &so, &lo, &th ) &&
+ CG_GetBuildableRangeMarkerProperties( buildable, &rmType, &range, rgb ) )
+ {
+ vec3_t localOrigin, angles;
+
+ if( buildable == BA_A_HIVE || buildable == BA_H_TESLAGEN )
+ VectorMA( origin, BG_BuildableConfig( buildable )->maxs[ 2 ], normal, localOrigin );
+ else
+ VectorCopy( origin, localOrigin );
+
+ if( rmType != RMT_SPHERE )
+ vectoangles( normal, angles );
+
+ CG_DrawRangeMarker( rmType, localOrigin, ( rmType != RMT_SPHERE ? angles : NULL ),
+ range, drawS, drawI, drawF, rgb, so, lo, th );
+ }
+}
+
+// cg_ents.c
+
+/*
+================
+CG_RangeMarker
+================
+*/
+void CG_RangeMarker( centity_t *cent )
+{
+ qboolean drawS, drawI, drawF;
+ float so, lo, th;
+ rangeMarkerType_t rmType;
+ float range;
+ vec3_t rgb;
+
+ if( CG_GetRangeMarkerPreferences( &drawS, &drawI, &drawF, &so, &lo, &th ) &&
+ CG_GetBuildableRangeMarkerProperties( cent->currentState.modelindex, &rmType, &range, rgb ) )
+ {
+ CG_DrawRangeMarker( rmType, cent->lerpOrigin, ( rmType > 0 ? cent->lerpAngles : NULL ),
+ range, drawS, drawI, drawF, rgb, so, lo, th );
+ }
+}
+
diff --git a/src/cgame/cg_scanner.c b/src/cgame/cg_scanner.c
index 033960e..a7df030 100644
--- a/src/cgame/cg_scanner.c
+++ b/src/cgame/cg_scanner.c
@@ -1,12 +1,13 @@
/*
===========================================================================
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -15,15 +16,14 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
-
#include "cg_local.h"
-static entityPos_t entityPositions;
+static entityPos_t entityPositions;
#define HUMAN_SCANNER_UPDATE_PERIOD 700
@@ -39,7 +39,7 @@ void CG_UpdateEntityPositions( void )
centity_t *cent = NULL;
int i;
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
if( entityPositions.lastUpdateTime + HUMAN_SCANNER_UPDATE_PERIOD > cg.time )
return;
@@ -58,10 +58,11 @@ void CG_UpdateEntityPositions( void )
{
cent = &cg_entities[ cg.snap->entities[ i ].number ];
- if( cent->currentState.eType == ET_BUILDABLE )
+ if( cent->currentState.eType == ET_BUILDABLE &&
+ !( cent->currentState.eFlags & EF_DEAD ) )
{
- //TA: add to list of item positions (for creep)
- if( cent->currentState.modelindex2 == BIT_ALIENS )
+ // add to list of item positions (for creep)
+ if( cent->currentState.modelindex2 == TEAM_ALIENS )
{
VectorCopy( cent->lerpOrigin, entityPositions.alienBuildablePos[
entityPositions.numAlienBuildables ] );
@@ -71,7 +72,7 @@ void CG_UpdateEntityPositions( void )
if( entityPositions.numAlienBuildables < MAX_GENTITIES )
entityPositions.numAlienBuildables++;
}
- else if( cent->currentState.modelindex2 == BIT_HUMANS )
+ else if( cent->currentState.modelindex2 == TEAM_HUMANS )
{
VectorCopy( cent->lerpOrigin, entityPositions.humanBuildablePos[
entityPositions.numHumanBuildables ] );
@@ -84,7 +85,7 @@ void CG_UpdateEntityPositions( void )
{
int team = cent->currentState.misc & 0x00FF;
- if( team == PTE_ALIENS )
+ if( team == TEAM_ALIENS )
{
VectorCopy( cent->lerpOrigin, entityPositions.alienClientPos[
entityPositions.numAlienClients ] );
@@ -92,7 +93,7 @@ void CG_UpdateEntityPositions( void )
if( entityPositions.numAlienClients < MAX_CLIENTS )
entityPositions.numAlienClients++;
}
- else if( team == PTE_HUMANS )
+ else if( team == TEAM_HUMANS )
{
VectorCopy( cent->lerpOrigin, entityPositions.humanClientPos[
entityPositions.numHumanClients ] );
@@ -104,8 +105,8 @@ void CG_UpdateEntityPositions( void )
}
}
-#define STALKWIDTH 2.0f
-#define BLIPX 16.0f
+#define STALKWIDTH (2.0f * cgDC.aspectScale)
+#define BLIPX (16.0f * cgDC.aspectScale)
#define BLIPY 8.0f
#define FAR_ALPHA 0.8f
#define NEAR_ALPHA 1.2f
@@ -162,7 +163,7 @@ static void CG_DrawBlips( rectDef_t *rect, vec3_t origin, vec4_t colour )
trap_R_SetColor( NULL );
}
-#define BLIPX2 24.0f
+#define BLIPX2 (24.0f * cgDC.aspectScale)
#define BLIPY2 24.0f
/*
@@ -183,15 +184,7 @@ static void CG_DrawDir( rectDef_t *rect, vec3_t origin, vec4_t colour )
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 );
+ BG_GetClientNormal( ps, normal );
AngleVectors( entityPositions.vangles, view, NULL, NULL );
@@ -363,3 +356,125 @@ void CG_Scanner( rectDef_t *rect, qhandle_t shader, vec4_t color )
CG_DrawBlips( rect, relOrigin, aIabove );
}
}
+
+void THZ_DrawScanner( rectDef_t *rect )
+{
+ vec4_t colorB = { 0.0f, 0.0f, 0.0f, 0.5f };
+ vec4_t color = { 1.0f, 1.0f, 1.0f, 0.2f };
+
+ vec4_t aliencolor = { 1.0f, 0.0f, 0.0f, 0.8f };
+ vec4_t humancolor = { 0.0f, 0.0f, 1.0f, 0.8f };
+ vec4_t buildcolor = { 0.0f, 1.0f, 1.0f, 0.8f };
+ vec4_t buildcolor2 = { 1.0f, 1.0f, 0.0f, 0.8f };
+
+ vec3_t drawOrigin = { 0.0f, 0.0f, 0.0f };
+ vec3_t origin = { 0.0f, 0.0f, 0.0f };
+ vec3_t relOrigin = { 0.0f, 0.0f, 0.0f };
+
+ static vec3_t up = { 0.0f, 0.0f, 1.0f };
+
+ int i;
+
+ if( !thz_radar.integer )
+ return;
+
+ //CG_FillRect( rect->x, rect->y, rect->w, rect->h, colorB );
+
+ // Draw cross
+ CG_FillRect( rect->x + (rect->w/2),
+ rect->y,
+ 1,
+ rect->h,
+ color );
+ CG_FillRect( rect->x,
+ rect->y+(rect->h/2),
+ rect->w,
+ 1,
+ color );
+
+ // update the player positions
+ CG_UpdateEntityPositions( );
+
+ // blips
+ VectorCopy( entityPositions.origin, origin );
+
+ // human buildables
+ for( i = 0; i < entityPositions.numHumanBuildables; i++ )
+ {
+ VectorClear( relOrigin );
+ VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );
+
+ if( VectorLength( relOrigin ) < thz_radarrange.integer )
+ {
+ RotatePointAroundVector( drawOrigin, up, relOrigin, -entityPositions.vangles[ 1 ] - 90 );
+
+ drawOrigin[ 0 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+ drawOrigin[ 1 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->h);
+ drawOrigin[ 2 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+
+ CG_FillRect( rect->x + (rect->w / 2) + -drawOrigin[0] - 5,
+ rect->y + (rect->h / 2) + drawOrigin[1] - 5,
+ 10, 10, buildcolor );
+ }
+ }
+
+ // humans
+ for( i = 0; i < entityPositions.numHumanClients; i++ )
+ {
+ VectorClear( relOrigin );
+ VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );
+
+ if( VectorLength( relOrigin ) < thz_radarrange.integer )
+ {
+ RotatePointAroundVector( drawOrigin, up, relOrigin, -entityPositions.vangles[ 1 ] - 90 );
+
+ drawOrigin[ 0 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+ drawOrigin[ 1 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->h);
+ drawOrigin[ 2 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+
+ CG_FillRect( rect->x + (rect->w / 2) + -drawOrigin[0] -3,
+ rect->y + (rect->h / 2) + drawOrigin[1] -3,
+ 6, 6, humancolor );
+ }
+ }
+
+ // alien structures
+ for( i = 0; i < entityPositions.numAlienBuildables; i++ )
+ {
+ VectorClear( relOrigin );
+ VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin );
+
+ if( VectorLength( relOrigin ) < thz_radarrange.integer )
+ {
+ RotatePointAroundVector( drawOrigin, up, relOrigin, -entityPositions.vangles[ 1 ] - 90 );
+
+ drawOrigin[ 0 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+ drawOrigin[ 1 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->h);
+ drawOrigin[ 2 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+
+ CG_FillRect( rect->x + (rect->w / 2) + -drawOrigin[0] - 5,
+ rect->y + (rect->h / 2) + drawOrigin[1] - 5,
+ 10, 10, buildcolor2 );
+ }
+ }
+
+ // aliens
+ for( i = 0; i < entityPositions.numAlienClients; i++ )
+ {
+ VectorClear( relOrigin );
+ VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin );
+
+ if( VectorLength( relOrigin ) < thz_radarrange.integer )
+ {
+ RotatePointAroundVector( drawOrigin, up, relOrigin, -entityPositions.vangles[ 1 ] - 90 );
+
+ drawOrigin[ 0 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+ drawOrigin[ 1 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->h);
+ drawOrigin[ 2 ] /= ((float)(1.25f * (float)thz_radarrange.integer) / (float)rect->w);
+
+ CG_FillRect( rect->x + (rect->w / 2) + -drawOrigin[0] -3,
+ rect->y + (rect->h / 2) + drawOrigin[1] -3,
+ 6, 6, aliencolor );
+ }
+ }
+}
diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c
index 7fb3e06..4de7586 100644
--- a/src/cgame/cg_servercmds.c
+++ b/src/cgame/cg_servercmds.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -25,7 +26,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// these are processed at snapshot transition time, so there will definately
// be a valid snapshot this frame
-
#include "cg_local.h"
/*
@@ -38,13 +38,13 @@ static void CG_ParseScores( void )
{
int i;
- cg.numScores = atoi( CG_Argv( 1 ) );
+ cg.numScores = ( trap_Argc( ) - 3 ) / 6;
if( cg.numScores > MAX_CLIENTS )
cg.numScores = MAX_CLIENTS;
- cg.teamScores[ 0 ] = atoi( CG_Argv( 2 ) );
- cg.teamScores[ 1 ] = atoi( CG_Argv( 3 ) );
+ cg.teamScores[ 0 ] = atoi( CG_Argv( 1 ) );
+ cg.teamScores[ 1 ] = atoi( CG_Argv( 2 ) );
memset( cg.scores, 0, sizeof( cg.scores ) );
@@ -54,18 +54,17 @@ static void CG_ParseScores( void )
for( i = 0; i < cg.numScores; i++ )
{
//
- cg.scores[ i ].client = atoi( CG_Argv( i * 6 + 4 ) );
- cg.scores[ i ].score = atoi( CG_Argv( i * 6 + 5 ) );
- cg.scores[ i ].ping = atoi( CG_Argv( i * 6 + 6 ) );
- cg.scores[ i ].time = atoi( CG_Argv( i * 6 + 7 ) );
- cg.scores[ i ].weapon = atoi( CG_Argv( i * 6 + 8 ) );
- cg.scores[ i ].upgrade = atoi( CG_Argv( i * 6 + 9 ) );
+ cg.scores[ i ].client = atoi( CG_Argv( i * 6 + 3 ) );
+ cg.scores[ i ].score = atoi( CG_Argv( i * 6 + 4 ) );
+ cg.scores[ i ].ping = atoi( CG_Argv( i * 6 + 5 ) );
+ cg.scores[ i ].time = atoi( CG_Argv( i * 6 + 6 ) );
+ cg.scores[ i ].weapon = atoi( CG_Argv( i * 6 + 7 ) );
+ cg.scores[ i ].upgrade = atoi( CG_Argv( i * 6 + 8 ) );
if( cg.scores[ i ].client < 0 || cg.scores[ i ].client >= MAX_CLIENTS )
cg.scores[ i ].client = 0;
cgs.clientinfo[ cg.scores[ i ].client ].score = cg.scores[ i ].score;
- cgs.clientinfo[ cg.scores[ i ].client ].powerups = 0;
cg.scores[ i ].team = cgs.clientinfo[ cg.scores[ i ].client ].team;
}
@@ -80,22 +79,33 @@ CG_ParseTeamInfo
static void CG_ParseTeamInfo( void )
{
int i;
+ int count;
int client;
- numSortedTeamPlayers = atoi( CG_Argv( 1 ) );
+ count = trap_Argc( );
- for( i = 0; i < numSortedTeamPlayers; i++ )
+ for( i = 1; i < count; i++ ) // i is also incremented when writing into cgs.clientinfo
{
- client = atoi( CG_Argv( i * 6 + 2 ) );
+ client = atoi( CG_Argv( i ) );
+
+ // wrong team? drop the remaining info
+ if( cgs.clientinfo[ client ].team != cg.snap->ps.stats[ STAT_TEAM ] )
+ return;
- sortedTeamPlayers[ i ] = client;
+ if( client < 0 || client >= MAX_CLIENTS )
+ {
+ CG_Printf( "[skipnotify]CG_ParseTeamInfo: bad client number: %d\n", client );
+ return;
+ }
- cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) );
- cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) );
- cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) );
- cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) );
- cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) );
+ cgs.clientinfo[ client ].location = atoi( CG_Argv( ++i ) );
+ cgs.clientinfo[ client ].health = atoi( CG_Argv( ++i ) );
+ cgs.clientinfo[ client ].curWeaponClass = atoi( CG_Argv( ++i ) );
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ cgs.clientinfo[ client ].upgrade = atoi( CG_Argv( ++i ) );
}
+
+ cgs.teaminfoReceievedTime = cg.time;
}
@@ -133,13 +143,7 @@ static void CG_ParseWarmup( void )
info = CG_ConfigString( CS_WARMUP );
warmup = atoi( info );
- cg.warmupCount = -1;
-
- if( warmup == 0 && cg.warmup )
- {
- }
-
- cg.warmup = warmup;
+ cg.warmupTime = warmup;
}
/*
@@ -151,19 +155,28 @@ Called on load to set the initial values from configure strings
*/
void CG_SetConfigValues( void )
{
- sscanf( CG_ConfigString( CS_BUILDPOINTS ),
- "%d %d %d %d %d", &cgs.alienBuildPoints,
- &cgs.alienBuildPointsTotal,
- &cgs.humanBuildPoints,
- &cgs.humanBuildPointsTotal,
- &cgs.humanBuildPointsPowered );
+ const char *alienStages = CG_ConfigString( CS_ALIEN_STAGES );
+ const char *humanStages = CG_ConfigString( CS_HUMAN_STAGES );
- sscanf( CG_ConfigString( CS_STAGES ), "%d %d %d %d %d %d", &cgs.alienStage, &cgs.humanStage,
- &cgs.alienKills, &cgs.humanKills, &cgs.alienNextStageThreshold, &cgs.humanNextStageThreshold );
- sscanf( CG_ConfigString( CS_SPAWNS ), "%d %d", &cgs.numAlienSpawns, &cgs.numHumanSpawns );
+ if( alienStages[0] )
+ {
+ sscanf( alienStages, "%d %d %d", &cgs.alienStage, &cgs.alienCredits,
+ &cgs.alienNextStageThreshold );
+ }
+ else
+ cgs.alienStage = cgs.alienCredits = cgs.alienNextStageThreshold = 0;
+
+
+ if( humanStages[0] )
+ {
+ sscanf( humanStages, "%d %d %d", &cgs.humanStage, &cgs.humanCredits,
+ &cgs.humanNextStageThreshold );
+ }
+ else
+ cgs.humanStage = cgs.humanCredits = cgs.humanNextStageThreshold = 0;
cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) );
- cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) );
+ cg.warmupTime = atoi( CG_ConfigString( CS_WARMUP ) );
}
@@ -224,7 +237,7 @@ CG_AnnounceAlienStageTransistion
*/
static void CG_AnnounceAlienStageTransistion( stage_t from, stage_t to )
{
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] != PTE_ALIENS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] != TEAM_ALIENS )
return;
trap_S_StartLocalSound( cgs.media.alienStageTransition, CHAN_ANNOUNCER );
@@ -238,7 +251,7 @@ CG_AnnounceHumanStageTransistion
*/
static void CG_AnnounceHumanStageTransistion( stage_t from, stage_t to )
{
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] != PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] != TEAM_HUMANS )
return;
trap_S_StartLocalSound( cgs.media.humanStageTransition, CHAN_ANNOUNCER );
@@ -272,91 +285,72 @@ static void CG_ConfigStringModified( void )
CG_ParseServerinfo( );
else if( num == CS_WARMUP )
CG_ParseWarmup( );
- else if( num == CS_BUILDPOINTS )
- sscanf( str, "%d %d %d %d %d", &cgs.alienBuildPoints,
- &cgs.alienBuildPointsTotal,
- &cgs.humanBuildPoints,
- &cgs.humanBuildPointsTotal,
- &cgs.humanBuildPointsPowered );
- else if( num == CS_STAGES )
+ else if( num == CS_ALIEN_STAGES )
{
stage_t oldAlienStage = cgs.alienStage;
- stage_t oldHumanStage = cgs.humanStage;
-
- sscanf( str, "%d %d %d %d %d %d",
- &cgs.alienStage, &cgs.humanStage,
- &cgs.alienKills, &cgs.humanKills,
- &cgs.alienNextStageThreshold, &cgs.humanNextStageThreshold );
-
- if( cgs.alienStage != oldAlienStage )
- CG_AnnounceAlienStageTransistion( oldAlienStage, cgs.alienStage );
- if( cgs.humanStage != oldHumanStage )
- CG_AnnounceHumanStageTransistion( oldHumanStage, cgs.humanStage );
- }
- else if( num == CS_SPAWNS )
- sscanf( str, "%d %d", &cgs.numAlienSpawns, &cgs.numHumanSpawns );
- else if( num == CS_LEVEL_START_TIME )
- cgs.levelStartTime = atoi( str );
- else if( num == CS_VOTE_TIME )
- {
- cgs.voteTime = atoi( str );
- cgs.voteModified = qtrue;
+ if( str[0] )
+ {
+ sscanf( str, "%d %d %d", &cgs.alienStage, &cgs.alienCredits,
+ &cgs.alienNextStageThreshold );
- if( cgs.voteTime )
- trap_Cvar_Set( "ui_voteActive", "1" );
+ if( cgs.alienStage != oldAlienStage )
+ CG_AnnounceAlienStageTransistion( oldAlienStage, cgs.alienStage );
+ }
else
- trap_Cvar_Set( "ui_voteActive", "0" );
- }
- else if( num == CS_VOTE_YES )
- {
- cgs.voteYes = atoi( str );
- cgs.voteModified = qtrue;
- }
- else if( num == CS_VOTE_NO )
- {
- cgs.voteNo = atoi( str );
- cgs.voteModified = qtrue;
+ {
+ cgs.alienStage = cgs.alienCredits = cgs.alienNextStageThreshold = 0;
+ }
}
- else if( num == CS_VOTE_STRING )
- Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) );
- else if( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1 )
+ else if( num == CS_HUMAN_STAGES )
{
- int cs_offset = num - CS_TEAMVOTE_TIME;
-
- cgs.teamVoteTime[ cs_offset ] = atoi( str );
- cgs.teamVoteModified[ cs_offset ] = qtrue;
+ stage_t oldHumanStage = cgs.humanStage;
- if( cs_offset == 0 )
+ if( str[0] )
{
- if( cgs.teamVoteTime[ cs_offset ] )
- trap_Cvar_Set( "ui_humanTeamVoteActive", "1" );
- else
- trap_Cvar_Set( "ui_humanTeamVoteActive", "0" );
+ sscanf( str, "%d %d %d", &cgs.humanStage, &cgs.humanCredits,
+ &cgs.humanNextStageThreshold );
+
+ if( cgs.humanStage != oldHumanStage )
+ CG_AnnounceHumanStageTransistion( oldHumanStage, cgs.humanStage );
}
- else if( cs_offset == 1 )
+ else
{
- if( cgs.teamVoteTime[ cs_offset ] )
- trap_Cvar_Set( "ui_alienTeamVoteActive", "1" );
- else
- trap_Cvar_Set( "ui_alienTeamVoteActive", "0" );
+ cgs.humanStage = cgs.humanCredits = cgs.humanNextStageThreshold = 0;
}
}
- else if( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1 )
+ else if( num == CS_LEVEL_START_TIME )
+ cgs.levelStartTime = atoi( str );
+ else if( num >= CS_VOTE_TIME && num < CS_VOTE_TIME + NUM_TEAMS )
{
- cgs.teamVoteYes[ num - CS_TEAMVOTE_YES ] = atoi( str );
- cgs.teamVoteModified[ num - CS_TEAMVOTE_YES ] = qtrue;
+ cgs.voteTime[ num - CS_VOTE_TIME ] = atoi( str );
+ cgs.voteModified[ num - CS_VOTE_TIME ] = qtrue;
+
+ if( num - CS_VOTE_TIME == TEAM_NONE )
+ trap_Cvar_Set( "ui_voteActive", cgs.voteTime[ TEAM_NONE ] ? "1" : "0" );
+ else if( num - CS_VOTE_TIME == TEAM_ALIENS )
+ trap_Cvar_Set( "ui_alienTeamVoteActive",
+ cgs.voteTime[ TEAM_ALIENS ] ? "1" : "0" );
+ else if( num - CS_VOTE_TIME == TEAM_HUMANS )
+ trap_Cvar_Set( "ui_humanTeamVoteActive",
+ cgs.voteTime[ TEAM_HUMANS ] ? "1" : "0" );
}
- else if( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1 )
+ else if( num >= CS_VOTE_YES && num < CS_VOTE_YES + NUM_TEAMS )
{
- cgs.teamVoteNo[ num - CS_TEAMVOTE_NO ] = atoi( str );
- cgs.teamVoteModified[ num - CS_TEAMVOTE_NO ] = qtrue;
+ cgs.voteYes[ num - CS_VOTE_YES ] = atoi( str );
+ cgs.voteModified[ num - CS_VOTE_YES ] = qtrue;
}
- else if( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1 )
+ else if( num >= CS_VOTE_NO && num < CS_VOTE_NO + NUM_TEAMS )
{
- Q_strncpyz( cgs.teamVoteString[ num - CS_TEAMVOTE_STRING ], str,
- sizeof( cgs.teamVoteString[ num - CS_TEAMVOTE_STRING ] ) );
+ cgs.voteNo[ num - CS_VOTE_NO ] = atoi( str );
+ cgs.voteModified[ num - CS_VOTE_NO ] = qtrue;
}
+ else if( num >= CS_VOTE_STRING && num < CS_VOTE_STRING + NUM_TEAMS )
+ Q_strncpyz( cgs.voteString[ num - CS_VOTE_STRING ], str,
+ sizeof( cgs.voteString[ num - CS_VOTE_STRING ] ) );
+ else if( num >= CS_VOTE_CALLER && num < CS_VOTE_CALLER + NUM_TEAMS )
+ Q_strncpyz( cgs.voteCaller[ num - CS_VOTE_CALLER ], str,
+ sizeof( cgs.voteCaller[ num - CS_VOTE_CALLER ] ) );
else if( num == CS_INTERMISSION )
cg.intermissionStarted = atoi( str );
else if( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS )
@@ -413,7 +407,7 @@ static void CG_MapRestart( void )
cg.intermissionStarted = qfalse;
- cgs.voteTime = 0;
+ cgs.voteTime[ TEAM_NONE ] = 0;
cg.mapRestart = qtrue;
@@ -423,574 +417,903 @@ static void CG_MapRestart( void )
// we really should clear more parts of cg here and stop sounds
- // play the "fight" sound if this is a restart without warmup
- if( cg.warmup == 0 )
- CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH * 2 );
-
trap_Cvar_Set( "cg_thirdPerson", "0" );
}
/*
-=================
-CG_RemoveChatEscapeChar
-=================
-*/
-static void CG_RemoveChatEscapeChar( char *text )
-{
- int i, l;
-
- l = 0;
- for( i = 0; text[ i ]; i++ )
- {
- if( text[ i ] == '\x19' )
- continue;
-
- text[ l++ ] = text[ i ];
- }
-
- text[ l ] = '\0';
-}
-
-/*
-===============
-CG_SetUIVars
-
-Set some cvars used by the UI
-===============
-*/
-static void CG_SetUIVars( void )
-{
- int i;
- char carriageCvar[ MAX_TOKEN_CHARS ];
-
- *carriageCvar = 0;
-
- //determine what the player is carrying
- for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
- {
- if( BG_InventoryContainsWeapon( i, cg.snap->ps.stats ) &&
- BG_FindPurchasableForWeapon( i ) )
- strcat( carriageCvar, va( "W%d ", i ) );
- }
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- if( BG_InventoryContainsUpgrade( i, cg.snap->ps.stats ) &&
- BG_FindPurchasableForUpgrade( i ) )
- strcat( carriageCvar, va( "U%d ", i ) );
- }
- strcat( carriageCvar, "$" );
-
- trap_Cvar_Set( "ui_carriage", carriageCvar );
-
- trap_Cvar_Set( "ui_stages", va( "%d %d", cgs.alienStage, cgs.humanStage ) );
-}
-
-
-/*
==============
CG_Menu
==============
*/
-void CG_Menu( int menu )
+void CG_Menu( int menu, int arg )
{
- const char *cmd = NULL; // command to send
- const char *longMsg = NULL; // command parameter
- const char *shortMsg = NULL; // non-modal version of message
- CG_SetUIVars( );
+ const char *cmd; // command to send
+ const char *longMsg = NULL; // command parameter
+ const char *shortMsg = NULL; // non-modal version of message
+ const char *dialog;
+ dialogType_t type = 0; // controls which cg_disable var will switch it off
+
+ switch( cg.snap->ps.stats[ STAT_TEAM ] )
+ {
+ case TEAM_ALIENS:
+ dialog = "menu tremulous_alien_dialog\n";
+ break;
+ case TEAM_HUMANS:
+ dialog = "menu tremulous_human_dialog\n";
+ break;
+ default:
+ dialog = "menu tremulous_default_dialog\n";
+ }
+ cmd = dialog;
- // string literals have static storage duration, this is safe,
- // cleaner and much more readable.
switch( menu )
{
case MN_TEAM:
cmd = "menu tremulous_teamselect\n";
+ type = DT_INTERACTIVE;
break;
case MN_A_CLASS:
cmd = "menu tremulous_alienclass\n";
+ type = DT_INTERACTIVE;
break;
case MN_H_SPAWN:
cmd = "menu tremulous_humanitem\n";
+ type = DT_INTERACTIVE;
break;
case MN_A_BUILD:
cmd = "menu tremulous_alienbuild\n";
+ type = DT_INTERACTIVE;
break;
case MN_H_BUILD:
cmd = "menu tremulous_humanbuild\n";
+ type = DT_INTERACTIVE;
break;
case MN_H_ARMOURY:
cmd = "menu tremulous_humanarmoury\n";
+ type = DT_INTERACTIVE;
+ break;
+
+ case MN_H_UNKNOWNITEM:
+ shortMsg = "Unknown item";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_A_TEAMFULL:
longMsg = "The alien team has too many players. Please wait until slots "
"become available or join the human team.";
- shortMsg = "The alien team has too many players\n";
- cmd = "menu tremulous_alien_dialog\n";
+ shortMsg = "The alien team has too many players";
+ type = DT_COMMAND;
break;
case MN_H_TEAMFULL:
longMsg = "The human team has too many players. Please wait until slots "
"become available or join the alien team.";
- shortMsg = "The human team has too many players\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "The human team has too many players";
+ type = DT_COMMAND;
break;
- case MN_A_TEAMCHANGEBUILDTIMER:
- longMsg = "You cannot leave the Alien team until your build timer "
- "has expired.";
- shortMsg = "You cannot change teams until your build timer expires.\n";
- cmd = "menu tremulous_alien_dialog\n";
+ case MN_A_TEAMLOCKED:
+ longMsg = "The alien team is locked. You cannot join the aliens "
+ "at this time.";
+ shortMsg = "The alien team is locked";
+ type = DT_COMMAND;
break;
- case MN_H_TEAMCHANGEBUILDTIMER:
- longMsg = "You cannot leave the Human team until your build timer "
- "has expired.";
- shortMsg = "You cannot change teams until your build timer expires.\n";
- cmd = "menu tremulous_human_dialog\n";
+ case MN_H_TEAMLOCKED:
+ longMsg = "The human team is locked. You cannot join the humans "
+ "at this time.";
+ shortMsg = "The human team is locked";
+ type = DT_COMMAND;
+ break;
+
+ case MN_PLAYERLIMIT:
+ longMsg = "The maximum number of playing clients has been reached. "
+ "Please wait until slots become available.";
+ shortMsg = "No free player slots";
+ type = DT_COMMAND;
break;
//===============================
- case MN_H_NOROOM:
- longMsg = "There is no room to build here. Move until the buildable turns "
- "translucent green indicating a valid build location.";
- shortMsg = "There is no room to build here\n";
- cmd = "menu tremulous_human_dialog\n";
+ // Since cheating commands have no default binds, they will often be done
+ // via console. In light of this, perhaps opening a menu is
+ // counterintuitive
+ case MN_CMD_CHEAT:
+ //longMsg = "This action is considered cheating. It can only be used "
+ // "in cheat mode, which is not enabled on this server.";
+ shortMsg = "Cheats are not enabled on this server";
+ type = DT_COMMAND;
break;
- case MN_H_NOPOWER:
- longMsg = "There is no power remaining. Free up power by destroying "
- "existing buildable objects.";
- shortMsg = "There is no power remaining\n";
- cmd = "menu tremulous_human_dialog\n";
+ case MN_CMD_CHEAT_TEAM:
+ shortMsg = "Cheats are not enabled on this server, so "
+ "you may not use this command while on a team";
+ type = DT_COMMAND;
break;
- case MN_H_NOTPOWERED:
- longMsg = "This buildable is not powered. Build a Reactor and/or Repeater "
- "in order to power it.";
- shortMsg = "This buildable is not powered\n";
- cmd = "menu tremulous_human_dialog\n";
+ case MN_CMD_TEAM:
+ //longMsg = "You must be on a team to perform this action. Join the alien"
+ // "or human team and try again.";
+ shortMsg = "Join a team first";
+ type = DT_COMMAND;
break;
- case MN_H_NORMAL:
+ case MN_CMD_SPEC:
+ //longMsg = "You may not perform this action while on a team. Become a "
+ // "spectator before trying again.";
+ shortMsg = "You can only use this command when spectating";
+ type = DT_COMMAND;
+ break;
+
+ case MN_CMD_ALIEN:
+ //longMsg = "You must be on the alien team to perform this action.";
+ shortMsg = "Must be alien to use this command";
+ type = DT_COMMAND;
+ break;
+
+ case MN_CMD_HUMAN:
+ //longMsg = "You must be on the human team to perform this action.";
+ shortMsg = "Must be human to use this command";
+ type = DT_COMMAND;
+ break;
+
+ case MN_CMD_ALIVE:
+ //longMsg = "You must be alive to perform this action.";
+ shortMsg = "Must be alive to use this command";
+ type = DT_COMMAND;
+ break;
+
+
+ //===============================
+
+ case MN_B_NOROOM:
+ longMsg = "There is no room to build here. Move until the structure turns "
+ "translucent green, indicating a valid build location.";
+ shortMsg = "There is no room to build here";
+ type = DT_BUILD;
+ break;
+
+ case MN_B_NORMAL:
longMsg = "Cannot build on this surface. The surface is too steep or "
- "unsuitable to build on. Please choose another site for this "
- "structure.";
- shortMsg = "Cannot build on this surface\n";
- cmd = "menu tremulous_human_dialog\n";
+ "unsuitable for building. Please choose another site for this "
+ "structure.";
+ shortMsg = "Cannot build on this surface";
+ type = DT_BUILD;
+ break;
+
+ case MN_B_CANNOT:
+ longMsg = NULL;
+ shortMsg = "You cannot build that structure";
+ type = DT_BUILD;
+ break;
+
+ // FIXME: MN_H_ and MN_A_?
+ case MN_B_LASTSPAWN:
+ longMsg = "This action would remove your team's last spawn point, "
+ "which often quickly results in a loss. Try building more "
+ "spawns.";
+ shortMsg = "You may not deconstruct the last spawn";
+ break;
+
+ case MN_B_SUDDENDEATH:
+ longMsg = "Neither team has prevailed after a certain time and the "
+ "game has entered Sudden Death. During Sudden Death "
+ "building is not allowed.";
+ shortMsg = "Cannot build during Sudden Death";
+ type = DT_BUILD;
+ break;
+
+ case MN_B_REVOKED:
+ longMsg = "Your teammates have lost faith in your ability to build "
+ "for the team. You will not be allowed to build until your "
+ "team votes to reinstate your building rights.";
+ shortMsg = "Your building rights have been revoked";
+ type = DT_BUILD;
break;
- case MN_H_REACTOR:
- longMsg = "There can only be one Reactor. Destroy the existing one if you "
+ case MN_B_SURRENDER:
+ longMsg = "Your team has decided to admit defeat and concede the game:"
+ "traitors and cowards are not allowed to build.";
+ // too harsh?
+ shortMsg = "Building is denied to traitorous cowards";
+ break;
+
+ //===============================
+
+ case MN_H_NOBP:
+ if( cgs.markDeconstruct )
+ longMsg = "There is no power remaining. Free up power by marking "
+ "existing buildable objects.";
+ else
+ longMsg = "There is no power remaining. Free up power by deconstructing "
+ "existing buildable objects.";
+ shortMsg = "There is no power remaining";
+ type = DT_BUILD;
+ break;
+
+ case MN_H_NOTPOWERED:
+ longMsg = "This buildable is not powered. Build a Reactor and/or Repeater "
+ "in order to power it.";
+ shortMsg = "This buildable is not powered";
+ type = DT_BUILD;
+ break;
+
+ case MN_H_ONEREACTOR:
+ longMsg = "There can only be one Reactor. Deconstruct the existing one if you "
"wish to move it.";
- shortMsg = "There can only be one Reactor\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "There can only be one Reactor";
+ type = DT_BUILD;
break;
- case MN_H_REPEATER:
+ case MN_H_NOPOWERHERE:
longMsg = "There is no power here. If available, a Repeater may be used to "
"transmit power to this location.";
- shortMsg = "There is no power here\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "There is no power here";
+ type = DT_BUILD;
break;
case MN_H_NODCC:
longMsg = "There is no Defense Computer. A Defense Computer is needed to "
"build this.";
- shortMsg = "There is no Defense Computer\n";
- cmd = "menu tremulous_human_dialog\n";
- break;
-
- case MN_H_TNODEWARN:
- longMsg = "WARNING: This Telenode will not be powered. Build near a power "
- "structure to prevent seeing this message again.";
- shortMsg = "This Telenode will not be powered\n";
- cmd = "menu tremulous_human_dialog\n";
- break;
-
- case MN_H_RPTWARN:
- longMsg = "WARNING: This Repeater will not be powered as there is no parent "
- "Reactor providing power. Build a Reactor.";
- shortMsg = "This Repeater will not be powered\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "There is no Defense Computer";
+ type = DT_BUILD;
break;
- case MN_H_RPTWARN2:
+ case MN_H_RPTPOWERHERE:
longMsg = "This area already has power. A Repeater is not required here.";
- shortMsg = "This area already has power\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "This area already has power";
+ type = DT_BUILD;
break;
case MN_H_NOSLOTS:
longMsg = "You have no room to carry this. Please sell any conflicting "
"upgrades before purchasing this item.";
- shortMsg = "You have no room to carry this\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "You have no room to carry this";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_NOFUNDS:
longMsg = "Insufficient funds. You do not have enough credits to perform "
"this action.";
- shortMsg = "Insufficient funds\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "Insufficient funds";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_ITEMHELD:
longMsg = "You already hold this item. It is not possible to carry multiple "
"items of the same type.";
- shortMsg = "You already hold this item\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "You already hold this item";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_NOARMOURYHERE:
longMsg = "You must be near a powered Armoury in order to purchase "
- "weapons, upgrades or non-energy ammunition.";
- shortMsg = "You must be near a powered Armoury\n";
- cmd = "menu tremulous_human_dialog\n";
+ "weapons, upgrades or ammunition.";
+ shortMsg = "You must be near a powered Armoury";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_NOENERGYAMMOHERE:
- longMsg = "You must be near an Armoury, Reactor or Repeater in order "
- "to purchase energy ammunition.";
- shortMsg = "You must be near an Armoury, Reactor or Repeater\n";
- cmd = "menu tremulous_human_dialog\n";
+ longMsg = "You must be near a Reactor or a powered Armoury or Repeater "
+ "in order to purchase energy ammunition.";
+ shortMsg = "You must be near a Reactor or a powered Armoury or Repeater";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_NOROOMBSUITON:
longMsg = "There is not enough room here to put on a Battle Suit. "
"Make sure you have enough head room to climb in.";
- shortMsg = "Not enough room here to put on a Battle Suit\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "Not enough room here to put on a Battle Suit";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_NOROOMBSUITOFF:
longMsg = "There is not enough room here to take off your Battle Suit. "
"Make sure you have enough head room to climb out.";
- shortMsg = "Not enough room here to take off your Battle Suit\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "Not enough room here to take off your Battle Suit";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_H_ARMOURYBUILDTIMER:
longMsg = "You are not allowed to buy or sell weapons until your "
"build timer has expired.";
- shortMsg = "You can not buy or sell weapos until your build timer "
- "expires\n";
- cmd = "menu tremulous_human_dialog\n";
+ shortMsg = "You can not buy or sell weapons until your build timer "
+ "expires";
+ type = DT_ARMOURYEVOLVE;
break;
+ case MN_H_DEADTOCLASS:
+ shortMsg = "You must be dead to use the class command";
+ type = DT_COMMAND;
+ break;
- //===============================
-
- case MN_A_NOROOM:
- longMsg = "There is no room to build here. Move until the structure turns "
- "translucent green indicating a valid build location.";
- shortMsg = "There is no room to build here\n";
- cmd = "menu tremulous_alien_dialog\n";
+ case MN_H_UNKNOWNSPAWNITEM:
+ shortMsg = "Unknown starting item";
+ type = DT_COMMAND;
break;
+ //===============================
+
case MN_A_NOCREEP:
longMsg = "There is no creep here. You must build near existing Eggs or "
"the Overmind. Alien structures will not support themselves.";
- shortMsg = "There is no creep here\n";
- cmd = "menu tremulous_alien_dialog\n";
+ shortMsg = "There is no creep here";
+ type = DT_BUILD;
break;
case MN_A_NOOVMND:
longMsg = "There is no Overmind. An Overmind must be built to control "
- "the structure you tried to place";
- shortMsg = "There is no Overmind\n";
- cmd = "menu tremulous_alien_dialog\n";
- break;
-
- case MN_A_OVERMIND:
- longMsg = "There can only be one Overmind. Destroy the existing one if you "
- "wish to move it.";
- shortMsg = "There can only be one Overmind\n";
- cmd = "menu tremulous_alien_dialog\n";
+ "the structure you tried to place.";
+ shortMsg = "There is no Overmind";
+ type = DT_BUILD;
break;
- case MN_A_HOVEL:
- longMsg = "There can only be one Hovel. Destroy the existing one if you "
+ case MN_A_ONEOVERMIND:
+ longMsg = "There can only be one Overmind. Deconstruct the existing one if you "
"wish to move it.";
- shortMsg = "There can only be one Hovel\n";
- cmd = "menu tremulous_alien_dialog\n";
+ shortMsg = "There can only be one Overmind";
+ type = DT_BUILD;
break;
- case MN_A_NOASSERT:
- longMsg = "The Overmind cannot control any more structures. Destroy existing "
+ case MN_A_NOBP:
+ longMsg = "The Overmind cannot control any more structures. Deconstruct existing "
"structures to build more.";
- shortMsg = "The Overmind cannot control any more structures\n";
- cmd = "menu tremulous_alien_dialog\n";
- break;
-
- case MN_A_SPWNWARN:
- longMsg = "WARNING: This spawn will not be controlled by an Overmind. "
- "Build an Overmind to prevent seeing this message again.";
- shortMsg = "This spawn will not be controlled by an Overmind\n";
- cmd = "menu tremulous_alien_dialog\n";
- break;
-
- case MN_A_NORMAL:
- longMsg = "Cannot build on this surface. This surface is too steep or "
- "unsuitable to build on. Please choose another site for this "
- "structure.";
- shortMsg = "Cannot build on this surface\n";
- cmd = "menu tremulous_alien_dialog\n";
+ shortMsg = "The Overmind cannot control any more structures";
+ type = DT_BUILD;
break;
case MN_A_NOEROOM:
longMsg = "There is no room to evolve here. Move away from walls or other "
- "nearby objects and try again.";
- cmd = "menu tremulous_alien_dialog\n";
- shortMsg = "There is no room to evolve here\n";
+ "nearby objects and try again.";
+ shortMsg = "There is no room to evolve here";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_A_TOOCLOSE:
longMsg = "This location is too close to the enemy to evolve. Move away "
- "until you are no longer aware of the enemy's presence and try "
- "again.";
- shortMsg = "This location is too close to the enemy to evolve\n";
- cmd = "menu tremulous_alien_dialog\n";
+ "from the enemy's presence and try again.";
+ shortMsg = "This location is too close to the enemy to evolve";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_A_NOOVMND_EVOLVE:
longMsg = "There is no Overmind. An Overmind must be built to allow "
"you to upgrade.";
- shortMsg = "There is no Overmind\n";
- cmd = "menu tremulous_alien_dialog\n";
+ shortMsg = "There is no Overmind";
+ type = DT_ARMOURYEVOLVE;
break;
case MN_A_EVOLVEBUILDTIMER:
- longMsg = "You cannot Evolve until your build timer has expired.";
- shortMsg = "You cannot Evolve until your build timer expires\n";
- cmd = "menu tremulous_alien_dialog\n";
+ longMsg = "You cannot evolve until your build timer has expired.";
+ shortMsg = "You cannot evolve until your build timer expires";
+ type = DT_ARMOURYEVOLVE;
break;
- case MN_A_HOVEL_OCCUPIED:
- longMsg = "This Hovel is already occupied by another builder.";
- shortMsg = "This Hovel is already occupied by another builder\n";
- cmd = "menu tremulous_alien_dialog\n";
+ case MN_A_INFEST:
+ trap_Cvar_Set( "ui_currentClass",
+ va( "%d %d", cg.snap->ps.stats[ STAT_CLASS ],
+ cg.snap->ps.persistant[ PERS_CREDIT ] ) );
+
+ cmd = "menu tremulous_alienupgrade\n";
+ type = DT_INTERACTIVE;
break;
- case MN_A_HOVEL_BLOCKED:
- longMsg = "The exit to this Hovel is currently blocked. Please wait until it "
- "becomes clear then try again.";
- shortMsg = "The exit to this Hovel is currently blocked\n";
- cmd = "menu tremulous_alien_dialog\n";
+ case MN_A_CANTEVOLVE:
+ shortMsg = va( "You cannot evolve into a %s",
+ BG_ClassConfig( arg )->humanName );
+ type = DT_ARMOURYEVOLVE;
break;
- case MN_A_HOVEL_EXIT:
- longMsg = "The exit to this Hovel would always be blocked. Please choose "
- "a more suitable location.";
- shortMsg = "The exit to this Hovel would always be blocked\n";
- cmd = "menu tremulous_alien_dialog\n";
+ case MN_A_EVOLVEWALLWALK:
+ shortMsg = "You cannot evolve while wallwalking";
+ type = DT_ARMOURYEVOLVE;
break;
- case MN_A_INFEST:
- trap_Cvar_Set( "ui_currentClass", va( "%d %d", cg.snap->ps.stats[ STAT_PCLASS ],
- cg.snap->ps.persistant[ PERS_CREDIT ] ) );
- cmd = "menu tremulous_alienupgrade\n";
+ case MN_A_UNKNOWNCLASS:
+ shortMsg = "Unknown class";
+ type = DT_ARMOURYEVOLVE;
+ break;
+
+ case MN_A_CLASSNOTSPAWN:
+ shortMsg = va( "You cannot spawn as a %s",
+ BG_ClassConfig( arg )->humanName );
+ type = DT_ARMOURYEVOLVE;
+ break;
+
+ case MN_A_CLASSNOTALLOWED:
+ shortMsg = va( "The %s is not allowed",
+ BG_ClassConfig( arg )->humanName );
+ type = DT_ARMOURYEVOLVE;
+ break;
+
+ case MN_A_CLASSNOTATSTAGE:
+ shortMsg = va( "The %s is not allowed at Stage %d",
+ BG_ClassConfig( arg )->humanName,
+ cgs.alienStage + 1 );
+ type = DT_ARMOURYEVOLVE;
break;
default:
Com_Printf( "cgame: debug: no such menu %d\n", menu );
}
+
+ if( type == DT_ARMOURYEVOLVE && cg_disableUpgradeDialogs.integer )
+ return;
- if( !cg_disableWarningDialogs.integer || !shortMsg )
- {
- // Player either wants dialog window or there's no short message
- if( cmd )
- {
- if( longMsg )
- trap_Cvar_Set( "ui_dialog", longMsg );
+ if( type == DT_BUILD && cg_disableBuildDialogs.integer )
+ return;
- trap_SendConsoleCommand( cmd );
- }
- }
- else
+ if( type == DT_COMMAND && cg_disableCommandDialogs.integer )
+ return;
+
+ if( cmd != dialog )
+ {
+ trap_SendConsoleCommand( cmd );
+ }
+ else if( longMsg && cg_disableWarningDialogs.integer == 0 )
{
- // There is short message and player wants it
- CG_Printf( shortMsg );
- }
+ trap_Cvar_Set( "ui_dialog", longMsg );
+ trap_SendConsoleCommand( cmd );
+ }
+ else if( shortMsg && cg_disableWarningDialogs.integer < 2 )
+ {
+ CG_Printf( "%s\n", shortMsg );
+ }
}
/*
=================
-CG_ServerCommand
-
-The string has been tokenized and can be retrieved with
-Cmd_Argc() / Cmd_Argv()
+CG_Say
=================
*/
-static void CG_ServerCommand( void )
+static void CG_Say( int clientNum, saymode_t mode, const char *text )
{
- const char *cmd;
- char text[ MAX_SAY_TEXT ];
+ char *name;
+ char prefix[ 11 ] = "";
+ char *ignore = "";
+ const char *location = "";
+ char *color;
+ char *maybeColon;
+
+ if( clientNum >= 0 && clientNum < MAX_CLIENTS )
+ {
+ clientInfo_t *ci = &cgs.clientinfo[ clientNum ];
+ char *tcolor = S_COLOR_WHITE;
- cmd = CG_Argv( 0 );
+ name = ci->name;
- if( !cmd[ 0 ] )
- {
- // server claimed the command
- return;
- }
+ if( ci->team == TEAM_ALIENS )
+ tcolor = S_COLOR_RED;
+ else if( ci->team == TEAM_HUMANS )
+ tcolor = S_COLOR_CYAN;
- if( !strcmp( cmd, "cp" ) )
- {
- CG_CenterPrint( CG_Argv( 1 ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
- return;
- }
+ if( cg_chatTeamPrefix.integer )
+ Com_sprintf( prefix, sizeof( prefix ), "[%s%c" S_COLOR_WHITE "] ",
+ tcolor, toupper( *( BG_TeamName( ci->team ) ) ) );
- if( !strcmp( cmd, "cs" ) )
- {
- CG_ConfigStringModified( );
- return;
+ if( ( mode == SAY_TEAM || mode == SAY_AREA ) &&
+ cg.snap->ps.pm_type != PM_INTERMISSION )
+ {
+ int locationNum;
+
+ if( clientNum == cg.snap->ps.clientNum )
+ {
+ centity_t *locent;
+
+ locent = CG_GetPlayerLocation( );
+ if( locent )
+ locationNum = locent->currentState.generic1;
+ else
+ locationNum = -1;
+ }
+ else
+ locationNum = ci->location;
+
+ if( locationNum >= 0 && locationNum < MAX_LOCATIONS )
+ {
+ const char *s = CG_ConfigString( CS_LOCATIONS + locationNum );
+
+ if( *s )
+ location = va( " (%s" S_COLOR_WHITE ")", s );
+ }
+ }
}
+ else
+ name = "console";
- if( !strcmp( cmd, "print" ) )
+ // IRC-like /me parsing
+ if( mode != SAY_RAW && Q_stricmpn( text, "/me ", 4 ) == 0 )
{
- CG_Printf( "%s", CG_Argv( 1 ) );
- return;
+ text += 4;
+ Q_strcat( prefix, sizeof( prefix ), "* " );
+ maybeColon = "";
}
+ else
+ maybeColon = ":";
- if( !strcmp( cmd, "chat" ) )
+ switch( mode )
{
- if( !cg_teamChatsOnly.integer )
- {
- Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT );
- if( Q_stricmpn( text, "[skipnotify]", 12 ) )
- trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
- CG_RemoveChatEscapeChar( text );
+ case SAY_ALL:
+ // might already be ignored but in that case no harm is done
+ if( cg_teamChatsOnly.integer )
+ ignore = "[skipnotify]";
+
+#ifdef MODULE_INTERFACE_11
+ CG_Printf( "%s%s%s" S_COLOR_WHITE "%s " S_COLOR_GREEN "%s\n",
+ ignore, prefix, name, maybeColon, text );
+#else
+ CG_Printf( "%s%s%s" S_COLOR_WHITE "%s %c" S_COLOR_GREEN "%s\n",
+ ignore, prefix, name, maybeColon, INDENT_MARKER, text );
+#endif
+ break;
+ case SAY_TEAM:
+#ifdef MODULE_INTERFACE_11
+ CG_Printf( "%s%s(%s" S_COLOR_WHITE ")%s%s " S_COLOR_CYAN "%s\n",
+ ignore, prefix, name, location, maybeColon, text );
+#else
+ CG_Printf( "%s%s(%s" S_COLOR_WHITE ")%s%s %c" S_COLOR_CYAN "%s\n",
+ ignore, prefix, name, location, maybeColon, INDENT_MARKER, text );
+#endif
+ break;
+ case SAY_ADMINS:
+ case SAY_ADMINS_PUBLIC:
+#ifdef MODULE_INTERFACE_11
+ CG_Printf( "%s%s%s%s" S_COLOR_WHITE "%s " S_COLOR_MAGENTA "%s\n",
+ ignore, prefix,
+ ( mode == SAY_ADMINS ) ? "[ADMIN]" : "[PLAYER]",
+ name, maybeColon, text );
+#else
+ CG_Printf( "%s%s%s%s" S_COLOR_WHITE "%s %c" S_COLOR_MAGENTA "%s\n",
+ ignore, prefix,
+ ( mode == SAY_ADMINS ) ? "[ADMIN]" : "[PLAYER]",
+ name, maybeColon, INDENT_MARKER, text );
+#endif
+ break;
+ case SAY_AREA:
+#ifdef MODULE_INTERFACE_11
+ CG_Printf( "%s%s<%s" S_COLOR_WHITE ">%s%s " S_COLOR_BLUE "%s\n",
+ ignore, prefix, name, location, maybeColon, text );
+#else
+ CG_Printf( "%s%s<%s" S_COLOR_WHITE ">%s%s %c" S_COLOR_BLUE "%s\n",
+ ignore, prefix, name, location, maybeColon, INDENT_MARKER, text );
+#endif
+ break;
+ case SAY_PRIVMSG:
+ case SAY_TPRIVMSG:
+ color = ( mode == SAY_TPRIVMSG ) ? S_COLOR_CYAN : S_COLOR_GREEN;
+#ifdef MODULE_INTERFACE_11
+ CG_Printf( "%s%s[%s" S_COLOR_WHITE " -> %s" S_COLOR_WHITE "]%s %s%s\n",
+ ignore, prefix, name, cgs.clientinfo[ cg.clientNum ].name,
+ maybeColon, color, text );
+#else
+ CG_Printf( "%s%s[%s" S_COLOR_WHITE " -> %s" S_COLOR_WHITE "]%s %c%s%s\n",
+ ignore, prefix, name, cgs.clientinfo[ cg.clientNum ].name,
+ maybeColon, INDENT_MARKER, color, text );
+#endif
+ if( !ignore[0] )
+ {
+ CG_CenterPrint( va( "%sPrivate message from: " S_COLOR_WHITE "%s",
+ color, name ), 200, GIANTCHAR_WIDTH * 4 );
+ if( clientNum < 0 || clientNum >= MAX_CLIENTS )
+ clientNum = cg.clientNum;
+ CG_Printf( ">> to reply, say: /m %d [your message] <<\n", clientNum );
+ }
+ break;
+ case SAY_RAW:
CG_Printf( "%s\n", text );
- }
-
- return;
+ break;
}
- if( !strcmp( cmd, "tchat" ) )
+ switch( mode )
{
- Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT );
- if( Q_stricmpn( text, "[skipnotify]", 12 ) )
- {
- if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ case SAY_TEAM:
+ case SAY_AREA:
+ case SAY_TPRIVMSG:
+ if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
trap_S_StartLocalSound( cgs.media.alienTalkSound, CHAN_LOCAL_SOUND );
- else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ break;
+ }
+ else if( cg.snap->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ {
trap_S_StartLocalSound( cgs.media.humanTalkSound, CHAN_LOCAL_SOUND );
- else
- trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
- }
- CG_RemoveChatEscapeChar( text );
- CG_Printf( "%s\n", text );
- return;
+ break;
+ }
+ default:
+ trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
}
+}
- if( !strcmp( cmd, "scores" ) )
- {
- CG_ParseScores( );
- return;
- }
+/*
+=================
+CG_VoiceTrack
- if( !strcmp( cmd, "tinfo" ) )
+return the voice indexed voice track or print errors quietly to console
+in case someone is on an unpure server and wants to know which voice pak
+is missing or incomplete
+=================
+*/
+static voiceTrack_t *CG_VoiceTrack( char *voice, int cmd, int track )
+{
+ voice_t *v;
+ voiceCmd_t *c;
+ voiceTrack_t *t;
+
+ v = BG_VoiceByName( cgs.voices, voice );
+ if( !v )
{
- CG_ParseTeamInfo( );
- return;
+ CG_Printf( "[skipnotify]WARNING: could not find voice \"%s\"\n", voice );
+ return NULL;
}
-
- if( !strcmp( cmd, "map_restart" ) )
+ c = BG_VoiceCmdByNum( v->cmds, cmd );
+ if( !c )
{
- CG_MapRestart( );
- return;
+ CG_Printf( "[skipnotify]WARNING: could not find command %d "
+ "in voice \"%s\"\n", cmd, voice );
+ return NULL;
}
-
- if( Q_stricmp( cmd, "remapShader" ) == 0 )
+ t = BG_VoiceTrackByNum( c->tracks, track );
+ if( !t )
{
- if( trap_Argc( ) == 4 )
- trap_R_RemapShader( CG_Argv( 1 ), CG_Argv( 2 ), CG_Argv( 3 ) );
+ CG_Printf( "[skipnotify]WARNING: could not find track %d for command %d in "
+ "voice \"%s\"\n", track, cmd, voice );
+ return NULL;
}
+ return t;
+}
- // clientLevelShot is sent before taking a special screenshot for
- // the menu system during development
- if( !strcmp( cmd, "clientLevelShot" ) )
- {
- cg.levelShot = qtrue;
+/*
+=================
+CG_ParseVoice
+
+voice clientNum vChan cmdNum trackNum [sayText]
+=================
+*/
+static void CG_ParseVoice( void )
+{
+ int clientNum;
+ int vChan;
+ char sayText[ MAX_SAY_TEXT] = {""};
+ voiceTrack_t *track;
+ clientInfo_t *ci;
+
+ if( trap_Argc() < 5 || trap_Argc() > 6 )
return;
- }
- //the server has triggered a menu
- if( !strcmp( cmd, "servermenu" ) )
- {
- if( trap_Argc( ) == 2 && !cg.demoPlayback )
- CG_Menu( atoi( CG_Argv( 1 ) ) );
+ if( trap_Argc() == 6 )
+ Q_strncpyz( sayText, CG_Argv( 5 ), sizeof( sayText ) );
+ clientNum = atoi( CG_Argv( 1 ) );
+ if( clientNum < 0 || clientNum >= MAX_CLIENTS )
return;
- }
- //the server thinks this client should close all menus
- if( !strcmp( cmd, "serverclosemenus" ) )
- {
- trap_SendConsoleCommand( "closemenus\n" );
+ vChan = atoi( CG_Argv( 2 ) );
+ if( vChan < 0 || vChan >= VOICE_CHAN_NUM_CHANS )
return;
- }
- //poison cloud effect needs to be reliable
- if( !strcmp( cmd, "poisoncloud" ) )
- {
- cg.poisonedTime = cg.time;
+ if( cg_teamChatsOnly.integer && vChan != VOICE_CHAN_TEAM )
+ return;
- if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) )
- {
- cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS );
- CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity );
- CG_AttachToCent( &cg.poisonCloudPS->attachment );
- }
+ ci = &cgs.clientinfo[ clientNum ];
+ // this joker is still talking
+ if( ci->voiceTime > cg.time )
return;
- }
- if( !strcmp( cmd, "weaponswitch" ) )
+ track = CG_VoiceTrack( ci->voice, atoi( CG_Argv( 3 ) ), atoi( CG_Argv( 4 ) ) );
+
+ // keep track of how long the player will be speaking
+ // assume it takes 3s to say "*unintelligible gibberish*"
+ if( track )
+ ci->voiceTime = cg.time + track->duration;
+ else
+ ci->voiceTime = cg.time + 3000;
+
+ if( !sayText[ 0 ] )
{
- CG_Printf( "client weaponswitch\n" );
- if( trap_Argc( ) == 2 )
+ if( track )
+ Q_strncpyz( sayText, track->text, sizeof( sayText ) );
+ else
+ Q_strncpyz( sayText, "*unintelligible gibberish*", sizeof( sayText ) );
+ }
+
+ if( !cg_noVoiceText.integer )
+ {
+ switch( vChan )
{
- cg.weaponSelect = atoi( CG_Argv( 1 ) );
- cg.weaponSelectTime = cg.time;
+ case VOICE_CHAN_ALL:
+ CG_Say( clientNum, SAY_ALL, sayText );
+ break;
+ case VOICE_CHAN_TEAM:
+ CG_Say( clientNum, SAY_TEAM, sayText );
+ break;
+ default:
+ break;
}
+ }
+ // playing voice audio tracks disabled
+ if( cg_noVoiceChats.integer )
return;
- }
- // server requests a ptrc
- if( !strcmp( cmd, "ptrcrequest" ) )
+ // no audio track to play
+ if( !track )
+ return;
+
+ switch( vChan )
{
- int code = CG_ReadPTRCode( );
+ case VOICE_CHAN_ALL:
+ trap_S_StartLocalSound( track->track, CHAN_VOICE );
+ break;
+ case VOICE_CHAN_TEAM:
+ trap_S_StartLocalSound( track->track, CHAN_VOICE );
+ break;
+ case VOICE_CHAN_LOCAL:
+ trap_S_StartSound( NULL, clientNum, CHAN_VOICE, track->track );
+ break;
+ default:
+ break;
+ }
+}
- trap_SendClientCommand( va( "ptrcverify %d", code ) );
- return;
- }
+/*
+=================
+CG_CenterPrint_f
+=================
+*/
+void CG_CenterPrint_f( void )
+{
+ CG_CenterPrint( CG_Argv( 1 ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
+}
+
+/*
+=================
+CG_Print_f
+=================
+*/
+static void CG_Print_f( void )
+{
+ CG_Printf( "%s", CG_Argv( 1 ) );
+}
+
+/*
+=================
+CG_Chat_f
+=================
+*/
+static void CG_Chat_f( void )
+{
+ char id[ 3 ];
+ char mode[ 3 ];
+
+ trap_Argv( 1, id, sizeof( id ) );
+ trap_Argv( 2, mode, sizeof( mode ) );
+
+ CG_Say( atoi( id ), atoi( mode ), CG_Argv( 3 ) );
+}
- // server issues a ptrc
- if( !strcmp( cmd, "ptrcissue" ) )
+/*
+=================
+CG_ServerMenu_f
+=================
+*/
+static void CG_ServerMenu_f( void )
+{
+ if( !cg.demoPlayback )
{
if( trap_Argc( ) == 2 )
- {
- int code = atoi( CG_Argv( 1 ) );
+ CG_Menu( atoi( CG_Argv( 1 ) ), 0 );
+ else if( trap_Argc( ) == 3 )
+ CG_Menu( atoi( CG_Argv( 1 ) ), atoi( CG_Argv( 2 ) ) );
+ }
+}
- CG_WritePTRCode( code );
- }
+/*
+=================
+CG_ServerCloseMenus_f
+=================
+*/
+static void CG_ServerCloseMenus_f( void )
+{
+ trap_SendConsoleCommand( "closemenus\n" );
+}
- return;
+/*
+=================
+CG_PoisonCloud_f
+=================
+*/
+static void CG_PoisonCloud_f( void )
+{
+ cg.poisonedTime = cg.time;
+
+ if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) )
+ {
+ cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS );
+ CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity );
+ CG_AttachToCent( &cg.poisonCloudPS->attachment );
}
+}
+
+static char registeredCmds[ 8192 ]; // cmd1\0cmd2\0cmdn\0\0
+static size_t gcmdsOffset;
+static void CG_GameCmds_f( void )
+{
+ int i;
+ int c = trap_Argc( );
+ const char *cmd;
+ size_t len;
- // reply to ptrcverify
- if( !strcmp( cmd, "ptrcconfirm" ) )
+ for( i = 1; i < c; i++ )
{
- trap_SendConsoleCommand( "menu ptrc_popmenu\n" );
+ cmd = CG_Argv( i );
+ len = strlen( cmd ) + 1;
+ if( len + gcmdsOffset >= sizeof( registeredCmds ) - 1 )
+ {
+ CG_Printf( "AddCommand: too many commands (%d >= %d)\n",
+ (int)( len + gcmdsOffset ), (int)( sizeof( registeredCmds ) - 1 ) );
+ return;
+ }
+ trap_AddCommand( cmd );
+ strcpy( registeredCmds + gcmdsOffset, cmd );
+ gcmdsOffset += len;
+ }
+}
+void CG_UnregisterCommands( void )
+{
+ size_t len, offset = 0;
+ while( registeredCmds[ offset ] )
+ {
+ len = strlen( registeredCmds + offset );
+ trap_RemoveCommand( registeredCmds + offset );
+ offset += len + 1;
+ }
+ memset( registeredCmds, 0, 2 );
+ gcmdsOffset = 0;
+}
+
+static consoleCommand_t svcommands[ ] =
+{
+ { "chat", CG_Chat_f },
+ { "cmds", CG_GameCmds_f },
+ { "cp", CG_CenterPrint_f },
+ { "cs", CG_ConfigStringModified },
+ { "map_restart", CG_MapRestart },
+ { "poisoncloud", CG_PoisonCloud_f },
+ { "print", CG_Print_f },
+ { "scores", CG_ParseScores },
+ { "serverclosemenus", CG_ServerCloseMenus_f },
+ { "servermenu", CG_ServerMenu_f },
+ { "tinfo", CG_ParseTeamInfo },
+ { "voice", CG_ParseVoice }
+};
+
+/*
+=================
+CG_ServerCommand
+
+The string has been tokenized and can be retrieved with
+Cmd_Argc() / Cmd_Argv()
+=================
+*/
+static void CG_ServerCommand( void )
+{
+ const char *cmd;
+ consoleCommand_t *command;
+
+ cmd = CG_Argv( 0 );
+ command = bsearch( cmd, svcommands, ARRAY_LEN( svcommands ),
+ sizeof( svcommands[ 0 ] ), cmdcmp );
+
+ if( command )
+ {
+ command->function( );
return;
}
diff --git a/src/cgame/cg_snapshot.c b/src/cgame/cg_snapshot.c
index 84b419f..3316abb 100644
--- a/src/cgame/cg_snapshot.c
+++ b/src/cgame/cg_snapshot.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,15 +17,14 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_snapshot.c -- things that happen on snapshot transition,
// not necessarily every single rendered frame
-
#include "cg_local.h"
/*
@@ -41,8 +41,8 @@ static void CG_ResetEntity( centity_t *cent )
cent->trailTime = cg.snap->serverTime;
- VectorCopy( cent->currentState.origin, cent->lerpOrigin );
- VectorCopy( cent->currentState.angles, cent->lerpAngles );
+ VectorCopy( cent->currentState.pos.trBase, cent->lerpOrigin );
+ VectorCopy( cent->currentState.apos.trBase, cent->lerpAngles );
if( cent->currentState.eType == ET_PLAYER )
CG_ResetPlayerEntity( cent );
@@ -142,9 +142,6 @@ static void CG_TransitionSnapshot( void )
// 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++ )
{
@@ -311,7 +308,7 @@ static snapshot_t *CG_ReadNextSnapshot( void )
if( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 )
{
- CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
+ CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i\n",
cg.latestSnapshotNum, cgs.processedSnapshotNum );
}
@@ -461,4 +458,3 @@ void CG_ProcessSnapshots( void )
if( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time )
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
}
-
diff --git a/src/cgame/cg_syscalls.asm b/src/cgame/cg_syscalls.asm
index 2537c91..3b8ed01 100644
--- a/src/cgame/cg_syscalls.asm
+++ b/src/cgame/cg_syscalls.asm
@@ -12,7 +12,7 @@ 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_Write -13
equ trap_FS_FCloseFile -14
equ trap_SendConsoleCommand -15
equ trap_AddCommand -16
@@ -106,6 +106,7 @@ equ trap_Key_SetOverstrikeMode -102
equ trap_Key_GetOverstrikeMode -103
equ trap_S_SoundDuration -104
+equ trap_Field_CompleteList -105
equ memset -201
equ memcpy -202
@@ -118,4 +119,3 @@ equ floor -208
equ ceil -209
equ testPrintInt -210
equ testPrintFloat -211
-
diff --git a/src/cgame/cg_syscalls.c b/src/cgame/cg_syscalls.c
index aa42019..6b81d64 100644
--- a/src/cgame/cg_syscalls.c
+++ b/src/cgame/cg_syscalls.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,15 +17,14 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// 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;
@@ -38,9 +38,9 @@ Q_EXPORT void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) )
int PASSFLOAT( float x )
{
- float floatTemp;
- floatTemp = x;
- return *(int *)&floatTemp;
+ floatint_t fi;
+ fi.f = x;
+ return fi.i;
}
void trap_Print( const char *fmt )
@@ -51,6 +51,8 @@ void trap_Print( const char *fmt )
void trap_Error( const char *fmt )
{
syscall( CG_ERROR, fmt );
+ // shut up GCC warning about returning functions, because we know better
+ exit(1);
}
int trap_Milliseconds( void )
@@ -98,7 +100,7 @@ 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 )
+int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, enum FS_Mode mode )
{
return syscall( CG_FS_FOPENFILE, qpath, f, mode );
}
@@ -118,7 +120,7 @@ void trap_FS_FCloseFile( fileHandle_t f )
syscall( CG_FS_FCLOSEFILE, f );
}
-void trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin )
+void trap_FS_Seek( fileHandle_t f, long offset, enum FS_Origin origin )
{
syscall( CG_FS_SEEK, f, offset, origin );
}
@@ -290,6 +292,13 @@ sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed )
return syscall( CG_S_REGISTERSOUND, sample, compressed );
}
+#ifndef MODULE_INTERFACE_11
+int trap_S_SoundDuration( sfxHandle_t handle )
+{
+ return syscall( CG_S_SOUNDDURATION, handle );
+}
+#endif
+
void trap_S_StartBackgroundTrack( const char *intro, const char *loop )
{
syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop );
@@ -340,6 +349,11 @@ void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *
syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
}
+qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 )
+{
+ return syscall( CG_R_INPVS, p1, p2 );
+}
+
void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num )
{
syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num );
@@ -370,6 +384,13 @@ void trap_R_SetColor( const float *rgba )
syscall( CG_R_SETCOLOR, rgba );
}
+#ifndef MODULE_INTERFACE_11
+void trap_R_SetClipRegion( const float *region )
+{
+ syscall( CG_R_SETCLIPREGION, region );
+}
+#endif
+
void trap_R_DrawStretchPic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader )
{
@@ -572,3 +593,16 @@ void trap_Key_SetBinding( int keynum, const char *binding ) {
syscall( CG_KEY_SETBINDING, keynum, binding );
}
+#ifndef MODULE_INTERFACE_11
+void trap_Key_SetOverstrikeMode( qboolean state ) {
+ syscall( CG_KEY_SETOVERSTRIKEMODE, state );
+}
+
+qboolean trap_Key_GetOverstrikeMode( void ) {
+ return syscall( CG_KEY_GETOVERSTRIKEMODE );
+}
+
+void trap_Field_CompleteList( char *listJson ) {
+ syscall( CG_FIELD_COMPLETELIST, listJson );
+}
+#endif
diff --git a/src/cgame/cg_syscalls_11.asm b/src/cgame/cg_syscalls_11.asm
index 0893ebc..13c262c 100644
--- a/src/cgame/cg_syscalls_11.asm
+++ b/src/cgame/cg_syscalls_11.asm
@@ -12,7 +12,7 @@ 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_Write -13
equ trap_FS_FCloseFile -14
equ trap_SendConsoleCommand -15
equ trap_AddCommand -16
@@ -112,4 +112,3 @@ equ floor -208
equ ceil -209
equ testPrintInt -210
equ testPrintFloat -211
-
diff --git a/src/cgame/cg_trails.c b/src/cgame/cg_trails.c
index c8943ed..c7d2662 100644
--- a/src/cgame/cg_trails.c
+++ b/src/cgame/cg_trails.c
@@ -1,12 +1,13 @@
/*
===========================================================================
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -15,14 +16,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_trails.c -- the trail system
-
#include "cg_local.h"
static baseTrailSystem_t baseTrailSystems[ MAX_BASETRAIL_SYSTEMS ];
@@ -60,7 +60,7 @@ static void CG_CalculateBeamNodeProperties( trailBeam_t *tb )
if( ts->destroyTime > 0 && btb->fadeOutTime )
{
- fadeAlpha -= ( cg.time - ts->destroyTime ) / btb->fadeOutTime;
+ fadeAlpha -= (float)( cg.time - ts->destroyTime ) / btb->fadeOutTime;
if( fadeAlpha < 0.0f )
fadeAlpha = 0.0f;
@@ -559,7 +559,7 @@ static void CG_UpdateBeam( trailBeam_t *tb )
tb->lastEvalTime = cg.time;
// first make sure this beam has enough nodes
- if( ts->destroyTime <= 0 )
+ if( ts->destroyTime <= 0 || btb->fadeOutTime > 0 )
{
nodesToAdd = btb->numSegments - CG_CountBeamNodes( tb ) + 1;
@@ -898,7 +898,7 @@ static qboolean CG_ParseTrailBeam( baseTrailBeam_t *btb, char **text_p )
{
if( btb->numJitters == MAX_TRAIL_BEAM_JITTERS )
{
- CG_Printf( S_COLOR_RED "ERROR: too many jitters\n", token );
+ CG_Printf( S_COLOR_RED "ERROR: too many jitters\n" );
break;
}
@@ -1009,6 +1009,15 @@ static qboolean CG_ParseTrailSystem( baseTrailSystem_t *bts, char **text_p, cons
}
else if( !Q_stricmp( token, "thirdPersonOnly" ) )
bts->thirdPersonOnly = qtrue;
+ else if( !Q_stricmp( token, "lifeTime" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ bts->lifeTime = atoi_neg( token, qfalse );
+ continue;
+ }
else if( !Q_stricmp( token, "beam" ) ) //acceptable text
continue;
else if( !Q_stricmp( token, "}" ) )
@@ -1051,9 +1060,11 @@ static qboolean CG_ParseTrailFile( const char *fileName )
if( len <= 0 )
return qfalse;
- if( len >= sizeof( text ) - 1 )
+ if( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( S_COLOR_RED "ERROR: trail file %s too long\n", fileName );
+ trap_FS_FCloseFile( f );
+ CG_Printf( S_COLOR_RED "ERROR: trail file %s is %s\n", fileName,
+ len == 0 ? "empty" : "too long" );
return qfalse;
}
@@ -1251,11 +1262,14 @@ static trailBeam_t *CG_SpawnNewTrailBeam( baseTrailBeam_t *btb,
if( cg_debugTrails.integer >= 1 )
CG_Printf( "TB %s created\n", ts->class->name );
- break;
+ return tb;
}
}
- return tb;
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "MAX_TRAIL_BEAMS\n" );
+
+ return NULL;
}
@@ -1291,18 +1305,22 @@ trailSystem_t *CG_SpawnNewTrailSystem( qhandle_t psHandle )
ts->valid = qtrue;
ts->destroyTime = -1;
-
+ ts->birthTime = cg.time;
+
for( j = 0; j < bts->numBeams; j++ )
CG_SpawnNewTrailBeam( bts->beams[ j ], ts );
if( cg_debugTrails.integer >= 1 )
CG_Printf( "TS %s created\n", bts->name );
- break;
+ return ts;
}
}
- return ts;
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "MAX_TRAIL_SYSTEMS\n" );
+
+ return NULL;
}
/*
@@ -1406,6 +1424,19 @@ static void CG_GarbageCollectTrailSystems( void )
CG_DestroyTrailSystem( &tempTS );
}
+ // lifetime expired
+ if( ts->destroyTime <= 0 && ts->class->lifeTime &&
+ ts->birthTime + ts->class->lifeTime < cg.time )
+ {
+ trailSystem_t *tempTS = ts;
+
+ CG_DestroyTrailSystem( &tempTS );
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "TS %s expired (born %d, lives %d, now %d)\n",
+ ts->class->name, ts->birthTime, ts->class->lifeTime,
+ cg.time );
+ }
+
if( cg_debugTrails.integer >= 1 && !ts->valid )
CG_Printf( "TS %s garbage collected\n", ts->class->name );
}
diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c
index c418726..e9dbcd6 100644
--- a/src/cgame/cg_tutorial.c
+++ b/src/cgame/cg_tutorial.c
@@ -1,12 +1,13 @@
/*
===========================================================================
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -15,8 +16,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -35,7 +36,8 @@ static bind_t bindings[ ] =
{
{ "+button2", "Activate Upgrade", { -1, -1 } },
{ "+speed", "Run/Walk", { -1, -1 } },
- { "boost", "Sprint", { -1, -1 } },
+ { "+button6", "Dodge", { -1, -1 } },
+ { "+button8", "Sprint", { -1, -1 } },
{ "+moveup", "Jump", { -1, -1 } },
{ "+movedown", "Crouch", { -1, -1 } },
{ "+attack", "Primary Attack", { -1, -1 } },
@@ -49,7 +51,7 @@ static bind_t bindings[ ] =
{ "weapnext", "Next Upgrade", { -1, -1 } }
};
-static const int numBindings = sizeof( bindings ) / sizeof( bind_t );
+static const size_t numBindings = ARRAY_LEN( bindings );
/*
=================
@@ -122,9 +124,8 @@ static const char *CG_KeyNameForCommand( const char *command )
}
else
{
- Q_strncpyz( buffer, va( "\"%s\"", bindings[ i ].humanName ),
- MAX_STRING_CHARS );
- Q_strcat( buffer, MAX_STRING_CHARS, " (unbound)" );
+ Com_sprintf( buffer, MAX_STRING_CHARS, "\"%s\" (unbound)",
+ bindings[ i ].humanName );
}
return buffer;
@@ -157,12 +158,12 @@ static entityState_t *CG_BuildableInRange( playerState_t *ps, float *healthFract
if( healthFraction )
{
- health = es->generic1 & B_HEALTH_MASK;
- *healthFraction = (float)health / B_HEALTH_MASK;
+ health = es->misc;
+ *healthFraction = (float)health / BG_Buildable( es->modelindex )->health;
}
if( es->eType == ET_BUILDABLE &&
- ps->stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
+ ps->stats[ STAT_TEAM ] == BG_Buildable( es->modelindex )->team )
return es;
else
return NULL;
@@ -183,61 +184,61 @@ static void CG_AlienBuilderText( char *text, playerState_t *ps )
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to place the %s\n",
CG_KeyNameForCommand( "+attack" ),
- BG_FindHumanNameForBuildable( buildable ) ) );
+ BG_Buildable( buildable )->humanName ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to cancel placing the %s\n",
CG_KeyNameForCommand( "+button5" ),
- BG_FindHumanNameForBuildable( buildable ) ) );
+ BG_Buildable( buildable )->humanName ) );
}
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( ( es = CG_BuildableInRange( ps, NULL ) ) )
+ {
+ if( cgs.markDeconstruct )
{
- if( cgs.markDeconstruct )
+ if( es->eFlags & EF_B_MARKED )
{
- 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" ) ) );
- }
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to unmark this structure for replacement\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
}
else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to destroy this structure\n",
+ va( "Press %s to mark this structure for replacement\n",
CG_KeyNameForCommand( "deconstruct" ) ) );
}
}
- }
-
- if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG )
- {
- if( ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) == BA_NONE )
+ else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to swipe\n",
- CG_KeyNameForCommand( "+button5" ) ) );
+ va( "Press %s to destroy this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
}
+ }
+ 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" ) ) );
+ }
+
+ if( ps->stats[ STAT_CLASS ] == PCL_ALIEN_BUILDER0_UPG )
+ {
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to launch a projectile\n",
- CG_KeyNameForCommand( "+button2" ) ) );
+ CG_KeyNameForCommand( "+button2" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to walk on walls\n",
- CG_KeyNameForCommand( "+movedown" ) ) );
+ CG_KeyNameForCommand( "+movedown" ) ) );
}
}
@@ -249,7 +250,7 @@ CG_AlienLevel0Text
static void CG_AlienLevel0Text( char *text, playerState_t *ps )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- "Touch a human to damage it\n" );
+ "Touch humans to damage them\n" );
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to walk on walls\n",
@@ -264,13 +265,13 @@ CG_AlienLevel1Text
static void CG_AlienLevel1Text( char *text, playerState_t *ps )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- "Touch a human to grab it\n" );
+ "Touch humans to grab them\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 )
+ if( ps->stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL1_UPG )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to spray poisonous gas\n",
@@ -293,7 +294,7 @@ static void CG_AlienLevel2Text( char *text, playerState_t *ps )
va( "Press %s to bite\n",
CG_KeyNameForCommand( "+attack" ) ) );
- if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL2_UPG )
+ if( ps->stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL2_UPG )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to invoke an electrical attack\n",
@@ -316,7 +317,7 @@ static void CG_AlienLevel3Text( char *text, playerState_t *ps )
va( "Press %s to bite\n",
CG_KeyNameForCommand( "+attack" ) ) );
- if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL3_UPG )
+ if( ps->stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL3_UPG )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to launch a projectile\n",
@@ -340,7 +341,7 @@ static void CG_AlienLevel4Text( char *text, playerState_t *ps )
CG_KeyNameForCommand( "+attack" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Hold down and release %s to charge\n",
+ va( "Hold down and release %s to trample\n",
CG_KeyNameForCommand( "+button5" ) ) );
}
@@ -351,36 +352,47 @@ CG_HumanCkitText
*/
static void CG_HumanCkitText( char *text, playerState_t *ps )
{
- buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
- float health;
+ 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 ) ) );
+ BG_Buildable( buildable )->humanName ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to cancel placing the %s\n",
CG_KeyNameForCommand( "+button5" ),
- BG_FindHumanNameForBuildable( buildable ) ) );
+ BG_Buildable( buildable )->humanName ) );
}
else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to build a structure\n",
CG_KeyNameForCommand( "+attack" ) ) );
+ }
- if( CG_BuildableInRange( ps, &health ) )
+ if( ( es = CG_BuildableInRange( ps, NULL ) ) )
+ {
+ if( cgs.markDeconstruct )
{
- if( health < 1.0f )
+ if( es->eFlags & EF_B_MARKED )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Hold %s to repair this structure\n",
- CG_KeyNameForCommand( "+button5" ) ) );
+ 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" ) ) );
@@ -396,21 +408,17 @@ 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 )
+ if( cg.weaponSelect < 32 )
name = cg_weapons[ cg.weaponSelect ].humanName;
- else if( cg.weaponSelect > 32 )
+ else
{
name = cg_upgrades[ cg.weaponSelect - 32 ].humanName;
upgrade = cg.weaponSelect - 32;
}
- ammo = ps->ammo;
- clips = ps->clips;
-
- if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( ps->weapon ) )
+ if( !ps->ammo && !ps->clips && !BG_Weapon( ps->weapon )->infiniteAmmo )
{
//no ammo
switch( ps->weapon )
@@ -429,7 +437,7 @@ static void CG_HumanText( char *text, playerState_t *ps )
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",
+ va( "Find an Armoury, Reactor, or Repeater and press %s for more ammo\n",
CG_KeyNameForCommand( "buy ammo" ) ) );
break;
@@ -451,14 +459,14 @@ static void CG_HumanText( char *text, playerState_t *ps )
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to fire the %s\n",
CG_KeyNameForCommand( "+attack" ),
- BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ BG_Weapon( ps->weapon )->humanName ) );
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 ) ) );
+ BG_Weapon( ps->weapon )->humanName ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Hold %s to zoom\n",
@@ -469,7 +477,7 @@ static void CG_HumanText( char *text, playerState_t *ps )
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Hold %s to activate the %s\n",
CG_KeyNameForCommand( "+attack" ),
- BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ BG_Weapon( ps->weapon )->humanName ) );
break;
case WP_LUCIFER_CANNON:
@@ -480,11 +488,10 @@ static void CG_HumanText( char *text, playerState_t *ps )
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to fire the %s\n",
CG_KeyNameForCommand( "+button5" ),
- BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ BG_Weapon( ps->weapon )->humanName ) );
break;
case WP_HBUILD:
- case WP_HBUILD2:
CG_HumanCkitText( text, ps );
break;
@@ -501,7 +508,7 @@ static void CG_HumanText( char *text, playerState_t *ps )
CG_KeyNameForCommand( "weapnext" ) ) );
if( upgrade == UP_NONE ||
- ( upgrade > UP_NONE && BG_FindUsableForUpgrade( upgrade ) ) )
+ ( upgrade > UP_NONE && BG_Upgrade( upgrade )->usable ) )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to use the %s\n",
@@ -515,16 +522,52 @@ static void CG_HumanText( char *text, playerState_t *ps )
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to use your %s\n",
CG_KeyNameForCommand( "itemact medkit" ),
- BG_FindHumanNameForUpgrade( UP_MEDKIT ) ) );
+ BG_Upgrade( UP_MEDKIT )->humanName ) );
+ }
+
+ if( ps->stats[ STAT_STAMINA ] <= STAMINA_BLACKOUT_LEVEL )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "You are blacking out. Stop sprinting to recover stamina\n" );
+ }
+ else if( ps->stats[ STAT_STAMINA ] <= STAMINA_SLOW_LEVEL )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "Your stamina is low. Stop sprinting to recover\n" );
+ }
+
+ switch( cg.nearUsableBuildable )
+ {
+ case BA_H_ARMOURY:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to buy equipment upgrades at the %s. Sell your old weapon first!\n",
+ CG_KeyNameForCommand( "+button7" ),
+ BG_Buildable( cg.nearUsableBuildable )->humanName ) );
+ break;
+ case BA_H_REPEATER:
+ case BA_H_REACTOR:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to refill your energy weapon's ammo at the %s\n",
+ CG_KeyNameForCommand( "+button7" ),
+ BG_Buildable( cg.nearUsableBuildable )->humanName ) );
+ break;
+ case BA_NONE:
+ break;
+ default:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to use the %s\n",
+ CG_KeyNameForCommand( "+button7" ),
+ BG_Buildable( cg.nearUsableBuildable )->humanName ) );
+ break;
}
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to use a structure\n",
- CG_KeyNameForCommand( "+button7" ) ) );
+ va( "Press %s and any direction to sprint\n",
+ CG_KeyNameForCommand( "+button8" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to sprint\n",
- CG_KeyNameForCommand( "boost" ) ) );
+ va( "Press %s and back or strafe to dodge\n",
+ CG_KeyNameForCommand( "+button6" ) ) );
}
/*
@@ -534,37 +577,56 @@ CG_SpectatorText
*/
static void CG_SpectatorText( char *text, playerState_t *ps )
{
- if( cgs.clientinfo[ cg.clientNum ].team != PTE_NONE )
+ if( cgs.clientinfo[ cg.clientNum ].team != TEAM_NONE )
{
- Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to spawn\n", CG_KeyNameForCommand( "+attack" ) ) );
+ if( ps->pm_flags & PMF_QUEUED )
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to leave spawn queue\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+ else
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to spawn\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
}
else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to join a team\n", CG_KeyNameForCommand( "+attack" ) ) );
+ va( "Press %s to join a team\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
}
if( ps->pm_flags & PMF_FOLLOW )
{
- Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to stop following\n",
- CG_KeyNameForCommand( "+button2" ) ) );
+ if( !cg.chaseFollow )
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to switch to chase-cam spectator mode\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ else if( cgs.clientinfo[ cg.clientNum ].team == TEAM_NONE )
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to return to free spectator mode\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ else
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to stop following\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s or ", CG_KeyNameForCommand( "weapprev" ) ) );
+ va( "Press %s or ",
+ CG_KeyNameForCommand( "weapprev" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "%s to change player\n", CG_KeyNameForCommand( "weapnext" ) ) );
+ va( "%s to change player\n",
+ CG_KeyNameForCommand( "weapnext" ) ) );
}
else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to follow a %s\n", CG_KeyNameForCommand( "+button2" ),
- ( cgs.clientinfo[ cg.clientNum ].team == PTE_NONE )
- ? "player" : "teammate" ) );
+ va( "Press %s to follow a player\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
}
}
+#define BINDING_REFRESH_INTERVAL 30
+
/*
===============
CG_TutorialText
@@ -576,22 +638,26 @@ const char *CG_TutorialText( void )
{
playerState_t *ps;
static char text[ MAX_TUTORIAL_TEXT ];
+ static int refreshBindings = 0;
+
+ if( refreshBindings == 0 )
+ CG_GetBindings( );
- CG_GetBindings( );
+ refreshBindings = ( refreshBindings + 1 ) % BINDING_REFRESH_INTERVAL;
text[ 0 ] = '\0';
ps = &cg.snap->ps;
if( !cg.intermissionStarted && !cg.demoPlayback )
{
- if( ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR ||
+ if( ps->persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ||
ps->pm_flags & PMF_FOLLOW )
{
CG_SpectatorText( text, ps );
}
else if( ps->stats[ STAT_HEALTH ] > 0 )
{
- switch( ps->stats[ STAT_PCLASS ] )
+ switch( ps->stats[ STAT_CLASS ] )
{
case PCL_ALIEN_BUILDER0:
case PCL_ALIEN_BUILDER0_UPG:
@@ -622,6 +688,7 @@ const char *CG_TutorialText( void )
break;
case PCL_HUMAN:
+ case PCL_HUMAN_BSUIT:
CG_HumanText( text, ps );
break;
@@ -629,26 +696,11 @@ const char *CG_TutorialText( void )
break;
}
- if( ps->stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( ps->stats[ STAT_TEAM ] == TEAM_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 ) )
+ if( BG_AlienCanEvolve( ps->stats[ STAT_CLASS ],
+ ps->persistant[ PERS_CREDIT ],
+ cgs.alienStage ) )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s to evolve\n",
@@ -656,7 +708,23 @@ const char *CG_TutorialText( void )
}
}
}
+ }
+ else if( !cg.demoPlayback )
+ {
+ if( !CG_ClientIsReady( ps->clientNum ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s when ready to continue\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT, "Waiting for other players to be ready\n" );
+ }
+ }
+ if( !cg.demoPlayback )
+ {
Q_strcat( text, MAX_TUTORIAL_TEXT, "Press ESC for the menu" );
}
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 428f299..871b095 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,18 +17,16 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_view.c -- setup all the parameters (position, angle, etc)
// for a 3D rendering
-
#include "cg_local.h"
-
/*
=============================================================================
@@ -181,7 +180,7 @@ static void CG_AddTestModel( void )
return;
}
- // if testing a gun, set the origin reletive to the view origin
+ // if testing a gun, set the origin relative to the view origin
if( cg.testGun )
{
VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
@@ -229,21 +228,7 @@ static void CG_CalcVrect( void )
if( cg.snap->ps.pm_type == PM_INTERMISSION )
size = 100;
else
- {
- // bound normal viewsize
- if( cg_viewsize.integer < 30 )
- {
- trap_Cvar_Set( "cg_viewsize", "30" );
- size = 30;
- }
- else if( cg_viewsize.integer > 100 )
- {
- trap_Cvar_Set( "cg_viewsize","100" );
- size = 100;
- }
- else
- size = cg_viewsize.integer;
- }
+ size = cg_viewsize.integer;
cg.refdef.width = cgs.glconfig.vidWidth * size / 100;
cg.refdef.width &= ~1;
@@ -257,119 +242,282 @@ static void CG_CalcVrect( void )
//==============================================================================
-
/*
===============
CG_OffsetThirdPersonView
===============
*/
-#define FOCUS_DISTANCE 512
-static void CG_OffsetThirdPersonView( void )
+void CG_OffsetThirdPersonView( void )
{
+ int i;
vec3_t forward, right, up;
vec3_t view;
- vec3_t focusAngles;
trace_t trace;
static vec3_t mins = { -8, -8, -8 };
static vec3_t maxs = { 8, 8, 8 };
vec3_t focusPoint;
- float focusDist;
- float forwardScale, sideScale;
vec3_t surfNormal;
-
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
+ int cmdNum;
+ usercmd_t cmd, oldCmd;
+ float range;
+ vec3_t mouseInputAngles;
+ vec3_t rotationAngles;
+ vec3_t axis[ 3 ], rotaxis[ 3 ];
+ float deltaPitch;
+ static float pitch;
+ static vec3_t killerPos = { 0, 0, 0 };
+
+ // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead
+ // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing
+ // a wallwalker because it's really erratic to watch
+ if( ( cg_thirdPersonShoulderViewMode.integer == 2 ) ||
+ ( ( cg_thirdPersonShoulderViewMode.integer == 1 ) &&
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) &&
+ ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) ) )
{
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
- VectorSet( surfNormal, 0.0f, 0.0f, -1.0f );
- else
- VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );
+ CG_OffsetShoulderView( );
+ return;
}
- else
- VectorSet( surfNormal, 0.0f, 0.0f, 1.0f );
+ BG_GetClientNormal( &cg.predictedPlayerState, surfNormal );
+ // Set the view origin to the class's view height
VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg );
- VectorCopy( cg.refdefViewAngles, focusAngles );
+ // Set the focus point where the camera will look (at the player's vieworg)
+ VectorCopy( cg.refdef.vieworg, focusPoint );
- // if dead, look at killer
+ // If player is dead, we want the player to be between us and the killer
+ // so pretend that the player was looking at the killer, then place cam behind them.
if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
{
- focusAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
- cg.refdefViewAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
+ int killerEntNum = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
+
+ // already looking at ourself
+ if( killerEntNum != cg.snap->ps.clientNum )
+ {
+ vec3_t lookDirection;
+ if( cg.wasDeadLastFrame == qfalse || !cg_staticDeathCam.integer )
+ {
+ VectorCopy( cg_entities[ killerEntNum ].lerpOrigin, killerPos );
+ cg.wasDeadLastFrame = qtrue;
+ }
+ VectorSubtract( killerPos, cg.refdef.vieworg, lookDirection );
+ vectoangles( lookDirection, cg.refdefViewAngles );
+ }
}
- //if ( focusAngles[PITCH] > 45 ) {
- // focusAngles[PITCH] = 45; // don't go too far overhead
- //}
- AngleVectors( focusAngles, forward, NULL, NULL );
+ // get and rangecheck cg_thirdPersonRange
+ range = cg_thirdPersonRange.value;
+ if( range > 150.0f ) range = 150.0f;
+ if( range < 30.0f ) range = 30.0f;
+
+ // Calculate the angle of the camera's position around the player.
+ // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player
+ // to control camera position offsets using the mouse position.
+ if( cg.demoPlayback ||
+ ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) &&
+ ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) )
+ {
+ // Collect our input values from the mouse.
+ cmdNum = trap_GetCurrentCmdNumber();
+ trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldCmd );
- VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
+ // Prevent pitch from wrapping and clamp it within a [-75, 90] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves.
+ deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
+ if( fabs(deltaPitch) < 200.0f )
+ {
+ pitch += deltaPitch;
+ }
- VectorCopy( cg.refdef.vieworg, view );
+ mouseInputAngles[ PITCH ] = pitch;
+ mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted
+ mouseInputAngles[ ROLL ] = 0.0f;
+
+ for( i = 0; i < 3; i++ )
+ mouseInputAngles[ i ] = AngleNormalize180( mouseInputAngles[ i ] );
- VectorMA( view, 12, surfNormal, view );
+ // Set the rotation angles to be the view angles offset by the mouse input
+ // Ignore the original pitch though; it's too jerky otherwise
+ if( !cg_thirdPersonPitchFollow.integer )
+ cg.refdefViewAngles[ PITCH ] = 0.0f;
- //cg.refdefViewAngles[PITCH] *= 0.5;
+ for( i = 0; i < 3; i++ )
+ {
+ rotationAngles[ i ] = AngleNormalize180(cg.refdefViewAngles[ i ]) + mouseInputAngles[ i ];
+ AngleNormalize180( rotationAngles[ i ] );
+ }
- AngleVectors( cg.refdefViewAngles, forward, right, up );
+ // Don't let pitch go too high/too low or the camera flips around and
+ // that's really annoying.
+ // However, when we're not on the floor or ceiling (wallwalk) pitch
+ // may not be pitch, so just let it go.
+ if( surfNormal[ 2 ] > 0.5f || surfNormal[ 2 ] < -0.5f )
+ {
+ if( rotationAngles[ PITCH ] > 85.0f )
+ rotationAngles[ PITCH ] = 85.0f;
+ else if( rotationAngles[ PITCH ] < -85.0f )
+ rotationAngles[ PITCH ] = -85.0f;
+ }
+
+ // Perform the rotations specified by rotationAngles.
+ AnglesToAxis( rotationAngles, axis );
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
+ !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
+ cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
+ AxisCopy( axis, rotaxis );
+
+ // Convert the new axis back to angles.
+ AxisToAngles( rotaxis, rotationAngles );
+ }
+ else
+ {
+ if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 )
+ {
+ // If we're playing the game in third person, the viewangles already
+ // take care of our mouselook, so just use them.
+ for( i = 0; i < 3; i++ )
+ rotationAngles[ i ] = cg.refdefViewAngles[ i ];
+ }
+ else // dead
+ {
+ rotationAngles[ PITCH ] = 20.0f;
+ rotationAngles[ YAW ] = cg.refdefViewAngles[ YAW ];
+ }
+ }
- forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
- sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
- VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
- VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
+ rotationAngles[ YAW ] -= cg_thirdPersonAngle.value;
- // trace a ray from the origin to the viewpoint to make sure the view isn't
- // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
+ // Move the camera range distance back.
+ AngleVectors( rotationAngles, forward, right, up );
+ VectorCopy( cg.refdef.vieworg, view );
+ VectorMA( view, -range, forward, view );
+ // Ensure that the current camera position isn't out of bounds and that there
+ // is nothing between the camera and the player.
if( !cg_cameraMode.integer )
{
+ // Trace a ray from the origin to the viewpoint to make sure the view isn't
+ // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
- if( trace.fraction != 1.0 )
+ if( trace.fraction != 1.0f )
{
VectorCopy( trace.endpos, view );
- view[ 2 ] += ( 1.0 - trace.fraction ) * 32;
- // try another trace to this position, because a tunnel may have the ceiling
- // close enogh that this is poking out
+ view[ 2 ] += ( 1.0f - trace.fraction ) * 32;
+ // Try another trace to this position, because a tunnel may have the ceiling
+ // close enogh that this is poking out.
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
VectorCopy( trace.endpos, view );
}
}
+ // Set the camera position to what we calculated.
VectorCopy( view, cg.refdef.vieworg );
- // select pitch to look at focus point from vieword
- VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
- focusDist = sqrt( focusPoint[ 0 ] * focusPoint[ 0 ] + focusPoint[ 1 ] * focusPoint[ 1 ] );
- if ( focusDist < 1 ) {
- focusDist = 1; // should never happen
+ // The above checks may have moved the camera such that the existing viewangles
+ // may not still face the player. Recalculate them to do so.
+ // but if we're dead, don't bother because we'd rather see what killed us
+ if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 )
+ {
+ VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
+ vectoangles( focusPoint, cg.refdefViewAngles );
}
- cg.refdefViewAngles[ PITCH ] = -180 / M_PI * atan2( focusPoint[ 2 ], focusDist );
- cg.refdefViewAngles[ YAW ] -= cg_thirdPersonAngle.value;
}
+/*
+===============
+CG_OffsetShoulderView
+
+===============
+*/
+void CG_OffsetShoulderView( void )
+{
+ int i;
+ int cmdNum;
+ usercmd_t cmd, oldCmd;
+ vec3_t rotationAngles;
+ vec3_t axis[ 3 ], rotaxis[ 3 ];
+ float deltaMousePitch;
+ static float mousePitch;
+ vec3_t forward, right, up;
+ classConfig_t* classConfig;
+
+ // Ignore following pitch; it's too jerky otherwise.
+ if( !cg_thirdPersonPitchFollow.integer )
+ cg.refdefViewAngles[ PITCH ] = 0.0f;
+
+ AngleVectors( cg.refdefViewAngles, forward, right, up );
+
+ classConfig = BG_ClassConfig( cg.snap->ps.stats[ STAT_CLASS ] );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg );
+
+ // If someone is playing like this, the rest is already taken care of
+ // so just get the firstperson effects and leave.
+ if( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ {
+ CG_OffsetFirstPersonView();
+ return;
+ }
+
+ // Get mouse input for camera rotation.
+ cmdNum = trap_GetCurrentCmdNumber();
+ trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldCmd );
+
+ // Prevent pitch from wrapping and clamp it within a [30, -50] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves here.
+ deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
+ if( fabs(deltaMousePitch) < 200.0f )
+ mousePitch += deltaMousePitch;
+
+ // Handle pitch.
+ rotationAngles[ PITCH ] = mousePitch;
+
+ rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) );
+ if( rotationAngles [ PITCH ] < -90.0f ) rotationAngles [ PITCH ] = -90.0f;
+ if( rotationAngles [ PITCH ] > 90.0f ) rotationAngles [ PITCH ] = 90.0f;
+
+ // Yaw and Roll are much easier.
+ rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ];
+ rotationAngles[ ROLL ] = 0.0f;
+
+ // Perform the rotations.
+ AnglesToAxis( rotationAngles, axis );
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
+ !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
+ cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
+ AxisCopy( axis, rotaxis );
+
+ AxisToAngles( rotaxis, rotationAngles );
+
+ // Actually set the viewangles.
+ for( i = 0; i < 3; i++ )
+ cg.refdefViewAngles[ i ] = rotationAngles[ i ];
+
+ // Now run the first person stuff so we get various effects added.
+ CG_OffsetFirstPersonView( );
+}
+
// this causes a compiler bug on mac MrC compiler
static void CG_StepOffset( void )
{
float steptime;
- int timeDelta;
+ int timeDelta;
vec3_t normal;
playerState_t *ps = &cg.predictedPlayerState;
- 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 );
+ BG_GetClientNormal( ps, normal );
- steptime = BG_FindSteptimeForClass( ps->stats[ STAT_PCLASS ] );
+ steptime = BG_Class( ps->stats[ STAT_CLASS ] )->steptime;
// smooth out stair climbing
timeDelta = cg.time - cg.stepTime;
@@ -378,17 +526,15 @@ static void CG_StepOffset( void )
float stepChange = cg.stepChange
* (steptime - timeDelta) / steptime;
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg );
- else
- cg.refdef.vieworg[ 2 ] -= stepChange;
+ VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg );
}
}
-#define PCLOUD_ROLL_AMPLITUDE 25.0f
-#define PCLOUD_ROLL_FREQUENCY 0.4f
-#define PCLOUD_ZOOM_AMPLITUDE 15
-#define PCLOUD_ZOOM_FREQUENCY 0.7f
+#define PCLOUD_ROLL_AMPLITUDE 25.0f
+#define PCLOUD_ROLL_FREQUENCY 0.4f
+#define PCLOUD_ZOOM_AMPLITUDE 15
+#define PCLOUD_ZOOM_FREQUENCY 0.625f // 2.5s / 4
+#define PCLOUD_DISORIENT_DURATION 2500
/*
@@ -397,7 +543,7 @@ CG_OffsetFirstPersonView
===============
*/
-static void CG_OffsetFirstPersonView( void )
+void CG_OffsetFirstPersonView( void )
{
float *origin;
float *angles;
@@ -409,19 +555,10 @@ static void CG_OffsetFirstPersonView( void )
vec3_t predictedVelocity;
int timeDelta;
float bob2;
- vec3_t normal, baseOrigin;
+ vec3_t normal;
playerState_t *ps = &cg.predictedPlayerState;
- 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 );
-
+ BG_GetClientNormal( ps, normal );
if( cg.snap->ps.pm_type == PM_INTERMISSION )
return;
@@ -429,8 +566,6 @@ static void CG_OffsetFirstPersonView( void )
origin = cg.refdef.vieworg;
angles = cg.refdefViewAngles;
- VectorCopy( origin, baseOrigin );
-
// if dead, fix the angle and don't add any kick
if( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 )
{
@@ -441,9 +576,6 @@ static void CG_OffsetFirstPersonView( void )
return;
}
- // add angles based on weapon kick
- VectorAdd( angles, cg.kick_angles, angles );
-
// add angles based on damage kick
if( cg.damageTime )
{
@@ -485,10 +617,10 @@ static void CG_OffsetFirstPersonView( void )
// add angles based on bob
// bob amount is class dependant
- if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
bob2 = 0.0f;
else
- bob2 = BG_FindBobForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] );
+ bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;
#define LEVEL4_FEEDBACK 10.0f
@@ -498,7 +630,8 @@ static void CG_OffsetFirstPersonView( void )
{
if( ps->stats[ STAT_MISC ] > 0 )
{
- float fraction = (float)ps->stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME;
+ float fraction = (float)ps->stats[ STAT_MISC ] /
+ LEVEL4_TRAMPLE_CHARGE_MAX;
if( fraction > 1.0f )
fraction = 1.0f;
@@ -530,26 +663,24 @@ static void CG_OffsetFirstPersonView( void )
#define LEVEL3_FEEDBACK 20.0f
//provide some feedback for pouncing
- if( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
- cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG )
+ if( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
+ cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) &&
+ cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
{
- if( cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
- {
- float fraction1, fraction2;
- vec3_t forward;
-
- AngleVectors( angles, forward, NULL, NULL );
- VectorNormalize( forward );
+ float fraction1, fraction2;
+ vec3_t forward;
- fraction1 = (float)( cg.time - cg.weapon2Time ) / (float)LEVEL3_POUNCE_CHARGE_TIME;
+ AngleVectors( angles, forward, NULL, NULL );
+ VectorNormalize( forward );
- if( fraction1 > 1.0f )
- fraction1 = 1.0f;
+ fraction1 = (float)cg.predictedPlayerState.stats[ STAT_MISC ] /
+ LEVEL3_POUNCE_TIME_UPG;
+ if( fraction1 > 1.0f )
+ fraction1 = 1.0f;
- fraction2 = -sin( fraction1 * M_PI / 2 );
+ fraction2 = -sin( fraction1 * M_PI / 2 );
- VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
- }
+ VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
}
#define STRUGGLE_DIST 5.0f
@@ -562,7 +693,6 @@ static void CG_OffsetFirstPersonView( void )
usercmd_t cmd;
int cmdNum;
float fFraction, rFraction, uFraction;
- float fFraction2, rFraction2, uFraction2;
cmdNum = trap_GetCurrentCmdNumber();
trap_GetUserCmd( cmdNum, &cmd );
@@ -580,10 +710,6 @@ static void CG_OffsetFirstPersonView( void )
if( uFraction > 1.0f )
uFraction = 1.0f;
- fFraction2 = -sin( fFraction * M_PI / 2 );
- rFraction2 = -sin( rFraction * M_PI / 2 );
- uFraction2 = -sin( uFraction * M_PI / 2 );
-
if( cmd.forwardmove > 0 )
VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin );
else if( cmd.forwardmove < 0 )
@@ -606,14 +732,21 @@ static void CG_OffsetFirstPersonView( void )
cg.upMoveTime = cg.time;
}
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
+ if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
+ ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
!( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
- float fraction = sin( ( (float)cg.time / 1000.0f ) * M_PI * 2 * PCLOUD_ROLL_FREQUENCY );
- float pitchFraction = sin( ( (float)cg.time / 1000.0f ) * M_PI * 5 * PCLOUD_ROLL_FREQUENCY );
-
- fraction *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
- pitchFraction *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
+ float scale, fraction, pitchFraction;
+
+ scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
+ BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
+ if( scale < 0.0f )
+ scale = 0.0f;
+
+ fraction = sin( ( cg.time - cg.poisonedTime ) / 500.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
+ scale;
+ pitchFraction = sin( ( cg.time - cg.poisonedTime ) / 200.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
+ scale;
angles[ ROLL ] += fraction * PCLOUD_ROLL_AMPLITUDE;
angles[ YAW ] += fraction * PCLOUD_ROLL_AMPLITUDE;
@@ -621,17 +754,17 @@ static void CG_OffsetFirstPersonView( void )
}
// this *feels* more realisitic for humans
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS &&
+ ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
+ cg.predictedPlayerState.pm_type == PM_JETPACK ) )
{
angles[PITCH] += cg.bobfracsin * bob2 * 0.5;
// heavy breathing effects //FIXME: sound
- if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 )
+ if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < STAMINA_BREATHING_LEVEL )
{
- float deltaBreath = (float)(
- cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 ?
- -cg.predictedPlayerState.stats[ STAT_STAMINA ] :
- cg.predictedPlayerState.stats[ STAT_STAMINA ] ) / 200.0;
+ float deltaBreath = ( cg.predictedPlayerState.stats[ STAT_STAMINA ] -
+ STAMINA_BREATHING_LEVEL ) / -250.0;
float deltaAngle = cos( (float)cg.time/150.0 ) * deltaBreath;
deltaAngle += ( deltaAngle < 0 ? -deltaAngle : deltaAngle ) * 0.5;
@@ -643,11 +776,7 @@ static void CG_OffsetFirstPersonView( void )
//===================================
// add view height
- // when wall climbing the viewheight is not straight up
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( origin, ps->viewheight, normal, origin );
- else
- origin[ 2 ] += cg.predictedPlayerState.viewheight;
+ VectorMA( origin, ps->viewheight, normal, origin );
// smooth out duck height changes
timeDelta = cg.time - cg.duckTime;
@@ -663,12 +792,7 @@ static void CG_OffsetFirstPersonView( void )
if( bob > 6 )
bob = 6;
- // likewise for bob
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( origin, bob, normal, origin );
- else
- origin[ 2 ] += bob;
-
+ VectorMA( origin, bob, normal, origin );
// add fall height
delta = cg.time - cg.landTime;
@@ -687,10 +811,6 @@ static void CG_OffsetFirstPersonView( void )
// add step offset
CG_StepOffset( );
-
- // add kick offset
-
- VectorAdd (origin, cg.kick_origin, origin);
}
//======================================================================
@@ -702,14 +822,17 @@ CG_CalcFov
Fixed fov at intermissions, otherwise account for fov variable and zooms.
====================
*/
-#define WAVE_AMPLITUDE 1
-#define WAVE_FREQUENCY 0.4
+#define WAVE_AMPLITUDE 1.0f
+#define WAVE_FREQUENCY 0.4f
-#define FOVWARPTIME 400.0
+#define FOVWARPTIME 400.0f
+#define BASE_FOV_Y 73.739792f // atan2( 3, 4 / tan( 90 ) )
+#define MAX_FOV_Y 120.0f
+#define MAX_FOV_WARP_Y 127.5f
static int CG_CalcFov( void )
{
- float x;
+ float y;
float phase;
float v;
int contents;
@@ -719,95 +842,114 @@ static int CG_CalcFov( void )
int inwater;
int attribFov;
usercmd_t cmd;
+ usercmd_t oldcmd;
int cmdNum;
cmdNum = trap_GetCurrentCmdNumber( );
trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldcmd );
+
+ // switch follow modes if necessary: cycle between free -> follow -> third-person follow
+ if( cmd.buttons & BUTTON_USE_HOLDABLE && !( oldcmd.buttons & BUTTON_USE_HOLDABLE ) )
+ {
+ if ( cg.snap->ps.pm_flags & PMF_FOLLOW )
+ {
+ if( !cg.chaseFollow )
+ cg.chaseFollow = qtrue;
+ else
+ {
+ cg.chaseFollow = qfalse;
+ trap_SendClientCommand( "follow\n" );
+ }
+ }
+ else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
+ trap_SendClientCommand( "follow\n" );
+ }
if( cg.predictedPlayerState.pm_type == PM_INTERMISSION ||
- ( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) )
+ ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) ||
+ ( cg.renderingThirdPerson ) )
{
- // if in intermission, use a fixed value
- fov_x = 90;
+ // if in intermission or third person, use a fixed value
+ fov_y = BASE_FOV_Y;
}
else
{
// don't lock the fov globally - we need to be able to change it
- attribFov = BG_FindFovForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] );
- fov_x = attribFov;
+ attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov * 0.75f;
+ fov_y = attribFov;
- if ( fov_x < 1 )
- fov_x = 1;
- else if ( fov_x > 160 )
- fov_x = 160;
+ if ( fov_y < 1.0f )
+ fov_y = 1.0f;
+ else if ( fov_y > MAX_FOV_Y )
+ fov_y = MAX_FOV_Y;
if( cg.spawnTime > ( cg.time - FOVWARPTIME ) &&
- BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_FOVWARPS ) )
+ BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) )
{
- float temp, temp2;
-
- temp = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;
- temp2 = ( 170 - fov_x ) * temp;
-
- //Com_Printf( "%f %f\n", temp*100, temp2*100 );
+ float fraction = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;
- fov_x = 170 - temp2;
+ fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction );
}
// account for zooms
- zoomFov = BG_FindZoomFovForWeapon( cg.predictedPlayerState.weapon );
- if ( zoomFov < 1 )
- zoomFov = 1;
+ zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f;
+ if ( zoomFov < 1.0f )
+ zoomFov = 1.0f;
else if ( zoomFov > attribFov )
zoomFov = attribFov;
// only do all the zoom stuff if the client CAN zoom
// FIXME: zoom control is currently hard coded to BUTTON_ATTACK2
- if( BG_WeaponCanZoom( cg.predictedPlayerState.weapon ) )
+ if( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom )
{
if ( cg.zoomed )
{
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
- if ( f > 1.0 )
- fov_x = zoomFov;
+ if ( f > 1.0f )
+ fov_y = zoomFov;
else
- fov_x = fov_x + f * ( zoomFov - fov_x );
+ fov_y = fov_y + f * ( zoomFov - fov_y );
// BUTTON_ATTACK2 isn't held so unzoom next time
if( !( cmd.buttons & BUTTON_ATTACK2 ) )
{
cg.zoomed = qfalse;
- cg.zoomTime = cg.time;
+ cg.zoomTime = MIN( cg.time,
+ cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
}
}
else
{
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
- if ( f <= 1.0 )
- fov_x = zoomFov + f * ( fov_x - zoomFov );
+ if ( f > 1.0f )
+ fov_y = fov_y;
+ else
+ fov_y = zoomFov + f * ( fov_y - zoomFov );
// BUTTON_ATTACK2 is held so zoom next time
if( cmd.buttons & BUTTON_ATTACK2 )
{
cg.zoomed = qtrue;
- cg.zoomTime = cg.time;
+ cg.zoomTime = MIN( cg.time,
+ cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
}
}
}
}
- x = cg.refdef.width / tan( fov_x / 360 * M_PI );
- fov_y = atan2( cg.refdef.height, x );
- fov_y = fov_y * 360 / M_PI;
+ y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) );
+ fov_x = atan2( cg.refdef.width, y );
+ fov_x = 2.0f * RAD2DEG( fov_x );
// warp if underwater
contents = CG_PointContents( cg.refdef.vieworg, -1 );
if( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
{
- phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
+ phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f;
v = WAVE_AMPLITUDE * sin( phase );
fov_x += v;
fov_y -= v;
@@ -816,13 +958,16 @@ static int CG_CalcFov( void )
else
inwater = qfalse;
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
+ if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
+ ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 &&
!( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
- phase = cg.time / 1000.0 * PCLOUD_ZOOM_FREQUENCY * M_PI * 2;
- v = PCLOUD_ZOOM_AMPLITUDE * sin( phase );
- v *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
+ float scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
+ BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
+
+ phase = ( cg.time - cg.poisonedTime ) / 1000.0f * PCLOUD_ZOOM_FREQUENCY * M_PI * 2.0f;
+ v = PCLOUD_ZOOM_AMPLITUDE * sin( phase ) * scale;
fov_x += v;
fov_y += v;
}
@@ -833,9 +978,9 @@ static int CG_CalcFov( void )
cg.refdef.fov_y = fov_y;
if( !cg.zoomed )
- cg.zoomSensitivity = 1;
+ cg.zoomSensitivity = 1.0f;
else
- cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
+ cg.zoomSensitivity = cg.refdef.fov_y / 75.0f;
return inwater;
}
@@ -941,10 +1086,7 @@ static void CG_smoothWWTransitions( playerState_t *ps, const vec3_t in, vec3_t o
}
//set surfNormal
- if( !( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) )
- VectorCopy( ps->grapplePoint, surfNormal );
- else
- VectorCopy( ceilingNormal, surfNormal );
+ BG_GetClientNormal( ps, surfNormal );
AnglesToAxis( in, inAxis );
@@ -952,7 +1094,6 @@ static void CG_smoothWWTransitions( playerState_t *ps, const vec3_t in, vec3_t o
if( !VectorCompare( surfNormal, cg.lastNormal ) )
{
//if we moving from the ceiling to the floor special case
- //( x product of colinear vectors is undefined)
if( VectorCompare( ceilingNormal, cg.lastNormal ) &&
VectorCompare( refNormal, surfNormal ) )
{
@@ -1091,7 +1232,8 @@ static int CG_CalcViewValues( void )
ps = &cg.predictedPlayerState;
// intermission view
- if( ps->pm_type == PM_INTERMISSION )
+ if( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_FREEZE ||
+ ps->pm_type == PM_SPECTATOR )
{
VectorCopy( ps->origin, cg.refdef.vieworg );
VectorCopy( ps->viewangles, cg.refdefViewAngles );
@@ -1105,18 +1247,22 @@ static int CG_CalcViewValues( void )
cg.xyspeed = sqrt( ps->velocity[ 0 ] * ps->velocity[ 0 ] +
ps->velocity[ 1 ] * ps->velocity[ 1 ] );
+ // the bob velocity should't get too fast to avoid jerking
+ if( cg.xyspeed > 300.0f )
+ cg.xyspeed = 300.0f;
+
VectorCopy( ps->origin, cg.refdef.vieworg );
- if( BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLCLIMBER ) )
+ if( BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLCLIMBER ) )
CG_smoothWWTransitions( ps, ps->viewangles, cg.refdefViewAngles );
- else if( BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLJUMPER ) )
+ else if( BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
CG_smoothWJTransitions( ps, ps->viewangles, cg.refdefViewAngles );
else
VectorCopy( ps->viewangles, cg.refdefViewAngles );
//clumsy logic, but it needs to be this way round because the CS propogation
//delay screws things up otherwise
- if( !BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLJUMPER ) )
+ if( !BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
{
if( !( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) )
VectorSet( cg.lastNormal, 0.0f, 0.0f, 1.0f );
@@ -1143,6 +1289,8 @@ static int CG_CalcViewValues( void )
if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) )
CG_DestroyParticleSystem( &cg.poisonCloudPS );
}
+ else
+ cg.wasDeadLastFrame = qfalse;
if( cg.renderingThirdPerson )
{
@@ -1155,7 +1303,7 @@ static int CG_CalcViewValues( void )
CG_OffsetFirstPersonView( );
}
- // position eye reletive to origin
+ // position eye relative to origin
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
if( cg.hyperspace )
@@ -1260,7 +1408,11 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
CG_PredictPlayerState( );
// decide on third person view
- cg.renderingThirdPerson = cg_thirdPerson.integer || ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 );
+ cg.renderingThirdPerson = ( cg_thirdPerson.integer || ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 ) ||
+ ( cg.chaseFollow && cg.snap->ps.pm_flags & PMF_FOLLOW) );
+
+ // update speedometer
+ CG_AddSpeed( );
// build cg.refdef
inwater = CG_CalcViewValues( );
@@ -1335,4 +1487,3 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
if( cg_stats.integer )
CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
}
-
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index 9aa9f9d..fc88606 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,14 +17,13 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_weapons.c -- events and effects dealing with weapons
-
#include "cg_local.h"
/*
@@ -38,26 +38,32 @@ void CG_RegisterUpgrade( int upgradeNum )
upgradeInfo_t *upgradeInfo;
char *icon;
- upgradeInfo = &cg_upgrades[ upgradeNum ];
-
- if( upgradeNum == 0 )
+ if( upgradeNum <= UP_NONE || upgradeNum >= UP_NUM_UPGRADES )
+ {
+ CG_Error( "CG_RegisterUpgrade: out of range: %d", upgradeNum );
return;
+ }
+
+ upgradeInfo = &cg_upgrades[ upgradeNum ];
if( upgradeInfo->registered )
+ {
+ CG_Printf( "CG_RegisterUpgrade: already registered: (%d) %s\n", upgradeNum,
+ BG_Upgrade( upgradeNum )->name );
return;
+ }
- memset( upgradeInfo, 0, sizeof( *upgradeInfo ) );
upgradeInfo->registered = qtrue;
- if( !BG_FindNameForUpgrade( upgradeNum ) )
+ if( !BG_Upgrade( upgradeNum )->name[ 0 ] )
CG_Error( "Couldn't find upgrade %i", upgradeNum );
- upgradeInfo->humanName = BG_FindHumanNameForUpgrade( upgradeNum );
+ upgradeInfo->humanName = BG_Upgrade( upgradeNum )->humanName;
//la la la la la, i'm not listening!
if( upgradeNum == UP_GRENADE )
upgradeInfo->upgradeIcon = cg_weapons[ WP_GRENADE ].weaponIcon;
- else if( ( icon = BG_FindIconForUpgrade( upgradeNum ) ) )
+ else if( ( icon = BG_Upgrade( upgradeNum )->icon ) )
upgradeInfo->upgradeIcon = trap_R_RegisterShader( icon );
}
@@ -72,7 +78,7 @@ void CG_InitUpgrades( void )
{
int i;
- memset( cg_upgrades, 0, sizeof( cg_upgrades ) );
+ Com_Memset( cg_upgrades, 0, sizeof( cg_upgrades ) );
for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
CG_RegisterUpgrade( i );
@@ -80,6 +86,105 @@ void CG_InitUpgrades( void )
/*
+======================
+CG_ParseWeaponAnimationFile
+
+Read a configuration file containing animation counts and rates
+models/weapons/rifle/animation.cfg, etc
+======================
+*/
+static qboolean CG_ParseWeaponAnimationFile( const char *filename, weaponInfo_t *weapon )
+{
+ char *text_p;
+ int len;
+ int i;
+ char *token;
+ float fps;
+ char text[ 20000 ];
+ fileHandle_t f;
+ animation_t *animations;
+
+ animations = weapon->animations;
+
+ // load the file
+ len = trap_FS_FOpenFile( filename, &f, FS_READ );
+ if( len < 0 )
+ return qfalse;
+
+ if( len == 0 || len >= sizeof( text ) - 1 )
+ {
+ trap_FS_FCloseFile( f );
+ CG_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
+ return qfalse;
+ }
+
+ trap_FS_Read( text, len, f );
+ text[ len ] = 0;
+ trap_FS_FCloseFile( f );
+
+ // parse the text
+ text_p = text;
+
+ // read information for each frame
+ for( i = WANIM_NONE + 1; i < MAX_WEAPON_ANIMATIONS; i++ )
+ {
+
+ token = COM_Parse( &text_p );
+ if( !*token )
+ break;
+
+ if( !Q_stricmp( token, "noDrift" ) )
+ {
+ weapon->noDrift = qtrue;
+ continue;
+ }
+
+ animations[ i ].firstFrame = atoi( token );
+
+ token = COM_Parse( &text_p );
+ if( !*token )
+ break;
+
+ animations[ i ].numFrames = atoi( token );
+ animations[ i ].reversed = qfalse;
+ animations[ i ].flipflop = qfalse;
+
+ // if numFrames is negative the animation is reversed
+ if( animations[ i ].numFrames < 0 )
+ {
+ animations[ i ].numFrames = -animations[ i ].numFrames;
+ animations[ i ].reversed = qtrue;
+ }
+
+ token = COM_Parse( &text_p );
+ if ( !*token )
+ break;
+
+ animations[i].loopFrames = atoi( token );
+
+ token = COM_Parse( &text_p );
+ if( !*token )
+ break;
+
+ fps = atof( token );
+ if( fps == 0 )
+ fps = 1;
+
+ animations[ i ].frameLerp = 1000 / fps;
+ animations[ i ].initialLerp = 1000 / fps;
+ }
+
+ if( i != MAX_WEAPON_ANIMATIONS )
+ {
+ CG_Printf( "Error parsing animation file: %s\n", filename );
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+
+/*
===============
CG_ParseWeaponModeSection
@@ -141,6 +246,16 @@ static qboolean CG_ParseWeaponModeSection( weaponInfoMode_t *wim, char **text_p
continue;
}
+ else if( !Q_stricmp( token, "missileSpriteCharge" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !token )
+ break;
+
+ wim->missileSpriteCharge = atof( token );
+
+ continue;
+ }
else if( !Q_stricmp( token, "missileRotates" ) )
{
wim->missileRotates = qtrue;
@@ -429,12 +544,13 @@ static qboolean CG_ParseWeaponFile( const char *filename, weaponInfo_t *wi )
// load the file
len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if( len <= 0 )
+ if( len < 0 )
return qfalse;
- if( len >= sizeof( text ) - 1 )
+ if( len == 0 || len >= sizeof( text ) - 1 )
{
- CG_Printf( "File %s too long\n", filename );
+ trap_FS_FCloseFile( f );
+ CG_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
return qfalse;
}
@@ -517,8 +633,33 @@ static qboolean CG_ParseWeaponFile( const char *filename, weaponInfo_t *wi )
strcat( path, "_hand.md3" );
wi->handsModel = trap_R_RegisterModel( path );
- if( !wi->handsModel )
- wi->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
+ continue;
+ }
+ else if( !Q_stricmp( token, "weaponModel3rdPerson" ) )
+ {
+ char path[ MAX_QPATH ];
+
+ token = COM_Parse( &text_p );
+ if( !token )
+ break;
+
+ wi->weaponModel3rdPerson = trap_R_RegisterModel( token );
+
+ if( !wi->weaponModel3rdPerson )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: 3rd person weapon "
+ "model not found %s\n", token );
+ }
+
+ strcpy( path, token );
+ COM_StripExtension( path, path, MAX_QPATH );
+ strcat( path, "_flash.md3" );
+ wi->flashModel3rdPerson = trap_R_RegisterModel( path );
+
+ strcpy( path, token );
+ COM_StripExtension( path, path, MAX_QPATH );
+ strcat( path, "_barrel.md3" );
+ wi->barrelModel3rdPerson = trap_R_RegisterModel( path );
continue;
}
@@ -596,35 +737,42 @@ void CG_RegisterWeapon( int weaponNum )
vec3_t mins, maxs;
int i;
- weaponInfo = &cg_weapons[ weaponNum ];
-
- if( weaponNum == 0 )
+ if( weaponNum <= WP_NONE || weaponNum >= WP_NUM_WEAPONS )
+ {
+ CG_Error( "CG_RegisterWeapon: out of range: %d", weaponNum );
return;
+ }
+
+ weaponInfo = &cg_weapons[ weaponNum ];
if( weaponInfo->registered )
+ {
+ CG_Printf( "CG_RegisterWeapon: already registered: (%d) %s\n", weaponNum,
+ BG_Weapon( weaponNum )->name );
return;
+ }
- memset( weaponInfo, 0, sizeof( *weaponInfo ) );
weaponInfo->registered = qtrue;
- if( !BG_FindNameForWeapon( weaponNum ) )
+ if( !BG_Weapon( weaponNum )->name[ 0 ] )
CG_Error( "Couldn't find weapon %i", weaponNum );
- Com_sprintf( path, MAX_QPATH, "models/weapons/%s/weapon.cfg", BG_FindNameForWeapon( weaponNum ) );
+ Com_sprintf( path, MAX_QPATH, "models/weapons/%s/weapon.cfg", BG_Weapon( weaponNum )->name );
- weaponInfo->humanName = BG_FindHumanNameForWeapon( weaponNum );
+ weaponInfo->humanName = BG_Weapon( weaponNum )->humanName;
if( !CG_ParseWeaponFile( path, weaponInfo ) )
Com_Printf( S_COLOR_RED "ERROR: failed to parse %s\n", path );
+ Com_sprintf( path, MAX_QPATH, "models/weapons/%s/animation.cfg", BG_Weapon( weaponNum )->name );
+
+ if( !CG_ParseWeaponAnimationFile( path, weaponInfo ) )
+ Com_Printf( S_COLOR_RED "ERROR: failed to parse %s\n", path );
+
// calc midpoint for rotation
trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs );
for( i = 0 ; i < 3 ; i++ )
weaponInfo->weaponMidpoint[ i ] = mins[ i ] + 0.5 * ( maxs[ i ] - mins[ i ] );
-
- //FIXME:
- for( i = WPM_NONE + 1; i < WPM_NUM_WEAPONMODES; i++ )
- weaponInfo->wim[ i ].loopFireSound = qfalse;
}
/*
@@ -638,7 +786,7 @@ void CG_InitWeapons( void )
{
int i;
- memset( cg_weapons, 0, sizeof( cg_weapons ) );
+ Com_Memset( cg_weapons, 0, sizeof( cg_weapons ) );
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
CG_RegisterWeapon( i );
@@ -656,6 +804,53 @@ VIEW WEAPON
*/
/*
+===============
+CG_SetWeaponLerpFrameAnimation
+
+may include ANIM_TOGGLEBIT
+===============
+*/
+static void CG_SetWeaponLerpFrameAnimation( weapon_t weapon, lerpFrame_t *lf, int newAnimation )
+{
+ animation_t *anim;
+
+ lf->animationNumber = newAnimation;
+ newAnimation &= ~ANIM_TOGGLEBIT;
+
+ if( newAnimation < 0 || newAnimation >= MAX_WEAPON_ANIMATIONS )
+ CG_Error( "Bad animation number: %i", newAnimation );
+
+ anim = &cg_weapons[ weapon ].animations[ newAnimation ];
+
+ lf->animation = anim;
+ lf->animationTime = lf->frameTime + anim->initialLerp;
+
+ if( cg_debugAnim.integer )
+ CG_Printf( "Anim: %i\n", newAnimation );
+}
+
+/*
+===============
+CG_WeaponAnimation
+===============
+*/
+static void CG_WeaponAnimation( centity_t *cent, int *old, int *now, float *backLerp )
+{
+ lerpFrame_t *lf = &cent->pe.weapon;
+ entityState_t *es = &cent->currentState;
+
+ // see if the animation sequence is switching
+ if( es->weaponAnim != lf->animationNumber || !lf->animation )
+ CG_SetWeaponLerpFrameAnimation( es->weapon, lf, es->weaponAnim );
+
+ CG_RunLerpFrame( lf, 1.0f );
+
+ *old = lf->oldFrame;
+ *now = lf->frame;
+ *backLerp = lf->backlerp;
+}
+
+/*
=================
CG_MapTorsoToWeaponFrame
@@ -690,10 +885,13 @@ CG_CalculateWeaponPosition
*/
static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles )
{
- float scale;
- int delta;
- float fracsin;
- float bob;
+ float scale;
+ int delta;
+ float fracsin;
+ float bob;
+ weaponInfo_t *weapon;
+
+ weapon = &cg_weapons[ cg.predictedPlayerState.weapon ];
VectorCopy( cg.refdef.vieworg, origin );
VectorCopy( cg.refdefViewAngles, angles );
@@ -706,7 +904,7 @@ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles )
// gun angles from bobbing
// bob amount is class dependant
- bob = BG_FindBobForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] );
+ bob = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;
if( bob != 0 )
{
@@ -716,7 +914,7 @@ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles )
}
// drop the weapon when landing
- if( !BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_NOWEAPONDRIFT ) )
+ if( !weapon->noDrift )
{
delta = cg.time - cg.landTime;
if( delta < LAND_DEFLECT_TIME )
@@ -795,6 +993,13 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
weaponNum = cent->currentState.weapon;
weaponMode = cent->currentState.generic1;
+ if( weaponNum <= WP_NONE || weaponNum >= WP_NUM_WEAPONS )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_AddPlayerWeapon: weapon "
+ "number %i is out of bounds", weaponNum );
+ return;
+ }
+
if( weaponMode <= WPM_NONE || weaponMode >= WPM_NUM_WEAPONMODES )
weaponMode = WPM_PRIMARY;
@@ -805,16 +1010,23 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
else
firing = qfalse;
- CG_RegisterWeapon( weaponNum );
weapon = &cg_weapons[ weaponNum ];
+ if( !weapon->registered )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_AddPlayerWeapon: weapon %d (%s) "
+ "is not registered\n", weaponNum, BG_Weapon( weaponNum )->name );
+ return;
+ }
// add the weapon
- memset( &gun, 0, sizeof( gun ) );
+ Com_Memset( &gun, 0, sizeof( gun ) );
+ Com_Memset( &barrel, 0, sizeof( barrel ) );
+ Com_Memset( &flash, 0, sizeof( flash ) );
+
VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
gun.shadowPlane = parent->shadowPlane;
gun.renderfx = parent->renderfx;
- // set custom shading for railgun refire rate
if( ps )
{
gun.shaderRGBA[ 0 ] = 255;
@@ -842,7 +1054,15 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
}
}
- gun.hModel = weapon->weaponModel;
+ if( !ps )
+ {
+ gun.hModel = weapon->weaponModel3rdPerson;
+
+ if( !gun.hModel )
+ gun.hModel = weapon->weaponModel;
+ }
+ else
+ gun.hModel = weapon->weaponModel;
noGunModel = ( ( !ps || cg.renderingThirdPerson ) && weapon->disableIn3rdPerson ) || !gun.hModel;
@@ -858,27 +1078,46 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound );
}
+ // Lucifer cannon charge warning beep
+ if( weaponNum == WP_LUCIFER_CANNON &&
+ ( cent->currentState.eFlags & EF_WARN_CHARGE ) &&
+ cg.snap->ps.stats[ STAT_TEAM ] != TEAM_ALIENS )
+ {
+ trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
+ vec3_origin, ps ? cgs.media.lCannonWarningSound :
+ cgs.media.lCannonWarningSound2 );
+ }
+
if( !noGunModel )
{
CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon" );
+ CG_WeaponAnimation( cent, &gun.oldframe, &gun.frame, &gun.backlerp );
trap_R_AddRefEntityToScene( &gun );
+ if( !ps )
+ {
+ barrel.hModel = weapon->barrelModel3rdPerson;
+
+ if( !barrel.hModel )
+ barrel.hModel = weapon->barrelModel;
+ }
+ else
+ barrel.hModel = weapon->barrelModel;
+
// add the spinning barrel
- if( weapon->barrelModel )
+ if( barrel.hModel )
{
- memset( &barrel, 0, sizeof( barrel ) );
VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
barrel.shadowPlane = parent->shadowPlane;
barrel.renderfx = parent->renderfx;
- barrel.hModel = weapon->barrelModel;
angles[ YAW ] = 0;
angles[ PITCH ] = 0;
angles[ ROLL ] = CG_MachinegunSpinAngle( cent, firing );
AnglesToAxis( angles, barrel.axis );
- CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" );
+ CG_PositionRotatedEntityOnTag( &barrel, &gun, gun.hModel, "tag_barrel" );
trap_R_AddRefEntityToScene( &barrel );
}
@@ -892,7 +1131,7 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
if( noGunModel )
CG_SetAttachmentTag( &cent->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" );
else
- CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" );
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, gun.hModel, "tag_flash" );
}
//if the PS is infinite disable it when not firing
@@ -908,12 +1147,20 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
return;
}
- memset( &flash, 0, sizeof( flash ) );
VectorCopy( parent->lightingOrigin, flash.lightingOrigin );
flash.shadowPlane = parent->shadowPlane;
flash.renderfx = parent->renderfx;
- flash.hModel = weapon->flashModel;
+ if( !ps )
+ {
+ flash.hModel = weapon->flashModel3rdPerson;
+
+ if( !flash.hModel )
+ flash.hModel = weapon->flashModel;
+ }
+ else
+ flash.hModel = weapon->flashModel;
+
if( flash.hModel )
{
angles[ YAW ] = 0;
@@ -924,7 +1171,7 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
if( noGunModel )
CG_PositionRotatedEntityOnTag( &flash, parent, parent->hModel, "tag_weapon" );
else
- CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash" );
+ CG_PositionRotatedEntityOnTag( &flash, &gun, gun.hModel, "tag_flash" );
trap_R_AddRefEntityToScene( &flash );
}
@@ -941,7 +1188,7 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
if( noGunModel )
CG_SetAttachmentTag( &cent->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" );
else
- CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" );
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, gun.hModel, "tag_flash" );
CG_SetAttachmentCent( &cent->muzzlePS->attachment, cent );
CG_AttachToTag( &cent->muzzlePS->attachment );
@@ -970,6 +1217,9 @@ CG_AddViewWeapon
Add the weapon, and flash for the player's view
==============
*/
+
+#define WEAPON_CLICK_REPEAT 500
+
void CG_AddViewWeapon( playerState_t *ps )
{
refEntity_t hand;
@@ -981,20 +1231,23 @@ void CG_AddViewWeapon( playerState_t *ps )
weapon_t weapon = ps->weapon;
weaponMode_t weaponMode = ps->generic1;
+ // no weapon carried - can't draw it
+ if( weapon == WP_NONE )
+ return;
+
if( weaponMode <= WPM_NONE || weaponMode >= WPM_NUM_WEAPONMODES )
weaponMode = WPM_PRIMARY;
- CG_RegisterWeapon( weapon );
wi = &cg_weapons[ weapon ];
- cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
-
- if( ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) ||
- ( ps->stats[ STAT_STATE ] & SS_INFESTING ) ||
- ( ps->stats[ STAT_STATE ] & SS_HOVELING ) )
+ if( !wi->registered )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_AddViewWeapon: weapon %d (%s) "
+ "is not registered\n", weapon, BG_Weapon( weapon )->name );
return;
+ }
+ cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum];
- // no weapon carried - can't draw it
- if( weapon == WP_NONE )
+ if( ps->persistant[PERS_SPECSTATE] != SPECTATOR_NOT )
return;
if( ps->pm_type == PM_INTERMISSION )
@@ -1004,12 +1257,6 @@ void CG_AddViewWeapon( playerState_t *ps )
if( ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE )
CG_GhostBuildable( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT );
- if( weapon == WP_LUCIFER_CANNON && ps->stats[ STAT_MISC ] > 0 )
- {
- if( ps->stats[ STAT_MISC ] > ( LCANNON_TOTAL_CHARGE - ( LCANNON_TOTAL_CHARGE / 3 ) ) )
- trap_S_AddLoopingSound( ps->clientNum, ps->origin, vec3_origin, cgs.media.lCannonWarningSound );
- }
-
// no gun if in third person view
if( cg.renderingThirdPerson )
return;
@@ -1052,7 +1299,7 @@ void CG_AddViewWeapon( playerState_t *ps )
else
fovOffset = 0;
- memset( &hand, 0, sizeof( hand ) );
+ Com_Memset( &hand, 0, sizeof( hand ) );
// set up gun position
CG_CalculateWeaponPosition( hand.origin, angles );
@@ -1061,12 +1308,16 @@ void CG_AddViewWeapon( playerState_t *ps )
VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[ 1 ], hand.origin );
VectorMA( hand.origin, ( cg_gun_z.value + fovOffset ), cg.refdef.viewaxis[ 2 ], hand.origin );
+ // Lucifer Cannon vibration effect
if( weapon == WP_LUCIFER_CANNON && ps->stats[ STAT_MISC ] > 0 )
{
- float fraction = (float)ps->stats[ STAT_MISC ] / (float)LCANNON_TOTAL_CHARGE;
+ float fraction;
- VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 0 ], hand.origin );
- VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 1 ], hand.origin );
+ fraction = (float)ps->stats[ STAT_MISC ] / LCANNON_CHARGE_TIME_MAX;
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 0 ],
+ hand.origin );
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 1 ],
+ hand.origin );
}
AnglesToAxis( angles, hand.axis );
@@ -1109,19 +1360,7 @@ CG_WeaponSelectable
*/
static qboolean CG_WeaponSelectable( weapon_t weapon )
{
- //int ammo, clips;
- //
- //ammo = cg.snap->ps.ammo;
- //clips = cg.snap->ps.clips
- //
- // this is a pain in the ass
- //if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( i ) )
- // return qfalse;
-
- if( !BG_InventoryContainsWeapon( weapon, cg.snap->ps.stats ) )
- return qfalse;
-
- return qtrue;
+ return BG_InventoryContainsWeapon( weapon, cg.snap->ps.stats );
}
@@ -1135,7 +1374,7 @@ static qboolean CG_UpgradeSelectable( upgrade_t upgrade )
if( !BG_InventoryContainsUpgrade( upgrade, cg.snap->ps.stats ) )
return qfalse;
- return BG_FindUsableForUpgrade( upgrade );
+ return BG_Upgrade( upgrade )->usable;
}
@@ -1149,22 +1388,19 @@ CG_DrawItemSelect
void CG_DrawItemSelect( rectDef_t *rect, vec4_t color )
{
int i;
- int x = rect->x;
- int y = rect->y;
- int width = rect->w;
- int height = rect->h;
- int iconsize;
+ float x = rect->x;
+ float y = rect->y;
+ float width = rect->w;
+ float height = rect->h;
+ float iconWidth;
+ float iconHeight;
int items[ 64 ];
+ int colinfo[ 64 ];
int numItems = 0, selectedItem = 0;
int length;
- int selectWindow;
qboolean vertical;
- centity_t *cent;
playerState_t *ps;
-
- int colinfo[ 64 ];
- cent = &cg_entities[ cg.snap->ps.clientNum ];
ps = &cg.snap->ps;
// don't display if dead
@@ -1174,109 +1410,121 @@ void CG_DrawItemSelect( rectDef_t *rect, vec4_t color )
if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
// first make sure that whatever it selected is actually selectable
- if( cg.weaponSelect <= 32 && !CG_WeaponSelectable( cg.weaponSelect ) )
- CG_NextWeapon_f( );
- else if( cg.weaponSelect > 32 && !CG_UpgradeSelectable( cg.weaponSelect - 32 ) )
- CG_NextWeapon_f( );
+ if( cg.weaponSelect < 32 )
+ {
+ if( !CG_WeaponSelectable( cg.weaponSelect ) )
+ CG_NextWeapon_f( );
+ }
+ else
+ {
+ if( !CG_UpgradeSelectable( cg.weaponSelect - 32 ) )
+ CG_NextWeapon_f( );
+ }
}
// showing weapon select clears pickup item display, but not the blend blob
cg.itemPickupTime = 0;
- if( height > width )
- {
- vertical = qtrue;
- iconsize = width;
- length = height / width;
- }
- else
- {
- vertical = qfalse;
- iconsize = height;
- length = width / height;
- }
-
- selectWindow = length / 2;
-
+ // put all weapons in the items list
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
{
if( !BG_InventoryContainsWeapon( i, cg.snap->ps.stats ) )
continue;
-
- {
- int ammo, clips;
-
- ammo = cg.snap->ps.ammo;
- clips = cg.snap->ps.clips;
-
- if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( i ) )
- colinfo[ numItems ] = 1;
- else
- colinfo[ numItems ] = 0;
-
- }
+
+ if( !ps->ammo && !ps->clips && !BG_Weapon( i )->infiniteAmmo )
+ colinfo[ numItems ] = 1;
+ else
+ colinfo[ numItems ] = 0;
if( i == cg.weaponSelect )
selectedItem = numItems;
- CG_RegisterWeapon( i );
+ if( !cg_weapons[ i ].registered )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_DrawItemSelect: weapon %d (%s) "
+ "is not registered\n", i, BG_Weapon( i )->name );
+ continue;
+ }
items[ numItems ] = i;
numItems++;
}
+ // put all upgrades in the weapons list
for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
{
if( !BG_InventoryContainsUpgrade( i, cg.snap->ps.stats ) )
continue;
colinfo[ numItems ] = 0;
- if( !BG_FindUsableForUpgrade ( i ) )
+ if( !BG_Upgrade( i )->usable )
colinfo[ numItems ] = 2;
-
if( i == cg.weaponSelect - 32 )
selectedItem = numItems;
- CG_RegisterUpgrade( i );
+ if( !cg_upgrades[ i ].registered )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: CG_DrawItemSelect: upgrade %d (%s) "
+ "is not registered\n", i, BG_Upgrade( i )->name );
+ continue;
+ }
items[ numItems ] = i + 32;
numItems++;
}
+ // compute the length of the display window and determine orientation
+ vertical = height > width;
+ if( vertical )
+ {
+ iconWidth = width * cgDC.aspectScale;
+ iconHeight = width;
+ length = height / ( width * cgDC.aspectScale );
+ }
+ else
+ {
+ iconWidth = height * cgDC.aspectScale;
+ iconHeight = height;
+ length = width / ( height * cgDC.aspectScale );
+ }
+
+ // render icon ring
for( i = 0; i < length; i++ )
{
- int displacement = i - selectWindow;
- int item = displacement + selectedItem;
+ int item = i - length / 2 + selectedItem;
- if( ( item >= 0 ) && ( item < numItems ) )
+ if( item < 0 )
+ item += length;
+ else if( item >= length )
+ item -= length;
+ if( item >= 0 && item < numItems )
{
switch( colinfo[ item ] )
{
- case 0:
- color = colorCyan;
- break;
- case 1:
- color = colorRed;
- break;
- case 2:
- color = colorGray;
- break;
+ case 0:
+ color = colorCyan;
+ break;
+ case 1:
+ color = colorRed;
+ break;
+ case 2:
+ color = colorMdGrey;
+ break;
}
color[3] = 0.5;
-
trap_R_SetColor( color );
- if( items[ item ] <= 32 )
- CG_DrawPic( x, y, iconsize, iconsize, cg_weapons[ items[ item ] ].weaponIcon );
- else if( items[ item ] > 32 )
- CG_DrawPic( x, y, iconsize, iconsize, cg_upgrades[ items[ item ] - 32 ].upgradeIcon );
-
- trap_R_SetColor( NULL );
+ if( items[ item ] < 32 )
+ CG_DrawPic( x, y, iconWidth, iconHeight,
+ cg_weapons[ items[ item ] ].weaponIcon );
+ else
+ CG_DrawPic( x, y, iconWidth, iconHeight,
+ cg_upgrades[ items[ item ] - 32 ].upgradeIcon );
}
-
if( vertical )
- y += iconsize;
+ y += iconHeight;
else
- x += iconsize;
+ x += iconWidth;
}
+ trap_R_SetColor( NULL );
}
@@ -1298,29 +1546,29 @@ void CG_DrawItemSelectText( rectDef_t *rect, float scale, int textStyle )
trap_R_SetColor( color );
// draw the selected name
- if( cg.weaponSelect <= 32 )
+ if( cg.weaponSelect < 32 )
{
if( cg_weapons[ cg.weaponSelect ].registered &&
BG_InventoryContainsWeapon( cg.weaponSelect, cg.snap->ps.stats ) )
{
if( ( name = cg_weapons[ cg.weaponSelect ].humanName ) )
{
- w = CG_Text_Width( name, scale, 0 );
+ w = UI_Text_Width( name, scale );
x = rect->x + rect->w / 2;
- CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
+ UI_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
}
}
}
- else if( cg.weaponSelect > 32 )
+ else
{
if( cg_upgrades[ cg.weaponSelect - 32 ].registered &&
BG_InventoryContainsUpgrade( cg.weaponSelect - 32, cg.snap->ps.stats ) )
{
if( ( name = cg_upgrades[ cg.weaponSelect - 32 ].humanName ) )
{
- w = CG_Text_Width( name, scale, 0 );
+ w = UI_Text_Width( name, scale );
x = rect->x + rect->w / 2;
- CG_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
+ UI_Text_Paint( x - w / 2, rect->y + rect->h, scale, color, name, 0, 0, textStyle );
}
}
}
@@ -1357,12 +1605,12 @@ void CG_NextWeapon_f( void )
if( cg.weaponSelect == 64 )
cg.weaponSelect = 0;
- if( cg.weaponSelect <= 32 )
+ if( cg.weaponSelect < 32 )
{
if( CG_WeaponSelectable( cg.weaponSelect ) )
break;
}
- else if( cg.weaponSelect > 32 )
+ else
{
if( CG_UpgradeSelectable( cg.weaponSelect - 32 ) )
break;
@@ -1401,12 +1649,12 @@ void CG_PrevWeapon_f( void )
if( cg.weaponSelect == -1 )
cg.weaponSelect = 63;
- if( cg.weaponSelect <= 32 )
+ if( cg.weaponSelect < 32 )
{
if( CG_WeaponSelectable( cg.weaponSelect ) )
break;
}
- else if( cg.weaponSelect > 32 )
+ else
{
if( CG_UpgradeSelectable( cg.weaponSelect - 32 ) )
break;
@@ -1521,7 +1769,7 @@ Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
=================
*/
void CG_MissileHitWall( weapon_t weaponNum, weaponMode_t weaponMode, int clientNum,
- vec3_t origin, vec3_t dir, impactSound_t soundType )
+ vec3_t origin, vec3_t dir, impactSound_t soundType, int charge )
{
qhandle_t mark = 0;
qhandle_t ps = 0;
@@ -1579,6 +1827,7 @@ void CG_MissileHitWall( weapon_t weaponNum, weaponMode_t weaponMode, int clientN
CG_SetAttachmentPoint( &partSystem->attachment, origin );
CG_SetParticleSystemNormal( partSystem, dir );
CG_AttachToPoint( &partSystem->attachment );
+ partSystem->charge = charge;
}
}
@@ -1592,11 +1841,11 @@ void CG_MissileHitWall( weapon_t weaponNum, weaponMode_t weaponMode, int clientN
/*
=================
-CG_MissileHitPlayer
+CG_MissileHitEntity
=================
*/
-void CG_MissileHitPlayer( weapon_t weaponNum, weaponMode_t weaponMode,
- vec3_t origin, vec3_t dir, int entityNum )
+void CG_MissileHitEntity( weapon_t weaponNum, weaponMode_t weaponMode,
+ vec3_t origin, vec3_t dir, int entityNum, int charge )
{
vec3_t normal;
weaponInfo_t *weapon = &cg_weapons[ weaponNum ];
@@ -1610,7 +1859,25 @@ void CG_MissileHitPlayer( weapon_t weaponNum, weaponMode_t weaponMode,
weaponMode = WPM_PRIMARY;
if( weapon->wim[ weaponMode ].alwaysImpact )
- CG_MissileHitWall( weaponNum, weaponMode, 0, origin, dir, IMPACTSOUND_FLESH );
+ {
+ int sound;
+
+ if( cg_entities[ entityNum ].currentState.eType == ET_PLAYER )
+ {
+ // Players
+ sound = IMPACTSOUND_FLESH;
+ }
+ else if( cg_entities[ entityNum ].currentState.eType == ET_BUILDABLE &&
+ BG_Buildable( cg_entities[ entityNum ].currentState.modelindex )->team == TEAM_ALIENS )
+ {
+ // Alien buildables
+ sound = IMPACTSOUND_FLESH;
+ }
+ else
+ sound = IMPACTSOUND_DEFAULT;
+
+ CG_MissileHitWall( weaponNum, weaponMode, 0, origin, dir, sound, charge );
+ }
}
@@ -1772,7 +2039,7 @@ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh,
if( flesh )
CG_Bleed( end, normal, fleshEntityNum );
else
- CG_MissileHitWall( WP_MACHINEGUN, WPM_PRIMARY, 0, end, normal, IMPACTSOUND_DEFAULT );
+ CG_MissileHitWall( WP_MACHINEGUN, WPM_PRIMARY, 0, end, normal, IMPACTSOUND_DEFAULT, 0 );
}
/*
@@ -1818,12 +2085,13 @@ static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int othe
if( !( tr.surfaceFlags & SURF_NOIMPACT ) )
{
- if( cg_entities[ tr.entityNum ].currentState.eType == ET_PLAYER )
- CG_MissileHitPlayer( WP_SHOTGUN, WPM_PRIMARY, tr.endpos, tr.plane.normal, tr.entityNum );
+ if( cg_entities[ tr.entityNum ].currentState.eType == ET_PLAYER ||
+ cg_entities[ tr.entityNum ].currentState.eType == ET_BUILDABLE )
+ CG_MissileHitEntity( WP_SHOTGUN, WPM_PRIMARY, tr.endpos, tr.plane.normal, tr.entityNum, 0 );
else if( tr.surfaceFlags & SURF_METALSTEPS )
- CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL );
+ CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL, 0 );
else
- CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT );
+ CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT, 0 );
}
}
}
@@ -1845,3 +2113,54 @@ void CG_ShotgunFire( entityState_t *es )
CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum );
}
+/*
+=================
+CG_Bleed
+
+This is the spurt of blood when a character gets hit
+=================
+*/
+void CG_Bleed( vec3_t origin, vec3_t normal, int entityNum )
+{
+ team_t team;
+ qhandle_t bleedPS;
+ particleSystem_t *ps;
+
+ if( !cg_blood.integer )
+ return;
+
+ if( cg_entities[ entityNum ].currentState.eType == ET_PLAYER )
+ {
+ team = cgs.clientinfo[ entityNum ].team;
+ if( team == TEAM_ALIENS )
+ bleedPS = cgs.media.alienBleedPS;
+ else if( team == TEAM_HUMANS )
+ bleedPS = cgs.media.humanBleedPS;
+ else
+ return;
+ }
+ else if( cg_entities[ entityNum ].currentState.eType == ET_BUILDABLE )
+ {
+ //ew
+ team = BG_Buildable( cg_entities[ entityNum ].currentState.modelindex )->team;
+ if( team == TEAM_ALIENS )
+ bleedPS = cgs.media.alienBuildableBleedPS;
+ else if( team == TEAM_HUMANS )
+ bleedPS = cgs.media.humanBuildableBleedPS;
+ else
+ return;
+ }
+ else
+ return;
+
+ ps = CG_SpawnNewParticleSystem( bleedPS );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentPoint( &ps->attachment, origin );
+ CG_SetAttachmentCent( &ps->attachment, &cg_entities[ entityNum ] );
+ CG_AttachToPoint( &ps->attachment );
+
+ CG_SetParticleSystemNormal( ps, normal );
+ }
+}