summaryrefslogtreecommitdiff
path: root/src/cgame/cg_tutorial.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame/cg_tutorial.c')
-rw-r--r--src/cgame/cg_tutorial.c864
1 files changed, 864 insertions, 0 deletions
diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c
new file mode 100644
index 0000000..f68d070
--- /dev/null
+++ b/src/cgame/cg_tutorial.c
@@ -0,0 +1,864 @@
+/*
+===========================================================================
+Copyright (C) 2000-2009 Darklegion Development
+
+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"
+
+#define CG_AxisName(a) ((a)==0?"X (forward-backward)":((a)==1?"Y (left-right)":"Z (up-down)"))
+
+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 } },
+ { "+button8", "Sprint", { -1, -1 } },
+ { "+moveup", "Jump", { -1, -1 } },
+ { "+movedown", "Crouch", { -1, -1 } },
+ { "+attack", "Primary Attack", { -1, -1 } },
+ { "+button5", "Secondary Attack", { -1, -1 } },
+ { "reload", "Reload", { -1, -1 } },
+ { "buy ammo", "Buy Ammo / Refuel", { -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 } },
+ { "cuboidAxis next","Next Cuboid axis", { -1, -1 } },
+ { "cuboidRotate", "Rotate Cuboid", { -1, -1 } }
+};
+
+static const int numBindings = sizeof( bindings ) / sizeof( bind_t );
+
+/*
+=================
+CG_GetBindings
+=================
+*/
+static void CG_GetBindings( void )
+{
+ int i, j, numKeys;
+ char buffer[ MAX_STRING_CHARS ];
+
+ for( i = 0; i < numBindings; i++ )
+ {
+ bindings[ i ].keys[ 0 ] = bindings[ i ].keys[ 1 ] = K_NONE;
+ numKeys = 0;
+
+ for( j = 0; j < K_LAST_KEY; j++ )
+ {
+ trap_Key_GetBindingBuf( j, buffer, MAX_STRING_CHARS );
+
+ if( buffer[ 0 ] == 0 )
+ continue;
+
+ if( !Q_stricmp( buffer, bindings[ i ].command ) )
+ {
+ bindings[ i ].keys[ numKeys++ ] = j;
+
+ if( numKeys > 1 )
+ break;
+ }
+ }
+ }
+}
+
+/*
+===============
+CG_KeyNameForCommand
+===============
+*/
+static const char *CG_KeyNameForCommand( const char *command )
+{
+ int i, j;
+ static char buffer[ MAX_STRING_CHARS ];
+ int firstKeyLength;
+
+ buffer[ 0 ] = '\0';
+
+ for( i = 0; i < numBindings; i++ )
+ {
+ if( !Q_stricmp( command, bindings[ i ].command ) )
+ {
+ if( bindings[ i ].keys[ 0 ] != K_NONE )
+ {
+ trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 0 ],
+ buffer, MAX_STRING_CHARS );
+ firstKeyLength = strlen( buffer );
+
+ for( j = 0; j < firstKeyLength; j++ )
+ buffer[ j ] = toupper( buffer[ j ] );
+
+ if( bindings[ i ].keys[ 1 ] != K_NONE )
+ {
+ Q_strcat( buffer, MAX_STRING_CHARS, " or " );
+ trap_Key_KeynumToStringBuf( bindings[ i ].keys[ 1 ],
+ buffer + strlen( buffer ), MAX_STRING_CHARS - strlen( buffer ) );
+
+ for( j = firstKeyLength + 4; j < strlen( buffer ); j++ )
+ buffer[ j ] = toupper( buffer[ j ] );
+ }
+ }
+ else
+ {
+ Com_sprintf( buffer, MAX_STRING_CHARS, "\"%s\" (unbound)",
+ bindings[ i ].humanName );
+ }
+
+ return buffer;
+ }
+ }
+
+ return "";
+}
+
+#define MAX_TUTORIAL_TEXT 4096
+
+/*
+===============
+CG_BuildableInRange
+===============
+*/
+static entityState_t *CG_BuildableInRange( playerState_t *ps, float *healthFraction )
+{
+ vec3_t view, point;
+ trace_t trace;
+ entityState_t *es;
+ int health;
+
+ AngleVectors( cg.refdefViewAngles, view, NULL, NULL );
+ VectorMA( cg.refdef.vieworg, 64, view, point );
+ CG_Trace( &trace, cg.refdef.vieworg, NULL, NULL,
+ point, ps->clientNum, MASK_SHOT );
+
+ es = &cg_entities[ trace.entityNum ].currentState;
+
+ if( healthFraction )
+ {
+ health = es->generic1;
+ *healthFraction = (float)health / BG_Buildable( es->modelindex, es->angles )->health;
+ }
+
+ if( es->eType == ET_BUILDABLE &&
+ ps->stats[ STAT_TEAM ] == BG_Buildable( es->modelindex, NULL )->team )
+ return es;
+ else
+ return NULL;
+}
+
+/*
+===============
+CG_AlienBuilderText
+===============
+*/
+static void CG_AlienBuilderText( char *text, playerState_t *ps )
+{
+ buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
+ entityState_t *es;
+
+ if( buildable > BA_NONE )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to place the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_Buildable( buildable, NULL )->humanName ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to cancel placing the %s\n",
+ CG_KeyNameForCommand( "+button5" ),
+ BG_Buildable( buildable, NULL )->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( cgs.markDeconstruct )
+ {
+ if( es->eFlags & EF_B_MARKED )
+ {
+ 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 mark this structure for replacement\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to destroy this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+
+ if( ( ps->stats[ STAT_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" ) ) );
+
+ 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 humans to damage them\n" );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to walk on walls\n",
+ CG_KeyNameForCommand( "+movedown" ) ) );
+}
+
+/*
+===============
+CG_AlienLevel1Text
+===============
+*/
+static void CG_AlienLevel1Text( char *text, playerState_t *ps )
+{
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "Touch humans to grab them\n" );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to swipe\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+
+ if( ps->stats[ STAT_CLASS ] == 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_CLASS ] == 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_CLASS ] == 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 trample\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;
+ 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_Buildable( buildable, NULL )->humanName ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to cancel placing the %s\n",
+ CG_KeyNameForCommand( "+button5" ),
+ BG_Buildable( buildable, NULL )->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( cgs.markDeconstruct )
+ {
+ if( es->eFlags & EF_B_MARKED )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to unmark this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to mark this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to destroy this structure\n",
+ CG_KeyNameForCommand( "deconstruct" ) ) );
+ }
+ }
+}
+
+/*
+===============
+CG_HumanText
+===============
+*/
+static void CG_HumanText( char *text, playerState_t *ps )
+{
+ char *name;
+ upgrade_t upgrade = UP_NONE;
+
+ if( cg.weaponSelect < 32 )
+ name = cg_weapons[ cg.weaponSelect ].humanName;
+ else
+ {
+ name = cg_upgrades[ cg.weaponSelect - 32 ].humanName;
+ upgrade = cg.weaponSelect - 32;
+ }
+
+ if( !ps->ammo && !ps->clips && !BG_Weapon( ps->weapon )->infiniteAmmo )
+ {
+ //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 an Armoury, 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_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_Weapon( ps->weapon )->humanName ) );
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold %s to zoom\n",
+ CG_KeyNameForCommand( "+button5" ) ) );
+ break;
+
+ case WP_PAIN_SAW:
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Hold %s to activate the %s\n",
+ CG_KeyNameForCommand( "+attack" ),
+ BG_Weapon( ps->weapon )->humanName ) );
+ 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_Weapon( ps->weapon )->humanName ) );
+ break;
+
+ case WP_HBUILD:
+ 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_Upgrade( upgrade )->usable ) )
+ {
+ 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_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, NULL )->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, NULL )->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, NULL )->humanName ) );
+ break;
+ }
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s and any direction to sprint\n",
+ CG_KeyNameForCommand( "+button8" ) ) );
+}
+
+/*
+===============
+CG_SpectatorText
+===============
+*/
+static void CG_SpectatorText( char *text, playerState_t *ps )
+{
+ if( cgs.clientinfo[ cg.clientNum ].team != TEAM_NONE )
+ {
+ 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" ) ) );
+ }
+
+ if( ps->pm_flags & PMF_FOLLOW )
+ {
+ 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" ) ) );
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "%s to change player\n",
+ CG_KeyNameForCommand( "weapnext" ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to follow a player\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ }
+}
+
+#define BINDING_REFRESH_INTERVAL 30
+
+/*
+===============
+CG_TutorialText
+
+Returns context help for the current class/weapon
+===============
+*/
+static void CG_NewText( char *text, playerState_t *ps );
+
+const char *CG_TutorialText( void )
+{
+ playerState_t *ps;
+ static char text[ MAX_TUTORIAL_TEXT ];
+ static int refreshBindings = 0;
+ static qboolean checkedUpdate = qfalse;
+
+ // force mod tutorial if new version
+ if( !checkedUpdate )
+ {
+ trap_Cvar_Update( &cg_lastModVersion );
+
+ if( cg_lastModVersion.integer < MODVER_CURRENT )
+ {
+ trap_Cvar_Set( "cg_modTutorial", "1" );
+ trap_Cvar_Set( "cg_modTutorialReference", va( "%i", cg_lastModVersion.integer ) );
+ trap_Cvar_Set( "cg_lastModVersion", va( "%i", MODVER_CURRENT ) );
+ }
+ checkedUpdate = qtrue;
+ }
+
+ if( refreshBindings == 0 )
+ CG_GetBindings( );
+
+ refreshBindings = ( refreshBindings + 1 ) % BINDING_REFRESH_INTERVAL;
+
+ text[ 0 ] = '\0';
+ ps = &cg.snap->ps;
+
+ if( cg_modTutorial.integer )
+ {
+ CG_NewText( text, ps );
+
+ if( !cg_tutorial.integer )
+ return text;
+ }
+
+ if( !cg.intermissionStarted && !cg.demoPlayback )
+ {
+ 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_CLASS ] )
+ {
+ 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:
+ case PCL_HUMAN_BSUIT:
+ CG_HumanText( text, ps );
+ break;
+
+ default:
+ break;
+ }
+
+ if( ps->stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
+ 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",
+ CG_KeyNameForCommand( "+button7" ) ) );
+ }
+ }
+ }
+ }
+ 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" );
+ }
+
+ return text;
+}
+
+/*
+===============
+CG_NewText
+
+Informs the player about new mod features. cg_tutorial2 contains a
+mod version (table below). CG_NewText will explain all features
+introduced in that version and later.
+
+0 N/A vanilla GPP
+1 Sep 03 alpha 1
+
+===============
+*/
+
+static void CG_CuboidText( char *text, playerState_t *ps )
+{
+ buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT;
+
+ if ( buildable <= BA_NONE )
+ return;
+
+ if( !BG_Buildable( buildable, NULL )->cuboid )
+ return;
+
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "Cuboid requires new keybindings. They can be set in Options -> Controls -> Cuboid\n" );
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s and ",
+ CG_KeyNameForCommand( "weapnext" ) ) );
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "%s to resize the %s on %s axis.\n",
+ CG_KeyNameForCommand( "weapprev" ),
+ BG_Buildable( buildable, NULL )->humanName,
+ CG_AxisName( cg_cuboidResizeAxis.integer ) ) );
+ Q_strcat(text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to rotate %s around %s axis\n",
+ CG_KeyNameForCommand( "cuboidRotate" ),
+ BG_Buildable( buildable, NULL )->humanName,
+ CG_AxisName( cg_cuboidResizeAxis.integer ) ) );
+ Q_strcat(text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to select another axis.\n",
+ CG_KeyNameForCommand( "cuboidAxis next" ) ) );
+}
+
+static void CG_NewText( char *text, playerState_t *ps )
+{
+ int reference;
+
+ reference = cg_modTutorialReference.integer;
+
+ if( !cg.intermissionStarted && !cg.demoPlayback )
+ {
+ if( ps->persistant[ PERS_SPECSTATE ] == SPECTATOR_NOT &&
+ !( ps->pm_flags & PMF_FOLLOW ) &&
+ ps->stats[ STAT_HEALTH ] > 0 )
+ {
+ if( reference >= MODVER_C2_0_1_0 )
+ {
+ if( BG_InventoryContainsUpgrade( UP_JETPACK, ps->stats ) )
+ {
+ if( ps->stats[ STAT_FUEL ] > JETPACK_FUEL_JUMP )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to perform a jetpack-aided jump. It uses fuel instead of stamina\n",
+ CG_KeyNameForCommand( "+moveup" ) ) );
+ }
+ if( ps->stats[ STAT_FUEL ] <= JETPACK_FUEL_LOW )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "You are running low on jet fuel. Find an Armoury and press %s to refuel\n",
+ CG_KeyNameForCommand( "buy ammo" ) ) );
+ }
+ else if( ps->stats[ STAT_FUEL ] <= 0 )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "You are out of jet fuel. You can no longer fly. Find an Armoury and press %s to refuel\n",
+ CG_KeyNameForCommand( "buy ammo" ) ) );
+ }
+ }
+ }
+
+ switch( ps->weapon )
+ {
+ case WP_ABUILD:
+ case WP_ABUILD2:
+ case WP_HBUILD:
+ if( reference >= MODVER_C2_0_1_0 )
+ {
+ CG_CuboidText( text, ps );
+ if( ps->weapon == WP_ABUILD2 )
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ "Spitting at a human has a chance of impregnating him\n"
+ "with an alien egg. After a certain amount of time,\n"
+ "the egg can be spawned from by aliens, killing the human.\n" );
+ }
+ break;
+
+ case WP_ALEVEL4:
+ if( reference >= MODVER_C2_0_1_0 )
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to drop a Tyrant Bomb. It can be regerated by touching a Booster.\n",
+ CG_KeyNameForCommand( "+button2" ) ) );
+ break;
+ }
+ }
+ }
+}
+