summaryrefslogtreecommitdiff
path: root/src/cgame
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame')
-rw-r--r--src/cgame/cg_draw.c122
-rw-r--r--src/cgame/cg_local.h11
-rw-r--r--src/cgame/cg_main.c22
-rw-r--r--src/cgame/cg_players.c29
-rw-r--r--src/cgame/cg_public.h6
-rw-r--r--src/cgame/cg_syscalls.asm25
-rw-r--r--src/cgame/cg_syscalls.c13
-rw-r--r--src/cgame/cg_tutorial.c610
8 files changed, 767 insertions, 71 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index a3959ed7..7aa724b9 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -543,34 +543,6 @@ void CG_SetPrintString( int type, const char *p )
}
}
-/*
-===============
-CG_AtHighestClass
-
-Is the local client at the highest class possible?
-===============
-*/
-static qboolean CG_AtHighestClass( void )
-{
- int i;
- qboolean superiorClasses = qfalse;
-
- 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 )
- /*FIXME && G_ClassIsAllowed( i )*/ )
- {
- superiorClasses = qtrue;
- break;
- }
- }
-
- return !superiorClasses;
-}
-
#define NO_CREDITS_TIME 2000
static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean padding )
@@ -2285,67 +2257,95 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
/*
==============
-CG_DrawConsole
+CG_DrawTextBlock
==============
*/
-static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
- float scale, int align, int textStyle )
+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 )
{
float x, y, w, h;
- //for some reason if these are stored locally occasionally rendering fails
- //even though they are both live until the end of the function, hence static
- //possible compiler bug??
- static menuDef_t dummyParent;
- static itemDef_t textItem;
-
//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;
- textItem.text = cg.consoleText;
+ textItem->text = text;
- textItem.parent = &dummyParent;
- memcpy( textItem.window.foreColor, color, sizeof( vec4_t ) );
- textItem.window.flags = 0;
+ textItem->parent = parent;
+ memcpy( textItem->window.foreColor, color, sizeof( vec4_t ) );
+ textItem->window.flags = 0;
switch( align )
{
case ITEM_ALIGN_LEFT:
- textItem.window.rect.x = x;
+ textItem->window.rect.x = x;
break;
case ITEM_ALIGN_RIGHT:
- textItem.window.rect.x = x + w;
+ textItem->window.rect.x = x + w;
break;
case ITEM_ALIGN_CENTER:
- textItem.window.rect.x = x + ( w / 2 );
+ textItem->window.rect.x = x + ( w / 2 );
break;
default:
- textItem.window.rect.x = x;
+ textItem->window.rect.x = x;
break;
}
- 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;
+ 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;
//hack to utilise existing autowrap code
- Item_Text_AutoWrapped_Paint( &textItem );
+ Item_Text_AutoWrapped_Paint( textItem );
+}
+
+/*
+===================
+CG_DrawConsole
+===================
+*/
+static void CG_DrawConsole( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, 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 );
+}
+
+/*
+===================
+CG_DrawTutorial
+===================
+*/
+static void CG_DrawTutorial( rectDef_t *rect, float text_x, float text_y, vec4_t color,
+ float scale, int align, 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 );
}
/*
@@ -2731,6 +2731,10 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
CG_DrawConsole( &rect, text_x, text_y, color, scale, align, textStyle );
break;
+ case CG_TUTORIAL:
+ CG_DrawTutorial( &rect, text_x, text_y, color, scale, align, textStyle );
+ break;
+
default:
break;
}
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 43d924d0..6fbd815c 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1479,6 +1479,7 @@ extern vmCvar_t cg_debugTrails;
extern vmCvar_t cg_debugPVS;
extern vmCvar_t cg_disableWarningDialogs;
extern vmCvar_t cg_disableScannerPlane;
+extern vmCvar_t cg_tutorial;
extern vmCvar_t cg_painBlendUpRate;
extern vmCvar_t cg_painBlendDownRate;
@@ -1508,6 +1509,7 @@ void QDECL CG_Printf( const char *msg, ... );
void QDECL CG_Error( const char *msg, ... );
void CG_StartMusic( void );
+int CG_PlayerCount( void );
void CG_UpdateCvars( void );
@@ -1606,6 +1608,7 @@ void CG_PrecacheClientInfo( pClass_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 );
//
// cg_buildable.c
@@ -1814,6 +1817,11 @@ int CG_ReadPTRCode( void );
void CG_WritePTRCode( int code );
//
+// cg_tutorial.c
+//
+const char *CG_TutorialText( void );
+
+//
//===============================================
//
@@ -2000,6 +2008,9 @@ qboolean trap_Key_IsDown( int keynum );
int trap_Key_GetCatcher( void );
void trap_Key_SetCatcher( int catcher );
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 );
typedef enum
{
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index 34928132..5963d0bf 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -217,6 +217,7 @@ vmCvar_t cg_debugTrails;
vmCvar_t cg_debugPVS;
vmCvar_t cg_disableWarningDialogs;
vmCvar_t cg_disableScannerPlane;
+vmCvar_t cg_tutorial;
vmCvar_t cg_painBlendUpRate;
vmCvar_t cg_painBlendDownRate;
@@ -330,6 +331,7 @@ static cvarTable_t cvarTable[ ] =
{ &cg_debugPVS, "cg_debugPVS", "0", CVAR_CHEAT },
{ &cg_disableWarningDialogs, "cg_disableWarningDialogs", "0", CVAR_ARCHIVE },
{ &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE },
+ { &cg_tutorial, "cg_tutorial", "1", CVAR_ARCHIVE },
{ &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
{ &cg_painBlendUpRate, "cg_painBlendUpRate", "10.0", 0 },
@@ -992,6 +994,25 @@ void CG_StartMusic( void )
trap_S_StartBackgroundTrack( parm1, parm2 );
}
+/*
+======================
+CG_PlayerCount
+======================
+*/
+int CG_PlayerCount( void )
+{
+ int i, count = 0;
+
+ for( i = 0; i < cg.numScores; i++ )
+ {
+ if( cg.scores[ i ].team == PTE_ALIENS ||
+ cg.scores[ i ].team == PTE_HUMANS )
+ count++;
+ }
+
+ return count;
+}
+
//
// ==============================
// new hud stuff ( mission pack )
@@ -1708,6 +1729,7 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
//TA: load overrides
BG_InitClassOverrides( );
BG_InitBuildableOverrides( );
+ BG_InitAllowedGameElements( );
//TA: dyn memory
CG_InitMemory( );
diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c
index 3eb5e998..c6db37f1 100644
--- a/src/cgame/cg_players.c
+++ b/src/cgame/cg_players.c
@@ -2540,3 +2540,32 @@ void CG_Bleed( vec3_t origin, vec3_t normal, int entityNum )
CG_SetParticleSystemNormal( ps, normal );
}
}
+
+/*
+===============
+CG_AtHighestClass
+
+Is the local client at the highest class possible?
+===============
+*/
+qboolean CG_AtHighestClass( void )
+{
+ int i;
+ qboolean superiorClasses = qfalse;
+
+ 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;
+ }
+ }
+
+ return !superiorClasses;
+}
+
diff --git a/src/cgame/cg_public.h b/src/cgame/cg_public.h
index 0687cdfd..09f40d10 100644
--- a/src/cgame/cg_public.h
+++ b/src/cgame/cg_public.h
@@ -173,7 +173,11 @@ typedef enum
CG_GETDEMOPOS,
CG_GETDEMONAME,
- CG_MEMSET = 100,
+ CG_KEY_KEYNUMTOSTRINGBUF,
+ CG_KEY_GETBINDINGBUF,
+ CG_KEY_SETBINDING,
+
+ CG_MEMSET = 200,
CG_MEMCPY,
CG_STRNCPY,
CG_SIN,
diff --git a/src/cgame/cg_syscalls.asm b/src/cgame/cg_syscalls.asm
index cf373b8c..2fcc5296 100644
--- a/src/cgame/cg_syscalls.asm
+++ b/src/cgame/cg_syscalls.asm
@@ -97,16 +97,19 @@ equ trap_CM_TransformedBiSphereTrace -94
equ trap_GetDemoState -95
equ trap_GetDemoPos -96
equ trap_GetDemoName -97
+equ trap_Key_KeynumToStringBuf -98
+equ trap_Key_GetBindingBuf -99
+equ trap_Key_SetBinding -100
-equ memset -101
-equ memcpy -102
-equ strncpy -103
-equ sin -104
-equ cos -105
-equ atan2 -106
-equ sqrt -107
-equ floor -108
-equ ceil -109
-equ testPrintInt -110
-equ testPrintFloat -111
+equ memset -201
+equ memcpy -202
+equ strncpy -203
+equ sin -204
+equ cos -205
+equ atan2 -206
+equ sqrt -207
+equ floor -208
+equ ceil -209
+equ testPrintInt -210
+equ testPrintFloat -211
diff --git a/src/cgame/cg_syscalls.c b/src/cgame/cg_syscalls.c
index 37820fb4..1d982734 100644
--- a/src/cgame/cg_syscalls.c
+++ b/src/cgame/cg_syscalls.c
@@ -555,3 +555,16 @@ void trap_GetDemoName( char *buffer, int size )
{
syscall( CG_GETDEMONAME, buffer, size );
}
+
+void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) {
+ syscall( CG_KEY_KEYNUMTOSTRINGBUF, keynum, buf, buflen );
+}
+
+void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ) {
+ syscall( CG_KEY_GETBINDINGBUF, keynum, buf, buflen );
+}
+
+void trap_Key_SetBinding( int keynum, const char *binding ) {
+ syscall( CG_KEY_SETBINDING, keynum, binding );
+}
+
diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c
new file mode 100644
index 00000000..f54786be
--- /dev/null
+++ b/src/cgame/cg_tutorial.c
@@ -0,0 +1,610 @@
+/*
+===========================================================================
+Copyright (C) 2000-2006 Tim Angus
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cg_tutorial.c -- the tutorial system
+
+#include "cg_local.h"
+
+typedef struct
+{
+ char *command;
+ char *humanName;
+ keyNum_t keys[ 2 ];
+} bind_t;
+
+static bind_t bindings[ ] =
+{
+ { "+button2", "Activate Upgrade", { -1, -1 } },
+ { "+speed", "Run/Walk", { -1, -1 } },
+ { "boost", "Sprint", { -1, -1 } },
+ { "+moveup", "Jump", { -1, -1 } },
+ { "+movedown", "Crouch", { -1, -1 } },
+ { "+zoom", "ZoomView", { -1, -1 } },
+ { "+attack", "Primary Attack", { -1, -1 } },
+ { "+button5", "Secondary Attack", { -1, -1 } },
+ { "reload", "Reload", { -1, -1 } },
+ { "buy ammo", "Buy Ammo", { -1, -1 } },
+ { "itemact medkit", "Use Medkit", { -1, -1 } },
+ { "+button7", "Use Structure/Evolve", { -1, -1 } },
+ { "deconstruct", "Deconstruct Structure", { -1, -1 } },
+ { "weapprev", "Previous Upgrade", { -1, -1 } },
+ { "weapnext", "Next Upgrade", { -1, -1 } }
+};
+
+static const int numBindings = sizeof( bindings ) / sizeof( bind_t );
+
+/*
+=================
+CG_GetBindings
+=================
+*/
+static void CG_GetBindings( void )
+{
+ int i, j, numKeys;
+ char buffer[ MAX_STRING_CHARS ];
+
+ for( i = 0; i < numBindings; i++ )
+ {
+ bindings[ i ].keys[ 0 ] = bindings[ i ].keys[ 1 ] = -1;
+ numKeys = 0;
+
+ for( j = 0; j < K_LAST_KEY; j++ )
+ {
+ trap_Key_GetBindingBuf( j, buffer, MAX_STRING_CHARS );
+
+ if( buffer[ 0 ] == 0 )
+ continue;
+
+ if( !Q_stricmp( buffer, bindings[ i ].command ) )
+ {
+ bindings[ i ].keys[ numKeys++ ] = j;
+
+ if( numKeys > 1 )
+ break;
+ }
+ }
+ }
+}
+
+/*
+===============
+CG_KeyNameForCommand
+===============
+*/
+static const char *CG_KeyNameForCommand( const char *command )
+{
+ int i, j;
+ static char buffer[ MAX_STRING_CHARS ];
+ int firstKeyLength;
+
+ buffer[ 0 ] = '\0';
+
+ for( i = 0; i < numBindings; i++ )
+ {
+ if( !Q_stricmp( command, bindings[ i ].command ) )
+ {
+ if( bindings[ i ].keys[ 0 ] >= 0 )
+ {
+ trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 0 ],
+ buffer, MAX_STRING_CHARS );
+ firstKeyLength = strlen( buffer );
+
+ for( j = 0; j < firstKeyLength; j++ )
+ buffer[ j ] = toupper( buffer[ j ] );
+
+ if( bindings[ i ].keys[ 1 ] >= 0 )
+ {
+ Q_strcat( buffer, MAX_STRING_CHARS, " or " );
+ trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 1 ],
+ buffer + strlen( buffer ), MAX_STRING_CHARS );
+
+ for( j = firstKeyLength + 4; j < strlen( buffer ); j++ )
+ buffer[ j ] = toupper( buffer[ j ] );
+ }
+ }
+ else
+ {
+ Q_strncpyz( buffer, va( "\"%s\"", bindings[ i ].humanName ),
+ MAX_STRING_CHARS );
+ Q_strcat( buffer, MAX_STRING_CHARS, " (unbound)" );
+ }
+
+ return buffer;
+ }
+ }
+
+ return "";
+}
+
+#define MAX_TUTORIAL_TEXT 4096
+
+/*
+===============
+CG_BuildableInRange
+===============
+*/
+static qboolean CG_BuildableInRange( playerState_t *ps )
+{
+ vec3_t view, point;
+ trace_t trace;
+ entityState_t *es;
+
+ AngleVectors( cg.refdefViewAngles, view, NULL, NULL );
+ VectorMA( cg.refdef.vieworg, 64, view, point );
+ CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL,
+ point, ps->clientNum, MASK_SHOT );
+
+ es = &cg_entities[ trace.entityNum ].currentState;
+
+ if( es->eType == ET_BUILDABLE &&
+ ps->stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( es->modelindex ) )
+ return qtrue;
+ else
+ return qfalse;
+}
+
+/*
+===============
+CG_AlienBuilderText
+===============
+*/
+static void CG_AlienBuilderText( char *text, playerState_t *ps )
+{
+ buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
+
+ if( buildable > BA_NONE )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to place the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_FindHumanNameForBuildable( buildable ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to cancel placing the %s\n",
+ CG_KeyNameForCommand( "+button5" ),
+ BG_FindHumanNameForBuildable( buildable ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to build a structure\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( CG_BuildableInRange( ps ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to destroy this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+
+ if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG )
+ {
+ if( ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) == BA_NONE )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to swipe\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to lauch a projectile\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to walk on walls\n",
+ CG_KeyNameForCommand( "+movedown" ) ) );
+ }
+}
+
+/*
+===============
+CG_AlienLevel0Text
+===============
+*/
+static void CG_AlienLevel0Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "Touch a human to damage it\n" );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to walk on walls\n",
+ CG_KeyNameForCommand( "+movedown" ) ) );
+}
+
+/*
+===============
+CG_AlienLevel1Text
+===============
+*/
+static void CG_AlienLevel1Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to swipe\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL1_UPG )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to spray poisonous gas\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to walk on walls\n",
+ CG_KeyNameForCommand( "+movedown" ) ) );
+}
+
+/*
+===============
+CG_AlienLevel2Text
+===============
+*/
+static void CG_AlienLevel2Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to bite\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL2_UPG )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to invoke an electrical attack\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold down %s then touch a wall to wall jump\n",
+ CG_KeyNameForCommand( "+moveup" ) ) );
+}
+
+/*
+===============
+CG_AlienLevel3Text
+===============
+*/
+static void CG_AlienLevel3Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to bite\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( ps->stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL3_UPG )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to launch a projectile\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold down and release %s to pounce\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+}
+
+/*
+===============
+CG_AlienLevel4Text
+===============
+*/
+static void CG_AlienLevel4Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to swipe\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold down and release %s to charge\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+}
+
+/*
+===============
+CG_HumanCkitText
+===============
+*/
+static void CG_HumanCkitText( char *text, playerState_t *ps )
+{
+ buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
+
+ if( buildable > BA_NONE )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to place the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_FindHumanNameForBuildable( buildable ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to cancel placing the %s\n",
+ CG_KeyNameForCommand( "+button5" ),
+ BG_FindHumanNameForBuildable( buildable ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to build a structure\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( CG_BuildableInRange( ps ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to destroy this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+}
+
+/*
+===============
+CG_HumanText
+===============
+*/
+static void CG_HumanText( char *text, playerState_t *ps )
+{
+ char *name;
+ int ammo, clips;
+ upgrade_t upgrade = UP_NONE;
+
+ if( cg.weaponSelect <= 32 )
+ name = cg_weapons[ cg.weaponSelect ].humanName;
+ else if( cg.weaponSelect > 32 )
+ {
+ name = cg_upgrades[ cg.weaponSelect - 32 ].humanName;
+ upgrade = cg.weaponSelect - 32;
+ }
+
+ BG_UnpackAmmoArray( ps->weapon, ps->ammo, ps->powerups, &ammo, &clips );
+
+ if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( ps->weapon ) )
+ {
+ //no ammo
+ switch( ps->weapon )
+ {
+ case WP_MACHINEGUN:
+ case WP_CHAINGUN:
+ case WP_SHOTGUN:
+ case WP_FLAMER:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Find an Armoury and press %s for more ammo\n",
+ CG_KeyNameForCommand( "buy ammo" ) ) );
+ break;
+
+ case WP_LAS_GUN:
+ case WP_PULSE_RIFLE:
+ case WP_MASS_DRIVER:
+ case WP_LUCIFER_CANNON:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Find a Reactor or Repeater and press %s for more ammo\n",
+ CG_KeyNameForCommand( "buy ammo" ) ) );
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch( ps->weapon )
+ {
+ case WP_BLASTER:
+ case WP_MACHINEGUN:
+ case WP_SHOTGUN:
+ case WP_LAS_GUN:
+ case WP_CHAINGUN:
+ case WP_PULSE_RIFLE:
+ case WP_FLAMER:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to fire the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ break;
+
+ case WP_MASS_DRIVER:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to fire the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_FindHumanNameForWeapon( ps->weapon ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold %s to zoom\n",
+ CG_KeyNameForCommand( "+zoom" ) ) );
+ break;
+
+ case WP_PAIN_SAW:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold %s to activate the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ break;
+
+ case WP_LUCIFER_CANNON:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold and release %s to fire a charged shot\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to fire the %s\n",
+ CG_KeyNameForCommand( "+button5" ),
+ BG_FindHumanNameForWeapon( ps->weapon ) ) );
+ break;
+
+ case WP_HBUILD:
+ case WP_HBUILD2:
+ CG_HumanCkitText( text, ps );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s and ",
+ CG_KeyNameForCommand( "weapprev" ) ) );
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "%s to select an upgrade\n",
+ CG_KeyNameForCommand( "weapnext" ) ) );
+
+ if( upgrade == UP_NONE ||
+ ( upgrade > UP_NONE && BG_FindUsableForUpgrade( upgrade ) ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to use the %s\n",
+ CG_KeyNameForCommand( "+button2" ),
+ name ) );
+ }
+
+ if( ps->stats[ STAT_HEALTH ] <= 35 &&
+ BG_InventoryContainsUpgrade( UP_MEDKIT, ps->stats ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to use your %s\n",
+ CG_KeyNameForCommand( "itemact medkit" ),
+ BG_FindHumanNameForUpgrade( UP_MEDKIT ) ) );
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to use a structure\n",
+ CG_KeyNameForCommand( "+button7" ) ) );
+}
+
+/*
+===============
+CG_SpectatorText
+===============
+*/
+static void CG_SpectatorText( char *text, playerState_t *ps )
+{
+ if( ps->pm_type == PM_SPECTATOR )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ 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 return to free spectator mode\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+
+ if( CG_PlayerCount( ) > 1 )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s or ",
+ CG_KeyNameForCommand( "weapprev" ) ) );
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "%s to change player\n",
+ CG_KeyNameForCommand( "weapnext" ) ) );
+ }
+ }
+ else
+ {
+ if( CG_PlayerCount( ) > 0 )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to enter spectator follow mode\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ }
+ }
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to spawn\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+ }
+}
+
+/*
+===============
+CG_TutorialText
+
+Returns context help for the current class/weapon
+===============
+*/
+const char *CG_TutorialText( void )
+{
+ playerState_t *ps;
+ static char text[ MAX_TUTORIAL_TEXT ];
+
+ CG_GetBindings( );
+
+ text[ 0 ] = '\0';
+ ps = &cg.snap->ps;
+
+ if( !cg.intermissionStarted )
+ {
+ if( ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ CG_SpectatorText( text, ps );
+ else if( ps->stats[ STAT_HEALTH ] > 0 )
+ {
+ switch( ps->stats[ STAT_PCLASS ] )
+ {
+ case PCL_ALIEN_BUILDER0:
+ case PCL_ALIEN_BUILDER0_UPG:
+ CG_AlienBuilderText( text, ps );
+ break;
+
+ case PCL_ALIEN_LEVEL0:
+ CG_AlienLevel0Text( text, ps );
+ break;
+
+ case PCL_ALIEN_LEVEL1:
+ case PCL_ALIEN_LEVEL1_UPG:
+ CG_AlienLevel1Text( text, ps );
+ break;
+
+ case PCL_ALIEN_LEVEL2:
+ case PCL_ALIEN_LEVEL2_UPG:
+ CG_AlienLevel2Text( text, ps );
+ break;
+
+ case PCL_ALIEN_LEVEL3:
+ case PCL_ALIEN_LEVEL3_UPG:
+ CG_AlienLevel3Text( text, ps );
+ break;
+
+ case PCL_ALIEN_LEVEL4:
+ CG_AlienLevel4Text( text, ps );
+ break;
+
+ case PCL_HUMAN:
+ CG_HumanText( text, ps );
+ break;
+
+ default:
+ break;
+ }
+
+ if( ps->stats[ STAT_PTEAM ] == PTE_ALIENS &&
+ BG_UpgradeClassAvailable( ps ) )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to evolve\n",
+ CG_KeyNameForCommand( "+button7" ) ) );
+ }
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT, "Press ESC for the menu" );
+ }
+
+ return text;
+}