summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cgame/cg_draw.c9
-rw-r--r--src/cgame/cg_event.c23
-rw-r--r--src/cgame/cg_local.h2
-rw-r--r--src/cgame/cg_predict.c2
-rw-r--r--src/cgame/cg_view.c14
-rw-r--r--src/cgame/cg_weapons.c5
-rw-r--r--src/game/bg_misc.c150
-rw-r--r--src/game/bg_pmove.c3
-rw-r--r--src/game/bg_public.h21
-rw-r--r--src/game/g_active.c42
-rw-r--r--src/game/g_buildable.c2
-rw-r--r--src/game/g_client.c61
-rw-r--r--src/game/g_cmds.c129
-rw-r--r--src/game/g_local.h48
-rw-r--r--src/game/g_team.c4
-rw-r--r--src/game/g_utils.c7
16 files changed, 371 insertions, 151 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index cc087856..ff9d2525 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -1094,7 +1094,8 @@ static float CG_DrawScores( float y ) {
x = 640;
score = cg.snap->ps.persistant[PERS_SCORE];
- spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR );
+ spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) ||
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING );
// always show your score in the second box if not in first place
if ( s1 != score ) {
@@ -1805,7 +1806,8 @@ static void CG_DrawCrosshair(void) {
return;
}
- if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) {
+ if( ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) ||
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) ) {
return;
}
@@ -2224,7 +2226,8 @@ static void CG_Draw2D( void ) {
return;
}
- if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
+ if( ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) ||
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_INFESTING ) ) {
CG_DrawSpectator();
CG_DrawCrosshair();
CG_DrawCrosshairNames();
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index c8a3fab2..a2be137c 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -421,6 +421,9 @@ CG_Menu
*/
void CG_Menu( int eventParm )
{
+ char menuDef[ MAX_STRING_CHARS ];
+ int i;
+
switch( eventParm )
{
case MN_TEAM:
@@ -447,6 +450,20 @@ void CG_Menu( int eventParm )
trap_SendConsoleCommand( "menu hmcumenu\n" );
break;
+ case MN_INFEST:
+ strcpy( menuDef, "5,5|Infest|0.8,0,0.8,1|0.6,0,0.6,0.8|1,0,1,1|1|16|" );
+ for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
+ {
+ if( BG_ClassCanEvolveFromTo( cg.snap->ps.stats[ STAT_PCLASS ], i ) )
+ strcat( menuDef, va( "%s, class %s|", BG_FindNameForClassNum( i ), BG_FindNameForClassNum( i ) ) );
+ }
+ strcat( menuDef, "|Choose a class|to evolve to" );
+
+ trap_SendConsoleCommand( va( "defmenu infest \"%s\"\n", menuDef ) );
+ trap_SendConsoleCommand( "menu infest\n" );
+ trap_SendConsoleCommand( "undefmenu infest\n" );
+ break;
+
default:
Com_Printf( "cgame: debug: no such menu %d\n", eventParm );
@@ -985,6 +1002,12 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
CG_Menu( es->eventParm );
break;
+ case EV_PLAYER_RESPAWN:
+ DEBUGNAME("EV_PLAYER_RESPAWN");
+ if( es->number == cg.clientNum )
+ cg.spawnTime = cg.time;
+ break;
+
default:
DEBUGNAME("UNKNOWN");
CG_Error( "Unknown event: %i", event );
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index d1703528..886fb16a 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -632,6 +632,8 @@ typedef struct {
char testModelName[MAX_QPATH];
qboolean testGun;
+ int spawnTime; //TA: fovwarp
+
} cg_t;
diff --git a/src/cgame/cg_predict.c b/src/cgame/cg_predict.c
index dfcaf752..6ded046c 100644
--- a/src/cgame/cg_predict.c
+++ b/src/cgame/cg_predict.c
@@ -432,6 +432,8 @@ void CG_PredictPlayerState( void ) {
cg_pmove.ps = &cg.predictedPlayerState;
cg_pmove.trace = CG_Trace;
cg_pmove.pointcontents = CG_PointContents;
+
+ //TA: FIXME BIG NEON SIGN..... COULD MAYBE BE A FIX FOR THAT HACK IN bg_pmove.c.. MAYBE \/
if ( cg_pmove.ps->pm_type == PM_DEAD ) {
cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
}
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index d8401334..393ac480 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -516,6 +516,7 @@ Fixed fov at intermissions, otherwise account for fov variable and zooms.
*/
#define WAVE_AMPLITUDE 1
#define WAVE_FREQUENCY 0.4
+#define FOVWARPTIME 400.0
static int CG_CalcFov( void ) {
float x;
@@ -545,6 +546,19 @@ static int CG_CalcFov( void ) {
fov_x = 1;
else if ( fov_x > 160 )
fov_x = 160;
+
+ if( cg.spawnTime > ( cg.time - FOVWARPTIME ) &&
+ BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_FOVWARPS ) )
+ {
+ float temp, temp2;
+
+ temp = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;
+ temp2 = ( 180 - fov_x ) * temp;
+
+ //Com_Printf( "%f %f\n", temp*100, temp2*100 );
+
+ fov_x = 180 - temp2;
+ }
}
// account for zooms
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index 15992501..318e227a 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -1070,7 +1070,8 @@ void CG_AddViewWeapon( playerState_t *ps ) {
vec3_t angles;
weaponInfo_t *weapon;
- if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
+ if( ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) ||
+ ( ps->stats[ STAT_STATE ] & SS_INFESTING ) ) {
return;
}
@@ -1105,7 +1106,7 @@ void CG_AddViewWeapon( playerState_t *ps ) {
//if ( cg_fov.integer > 90 ) {
//TA: the client side variable isn't used ( shouldn't iD have done this anyway? )
if( cg.refdef.fov_y > 90 )
- fovOffset = -0.2 * ( cg.refdef.fov_y - 90 );
+ fovOffset = -0.4 * ( cg.refdef.fov_y - 90 );
else
fovOffset = 0;
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 63eed9c8..f1ede9bb 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -891,8 +891,31 @@ int bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1;
classAttributes_t bg_classList[ ] =
{
+ {
+ PCL_D_B_BASE,
+ "Builder",
+ "lucy",
+ "default",
+ { -15, -15, -20 },
+ { 15, 15, 20 },
+ { 15, 15, 20 },
+ { -15, -15, -4 },
+ { 15, 15, 4 },
+ 12, 12,
+ 50,
+ 50,
+ SCA_TAKESFALLDAMAGE|SCA_FOVWARPS,
+ 80,
+ 0.015f,
+ 350,
+ 0.5f,
+ 1.0f,
+ { PCL_D_D_BASE, PCL_D_O_BASE, PCL_NONE },
+ 2000
+ },
{
PCL_D_O_BASE,
+ "Offensive",
"klesk",
"default",
{ -15, -15, -15 },
@@ -903,15 +926,18 @@ classAttributes_t bg_classList[ ] =
4, 4,
25,
0,
- SCA_WALLCLIMBER|SCA_CANJUMP|SCA_NOWEAPONDRIFT,
+ SCA_WALLCLIMBER|SCA_CANJUMP|SCA_NOWEAPONDRIFT|SCA_FOVWARPS,
140,
0.0f,
25,
2.0f,
- 5.0f
+ 5.0f,
+ { PCL_D_D_BASE, PCL_D_B_BASE, PCL_NONE },
+ 3000
},
{
PCL_D_D_BASE,
+ "Defensive",
"orbb",
"default",
{ -15, -15, -15 },
@@ -922,34 +948,18 @@ classAttributes_t bg_classList[ ] =
4, 4,
50,
0,
- SCA_WALLCLIMBER|SCA_CANJUMP|SCA_NOWEAPONDRIFT,
- 160,
+ SCA_WALLCLIMBER|SCA_CANJUMP|SCA_NOWEAPONDRIFT|SCA_FOVWARPS,
+ 90,
0.0f,
25,
1.5f,
- 3.0f
- },
- {
- PCL_D_B_BASE,
- "lucy",
- "default",
- { -15, -15, -20 },
- { 15, 15, 20 },
- { 15, 15, 20 },
- { -15, -15, -4 },
- { 15, 15, 4 },
- 12, 12,
- 50,
- 50,
- SCA_TAKESFALLDAMAGE,
- 80,
- 0.015f,
- 350,
- 0.5f,
- 1.0f
+ 3.0f,
+ { PCL_D_O_BASE, PCL_D_B_BASE, PCL_NONE },
+ 1000
},
{
PCL_H_BASE,
+ "Human",
"sarge",
"default",
{ -15, -15, -24 },
@@ -965,7 +975,9 @@ classAttributes_t bg_classList[ ] =
0.002f,
200,
1.0f,
- 1.0f
+ 1.0f,
+ { PCL_NONE, PCL_NONE, PCL_NONE },
+ 0
}
};
@@ -973,6 +985,44 @@ int bg_numPclasses = sizeof( bg_classList ) / sizeof( bg_classList[ 0 ] );
/*
==============
+BG_FindClassNumForName
+==============
+*/
+int BG_FindClassNumForName( char *name )
+{
+ int i;
+
+ for( i = 0; i < bg_numPclasses; i++ )
+ {
+ if( !Q_stricmp( bg_classList[ i ].className, name ) )
+ return bg_classList[ i ].classNum;
+ }
+
+ //wimp out
+ return PCL_NONE;
+}
+
+/*
+==============
+BG_FindNameForClassNum
+==============
+*/
+char *BG_FindNameForClassNum( int pclass )
+{
+ int i;
+
+ for( i = 0; i < bg_numPclasses; i++ )
+ {
+ if( bg_classList[ i ].classNum == pclass )
+ return bg_classList[ i ].className;
+ }
+
+ //wimp out
+ return "";
+}
+
+/*
+==============
BG_FindModelNameForClass
==============
*/
@@ -986,7 +1036,7 @@ char *BG_FindModelNameForClass( int pclass )
return bg_classList[ i ].modelName;
}
- //wimp out
+ //note: must return a valid modelName!
return bg_classList[ 0 ].modelName;
}
@@ -1208,7 +1258,6 @@ int BG_FindSteptimeForClass( int pclass )
return 200;
}
-
/*
==============
BG_ClassHasAbility
@@ -1233,6 +1282,52 @@ qboolean BG_ClassHasAbility( int pclass, int ability )
return qfalse;
}
+/*
+==============
+BG_ClassCanEvolveFromTo
+==============
+*/
+qboolean BG_ClassCanEvolveFromTo( int fclass, int tclass )
+{
+ int i, j;
+
+ if( tclass == PCL_NONE )
+ return qfalse;
+
+ for( i = 0; i < bg_numPclasses; i++ )
+ {
+ if( bg_classList[ i ].classNum == fclass )
+ {
+ for( j = 0; j <= 3; j++ )
+ if( bg_classList[ i ].children[ j ] == tclass ) return qtrue;
+
+ return qfalse; //may as well return by this point
+ }
+ }
+
+ return qfalse;
+}
+
+/*
+==============
+BG_FindEvolveTimeForClass
+==============
+*/
+int BG_FindEvolveTimeForClass( int pclass )
+{
+ int i;
+
+ for( i = 0; i < bg_numPclasses; i++ )
+ {
+ if( bg_classList[ i ].classNum == pclass )
+ {
+ return bg_classList[ i ].timeToEvolve;
+ }
+ }
+
+ return 5000;
+}
+
/*
==============
@@ -1605,6 +1700,7 @@ char *eventnames[] = {
"EV_USE_ITEM15",
"EV_ITEM_RESPAWN",
+ "EV_PLAYER_RESPAWN",
"EV_ITEM_POP",
"EV_PLAYER_TELEPORT_IN",
"EV_PLAYER_TELEPORT_OUT",
diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c
index 40915b22..0b50f4a2 100644
--- a/src/game/bg_pmove.c
+++ b/src/game/bg_pmove.c
@@ -2144,6 +2144,9 @@ static void PM_Weapon( void ) {
if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
}
+ if( pm->ps->stats[ STAT_STATE ] & SS_INFESTING ) {
+ return;
+ }
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index ab40ae77..49e71c38 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -249,12 +249,14 @@ typedef enum {
#define SCA_CANZOOM 4
#define SCA_CANJUMP 8
#define SCA_NOWEAPONDRIFT 16
+#define SCA_FOVWARPS 32
#define SS_WALLCLIMBING 1
#define SS_GPISROTVEC 2
#define SS_CREEPSLOWED 4
#define SS_WALLTRANSIDING 8
#define SS_SPEEDBOOST 16
+#define SS_INFESTING 32
// player_state->persistant[] indexes
@@ -450,6 +452,7 @@ typedef enum {
EV_USE_ITEM15,
EV_ITEM_RESPAWN,
+ EV_PLAYER_RESPAWN, //TA: for fovwarp effects
EV_ITEM_GROW, //droid items that grow
EV_ITEM_POP,
EV_PLAYER_TELEPORT_IN,
@@ -506,7 +509,8 @@ typedef enum
MN_HUMAN,
MN_ABUILD,
MN_HBUILD,
- MN_MCU
+ MN_MCU,
+ MN_INFEST
} dynMenu_t;
// animations
@@ -696,23 +700,34 @@ typedef struct gitem_s {
typedef struct
{
int classNum;
+
+ char *className;
+
char *modelName;
char *skinName;
+
vec3_t mins;
vec3_t maxs;
vec3_t crouchMaxs;
vec3_t deadMins;
vec3_t deadMaxs;
+
int viewheight;
int crouchViewheight;
+
int health;
int armor;
+
int abilities;
+
int fov;
float bob;
int steptime;
float speed;
float sticky;
+
+ int children[ 3 ];
+ int timeToEvolve;
} classAttributes_t;
@@ -728,6 +743,8 @@ gitem_t *BG_FindItemForPowerup( powerup_t pw );
gitem_t *BG_FindItemForHoldable( holdable_t pw );
//TA:
+int BG_FindClassNumForName( char *name );
+char *BG_FindNameForClassNum( int pclass );
char *BG_FindModelNameForClass( int pclass );
void BG_FindBBoxForClass( int pclass, vec3_t mins, vec3_t maxs, vec3_t cmaxs, vec3_t dmins, vec3_t dmaxs );
void BG_FindViewheightForClass( int pclass, int *viewheight, int *cViewheight );
@@ -739,6 +756,8 @@ float BG_FindSpeedForClass( int pclass );
float BG_FindStickyForClass( int pclass );
int BG_FindSteptimeForClass( int pclass );
qboolean BG_ClassHasAbility( int pclass, int ability );
+qboolean BG_ClassCanEvolveFromTo( int fclass, int tclass );
+int BG_FindEvolveTimeForClass( int pclass );
#define ITEM_INDEX(x) ((x)-bg_itemlist)
qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps );
diff --git a/src/game/g_active.c b/src/game/g_active.c
index 0cf4f644..514224c9 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -276,7 +276,8 @@ void G_TouchTriggers( gentity_t *ent ) {
}
// ignore most entities if a spectator
- if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
+ if( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) ||
+ ( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) ) {
if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
// this is ugly but adding a new ET_? type will
// most likely cause network incompatibilities
@@ -652,6 +653,13 @@ void ClientThink_real( gentity_t *ent ) {
int i;
qboolean cSlowed = qfalse;
+ //TA: time
+ static int lastTime;
+ int dTime;
+
+ dTime = level.time - lastTime;
+ lastTime = level.time;
+
//Com_Printf( "%d\n", G_LuminanceAtPoint( ent->s.origin ) );
client = ent->client;
@@ -724,11 +732,20 @@ void ClientThink_real( gentity_t *ent ) {
client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET );
}
- if ( client->noclip ) {
+ if( client->noclip )
+ {
client->ps.pm_type = PM_NOCLIP;
- } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
+ }
+ else if( client->ps.stats[STAT_HEALTH] <= 0 )
+ {
client->ps.pm_type = PM_DEAD;
- } else {
+ }
+ else if( client->ps.stats[ STAT_STATE ] & SS_INFESTING )
+ {
+ client->ps.pm_type = PM_FREEZE;
+ }
+ else
+ {
client->ps.pm_type = PM_NORMAL;
}
@@ -810,18 +827,18 @@ void ClientThink_real( gentity_t *ent ) {
if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && ucmd->upmove >= 0 )
{
//subtract stamina
- client->ps.stats[ STAT_STAMINA ] -= 2;
+ client->ps.stats[ STAT_STAMINA ] -= dTime/6.0f;
}
if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) )
{
//restore stamina
- client->ps.stats[ STAT_STAMINA ] += 3;
+ client->ps.stats[ STAT_STAMINA ] += dTime/4.0f;
}
else if( aForward <= 5 && aRight <= 5 )
{
//restore stamina faster
- client->ps.stats[ STAT_STAMINA ] += 2;
+ client->ps.stats[ STAT_STAMINA ] += dTime/6.0f;
}
// set up for pmove
@@ -848,6 +865,9 @@ void ClientThink_real( gentity_t *ent ) {
if ( pm.ps->pm_type == PM_DEAD ) {
pm.tracemask = MASK_PLAYERSOLID; // & ~CONTENTS_BODY;
}
+ if ( pm.ps->stats[ STAT_STATE ] & SS_INFESTING ) {
+ pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
+ }
else if ( ent->r.svFlags & SVF_BOT ) {
pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
}
@@ -954,6 +974,14 @@ void ClientThink_real( gentity_t *ent ) {
return;
}
+ if( ( ( client->lastInfestTime +
+ BG_FindEvolveTimeForClass( client->ps.stats[ STAT_PCLASS ] ) ) < level.time ) &&
+ ( client->ps.stats[ STAT_STATE ] & SS_INFESTING ) )
+ {
+ client->ps.stats[ STAT_STATE ] &= ~SS_INFESTING;
+ ClientSpawn( ent, client->infestBody );
+ }
+
// perform once-a-second actions
ClientTimerActions( ent, msec );
}
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index cb35b4c6..01fdcdb8 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -330,6 +330,8 @@ qboolean hdef1_checktarget(gentity_t *self, gentity_t *target)
return qfalse;
if( target->client->sess.sessionTeam == TEAM_SPECTATOR ) // is the target alive?
return qfalse;
+ if( target->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) // is the target alive?
+ return qfalse;
if( target->health <= 0 ) // is the target still alive?
return qfalse;
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 89dd7ded..cb41b37a 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -542,7 +542,7 @@ void InfestBody( gentity_t *self, gentity_t *other, gentity_t *activator )
{
if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_DROIDS ) return;
- G_AddPredictableEvent( activator, EV_MENU, MN_DROID );
+ G_AddPredictableEvent( activator, EV_MENU, MN_INFEST );
}
/*
@@ -935,7 +935,10 @@ void ClientUserinfoChanged( int clientNum ) {
if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
client->pers.maxHealth = 100;
}
- //client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
+
+ //hack to force a client update if the config string does not change between spawning
+ if( client->pers.pclass == PCL_NONE )
+ client->pers.maxHealth = 0;
// set model
s = BG_FindModelNameForClass( client->pers.pclass );
@@ -1156,22 +1159,24 @@ Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn ) {
- int index;
- vec3_t spawn_origin, spawn_angles;
- gclient_t *client;
- int i;
+ int index;
+ vec3_t spawn_origin, spawn_angles;
+ gclient_t *client;
+ int i;
clientPersistant_t saved;
- clientSession_t savedSess;
- int persistant[MAX_PERSISTANT];
- gentity_t *spawnPoint;
- int flags;
- int savedPing;
- int ammoIndex, ammoSubIndex;
- int teamLocal;
- int accuracy_hits, accuracy_shots;
- int savedEvents[MAX_PS_EVENTS];
- int eventSequence;
- char userinfo[MAX_INFO_STRING];
+ clientSession_t savedSess;
+ int persistant[MAX_PERSISTANT];
+ gentity_t *spawnPoint;
+ int flags;
+ int savedPing;
+ int ammoIndex, ammoSubIndex;
+ int teamLocal;
+ int accuracy_hits, accuracy_shots;
+ int savedEvents[MAX_PS_EVENTS];
+ int eventSequence;
+ char userinfo[MAX_INFO_STRING];
+ vec3_t bodyMaxs, classMins, up = { 0, 0, 1 };
+
index = ent - g_entities;
client = ent->client;
@@ -1204,19 +1209,17 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn ) {
}
else
{
+ //this is an infest spawn
if( spawn != NULL )
{
- vec3_t bodyMaxs;
- vec3_t classMins;
- vec3_t up = { 0, 0, 1 };
-
- VectorCopy ( spawn->s.pos.trBase, spawn_origin );
- VectorCopy ( spawn->s.angles, spawn_angles );
+ //spawn as new droid
+ VectorCopy( spawn->s.pos.trBase, spawn_origin );
+ VectorCopy( spawn->s.angles, spawn_angles );
BG_FindBBoxForClass( spawn->s.clientNum, NULL, NULL, NULL, NULL, bodyMaxs );
BG_FindBBoxForClass( ent->client->pers.pclass, classMins, NULL, NULL, NULL, NULL );
- spawn_origin[ 2 ] += 64;
+ spawn_origin[ 2 ] += bodyMaxs[ 2 ] + abs( classMins[ 2 ] ) + 1;
G_AddEvent( spawn, EV_GIB_DROID, DirToByte( up ) );
spawn->freeAfterEvent = qtrue;
}
@@ -1272,9 +1275,9 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn ) {
if( client->sess.sessionTeam == TEAM_SPECTATOR )
{
if( teamLocal == PTE_DROIDS )
- G_AddEvent( ent, EV_MENU, MN_DROID );
+ G_AddPredictableEvent( ent, EV_MENU, MN_DROID );
else if( teamLocal == PTE_HUMANS )
- G_AddEvent( ent, EV_MENU, MN_HUMAN );
+ G_AddPredictableEvent( ent, EV_MENU, MN_HUMAN );
}
// increment the spawncount so the client will detect the respawn
@@ -1306,8 +1309,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn ) {
client->ps.eFlags = flags;
client->ps.clientNum = index;
- VectorCopy (playerMins, ent->r.mins);
- VectorCopy (playerMaxs, ent->r.maxs);
+ BG_FindBBoxForClass( ent->client->pers.pclass, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );
client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = BG_FindHealthForClass( ent->client->pers.pclass );
client->ps.stats[ STAT_ARMOR ] = BG_FindArmorForClass( ent->client->pers.pclass );
@@ -1386,7 +1388,10 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn ) {
client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
client->ps.pm_time = 100;
+ //TA: STAT_SPAWNTIME for droid fov effects
client->respawnTime = level.time;
+ G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
+
client->inactivityTime = level.time + g_inactivity.integer * 1000;
client->latched_buttons = 0;
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 1b2c2be0..d6eb998b 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -419,12 +419,15 @@ Cmd_Kill_f
=================
*/
void Cmd_Kill_f( gentity_t *ent ) {
- if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
+ if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
return;
- }
- if (ent->health <= 0) {
+
+ if( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING )
return;
- }
+
+ if (ent->health <= 0)
+ return;
+
ent->flags &= ~FL_GODMODE;
ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
player_die (ent, ent, ent, 100000, MOD_SUICIDE);
@@ -1541,26 +1544,23 @@ Cmd_Class_f
*/
void Cmd_Class_f( gentity_t *ent )
{
- char s[ MAX_TOKEN_CHARS ];
- qboolean dontSpawn = qfalse;
- int clientNum;
+ char s[ MAX_TOKEN_CHARS ];
+ qboolean dontSpawn = qfalse;
+ int clientNum;
gentity_t *body, *victim;
vec3_t distance;
int length = 4096;
int i;
+ trace_t tr;
+ vec3_t infestOrigin, infestAngles;
clientNum = ent->client - level.clients;
trap_Argv( 1, s, sizeof( s ) );
- if( !strlen( s ) && ( ent->client->pers.pteam != PTE_HUMANS ) )
- {
- trap_SendServerCommand( ent-g_entities, va("print \"class number: %i\n\"", ent->client->pers.pclass ) );
- return;
- }
-
if( ent->client->pers.pteam == PTE_DROIDS )
{
- if( ent->client->pers.pclass )
+ //if we are not currently spectating, we are attempting evolution
+ if( ent->client->pers.pclass != PCL_NONE )
{
for ( i = 1, body = g_entities + i; i < level.num_entities; i++, body++ )
{
@@ -1575,58 +1575,72 @@ void Cmd_Class_f( gentity_t *ent )
}
}
+ //if a human corpse is nearby...
if( length <= 200 )
{
- if( !Q_stricmp(s, "0") )
- ent->client->pers.pclass = PCL_D_B_BASE;
- else if( !Q_stricmp(s, "1") )
- ent->client->pers.pclass = PCL_D_O_BASE;
- else if( !Q_stricmp(s, "2") )
- ent->client->pers.pclass = PCL_D_D_BASE;
- else
+ ent->client->pers.pclass = BG_FindClassNumForName( s );
+
+ //...check we can evolve to that class
+ if( !BG_ClassCanEvolveFromTo( ent->client->ps.stats[ STAT_PCLASS ],
+ ent->client->pers.pclass ) )
{
- trap_SendServerCommand( ent-g_entities, va("print \"Unknown class\n\"" ) );
- dontSpawn = qtrue;
+ trap_SendServerCommand( ent-g_entities, va("print \"You cannot evolve from your current class\n\"" ) );
+ return;
}
- if( !dontSpawn )
+ if( ent->client->pers.pclass != PCL_NONE )
{
+ //evolve
+ ent->client->ps.stats[ STAT_PCLASS ] = PCL_NONE;
ent->client->sess.sessionTeam = TEAM_FREE;
ClientUserinfoChanged( clientNum );
- ClientSpawn( ent, victim );
+ ent->client->ps.stats[ STAT_STATE ] |= SS_INFESTING;
+ ent->client->lastInfestTime = level.time;
+ ent->client->infestBody = victim;
+
+ VectorCopy( victim->s.pos.trBase, infestOrigin );
+ infestOrigin[ 2 ] += 128;
+
+ /*trap_Trace( &tr, victim->s.pos.trBase, NULL, NULL, infestOrigin, victim->s.number, MASK_PLAYERSOLID );
+ VectorCopy( tr.endpos, infestOrigin );*/
+ VectorCopy( victim->s.angles, infestAngles );
+
+ infestAngles[ PITCH ] = 90;
+
+ G_SetOrigin( ent, infestOrigin );
+ VectorCopy( infestOrigin, ent->client->ps.origin );
+
+ SetClientViewAngle( ent, infestAngles );
+ }
+ else
+ {
+ trap_SendServerCommand( ent-g_entities, va("print \"Unknown class\n\"" ) );
+ return;
}
}
-
- return;
- }
- else if( ent->client->pers.pclass != 0 )
- {
- trap_SendServerCommand( ent-g_entities, va("print \"You must be dead to spawn from a bioegg\n\"" ) );
- return;
}
-
- if( !Q_stricmp(s, "0") )
- ent->client->pers.pclass = PCL_D_B_BASE;
- else if( !Q_stricmp(s, "1") )
- ent->client->pers.pclass = PCL_D_O_BASE;
- else if( !Q_stricmp(s, "2") )
- ent->client->pers.pclass = PCL_D_D_BASE;
else
{
- trap_SendServerCommand( ent-g_entities, va("print \"Unknown class\n\"" ) );
- dontSpawn = qtrue;
- }
+ //spawing from a bioegg
+ ent->client->pers.pclass = BG_FindClassNumForName( s );
- if( !dontSpawn )
- {
- ent->client->sess.sessionTeam = TEAM_FREE;
- ClientUserinfoChanged( clientNum );
- ClientSpawn( ent, NULL );
+ if( ent->client->pers.pclass != PCL_NONE )
+ {
+ ent->client->sess.sessionTeam = TEAM_FREE;
+ ClientUserinfoChanged( clientNum );
+ ClientSpawn( ent, NULL );
+ }
+ else
+ {
+ trap_SendServerCommand( ent-g_entities, va("print \"Unknown class\n\"" ) );
+ return;
+ }
}
}
else if( ent->client->pers.pteam == PTE_HUMANS )
{
- if( ent->client->pers.pclass != 0 )
+ //humans cannot use this command whilst alive
+ if( ent->client->pers.pclass != PCL_NONE )
{
trap_SendServerCommand( ent-g_entities, va("print \"You must be dead to use the class command\n\"" ) );
return;
@@ -1634,26 +1648,29 @@ void Cmd_Class_f( gentity_t *ent )
ent->client->pers.pclass = PCL_H_BASE;
- if( !Q_stricmp( s, "0" ) )
+ //set the item to spawn with
+ if( !Q_stricmp( s, "rifle" ) )
ent->client->pers.pitem = WP_MACHINEGUN;
- else if( !Q_stricmp( s, "1" ) )
+ else if( !Q_stricmp( s, "ckit" ) )
ent->client->pers.pitem = WP_HBUILD;
-
+ else
+ {
+ trap_SendServerCommand( ent-g_entities, va("print \"Unknown starting item\n\"" ) );
+ return;
+ }
+
ent->client->sess.sessionTeam = TEAM_FREE;
ClientUserinfoChanged( clientNum );
ClientSpawn( ent, NULL );
}
else if( ent->client->pers.pteam == PTE_NONE )
{
- ent->client->pers.pclass = 0;
+ //can't use this command unless on a team
+ ent->client->pers.pclass = PCL_NONE;
ent->client->sess.sessionTeam = TEAM_FREE;
ClientSpawn( ent, NULL );
trap_SendServerCommand( ent-g_entities, va("print \"Join a team first\n\"" ) );
}
- else
- {
- trap_SendServerCommand( ent-g_entities, va("print \"?\n\"" ) );
- }
}
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 1ddc628e..b32f07ff 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -274,55 +274,57 @@ struct gclient_s {
qboolean noclip;
- int lastCmdTime; // level.time of last usercmd_t, for EF_CONNECTION
- // we can't just use pers.lastCommand.time, because
- // of the g_sycronousclients case
- int buttons;
- int oldbuttons;
- int latched_buttons;
+ int lastCmdTime; // level.time of last usercmd_t, for EF_CONNECTION
+ // we can't just use pers.lastCommand.time, because
+ // of the g_sycronousclients case
+ int buttons;
+ int oldbuttons;
+ int latched_buttons;
vec3_t oldOrigin;
// sum up damage over an entire frame, so
// shotgun blasts give a single big kick
- int damage_armor; // damage absorbed by armor
- int damage_blood; // damage taken out of health
- int damage_knockback; // impact damage
+ int damage_armor; // damage absorbed by armor
+ int damage_blood; // damage taken out of health
+ int damage_knockback; // impact damage
vec3_t damage_from; // origin for vector calculation
qboolean damage_fromWorld; // if true, don't use the damage_from vector
- int accurateCount; // for "impressive" reward sound
- int accuracy_shots; // total number of shots
- int accuracy_hits; // total number of hits
+ int accurateCount; // for "impressive" reward sound
+ int accuracy_shots; // total number of shots
+ int accuracy_hits; // total number of hits
//
- int lastkilled_client; // last client that this client killed
- int lasthurt_client; // last client that damaged this client
- int lasthurt_mod; // type of damage the client did
+ int lastkilled_client; // last client that this client killed
+ int lasthurt_client; // last client that damaged this client
+ int lasthurt_mod; // type of damage the client did
// timers
- int respawnTime; // can respawn when time > this, force after g_forcerespwan
- int inactivityTime; // kick players when time > this
+ int respawnTime; // can respawn when time > this, force after g_forcerespwan
+ int inactivityTime; // kick players when time > this
qboolean inactivityWarning; // qtrue if the five seoond warning has been given
- int rewardTime; // clear the EF_AWARD_IMPRESSIVE, etc when time > this
+ int rewardTime; // clear the EF_AWARD_IMPRESSIVE, etc when time > this
- int airOutTime;
+ int airOutTime;
- int lastKillTime; // for multiple kill rewards
+ int lastKillTime; // for multiple kill rewards
qboolean fireHeld; // used for hook
gentity_t *hook; // grapple hook if out
- int switchTeamTime; // time the player switched teams
+ int switchTeamTime; // time the player switched teams
gentity_t *torch; //TA: torch entity ( NULL if switched off )
// timeResidual is used to handle events that happen every second
// like health / armor countdowns and regeneration
- int timeResidual;
+ int timeResidual;
- char *areabits;
+ char *areabits;
float classSpeed; //TA: here to save STAT_ space
+ int lastInfestTime; //TA: to keep track of how long infests take
+ gentity_t *infestBody; //TA: body that is being infested. must be persistant
};
//
diff --git a/src/game/g_team.c b/src/game/g_team.c
index b6e2c8a7..ce9263d1 100644
--- a/src/game/g_team.c
+++ b/src/game/g_team.c
@@ -472,7 +472,7 @@ void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
/*if (targ->client->sess.sessionTeam == TEAM_HUMANS)
flag_pw = PW_BLUEFLAG;
else
- flag_pw = PW_REDFLAG;*/
+ flag_pw = PW_REDFLAG;
if (targ->client->ps.powerups[flag_pw] &&
targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
@@ -481,7 +481,7 @@ void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
// skulls
if (targ->client->ps.generic1 &&
targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
- attacker->client->pers.teamState.lasthurtcarrier = level.time;
+ attacker->client->pers.teamState.lasthurtcarrier = level.time;*/
}
diff --git a/src/game/g_utils.c b/src/game/g_utils.c
index dc960ab0..a72f57ad 100644
--- a/src/game/g_utils.c
+++ b/src/game/g_utils.c
@@ -540,10 +540,13 @@ void G_KillBox (gentity_t *ent) {
for (i=0 ; i<num ; i++) {
hit = &g_entities[touch[i]];
- if ( !hit->client ) {
+ if ( !hit->client )
continue;
- }
+ //TA: impossible to telefrag self
+ if( ent == hit )
+ continue;
+
// nail it
G_Damage ( hit, ent, ent, NULL, NULL,
100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);