diff options
author | Tim Angus <tim@ngus.net> | 2006-01-18 01:54:21 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2006-01-18 01:54:21 +0000 |
commit | b5a24aab3dbb3d65950843c19ab2fa9934064281 (patch) | |
tree | fef77c5cc94e3376442747136421f07f9bea74b0 /src/cgame | |
parent | 5cf09669133da12cf82ee3168e57a781e47b9e92 (diff) |
* Added BG_FindUsableForUpgrade
* Added BG_UpgradeClassAvailable
* Added generalised BG_*IsAllowed functions
* Added some binding traps to cgame
* Added tutorial mode (cg_tutorial)
Diffstat (limited to 'src/cgame')
-rw-r--r-- | src/cgame/cg_draw.c | 122 | ||||
-rw-r--r-- | src/cgame/cg_local.h | 11 | ||||
-rw-r--r-- | src/cgame/cg_main.c | 22 | ||||
-rw-r--r-- | src/cgame/cg_players.c | 29 | ||||
-rw-r--r-- | src/cgame/cg_public.h | 6 | ||||
-rw-r--r-- | src/cgame/cg_syscalls.asm | 25 | ||||
-rw-r--r-- | src/cgame/cg_syscalls.c | 13 | ||||
-rw-r--r-- | src/cgame/cg_tutorial.c | 610 |
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; +} |