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; +}  | 
