diff -urN --exclude=.svn et260/src/game/bg_animation.c etpub/src/game/bg_animation.c --- et260/src/game/bg_animation.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_animation.c 2005-05-11 17:28:51.000000000 -0500 @@ -488,7 +488,8 @@ BG_CopyStringIntoBuffer =============== */ -char *BG_CopyStringIntoBuffer( char *string, char *buffer, int bufSize, int *offset ) { +//CHRUKER: b069 - Cleaned up a few compiler warnings +char *BG_CopyStringIntoBuffer( char *string, char *buffer, unsigned int bufSize, unsigned int *offset ) { char *pch; // check for overloaded buffer @@ -1423,6 +1424,7 @@ animScriptItem_t *scriptItem = NULL; animScriptCommand_t *scriptCommand = NULL; int state = ps->aiState; + qboolean setTimer = qfalse; // Allow fallen movetype while dead if( ps->eFlags & EF_DEAD && movetype != ANIM_MT_FALLEN && movetype != ANIM_MT_FLAILING ) @@ -1462,14 +1464,46 @@ #ifdef DBGANIMS if( scriptCommand->bodyPart[0] ) - Com_Printf( "anim0 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[0]].string, animModelInfo->animations[scriptCommand->animIndex[0]]->name ); + Com_Printf( "anim0 (%s): %s %d", animBodyPartsStr[scriptCommand->bodyPart[0]].string, animModelInfo->animations[scriptCommand->animIndex[0]]->name, scriptCommand->animIndex[0] ); if( scriptCommand->bodyPart[1] ) - Com_Printf( "anim1 (%s): %s", animBodyPartsStr[scriptCommand->bodyPart[1]].string, animModelInfo->animations[scriptCommand->animIndex[1]]->name ); + Com_Printf( "anim1 (%s): %s %d", animBodyPartsStr[scriptCommand->bodyPart[1]].string, animModelInfo->animations[scriptCommand->animIndex[1]]->name, scriptCommand->animIndex[1]); Com_Printf( "\n" ); #endif + + // tjw: we need to match head boxes to the run animation + // in the game code. Is there a better way? + ps->eFlags &= ~EF_MOTION; + switch(scriptCommand->animIndex[0]) { + case 37: // alert_run_2h + case 40: // alert_crch_2h + case 44: // alert_bk_2h + case 47: // alert_crbk_2h + case 160: // prone_crawl + case 161: // prone_crawl_bk + ps->eFlags |= EF_MOTION; + break; + } + + + // tjw: this is a dirty dirty hack and should be removed + // if using a client mod (update the animation script). + // This is here because I added head hitboxes to + // corpses and we can't have their heads leaving the + // ground because the hitboxes can't do that. + if(movetype == ANIM_MT_FALLEN) { + setTimer = qtrue; + if(ps->torsoTimer < 3400) + return( BG_ExecuteCommand(ps, + animModelInfo, + scriptCommand, + setTimer, + qfalse, + qtrue) != -1 ); + + } // run it - return( BG_ExecuteCommand( ps, animModelInfo, scriptCommand, qfalse, isContinue, qfalse ) != -1 ); + return( BG_ExecuteCommand( ps, animModelInfo, scriptCommand, setTimer, isContinue, qfalse ) != -1 ); } /* diff -urN --exclude=.svn et260/src/game/bg_local.h etpub/src/game/bg_local.h --- et260/src/game/bg_local.h 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_local.h 2005-05-05 11:51:55.000000000 -0500 @@ -71,7 +71,11 @@ qboolean PM_SlideMove( qboolean gravity ); void PM_StepSlideMove( qboolean gravity ); -qboolean PM_SlideMoveProne( qboolean gravity ); -void PM_StepSlideMoveProne( qboolean gravity ); - void PM_BeginWeaponChange( int oldweapon, int newweapon, qboolean reload ); + +// tjw +extern vmCvar_t g_weapons; +extern vmCvar_t g_spinCorpse; +// josh +extern vmCvar_t g_misc; +extern vmCvar_t g_doubleJumpHeight; diff -urN --exclude=.svn et260/src/game/bg_misc.c etpub/src/game/bg_misc.c --- et260/src/game/bg_misc.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_misc.c 2005-05-23 11:42:15.000000000 -0500 @@ -13,12 +13,17 @@ #ifdef CGAMEDLL extern vmCvar_t cg_gameType; #define gametypeCvar cg_gameType +#define MISC_UW_AMMO_RESTORES_HELMET 0 // josh: no client mod #elif GAMEDLL extern vmCvar_t g_developer; extern vmCvar_t g_gametype; #define gametypeCvar g_gametype + extern vmCvar_t g_weapons; +#define MISC_UW_AMMO_RESTORES_HELMET \ + (g_weapons.integer & WPF_AMMO_RESTORES_HELMET) #else extern vmCvar_t ui_gameType; +#define MISC_UW_AMMO_RESTORES_HELMET 0 // josh: no ui stuff here #define gametypeCvar ui_gameType #endif @@ -64,6 +69,19 @@ "Bombardment Medal", "Silver Snake" }; +#ifdef GAMEDLL + +int skillLevels[SK_NUM_SKILLS][NUM_SKILL_LEVELS] = { + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, + {0, 20, 50, 90, 140}, +}; + +#else const int skillLevels[NUM_SKILL_LEVELS] = { 0, // reaching level 0 @@ -74,6 +92,8 @@ // 200 // reaching level 5 }; +#endif + vec3_t playerlegsProneMins = { -13.5f, -13.5f, -24.f }; vec3_t playerlegsProneMaxs = { 13.5f, 13.5f, -14.4f }; @@ -153,7 +173,7 @@ { 90, 1, 30, 30, 30, 2400, DELAY_LOW, 150, 0, 0, MOD_THOMPSON }, // WP_THOMPSON // 8 { 45, 1, 15, 0, 4, 1000, DELAY_THROW, 1600, 0, 0, MOD_GRENADE_PINEAPPLE }, // WP_GRENADE_PINEAPPLE // 9 - { 96, 1, 32, 32, 32, 3100, DELAY_LOW, 150, 1200, 450, MOD_STEN }, // WP_STEN // 10 + { 96, 1, 32, 64, 32, 3100, DELAY_LOW, 150, 1200, 450, MOD_STEN }, // WP_STEN // 10 { 10, 1, 1, 0, 10, 1500, 50, 1000, 0, 0, MOD_SYRINGE }, // WP_MEDIC_SYRINGE // 11 { 1, 0, 1, 0, 0, 3000, 50, 1000, 0, 0, MOD_AMMO, }, // WP_AMMO // 12 { 1, 0, 1, 0, 1, 3000, 50, 1000, 0, 0, MOD_ARTY, }, // WP_ARTY // 13 @@ -2908,6 +2928,15 @@ int clip; int weapNumOfClips; + // Michael: add the helmet first, then do weapons + // it looks like the way to check if they have a helmet is by + // seeing if they have already been shot in the head + // to add the helmet, just clear the bit + if(MISC_UW_AMMO_RESTORES_HELMET && (ps->eFlags & EF_HEADSHOT)) { + ps->eFlags &= (EF_HEADSHOT ^ 0xFFFFFFFF); + } + + // Gordon: handle grenades first i = BG_GrenadesForClass( ps->stats[STAT_PLAYER_CLASS], skill ); weapon = BG_GrenadeTypeForTeam( teamNum ); @@ -3999,6 +4028,11 @@ if ( snap ) { SnapVector( s->pos.trBase ); } + // josh: This wasn't in here?! Why!? It's in Quake 3 + VectorCopy( ps->velocity, s->pos.trDelta ); + if ( snap ) { + SnapVector( s->pos.trDelta ); + } s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); @@ -4027,9 +4061,11 @@ s->eFlags = ps->eFlags; - if ( ps->stats[STAT_HEALTH] <= 0 ) { + if(ps->stats[STAT_HEALTH] <= 0 && + !(ps->eFlags & EF_PLAYDEAD)) { s->eFlags |= EF_DEAD; - } else { + } + else { s->eFlags &= ~EF_DEAD; } @@ -4128,9 +4164,11 @@ } s->eFlags = ps->eFlags; - if ( ps->stats[STAT_HEALTH] <= 0 ) { + if(ps->stats[STAT_HEALTH] <= 0 && + !(ps->eFlags & EF_PLAYDEAD)) { s->eFlags |= EF_DEAD; - } else { + } + else { s->eFlags &= ~EF_DEAD; } @@ -4849,7 +4887,11 @@ { "vote_allow_warmupdamage", CV_SVF_WARMUPDAMAGE }, { "vote_allow_antilag", CV_SVF_ANTILAG }, { "vote_allow_balancedteams", CV_SVF_BALANCEDTEAMS }, - { "vote_allow_muting", CV_SVF_MUTING } + { "vote_allow_muting", CV_SVF_MUTING }, + { "vote_allow_surrender", CV_SVF_SURRENDER }, + { "vote_allow_restartcampaign", CV_SVF_RESTARTCAMPAIGN }, + { "vote_allow_nextcampaign", CV_SVF_NEXTCAMPAIGN }, + { "vote_allow_poll", CV_SVF_POLL } }; int numVotesAvailable = sizeof(voteToggles) / sizeof(voteType_t); @@ -5015,7 +5057,8 @@ // strip colors and control codes, copying up to dwMaxLength-1 "good" chars and nul-terminating // returns the length of the cleaned string -int BG_cleanName( const char *pszIn, char *pszOut, unsigned int dwMaxLength, qboolean fCRLF ) +// CHRUKER: b069 - Cleaned up a few compiler warnings +int BG_cleanName( const char *pszIn, char *pszOut, int dwMaxLength, qboolean fCRLF ) { const char *pInCopy = pszIn; const char *pszOutStart = pszOut; diff -urN --exclude=.svn et260/src/game/bg_pmove.c etpub/src/game/bg_pmove.c --- et260/src/game/bg_pmove.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_pmove.c 2005-05-23 09:27:57.000000000 -0500 @@ -15,8 +15,20 @@ #ifdef CGAMEDLL #define PM_GameType cg_gameType.integer +#define PM_UW_SYRINGE 0 // tjw: no client mod +#define PM_UW_PLIERS 0 // tjw: no client mod +#define PM_CORPSE_SPIN 0 // tjw: no client mod +#define PM_DOUBLE_JUMP 0 // josh: no client mod +#define PM_DOUBLE_JUMP_HEIGHT 0 // josh: no client mod +#define PM_GARAND_RELOADS 0 // tjw: no client mod #elif GAMEDLL #define PM_GameType g_gametype.integer +#define PM_UW_SYRINGE (g_weapons.integer & WPF_UNDERWATER_SYRINGE) +#define PM_UW_PLIERS (g_weapons.integer & WPF_UNDERWATER_PLIERS) +#define PM_CORPSE_SPIN g_spinCorpse.integer +#define PM_DOUBLE_JUMP (g_misc.integer & MISC_DOUBLE_JUMP) +#define PM_DOUBLE_JUMP_HEIGHT g_doubleJumpHeight.value +#define PM_GARAND_RELOADS (g_weapons.integer & WPF_GARAND_RELOADS) #endif #define PM_IsSinglePlayerGame() (PM_GameType == GT_SINGLE_PLAYER || PM_GameType == GT_COOP) @@ -307,7 +319,10 @@ flatforward[1] = sin(angle); flatforward[2] = 0; - VectorScale(flatforward, -32, ofs); + if(pm->ps->eFlags & EF_PRONE) + VectorScale(flatforward, -32, ofs); + else + VectorScale(flatforward, 32, ofs); VectorAdd(start, ofs, org); VectorAdd(end, ofs, point); @@ -316,8 +331,6 @@ trace->allsolid) { // legs are clipping sooner than body // see if our legs can step up - - // give it a try with the new height ofs[2] += STEPSIZE; VectorAdd(start, ofs, org); @@ -345,30 +358,99 @@ } } -/* Traces all player bboxes -- body and legs */ -void PM_TraceAllLegs( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end ) +void PM_TraceHead(trace_t *trace, + vec3_t start, + vec3_t end, + trace_t *bodytrace, + vec3_t viewangles, + void (tracefunc)(trace_t *results, + const vec3_t start, + const vec3_t mins, + const vec3_t maxs, + const vec3_t end, + int passEntityNum, + int contentMask), + int ignoreent, + int tracemask ) +{ + vec3_t ofs; + vec3_t flatforward; + vec3_t point; + float angle; + // tjw: more than just head, try to make a box for all the + // player model that extends out (weapons and arms too) + vec3_t mins = { -18.f, -18.f, -2.f }; + vec3_t maxs = { 18.f, 18.f, 10.f }; + + // don't let players block head + tracemask &= ~(CONTENTS_BODY | CONTENTS_CORPSE); + + angle = DEG2RAD(viewangles[YAW]); + flatforward[0] = cos(angle); + flatforward[1] = sin(angle); + flatforward[2] = 0; + + if(pm->ps->eFlags & EF_PRONE) {; + VectorScale(flatforward, 36, ofs); + } + else { + VectorScale(flatforward, -36, ofs); + } + + VectorAdd(end, ofs, point); + tracefunc(trace, start, mins, maxs, point, ignoreent, tracemask); +} + + +/* Traces all player bboxes -- body. legs, and head */ +void PM_TraceAllParts( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end ) { pm->trace(trace, start, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); - /* legs */ - if ( pm->ps->eFlags & EF_PRONE ) { + // legs and head + if((pm->ps->eFlags & EF_PRONE) || + (pm->ps->eFlags & EF_DEAD) || + (pm->ps->eFlags & EF_PLAYDEAD)) { + trace_t legtrace; + trace_t headtrace; + qboolean adjust = qfalse; - PM_TraceLegs( &legtrace, legsOffset, start, end, trace, pm->ps->viewangles, pm->trace, pm->ps->clientNum, pm->tracemask ); + PM_TraceLegs( &legtrace, legsOffset, start, end, trace, + pm->ps->viewangles, pm->trace, pm->ps->clientNum, + pm->tracemask); - if (legtrace.fraction < trace->fraction || + if(legtrace.fraction < trace->fraction || legtrace.startsolid || legtrace.allsolid) { - VectorSubtract( end, start, legtrace.endpos ); - VectorMA( start, legtrace.fraction, legtrace.endpos, legtrace.endpos ); + *trace = legtrace; + adjust = qtrue; + } + + PM_TraceHead( &headtrace, start, end, trace, + pm->ps->viewangles, pm->trace, pm->ps->clientNum, + pm->tracemask); + + if(headtrace.fraction < trace->fraction || + headtrace.startsolid || + headtrace.allsolid) { + + *trace = headtrace; + adjust = qtrue; + } + + if(adjust) { + VectorSubtract(end, start, trace->endpos); + VectorMA(start, trace->fraction, trace->endpos, + trace->endpos); } } } void PM_TraceAll( trace_t *trace, vec3_t start, vec3_t end ) { - PM_TraceAllLegs( trace, NULL, start, end ); + PM_TraceAllParts( trace, NULL, start, end ); } /* @@ -736,11 +818,10 @@ // JPW NERVE -- jumping in multiplayer uses and requires sprint juice (to prevent turbo skating, sprint + jumps) // don't allow jump accel - - // rain - revert to using pmext for this since pmext is fixed now. - // fix for #166 - if (pm->cmd.serverTime - pm->pmext->jumpTime < 850) - return qfalse; + //if (pm->cmd.serverTime - pm->pmext->jumpTime < 850) + if (pm->cmd.serverTime - pm->ps->jumpTime < 850 ) {// Arnout: NOTE : TEMP DEBUG + return qfalse; + } // don't allow if player tired // if (pm->pmext->sprintTime < 2500) // JPW pulled this per id request; made airborne jumpers wildly inaccurate with gunfire to compensate @@ -781,6 +862,66 @@ return qtrue; } +/* +============= +PM_CheckDoubleJump +============= +*/ +static qboolean PM_CheckDoubleJump( void ) { + + if ( !PM_DOUBLE_JUMP ) { + return qfalse; + } + + // no jumpin when prone + if( pm->ps->eFlags & EF_PRONE ) { + return qfalse; + } + + // josh: Don't allow if already double jumping + if (pm->ps->pm_flags & PMF_DOUBLEJUMPING) { + return qfalse; + } + + // Only allow double jump when going up + if (pm->ps->velocity[2] <= 0) { + return qfalse; + } + + if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + return qfalse; // don't allow jump until all buttons are up + } + + if ( pm->cmd.upmove < 10 ) { + // not holding jump + return qfalse; + } + + // must wait for jump to be released + if ( pm->ps->pm_flags & PMF_JUMP_HELD ) { + // clear upmove so cmdscale doesn't lower running speed + pm->cmd.upmove = 0; + return qfalse; + } + + pml.groundPlane = qfalse; // jumping away + pml.walking = qfalse; + pm->ps->pm_flags |= PMF_JUMP_HELD; + + pm->ps->groundEntityNum = ENTITYNUM_NONE; + pm->ps->velocity[2] = PM_DOUBLE_JUMP_HEIGHT*JUMP_VELOCITY; + PM_AddEvent( EV_JUMP ); + + if ( pm->cmd.forwardmove >= 0 ) { + BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMP, qfalse, qtrue ); + pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; + } else { + BG_AnimScriptEvent( pm->ps, pm->character->animModelInfo, ANIM_ET_JUMPBK, qfalse, qtrue ); + pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; + } + + return qtrue; +} /* ============= @@ -829,6 +970,113 @@ return qtrue; } + +/* + * PM_CheckPlayDead + * see if this player can lay down and look dead +*/ +static qboolean PM_CheckPlayDead (void) +{ + trace_t trace; + + if(pm->ps->pm_type != PM_PLAYDEAD) return qfalse; + + // PM_PLAYDEAD is a one time only pm_type + pm->ps->pm_type = PM_NORMAL; + + if(!(pm->ps->eFlags & EF_PLAYDEAD)) { + if(pm->ps->pm_flags & PMF_LADDER) { + return qfalse; + } + + if(pm->ps->persistant[PERS_HWEAPON_USE] || + pm->ps->eFlags & EF_MOUNTEDTANK) { + + return qfalse; + } + + if(pm->waterlevel > 1) { + return qfalse; + } + + pm->mins[0] = pm->ps->mins[0]; + pm->mins[1] = pm->ps->mins[1]; + + pm->maxs[0] = pm->ps->maxs[0]; + pm->maxs[1] = pm->ps->maxs[1]; + + pm->mins[2] = pm->ps->mins[2]; + pm->maxs[2] = pm->ps->crouchMaxZ; + + pm->ps->eFlags |= EF_PLAYDEAD; + PM_TraceAll(&trace, pm->ps->origin, pm->ps->origin); + pm->ps->eFlags &= ~EF_PLAYDEAD; + + if(!trace.startsolid && !trace.allsolid) { + pm->ps->eFlags |= EF_PLAYDEAD; + pm->ps->eFlags |= EF_DEAD; + pm->ps->pm_type = PM_DEAD; + + // client uses stats[STAT_HEALTH] as the indicator for + // revive sprites and player position + pm->ps->stats[STAT_HEALTH] = 0; + + // make the hitbox like a dead guy + pm->maxs[2] = pm->ps->maxs[2] = pm->ps->crouchMaxZ; + return qtrue; + } + } + else { + // see if we have the space to stop playing dead + pm->mins[0] = pm->ps->mins[0]; + pm->mins[1] = pm->ps->mins[1]; + + pm->maxs[0] = pm->ps->maxs[0]; + pm->maxs[1] = pm->ps->maxs[1]; + + pm->mins[2] = pm->ps->mins[2]; + pm->maxs[2] = pm->ps->crouchMaxZ; + + pm->ps->eFlags &= ~EF_PLAYDEAD; + PM_TraceAll(&trace, pm->ps->origin, pm->ps->origin); + pm->ps->eFlags |= EF_PLAYDEAD; + + if(trace.allsolid) { + // this would have been turned up to ent->health + // before running a PM_PLAYDEAD. Set it back down + // if we can't stand up. + pm->ps->stats[STAT_HEALTH] = 0; + return qfalse; + } + + // crouch for a bit + pm->ps->pm_flags |= PMF_DUCKED; + + // turn the hitbox back up + pm->maxs[2] = pm->ps->maxs[2] = pm->ps->standViewHeight; + + // stop playdead + pm->ps->eFlags &= ~EF_PLAYDEAD; + pm->ps->eFlags &= ~EF_DEAD; + + // this is need to force the end of the fallen animation + // because of the timer hack in bg_animation.c + pm->ps->torsoTimer = 0; + BG_AnimScriptEvent(pm->ps, + pm->character->animModelInfo, + ANIM_ET_JUMPBK, + qfalse, + qtrue); + + // don't jump for a bit + pm->pmext->jumpTime = pm->cmd.serverTime - 650; + pm->ps->jumpTime = pm->cmd.serverTime - 650; + + return qtrue; + } + return qfalse; +} + /* ============== PM_CheckProne @@ -874,10 +1122,12 @@ // return qfalse; //} - if( ((pm->ps->pm_flags & PMF_DUCKED && pm->cmd.doubleTap == DT_FORWARD) || - (pm->cmd.wbuttons & WBUTTON_PRONE)) && pm->cmd.serverTime - -pm->pmext->proneTime > 750 ) { - trace_t trace; + if(((pm->ps->pm_flags & PMF_DUCKED && + pm->cmd.doubleTap == DT_FORWARD) || + (pm->cmd.wbuttons & WBUTTON_PRONE)) && + pm->cmd.serverTime - -pm->pmext->proneTime > 750) { + trace_t trace; pm->mins[0] = pm->ps->mins[0]; pm->mins[1] = pm->ps->mins[1]; @@ -891,7 +1141,7 @@ PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin ); pm->ps->eFlags &= ~EF_PRONE; - if ( !trace.startsolid && !trace.allsolid ) { + if(trace.fraction == 1.0f) { // go prone pm->ps->pm_flags |= PMF_DUCKED; // crouched as well pm->ps->eFlags |= EF_PRONE; @@ -907,7 +1157,11 @@ pm->ps->eFlags & EF_MOUNTEDTANK || // zinx - what was the reason for this, anyway? removing fixes bug 424 // pm->cmd.serverTime - pm->pmext->proneGroundTime > 450 || - ((pm->cmd.doubleTap == DT_BACK || pm->cmd.upmove > 10 || pm->cmd.wbuttons & WBUTTON_PRONE) && pm->cmd.serverTime - pm->pmext->proneTime > 750) ) { + ((pm->cmd.doubleTap == DT_BACK || + pm->cmd.upmove > 10 || + pm->cmd.wbuttons & WBUTTON_PRONE) + && pm->cmd.serverTime - pm->pmext->proneTime > 750)) { + trace_t trace; // see if we have the space to stop prone @@ -918,13 +1172,12 @@ pm->maxs[1] = pm->ps->maxs[1]; pm->mins[2] = pm->ps->mins[2]; - pm->maxs[2] = pm->ps->crouchMaxZ; pm->ps->eFlags &= ~EF_PRONE; PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin ); pm->ps->eFlags |= EF_PRONE; - if( !trace.allsolid ) { + if(trace.fraction == 1.0f) { // crouch for a bit pm->ps->pm_flags |= PMF_DUCKED; @@ -975,9 +1228,12 @@ //if( frac > 1.f ) // frac = 1.f; - //pm->maxs[2] = pm->ps->maxs[2] - (frac * (pm->ps->standViewHeight - PRONE_VIEWHEIGHT)); - //pm->ps->viewheight = DEFAULT_VIEWHEIGHT - (frac * (DEFAULT_VIEWHEIGHT - PRONE_VIEWHEIGHT)); // default - prone to get a positive which is subtracted from default - pm->maxs[2] = pm->ps->maxs[2] - pm->ps->standViewHeight - PRONE_VIEWHEIGHT; + + //pm->maxs[2] = pm->ps->maxs[2] - pm->ps->standViewHeight - PRONE_VIEWHEIGHT; + // tjw: it appears that 12 is the magic number + // for the minimum maxs[2] that prevents + // player from getting stuck into the world. + pm->maxs[2] = 12; pm->ps->viewheight = PRONE_VIEWHEIGHT; return( qtrue ); @@ -1233,6 +1489,33 @@ float scale; usercmd_t cmd; + if ( PM_CheckDoubleJump () ) { + pm->ps->pm_flags |= PMF_DOUBLEJUMPING; + // jumped away + if ( pm->waterlevel > 1 ) { + PM_WaterMove(); + } //else { + // PM_AirMove(); + //} + + // this will probably never happen so the second jump won't + // drain your sprint meter. Just like shrub. + // Take out the if {} to make it drain + if (!(pm->cmd.serverTime - pm->pmext->jumpTime < 850)) { + + pm->pmext->sprintTime -= 2500; + if (pm->pmext->sprintTime < 0) + pm->pmext->sprintTime = 0; + + pm->pmext->jumpTime = pm->cmd.serverTime; + } + + // JPW NERVE + pm->ps->jumpTime = pm->cmd.serverTime; // Arnout: NOTE : TEMP DEBUG + + //return; + } + PM_Friction(); fmove = pm->cmd.forwardmove; @@ -1499,6 +1782,7 @@ VectorNormalize (pm->ps->velocity); VectorScale (pm->ps->velocity, forward, pm->ps->velocity); } + } @@ -1644,8 +1928,7 @@ // SURF_NODAMAGE is used for bounce pads where you don't ever // want to take damage or play a crunch sound - if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) - { + if(!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) && !pm->predict) { if ( pm->debugLevel ) Com_Printf ("delta: %5.2f\n", delta); @@ -1831,7 +2114,7 @@ point[2] = pm->ps->origin[2] - 0.25f; } - PM_TraceAllLegs( &trace, &pm->pmext->proneLegsOffset, pm->ps->origin, point ); + PM_TraceAllParts( &trace, &pm->pmext->proneLegsOffset, pm->ps->origin, point ); pml.groundTrace = trace; // do something corrective if the trace starts in a solid... @@ -1883,6 +2166,7 @@ pml.groundPlane = qtrue; pml.walking = qtrue; + pm->ps->pm_flags &= ~PMF_DOUBLEJUMPING; // hitting solid ground will end a waterjump if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) @@ -2000,7 +2284,7 @@ // try to stand up pm->maxs[2] = pm->ps->maxs[2]; PM_TraceAll( &trace, pm->ps->origin, pm->ps->origin ); - if (!trace.allsolid) + if (trace.fraction == 1.0f) pm->ps->pm_flags &= ~PMF_DUCKED; } } @@ -2037,14 +2321,13 @@ if (pm->ps->eFlags & EF_DEAD) { - - //if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) - if ( pm->ps->pm_flags & PMF_FLAILING ) { + if( pm->ps->pm_flags & PMF_FLAILING ) { animResult = BG_AnimScriptAnimation( pm->ps, pm->character->animModelInfo, ANIM_MT_FLAILING, qtrue ); if( !pm->ps->pm_time ) pm->ps->pm_flags &= ~PMF_FLAILING; // the eagle has landed - } else if ( !pm->ps->pm_time && !(pm->ps->pm_flags & PMF_LIMBO) ) { // DHM - Nerve :: before going to limbo, play a wounded/fallen animation + } + else if(!pm->ps->pm_time && !(pm->ps->pm_flags & PMF_LIMBO) ) { // DHM - Nerve :: before going to limbo, play a wounded/fallen animation if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { // takeoff! pm->ps->pm_flags |= PMF_FLAILING; @@ -2305,20 +2588,42 @@ ============== */ static void PM_BeginWeaponReload( int weapon ) { - gitem_t* item; + //gitem_t* item; int reloadTime; // only allow reload if the weapon isn't already occupied (firing is okay) if(pm->ps->weaponstate != WEAPON_READY && pm->ps->weaponstate != WEAPON_FIRING ) return; - if(((weapon == WP_CARBINE) && pm->ps->ammoclip[WP_CARBINE] != 0) || ((weapon == WP_MOBILE_MG42 || weapon == WP_MOBILE_MG42_SET) && pm->ps->ammoclip[WP_MOBILE_MG42] != 0) || ((weapon == WP_GARAND || weapon == WP_GARAND_SCOPE) && pm->ps->ammoclip[WP_GARAND] != 0)) { - return; // Gordon: no reloading of the carbine until clip is empty + //if(((weapon == WP_CARBINE) && pm->ps->ammoclip[WP_CARBINE] != 0) || ((weapon == WP_MOBILE_MG42 || weapon == WP_MOBILE_MG42_SET) && pm->ps->ammoclip[WP_MOBILE_MG42] != 0) || ((weapon == WP_GARAND || weapon == WP_GARAND_SCOPE) && pm->ps->ammoclip[WP_GARAND] != 0)) { + // return; // Gordon: no reloading of the carbine until clip is empty + //} + switch(weapon) { + case WP_CARBINE: + if(PM_GARAND_RELOADS) + break; + if(pm->ps->ammoclip[WP_CARBINE] != 0) + return; + break; + case WP_GARAND: + case WP_GARAND_SCOPE: + if(PM_GARAND_RELOADS) + break; + if(pm->ps->ammoclip[WP_GARAND] != 0) + return; + break; + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + if(pm->ps->ammoclip[WP_MOBILE_MG42] != 0) + return; + break; } if((weapon <= WP_NONE || weapon > WP_DYNAMITE) && !(weapon >= WP_KAR98 && weapon < WP_NUM_WEAPONS)) return; + // tjw: this should be handled in PM_CheckForReload() + /* item = BG_FindItemForWeapon(weapon); if(!item) { return; @@ -2327,6 +2632,7 @@ if(pm->ps->ammoclip[item->giAmmoIndex] >= GetAmmoTableData(weapon)->maxclip) { return; } + */ // no reload when you've got a chair in your hands /* if(pm->ps->eFlags & EF_MELEE_ACTIVE) @@ -2906,7 +3212,8 @@ pm->ps->weapon != WP_GRENADE_PINEAPPLE && pm->ps->weapon != WP_DYNAMITE && pm->ps->weapon != WP_SMOKE_BOMB && - pm->ps->weapon != WP_LANDMINE ) { + pm->ps->weapon != WP_LANDMINE && + pm->ps->weapon != WP_SATCHEL_DET) { return; } @@ -2924,9 +3231,6 @@ case WP_GRENADE_PINEAPPLE: case WP_DYNAMITE: COM_BitClear( pm->ps->weapons, pm->ps->weapon); - break; - default: - break; } PM_AddEvent( EV_NOAMMO ); @@ -3146,7 +3450,14 @@ } else { // take player view rotation into account for( i = 0; i < 2; i++ ) { - viewchange += fabs( SHORT2ANGLE(pm->cmd.angles[i]) - SHORT2ANGLE(pm->oldcmd.angles[i]) ); + // tjw: angles need to be normalized since they're + // often > 65535. + // FIXME: Maybe this normalization should in Pmove()? + // tjw: need to use AngleSubtract() instead of just minus. + //viewchange += fabs( SHORT2ANGLE(pm->cmd.angles[i]) - SHORT2ANGLE(pm->oldcmd.angles[i]) ); + viewchange += fabs(AngleSubtract( + SHORT2ANGLE(AngleNormalizeInt(pm->cmd.angles[i])), + SHORT2ANGLE(AngleNormalizeInt(pm->oldcmd.angles[i])))); } } @@ -3218,7 +3529,7 @@ // check for dead player if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { - if(!pm->ps->pm_flags & PMF_LIMBO) { + if(!(pm->ps->pm_flags & PMF_LIMBO)) { PM_CoolWeapons(); } @@ -3313,7 +3624,7 @@ pm->ps->weapHeat[WP_DUMMY_MG42] = 0; // rain - floor() to prevent 8-bit wrap - pm->ps->curWeapHeat = floor( ( ( (float)pm->ps->weapHeat[WP_DUMMY_MG42] / MAX_MG42_HEAT) ) * 255.0f); + pm->ps->curWeapHeat = floor(((float)pm->ps->weapHeat[WP_DUMMY_MG42] / MAX_MG42_HEAT) * 255.0f); } if( pm->ps->weaponTime > 0 ) { @@ -3618,7 +3929,10 @@ if( pm->ps->weapon == WP_MORTAR_SET ) { if( pm->skill[SK_HEAVY_WEAPONS] >= 1 ) { - if( pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->soldierChargeTime*0.5f*(1-0.3f)) ) { + // CHRUKER: b069 - Was using "0.5f*(1-0.3f)", however + // the 0.33f is used everywhere else, and + // is more precise. + if( pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->soldierChargeTime*0.33f) ) { return; } } else if( pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->soldierChargeTime*0.5f) ) { @@ -3796,10 +4110,23 @@ pm->ps->weapon != WP_DYNAMITE && pm->ps->weapon != WP_LANDMINE && pm->ps->weapon != WP_TRIPMINE && - pm->ps->weapon != WP_SMOKE_BOMB ) { - PM_AddEvent(EV_NOFIRE_UNDERWATER); // event for underwater 'click' for nofire + pm->ps->weapon != WP_SMOKE_BOMB && + !(pm->ps->weapon == WP_MEDIC_SYRINGE + && PM_UW_SYRINGE) && + !(pm->ps->weapon == WP_PLIERS + && PM_UW_PLIERS) + ) { + + // event for underwater 'click' for nofire + // tjw: since this depends on a server setting + // don't let the client predict this + // event. +#ifdef GAMEDLL + PM_AddEvent(EV_NOFIRE_UNDERWATER); +#endif pm->ps->weaponTime = 500; - pm->ps->weaponDelay = 0; // avoid insta-fire after water exit on delayed weapon attacks + // avoid insta-fire after water exit on delayed weapon attacks + pm->ps->weaponDelay = 0; return; } } @@ -4603,7 +4930,15 @@ // rain - using a full short and converting on the client so that // we get >1 degree resolution ps->stats[STAT_DEAD_YAW] = temp; - return; // no view changes at all + // tjw: don't let corpses bury their heads in the sand + ps->viewangles[2] = 0; + cmd->angles[2] = 0; + + ps->viewangles[0] = 0; + cmd->angles[0] = 0; + + if(!PM_CORPSE_SPIN) + return; } VectorCopy( ps->viewangles, oldViewAngles ); @@ -4623,7 +4958,7 @@ } ps->viewangles[i] = SHORT2ANGLE(temp); } - + if( BG_PlayerMounted(ps->eFlags) ) { float yaw, oldYaw; float degsSec = MG42_YAWSPEED; @@ -4806,7 +5141,11 @@ // Set delta_angles properly ps->delta_angles[PITCH] = ANGLE2SHORT(ps->viewangles[PITCH]) - cmd->angles[PITCH]; } - } else if( ps->eFlags & EF_PRONE ) { + } + else if((ps->eFlags & EF_PRONE) || + (ps->eFlags & EF_DEAD) || + (ps->eFlags & EF_PLAYDEAD)) { + //float degsSec = 60.f; float /*yaw, */oldYaw; trace_t traceres; // rain - renamed @@ -4892,17 +5231,30 @@ // see if we have the space to go prone // we know our main body isn't in a solid, check for our legs + ps->delta_angles[YAW] = newDeltaAngle; + // rain - bugfix - use supplied trace - pm may not be set PM_TraceLegs( &traceres, &pmext->proneLegsOffset, ps->origin, ps->origin, NULL, ps->viewangles, pm->trace, ps->clientNum, tracemask ); - if( traceres.allsolid /* && trace.entityNum >= MAX_CLIENTS */ ) { + if(traceres.fraction < 1.0f) { // starting in a solid, no space ps->viewangles[YAW] = oldYaw; ps->delta_angles[YAW] = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW]; - } else { - // all fine - ps->delta_angles[YAW] = newDeltaAngle; - } + } + + memset(&traceres, 0, sizeof(traceres)); + + PM_TraceHead(&traceres, ps->origin, ps->origin, + NULL, ps->viewangles, pm->trace, ps->clientNum, + tracemask ); + + if(traceres.fraction < 1.0f) { + ps->viewangles[YAW] = oldYaw; + ps->delta_angles[YAW] = + ANGLE2SHORT(ps->viewangles[YAW]) + - cmd->angles[YAW]; + } + } } @@ -5191,7 +5543,6 @@ if( pm->skill[SK_BATTLE_SENSE] >= 2 ) rechargebase *= 1.6f; } - pm->pmext->sprintTime += rechargebase*pml.frametime; // JPW NERVE adjusted for framerate independence if (pm->pmext->sprintTime > 5000) pm->pmext->sprintTime += rechargebase*pml.frametime; // JPW NERVE adjusted for framerate independence @@ -5262,7 +5613,10 @@ } // don't allow binocs if in the middle of throwing grenade - if( (pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE) && pm->ps->grenadeTimeLeft > 0) { + if( (pm->ps->weapon == WP_GRENADE_LAUNCHER || + pm->ps->weapon == WP_GRENADE_PINEAPPLE || + pm->ps->weapon == WP_DYNAMITE) && + pm->ps->grenadeTimeLeft > 0) { pm->ps->eFlags &= ~EF_ZOOMING; } } @@ -5428,14 +5782,14 @@ #endif } + // set watertype, and waterlevel PM_SetWaterLevel(); pml.previous_waterlevel = pmove->waterlevel; + PM_CheckPlayDead(); + // set mins, maxs, and viewheight - //if( !PM_CheckProne() ) { -// PM_CheckDuck (); - //} if( !PM_CheckProne() ) { PM_CheckDuck(); } @@ -5472,7 +5826,8 @@ } if( pm->ps->weapon == WP_SATCHEL_DET ) { if( !( pm->ps->ammoclip[ WP_SATCHEL_DET ] ) ) { - PM_BeginWeaponChange( WP_SATCHEL_DET, WP_SATCHEL, qtrue ); + // tjw: this causes the 2.60 client to freak out + //PM_BeginWeaponChange( WP_SATCHEL_DET, WP_SATCHEL, qtrue ); #ifdef CGAMEDLL cg.weaponSelect = WP_SATCHEL; #endif // CGAMEDLL @@ -5628,3 +5983,37 @@ return (0); } + +// PmovePredict +// +// Used to calculate player movement for g_skipCorrection. +// +// Before calling PmovePredict() the following player state +// values should be backed up and then restored after the +// new values have been copied to the entity state . +// +// PM_GroundTrace() and friends modify +// ps->groundEntityNum +// ps->pm_flags +// ps->pm_time +// ps->eFlags +// +// PM_StepSlideMove() and friends modify +// ps->origin +// ps->velocity + +void PmovePredict(pmove_t *pmove, float frametime) +{ + pm = pmove; + memset(&pml, 0, sizeof(pml)); + pml.frametime = frametime; + PM_GroundTrace(); + + // tjw: don't bother to figure out gravity if already on the ground + // or moving on a ladder. + if(pml.groundPlane || (pm->ps->pm_flags & PMF_LADDER)) + PM_StepSlideMove(qfalse); + else + PM_StepSlideMove(qtrue); + +} diff -urN --exclude=.svn et260/src/game/bg_public.h etpub/src/game/bg_public.h --- et260/src/game/bg_public.h 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_public.h 2005-05-24 16:09:14.000000000 -0500 @@ -425,10 +425,11 @@ typedef enum { PM_NORMAL, // can accelerate and turn PM_NOCLIP, // noclip movement - PM_SPECTATOR, // still run into walls + PM_SPECTATOR, // still run into walls PM_DEAD, // no acceleration or turning, but free falling PM_FREEZE, // stuck in place with no control - PM_INTERMISSION // no movement or status bar + PM_INTERMISSION, // no movement or status bar + PM_PLAYDEAD // no movement or status bar } pmtype_t; typedef enum { @@ -467,6 +468,8 @@ #define PMF_TIME_LOAD 8192 // hold for this time after a load game, and prevent large thinks #define PMF_LIMBO 16384 // JPW NERVE limbo state, pm_time is time until reinforce #define PMF_TIME_LOCKPLAYER 32768 // DHM - Nerve :: Lock all movement and view changes +// josh: +#define PMF_DOUBLEJUMPING 65536 // DHM - Nerve :: Lock all movement and view changes #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK|PMF_TIME_LOCKPLAYER/*|PMF_TIME_LOAD*/) @@ -502,6 +505,23 @@ int lastRecoilDeltaTime; qboolean releasedFire; + + // tjw: moved these here from playerState because we can't add + // anything to playerState_t without breaking the client + + // tjw: the number of times the player has dropped the + // objective this life. + int objDrops; + + qboolean poisoned; + int poisonerEnt; + int poisonHurt; + + int shoveTime; + + qboolean disoriented; + qboolean wasDisoriented; + } pmoveExt_t; // data used both in client and server - store it here // instead of playerstate to prevent different engine versions of playerstate between XP and MP @@ -558,11 +578,15 @@ // these will be different functions during game and cgame void (*trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ); int (*pointcontents)( const vec3_t point, int passEntityNum ); + // tjw: used to determine if the player move is for prediction + // if it is, the movement should trigger no events + qboolean predict; } pmove_t; // if a full pmove isn't done on the client, you can just update the angles void PM_UpdateViewAngles( playerState_t *ps, pmoveExt_t *pmext, usercmd_t *cmd, void (trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ), int tracemask ); int Pmove (pmove_t *pmove); +void PmovePredict(pmove_t *pmove, float frametime); //=================================================================================== @@ -644,7 +668,7 @@ #define EF_TAGCONNECT 0x00008000 // connected to another entity via tag #define EF_MOUNTEDTANK EF_TAGCONNECT // Gordon: duplicated for clarity -#define EF_SPARE3 0x00010000 // Gordon: freed +#define EF_FAKEBMODEL 0x00010000 // tjw: from etpro #define EF_PATH_LINK 0x00020000 // Gordon: linking trains together #define EF_ZOOMING 0x00040000 // client is zooming #define EF_PRONE 0x00080000 // player is prone @@ -652,15 +676,16 @@ #define EF_PRONE_MOVING 0x00100000 // player is prone and moving #define EF_VIEWING_CAMERA 0x00200000 // player is viewing a camera #define EF_AAGUN_ACTIVE 0x00400000 // Gordon: player is manning an AA gun -#define EF_SPARE0 0x00800000 // Gordon: freed +#define EF_PLAYDEAD 0x00800000 // tjw: player is playing dead // !! NOTE: only place flags that don't need to go to the client beyond 0x00800000 -#define EF_SPARE1 0x01000000 // Gordon: freed +#define EF_MOTION 0x01000000 // tjw: player animation is in motion #define EF_SPARE2 0x02000000 // Gordon: freed #define EF_BOUNCE 0x04000000 // for missiles #define EF_BOUNCE_HALF 0x08000000 // for missiles #define EF_MOVER_STOP 0x10000000 // will push otherwise // (SA) moved down to make space for one more client flag #define EF_MOVER_BLOCKED 0x20000000 // mover was blocked dont lerp on the client // xkan, moved down to make space for client flag +#define EF_SLASHKILL 0x40000000 // azyu: used in g_slashkill determination of chargebar status #define BG_PlayerMounted( eFlags ) (( eFlags & EF_MG42_ACTIVE ) || ( eFlags & EF_MOUNTEDTANK ) || ( eFlags & EF_AAGUN_ACTIVE )) @@ -823,6 +848,18 @@ // NOTE: this cannot be larger than 64 for AI/player weapons! } weapon_t; +#define WPF_NO_L0_FOPS_BINOCS 1 +#define WPF_UNDERWATER_SYRINGE 2 +#define WPF_UNDERWATER_PLIERS 4 +#define WPF_TM_AIRSTRIKE_RESTORE_FULL 8 +#define WPF_TM_AIRSTRIKE_RESTORE_HALF 16 +#define WPF_AMMO_RESTORES_HELMET 32 +#define WPF_DROP_BINOCS 64 +#define WPF_L4HW_KEEPS_PISTOL 128 +#define WPF_GARAND_RELOADS 256 + +#define MISC_DOUBLE_JUMP 1 + // JPW NERVE moved from cg_weapons (now used in g_active) for drop command, actual array in bg_misc.c extern int weapBanksMultiPlayer[MAX_WEAP_BANKS_MP][MAX_WEAPS_IN_BANK_MP]; // jpw @@ -861,7 +898,12 @@ extern const char* medalNames[SK_NUM_SKILLS]; #define NUM_SKILL_LEVELS 5 + +#ifdef GAMEDLL +extern int skillLevels[SK_NUM_SKILLS][NUM_SKILL_LEVELS]; +#else extern const int skillLevels[NUM_SKILL_LEVELS]; +#endif typedef struct { weaponStats_t weaponStats[WP_NUM_WEAPONS]; @@ -1458,6 +1500,10 @@ // OSP -- keep these 2 entries last MOD_SWITCHTEAM, + MOD_GOOMBA, + MOD_POISON, + MOD_FEAR, + MOD_NUM_MODS } meansOfDeath_t; @@ -2242,7 +2288,8 @@ int BG_colorstrncpyz(char *in, char *out, int str_max, int out_max); int BG_drawStrlen(const char *str); int BG_strRelPos(char *in, int index); -int BG_cleanName(const char *pszIn, char *pszOut, unsigned int dwMaxLength, qboolean fCRLF); +// CHRUKER: b069 - Cleaned up a few compiler warnings +int BG_cleanName(const char *pszIn, char *pszOut, int dwMaxLength, qboolean fCRLF); // Crosshair support void BG_setCrosshair(char *colString, float *col, float alpha, char *cvarName); @@ -2387,7 +2434,8 @@ int PM_AltSwitchToForWeapon ( int weapon ); void PM_TraceLegs( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end, trace_t *bodytrace, vec3_t viewangles, void (tracefunc)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ), int ignoreent, int tracemask ); -void PM_TraceAllLegs( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end ); +void PM_TraceHead( trace_t *trace, vec3_t start, vec3_t end, trace_t *bodytrace, vec3_t viewangles, void (tracefunc)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ), int ignoreent, int tracemask ); +void PM_TraceAllParts( trace_t *trace, float *legsOffset, vec3_t start, vec3_t end ); void PM_TraceAll( trace_t *trace, vec3_t start, vec3_t end ); #endif diff -urN --exclude=.svn et260/src/game/bg_slidemove.c etpub/src/game/bg_slidemove.c --- et260/src/game/bg_slidemove.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/bg_slidemove.c 2005-05-02 09:30:04.000000000 -0500 @@ -315,8 +315,9 @@ // check legs separately if ( pm->ps->eFlags & EF_PRONE ) { + memset(&trace, 0, sizeof(trace)); PM_TraceLegs( &trace, NULL, pm->ps->origin, down, NULL, pm->ps->viewangles, pm->trace, pm->ps->clientNum, pm->tracemask ); - if ( trace.allsolid ) { + if (trace.fraction < 1.0f) { // legs don't step, just fuzz. VectorCopy( down_o, pm->ps->origin ); VectorCopy( down_v, pm->ps->velocity ); @@ -325,8 +326,21 @@ } return; } + memset(&trace, 0, sizeof(trace)); + PM_TraceHead( &trace, pm->ps->origin, down, NULL, + pm->ps->viewangles, pm->trace, pm->ps->clientNum, + pm->tracemask); + if(trace.fraction < 1.0f) { + VectorCopy( down_o, pm->ps->origin ); + VectorCopy( down_v, pm->ps->velocity ); + if ( pm->debugLevel ) { + Com_Printf("%i:head unsteppable\n", c_pmove); + } + return; + } } + memset(&trace, 0, sizeof(trace)); pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask ); if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); diff -urN --exclude=.svn et260/src/game/etpub.h etpub/src/game/etpub.h --- et260/src/game/etpub.h 1969-12-31 18:00:00.000000000 -0600 +++ etpub/src/game/etpub.h 2005-05-27 11:09:04.000000000 -0500 @@ -0,0 +1,6 @@ +#ifndef _ETPUB_H +#define _ETPUB_H + +#define ETPUB_VERSION "0.5.1" + +#endif // ifndef _ETPUB_H diff -urN --exclude=.svn et260/src/game/g_active.c etpub/src/game/g_active.c --- et260/src/game/g_active.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/g_active.c 2005-05-25 10:53:45.000000000 -0500 @@ -1,4 +1,3 @@ - #include "g_local.h" /* @@ -17,10 +16,57 @@ vec3_t angles; client = player->client; - if ( client->ps.pm_type == PM_DEAD ) { + if ( client->ps.pm_type == PM_DEAD && + !(client->ps.eFlags & EF_PLAYDEAD)) { return; } + // tjw: poison effects + if(client->pmext.poisoned) { + if(g_poisonFlags.integer & POISF_DISORIENT) { + client->ps.delta_angles[ROLL] = 32000; + client->ps.viewangles[ROLL] = 0; + } + if(!(client->pmext.poisonHurt % 3) && + (g_poisonFlags.integer & POISF_BOBBLE)) { + + if(client->pmext.poisonHurt % 2) + client->ps.viewangles[ROLL] = 20; + else + client->ps.viewangles[ROLL] = -20; + } + if((g_poisonFlags.integer & POISF_HURL) && + (!(client->pmext.poisonHurt % 40) || + !((client->pmext.poisonHurt - 1) % 40) || + !((client->pmext.poisonHurt - 2) % 40) || + !((client->pmext.poisonHurt - 3) % 40) || + !((client->pmext.poisonHurt - 4) % 40) || + !((client->pmext.poisonHurt - 5) % 40))) { + + client->ps.viewangles[PITCH] = 90; + } + client->pmext.poisonHurt++; + if(!client->damage_blood) + return; + } + else { + // tjw: clear poison-induced disorientation. + if(client->pmext.poisonHurt) + client->ps.delta_angles[ROLL] = 0; + client->pmext.poisonHurt = 0; + } + + // tjw: handle disorientation + if(client->pmext.disoriented) { + client->ps.delta_angles[ROLL] = 32000; + client->ps.viewangles[ROLL] = 0; + client->pmext.wasDisoriented = qtrue; + } + else if(client->pmext.wasDisoriented) { + client->ps.delta_angles[ROLL] = 0; + client->pmext.wasDisoriented = qfalse; + } + // total points of damage shot at the player this frame count = client->damage_blood; if ( count == 0 ) { @@ -47,7 +93,11 @@ } // play an apropriate pain sound - if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && !(player->s.powerups & PW_INVULNERABLE) ) { //----(SA) + if ( (level.time > player->pain_debounce_time) && + !(player->flags & FL_GODMODE) && + !(player->s.powerups & PW_INVULNERABLE) && + !client->pmext.poisoned) { //----(SA) + player->pain_debounce_time = level.time + 700; G_AddEvent( player, EV_PAIN, player->health ); } @@ -374,7 +424,9 @@ ent->client->touchingTOI = NULL; // dead clients don't activate triggers! - if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) { + // tjw: unless they're only playing. + if ( ent->client->ps.stats[STAT_HEALTH] <= 0 && + !(ent->client->ps.eFlags & EF_PLAYDEAD)) { return; } @@ -437,6 +489,37 @@ } } +// returns true if a player was found to follow +qboolean G_SpectatorAttackFollow(gentity_t *ent) { + trace_t tr; + vec3_t forward, right, up; + vec3_t start, end; + gentity_t *vic; + + if(!ent->client) + return qfalse; + + AngleVectors(ent->client->ps.viewangles, forward, right, up); + VectorCopy(ent->client->ps.origin, start); + VectorMA(start, 8192, forward, end); + + G_HistoricalTrace(ent, + &tr, + start, + NULL, + NULL, + end, + ent->s.number, + MASK_SHOT); + vic = &g_entities[tr.entityNum]; + if(vic->client) { + ent->client->sess.spectatorState = SPECTATOR_FOLLOW; + ent->client->sess.spectatorClient = tr.entityNum; + return qtrue; + } + return qfalse; +} + /* ================= SpectatorThink @@ -522,11 +605,24 @@ client->oldwbuttons = client->wbuttons; client->wbuttons = ucmd->wbuttons; + // MV clients use these buttons locally for other things if(client->pers.mvCount < 1) { // attack button cycles through spectators - if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) { - Cmd_FollowCycle_f( ent, 1 ); + if(((client->buttons & BUTTON_ATTACK) && + !(client->oldbuttons & BUTTON_ATTACK)) && + !(client->buttons & BUTTON_ACTIVATE) && + ucmd->upmove == 0) { + + if(client->sess.spectatorState != SPECTATOR_FOLLOW) { + if(g_spectator.integer & SPECF_FL_CLICK_FOLLOW) { + if(G_SpectatorAttackFollow(ent)) + return; + if(!(g_spectator.integer & SPECF_FL_MISS_FOLLOW_NEXT)) + return; + } + } + Cmd_FollowCycle_f(ent, 1); } // activate button swaps places with bot else if( client->sess.sessionTeam != TEAM_SPECTATOR && @@ -535,14 +631,15 @@ ( g_entities[ent->client->sess.spectatorClient].r.svFlags & SVF_BOT ) ) { Cmd_SwapPlacesWithBot_f( ent, ent->client->sess.spectatorClient ); - } else if ( - ( client->sess.sessionTeam == TEAM_SPECTATOR ) && // don't let dead team players do free fly - ( client->sess.spectatorState == SPECTATOR_FOLLOW ) && - ( ( ( client->buttons & BUTTON_ACTIVATE ) && - ! ( client->oldbuttons & BUTTON_ACTIVATE )) || ucmd->upmove > 0 ) && - G_allowFollow(ent, TEAM_AXIS) && G_allowFollow(ent, TEAM_ALLIES) ) - { - // code moved to StopFollowing + } + else if(client->sess.sessionTeam == TEAM_SPECTATOR && + client->sess.spectatorState == SPECTATOR_FOLLOW && + (((client->buttons & BUTTON_ACTIVATE) && + !(client->oldbuttons & BUTTON_ACTIVATE)) || + ucmd->upmove > 0 ) && + G_allowFollow(ent, TEAM_AXIS) && + G_allowFollow(ent, TEAM_ALLIES)) { + StopFollowing(ent); } } @@ -580,7 +677,9 @@ g_spectatorInactivity.integer); } else if ( !client->pers.localClient ) { - if ( level.time > client->inactivityTime && client->inactivityWarning) { + if(level.time > client->inactivityTime && + client->inactivityWarning && + !G_shrubbot_permission(&g_entities[client-level.clients], SBF_IMMUNITY)) { client->inactivityWarning = qfalse; client->inactivityTime = level.time + 60 * 1000; trap_DropClient(client - level.clients, "Dropped due to inactivity", 0 ); @@ -608,6 +707,8 @@ */ void ClientTimerActions( gentity_t *ent, int msec ) { gclient_t *client; + gentity_t *attacker; + int regenRate1 = 3, regenRate2 = 2; client = ent->client; client->timeResidual += msec; @@ -615,15 +716,25 @@ while( client->timeResidual >= 1000 ) { client->timeResidual -= 1000; + // Calculate regen rate + if (g_medics.integer & MEDIC_REGENRATE21) { + regenRate1 = 2; + regenRate2 = 1; + } else if (g_medics.integer & MEDIC_REGENRATE10) { + regenRate1 = 1; + regenRate2 = 0; + } + // regenerate - if( client->sess.playerType == PC_MEDIC ) { + // tjw: dead players can't regenerate + if( client->sess.playerType == PC_MEDIC && !(client->ps.eFlags & EF_DEAD) ) { if( ent->health < client->ps.stats[STAT_MAX_HEALTH]) { - ent->health += 3; + ent->health += regenRate1; if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1){ ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1; } } else if( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 1.12) { - ent->health += 2; + ent->health += regenRate2; if( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.12 ) { ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.12; } @@ -634,7 +745,27 @@ ent->health--; } } + if(client->pmext.poisoned && ent->health > 0 && + g_poison.integer) { + + attacker = g_entities + client->pmext.poisonerEnt; + if(g_poisonSound.string[0]) { + G_AddEvent(ent, EV_GLOBAL_SOUND, + G_SoundIndex(g_poisonSound.string)); + } + G_Damage(ent, attacker, attacker, NULL, NULL, + g_poison.integer, 0, MOD_POISON); + } + if(client->pmext.poisoned && + (ent->health <= 0 || + client->ps.eFlags & EF_DEAD)) { + client->pmext.poisoned = qfalse; + } } + + // notify client that they are playing dead + if(client->ps.eFlags & EF_PLAYDEAD && ent->health > 0) + CP("cp \"Playing Dead\" 1"); } /* @@ -658,6 +789,135 @@ } +void G_FallDamage(gentity_t *ent, int event) +{ + int damage = 0; + int kb_time = 0; + gentity_t *victim; + + if ( ent->s.eType != ET_PLAYER ) { + return; // not in the player model + } + + victim = &level.gentities[ent->s.groundEntityNum]; + // groundEntityNum won't be set to the entity number + // of a wounded player if you landed on one. + // trace to see if we're on a wounded player. + if(!victim->client) { + trace_t tr; + vec3_t start, stop; + + VectorCopy(ent->r.currentOrigin, start); + VectorCopy(ent->r.currentOrigin, stop); + stop[2] -= 4; + trap_Trace (&tr, start, NULL, NULL, stop, + ent->s.number, MASK_SHOT); + victim = &level.gentities[tr.entityNum]; + + } + + switch(event) { + case EV_FALL_NDIE: + damage = 500; + break; + case EV_FALL_DMG_50: + damage = 50; + kb_time = 1000; + break; + case EV_FALL_DMG_25: + damage = 25; + kb_time = 500; + break; + case EV_FALL_DMG_15: + damage = 15; + kb_time = 250; + break; + case EV_FALL_DMG_10: + damage = 10; + kb_time = 250; + break; + case EV_FALL_SHORT: + if(g_goombaFlags.integer & GBF_NO_HOP_DAMAGE) + return; + if(victim && victim->client && + victim->client->sess.sessionTeam == + ent->client->sess.sessionTeam && + (g_goombaFlags.integer & GBF_NO_HOP_TEAMDAMAGE)) { + + return; + } + break; + default: + return; + } + + + + if((!g_goomba.integer || + !victim || + !victim->client || + !victim->takedamage)) { + + if(damage) { + if(kb_time) { + ent->client->ps.pm_time = kb_time; + ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; + } + // no normal pain sound + ent->pain_debounce_time = level.time + 200; + G_Damage(ent, NULL, NULL, NULL, NULL, + damage, 0, MOD_FALLING); + } + return; + } + + // tjw: if we make it this far, do goomba damage to victim + + if((g_goombaFlags.integer & GBF_ENEMY_ONLY) && + victim->client->sess.sessionTeam == + ent->client->sess.sessionTeam) { + + return; + } + + if(!damage) + damage = 5; + + + if(kb_time) { + victim->client->ps.pm_time = kb_time; + victim->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; + } + // no normal pain sound + victim->pain_debounce_time = level.time + 200; + if((g_goombaFlags.integer & GBF_INSTAGIB) && damage > 5) { + G_Damage(victim, ent, ent, NULL, NULL, 500, 0, MOD_GOOMBA); + } + else { + G_Damage(victim, ent, ent, NULL, NULL, + (damage * g_goomba.integer), 0, MOD_GOOMBA); + } + + + if(damage > 5) { + G_AddEvent(victim, EV_GENERAL_SOUND, + G_SoundIndex("sound/world/debris1.wav")); + if(!(g_goombaFlags.integer & GBF_NO_SELF_DAMAGE)) { + // faller has a soft landing + damage *= 0.2f; + G_Damage(ent, NULL, NULL, NULL, NULL, + damage, 0, MOD_FALLING); + } + } + else { + G_AddEvent(victim, EV_GENERAL_SOUND, + G_SoundIndex("sound/player/land_hurt.wav")); + } + + +} + + /* ================ ClientEvents @@ -670,8 +930,6 @@ int i; int event; gclient_t *client; - int damage; - vec3_t dir; client = ent->client; @@ -683,53 +941,12 @@ switch ( event ) { case EV_FALL_NDIE: - //case EV_FALL_SHORT: + case EV_FALL_SHORT: case EV_FALL_DMG_10: case EV_FALL_DMG_15: case EV_FALL_DMG_25: - //case EV_FALL_DMG_30: case EV_FALL_DMG_50: - //case EV_FALL_DMG_75: - - // rain - VectorClear() used to be done here whenever falling - // damage occured, but I moved it to bg_pmove where it belongs. - - if ( ent->s.eType != ET_PLAYER ) { - break; // not in the player model - } - if ( event == EV_FALL_NDIE ) - { - damage = 9999; - } - else if (event == EV_FALL_DMG_50) - { - damage = 50; - ent->client->ps.pm_time = 1000; - ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; - } - else if (event == EV_FALL_DMG_25) - { - damage = 25; - ent->client->ps.pm_time = 250; - ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; - } - else if (event == EV_FALL_DMG_15) - { - damage = 15; - ent->client->ps.pm_time = 1000; - ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; - } - else if (event == EV_FALL_DMG_10) - { - damage = 10; - ent->client->ps.pm_time = 1000; - ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; - } - else - damage = 5; // never used - VectorSet (dir, 0, 0, 1); - ent->pain_debounce_time = level.time + 200; // no normal pain sound - G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING); + G_FallDamage(ent, event); break; case EV_FIRE_WEAPON_MG42: @@ -859,7 +1076,9 @@ continue; } - if( cl->ps.stats[ STAT_PLAYER_CLASS ] != PC_MEDIC ) { + // tjw: ps.stats updated before spawn? + //if( cl->ps.stats[ STAT_PLAYER_CLASS ] != PC_MEDIC ) { + if(cl->sess.playerType != PC_MEDIC) { continue; } @@ -916,7 +1135,13 @@ return; } - if( ent->s.eFlags & EF_MOUNTEDTANK ) { + // tjw: this is causing the segfault associated with antilag + // I'm assuming it's not important since currentAngles never + // seem to get used for anything (always 0?) + // tjw: this is needed to set the correct limitations on tank mg + // angles. added check to make sure ent->tagParent is set + // to avoid the crash. + if(ent->s.eFlags & EF_MOUNTEDTANK && ent->tagParent) { client->pmext.centerangles[YAW] = ent->tagParent->r.currentAngles[ YAW ]; client->pmext.centerangles[PITCH] = ent->tagParent->r.currentAngles[ PITCH ]; } @@ -932,6 +1157,69 @@ ent->client->ps.identifyClient = ucmd->identClient; // NERVE - SMF +//unlagged - true ping + // save the estimated ping in a queue for averaging later + + // we use level.previousTime to account for 50ms lag correction + // besides, this will turn out numbers more like what players are used to + // josh: changed it back. People don't like it. + client->pers.pingsamples[client->pers.samplehead] = + level.previousTime + + client->frameOffset - + ucmd->serverTime; + client->pers.samplehead++; + if ( client->pers.samplehead >= NUM_PING_SAMPLES ) { + client->pers.samplehead -= NUM_PING_SAMPLES; + } + + // initialize the real ping + if ( g_truePing.integer ) { + int i, sum = 0; + + // get an average of the samples we saved up + for ( i = 0; i < NUM_PING_SAMPLES; i++ ) { + sum += client->pers.pingsamples[i]; + } + + client->pers.realPing = sum / NUM_PING_SAMPLES; + } + else { + // if g_truePing is off, use the normal ping + client->pers.realPing = client->ps.ping; + } +//unlagged - true ping + + if(client->warping && g_maxWarp.integer) { + int frames = (level.framenum - client->lastUpdateFrame); + + if(frames > g_maxWarp.integer) + frames = g_maxWarp.integer; + ucmd->serverTime = level.previousTime; + client->ps.commandTime = level.previousTime - + (frames * (level.time - level.previousTime)); + client->warped = qtrue; + } + client->warping = qfalse; + + +//unlagged - smooth clients #1 + // keep track of this for later - we'll use this to decide whether or not + // to send extrapolated positions for this client + client->lastUpdateFrame = level.framenum; + + + +//unlagged - smooth clients #1 + +//unlagged - true ping + // make sure the true ping is over 0 - with cl_timenudge it can be less + if ( client->pers.realPing < 0 ) { + client->pers.realPing = 0; + } +//unlagged - true ping + + + // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; @@ -942,6 +1230,18 @@ // G_Printf("serverTime >>>>>\n" ); } + //josh: Check for auto-mute and unmute if appropriate + if (client->sess.muted && + client->sess.auto_mute_time != -1 && + level.time - client->sess.auto_mute_time > + g_censorMuteTime.integer*1000) { + CPx(ent - g_entities, "print \"^5You've been auto-unmuted. Language penalty lifted.\""); + client->sess.muted = qfalse; + client->sess.auto_mute_time = -1; + AP(va("chat \"%s's^7 has been auto-unmuted. Language penalty lifted.\"", + ent->client->pers.netname )); + } + msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles @@ -993,6 +1293,11 @@ SpectatorThink( ent, ucmd ); return; } + // bani's flamethrower exploit fix + if( client->flametime && level.time > client->flametime ) { + client->flametime = 0; + ent->r.svFlags &= ~SVF_BROADCAST; + } if((client->ps.eFlags & EF_VIEWING_CAMERA) || level.match_pause != PAUSE_NONE #ifdef SAVEGAME_SUPPORT @@ -1019,11 +1324,19 @@ VectorClear(client->ps.velocity); client->ps.pm_type = PM_FREEZE; } - } else if ( client->noclip ) { + } + else if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; - } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { + } + else if(client->ps.pm_type == PM_PLAYDEAD) { + // no need to change it since it will + // be adjusted by PM_CheckPlayDead regardless + } + else if(client->ps.stats[STAT_HEALTH] <= 0 || + client->ps.eFlags & EF_PLAYDEAD) { client->ps.pm_type = PM_DEAD; - } else { + } + else { client->ps.pm_type = PM_NORMAL; } @@ -1041,6 +1354,16 @@ client->currentAimSpreadScale = (float)client->ps.aimSpreadScale/255.0; + // tjw: don't allow players to use +attack when poisoned + // this is not a good solution since client prediction + // makes it look like the gun fires (may need clientmod) + if((g_poisonFlags.integer & POISF_NO_ATTACK) && + client->pmext.poisoned && + (ucmd->buttons & BUTTON_ATTACK)) { + + ucmd->buttons &= ~BUTTON_ATTACK; + } + memset (&pm, 0, sizeof(pm)); pm.ps = &client->ps; @@ -1152,9 +1475,10 @@ monsterslick = Pmove( &pm ); // Gordon: thx to bani for this - // ikkyo - fix leaning players bug - VectorCopy( client->ps.velocity, ent->s.pos.trDelta ); - SnapVector( ent->s.pos.trDelta ); + // ikkyo - fix leaning players bug + // josh: This is now done in BG_PlayerStateToEntityState where it should be. + //VectorCopy( client->ps.velocity, ent->s.pos.trDelta ); + //SnapVector( ent->s.pos.trDelta ); // end // server cursor hints @@ -1178,12 +1502,24 @@ ent->r.eventTime = level.time; } +//unlagged - smooth clients #2 + // clients no longer do extrapolation if cg_smoothClients is 1, because + // skip correction is all handled server-side now + // since that's the case, it makes no sense to store the extra info + // in the client's snapshot entity, so let's save a little bandwidth + // Ridah, fixes jittery zombie movement +/* if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, level.time, qfalse ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qfalse ); } + else { +*/ + BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); +// } +//unlagged - smooth clients #2 if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { client->fireHeld = qfalse; // for grapple @@ -1216,6 +1552,12 @@ // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); + // store the client's current position for antilag traces + // Neil Toronto says this should be in ClientEndFrame NOT here. + // This should track server frames NOT client ones since clients + // interpolate what the server says + //G_StoreClientPosition( ent ); + // touch other objects ClientImpacts( ent, &pm ); @@ -1253,7 +1595,8 @@ } // check for respawning - if( client->ps.stats[STAT_HEALTH] <= 0 ) { + if( client->ps.stats[STAT_HEALTH] <= 0 && + !(client->ps.eFlags & EF_PLAYDEAD)) { // DHM - Nerve WolfFindMedic( ent ); @@ -1290,6 +1633,32 @@ } } + if(g_debugHitboxes.integer == 2) { + gentity_t *bboxEnt, *head; + vec3_t b1, b2; + vec3_t maxs; + + VectorCopy(ent->r.currentOrigin, b1); + VectorCopy(ent->r.currentOrigin, b2); + VectorAdd(b1, ent->r.mins, b1); + VectorCopy(ent->r.maxs, maxs); + maxs[2] = ClientHitboxMaxZ(ent); + VectorAdd(b2, maxs, b2); + bboxEnt = G_TempEntity( b1, EV_RAILTRAIL ); + VectorCopy(b2, bboxEnt->s.origin2); + bboxEnt->s.dmgFlags = 1; + + head = G_BuildHead(ent); + VectorCopy(head->r.currentOrigin, b1); + VectorCopy(head->r.currentOrigin, b2); + VectorAdd(b1, head->r.mins, b1); + VectorAdd(b2, head->r.maxs, b2); + bboxEnt = G_TempEntity( b1, EV_RAILTRAIL ); + VectorCopy(b2, bboxEnt->s.origin2); + bboxEnt->s.dmgFlags = 1; + G_FreeEntity( head ); + } + // perform once-a-second actions if(level.match_pause == PAUSE_NONE) { ClientTimerActions( ent, msec ); @@ -1310,9 +1679,14 @@ ent->client->pers.oldcmd = ent->client->pers.cmd; trap_GetUsercmd( clientNum, &ent->client->pers.cmd ); +//unlagged - smooth clients #1 + // this is handled differently now +/* // mark the time we got info, so we can display the // phone jack if they don't get any for a while ent->client->lastCmdTime = level.time; +*/ +//unlagged - smooth clients #1 #ifdef ALLOW_GSYNC if ( !g_synchronousClients.integer ) @@ -1375,6 +1749,13 @@ gclient_t *cl; qboolean do_respawn = qfalse; // JPW NERVE + /* + G_Printf("(dwRedReinfOffset %d + timeCurrent %d - startTime %d) lastReinforceTime %d\n", + level.dwRedReinfOffset, + level.timeCurrent, + level.startTime, + ent->client->pers.lastReinforceTime); + */ // Players can respawn quickly in warmup if(g_gamestate.integer != GS_PLAYING && ent->client->respawnTime <= level.timeCurrent && ent->client->sess.sessionTeam != TEAM_SPECTATOR) { @@ -1389,6 +1770,11 @@ do_respawn = (testtime < ent->client->pers.lastReinforceTime); ent->client->pers.lastReinforceTime = testtime; } + /* + G_Printf("testtime %d lastReinforceTime %d\n\n", + testtime, + ent->client->pers.lastReinforceTime); + */ if( g_gametype.integer != GT_WOLF_LMS ) { if ( ( g_maxlives.integer > 0 || g_alliedmaxlives.integer > 0 || g_axismaxlives.integer > 0 ) @@ -1442,7 +1828,9 @@ int flags = (cl->ps.eFlags & ~(EF_VOTED)) | (ent->client->ps.eFlags & (EF_VOTED)); int ping = ent->client->ps.ping; - if(ent->client->sess.sessionTeam != TEAM_SPECTATOR && (ent->client->ps.pm_flags & PMF_LIMBO)) { + if(ent->client->sess.sessionTeam != TEAM_SPECTATOR && + (ent->client->ps.pm_flags & PMF_LIMBO)) { + int savedScore = ent->client->ps.persistant[PERS_SCORE]; int savedRespawns = ent->client->ps.persistant[PERS_RESPAWNS_LEFT]; int savedRespawnPenalty = ent->client->ps.persistant[PERS_RESPAWNS_PENALTY]; @@ -1452,15 +1840,15 @@ do_respawn = ent->client->ps.pm_time; ent->client->ps = cl->ps; - ent->client->ps.pm_flags |= PMF_FOLLOW; - ent->client->ps.pm_flags |= PMF_LIMBO; - ent->client->ps.pm_time = do_respawn; // put pm_time back ent->client->ps.persistant[PERS_RESPAWNS_LEFT] = savedRespawns; ent->client->ps.persistant[PERS_RESPAWNS_PENALTY] = savedRespawnPenalty; ent->client->ps.persistant[PERS_SCORE] = savedScore; // put score back ent->client->ps.powerups[PW_MVCLIENTLIST] = savedMVList; ent->client->ps.stats[STAT_PLAYER_CLASS] = savedClass; // NERVE - SMF - put player class back + ent->client->ps.pm_flags |= PMF_FOLLOW; + ent->client->ps.pm_flags |= PMF_LIMBO; + } else { ent->client->ps = cl->ps; ent->client->ps.pm_flags |= PMF_FOLLOW; @@ -1624,7 +2012,9 @@ */ void ClientEndFrame( gentity_t *ent ) { int i; - +//unlagged - smooth clients #1 + int frames; +//unlagged - smooth clients #1 // used for informing of speclocked teams. // Zero out here and set only for certain specs @@ -1712,27 +2102,35 @@ // apply all the damage taken this frame P_DamageFeedback (ent); - + +//unlagged - smooth clients #1 + // this is handled differently now +/* // add the EF_CONNECTION flag if we haven't gotten commands recently if ( level.time - ent->client->lastCmdTime > 1000 ) { ent->s.eFlags |= EF_CONNECTION; } else { ent->s.eFlags &= ~EF_CONNECTION; } +*/ +//unlagged - smooth client #1 - ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... - // Gordon: WHY? other ents use it. + // don't tell the clients about this player's health + // when using playdead. It's a secret. + if(!(ent->s.eFlags & EF_PLAYDEAD)) + ent->client->ps.stats[STAT_HEALTH] = ent->health; G_SetClientSound (ent); // set the latest infor // Ridah, fixes jittery zombie movement - if (g_smoothClients.integer) { - BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, level.time, qfalse ); - } else { + // josh: smoothClients is EVIL. Puts everyone off by at least 50 ms + //if (g_smoothClients.integer) { + // BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); + //} else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qfalse ); - } + //} //SendPendingPredictableEvents( &ent->client->ps ); @@ -1746,7 +2144,11 @@ ent->r.contents = CONTENTS_CORPSE; } - if ( ent->health > 0 && ent->r.contents == CONTENTS_CORPSE && !(ent->s.eFlags & EF_MOUNTEDTANK)) { + if ( ent->health > 0 && + ent->r.contents == CONTENTS_CORPSE && + !(ent->s.eFlags & EF_MOUNTEDTANK) && + !(ent->s.eFlags & EF_PLAYDEAD) + ) { WolfReviveBbox( ent ); } @@ -1763,6 +2165,59 @@ // run entity scripting G_Script_ScriptRun( ent ); - // store the client's current position for antilag traces +//unlagged - smooth clients #1 + // mark as not missing updates initially + ent->client->ps.eFlags &= ~EF_CONNECTION; + + // see how many frames the client has missed + frames = level.framenum - ent->client->lastUpdateFrame - 1; + + if(g_maxWarp.integer && frames > g_maxWarp.integer) + ent->client->warping = qtrue; + + if(g_skipCorrection.integer && !ent->client->warped && frames > 0) { + if (frames > 3) { + // josh: I need frames to be = 2 here + frames = 3; + // these are disabled because the phone jack can give + // away other players position through walls. + //ent->client->ps.eFlags |= EF_CONNECTION; + //ent->s.eFlags |= EF_CONNECTION; + } + G_PredictPmove(ent, (float)frames / sv_fps.integer); + } + ent->client->warped = qfalse; + +//unlagged - smooth clients #1 + + if(g_debugHitboxes.integer == 1) { + gentity_t *bboxEnt, *head; + vec3_t b1, b2; + vec3_t maxs; + + VectorCopy(ent->r.currentOrigin, b1); + VectorCopy(ent->r.currentOrigin, b2); + VectorAdd(b1, ent->r.mins, b1); + VectorCopy(ent->r.maxs, maxs); + maxs[2] = ClientHitboxMaxZ(ent); + VectorAdd(b2, maxs, b2); + bboxEnt = G_TempEntity( b1, EV_RAILTRAIL ); + VectorCopy(b2, bboxEnt->s.origin2); + bboxEnt->s.dmgFlags = 1; + + head = G_BuildHead(ent); + VectorCopy(head->r.currentOrigin, b1); + VectorCopy(head->r.currentOrigin, b2); + VectorAdd(b1, head->r.mins, b1); + VectorAdd(b2, head->r.maxs, b2); + bboxEnt = G_TempEntity( b1, EV_RAILTRAIL ); + VectorCopy(b2, bboxEnt->s.origin2); + bboxEnt->s.dmgFlags = 1; + G_FreeEntity( head ); + + } + + // josh: moved over from ClientThink see the note there + // We want this to track the server's viewpoint G_StoreClientPosition( ent ); } diff -urN --exclude=.svn et260/src/game/g_antilag.c etpub/src/game/g_antilag.c --- et260/src/game/g_antilag.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/g_antilag.c 2005-05-19 15:46:08.000000000 -0500 @@ -1,38 +1,118 @@ #include "g_local.h" -void G_StoreClientPosition( gentity_t* ent ) { - int top; +/* +============= +TimeShiftLerp (from g_unlagged.c) + +Used below to interpolate between two previous vectors +Returns a vector "frac" times the distance between "start" and "end" +============= +*/ +static void TimeShiftLerp( vec3_t start, vec3_t end, float frac, vec3_t result ) { +// From CG_InterpolateEntityPosition in cg_ents.c: +/* + cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); + cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); + cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); + + Angles would be done in a different function, but here they are only the s.angles + lerpAngles is copied over from ent->s.angles + cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); + cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); + cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); + +*/ +// Making these exactly the same should avoid floating-point error + + result[0] = start[0] + frac * ( end[0] - start[0] ); + result[1] = start[1] + frac * ( end[1] - start[1] ); + result[2] = start[2] + frac * ( end[2] - start[2] ); +} + +qboolean G_AntilagSafe(gentity_t *ent) +{ + if(!ent) + return qfalse; + + if(!ent->inuse) + return qfalse; + + if(!ent->r.linked) + return qfalse; + + if(!ent->client) + return qfalse; + + if(ent->client->sess.sessionTeam != TEAM_AXIS + && ent->client->sess.sessionTeam != TEAM_ALLIES) + return qfalse; + + if(ent->health <= 0) + return qfalse; + + if((ent->client->ps.pm_flags & PMF_LIMBO)) + return qfalse; + + // tjw: don't store clientMarkers for corpses, etc + if(!(ent->client->ps.pm_type == PM_NORMAL)) + return qfalse; + + // tjw: don't store clientMarkers for the player on + // the tank + if((ent->client->ps.eFlags & EF_MOUNTEDTANK)) + return qfalse; - if(!( ent->inuse && - (ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES) && - ent->r.linked && - (ent->health > 0) && - !(ent->client->ps.pm_flags & PMF_LIMBO) && - (ent->client->ps.pm_type == PM_NORMAL) - )) { - return; - } + return qtrue; +} + +void G_StoreClientPosition( gentity_t* ent ) { + int top; + + if(!G_AntilagSafe(ent)) return; ent->client->topMarker++; - if( ent->client->topMarker >= MAX_CLIENT_MARKERS ) { + if ( ent->client->topMarker >= MAX_CLIENT_MARKERS ) { ent->client->topMarker = 0; } top = ent->client->topMarker; - VectorCopy( ent->r.mins, ent->client->clientMarkers[top].mins ); - VectorCopy( ent->r.maxs, ent->client->clientMarkers[top].maxs ); - VectorCopy( ent->s.pos.trBase, ent->client->clientMarkers[top].origin ); + VectorCopy(ent->r.mins, ent->client->clientMarkers[top].mins); + VectorCopy(ent->r.maxs, ent->client->clientMarkers[top].maxs); + + // change to trbase instead of origin, correct for client (use s!!) + VectorCopy(ent->s.pos.trBase, ent->client->clientMarkers[top].origin); + + ent->client->clientMarkers[top].time = level.time; + //ent->client->clientMarkers[top].servertime = level.time; + + //josh: more info needed for head / leg shots + // tjw: It seems that s.apos.trBase and ps.viewangles are in sync and + // the only usable angles for determining the direction the client + // is pointing. I don't know wtf r.currentAngles, + // s.angles, and s.angles2 are for. + // josh: Yeah, Neil says they were just copying the angle into every + // angle they could think of "just in case". He was very unimpressed. + VectorCopy(ent->s.apos.trBase, + ent->client->clientMarkers[top].viewangles); + + ent->client->clientMarkers[top].eFlags = ent->s.eFlags; + ent->client->clientMarkers[top].pm_flags = ent->client->ps.pm_flags; + ent->client->clientMarkers[top].viewheight = ent->client->ps.viewheight; + } -static void G_AdjustSingleClientPosition( gentity_t* ent, int time ) { +void G_AdjustSingleClientPosition( gentity_t* ent, int time, gentity_t* debugger) { int i, j; + // unlagged analogies: antilag i = unlagged j, antilag j = unlagged k if( time > level.time ) { time = level.time; } // no lerping forward.... + if(!G_AntilagSafe(ent)) return; + // find a pair of markers which bound the requested time i = j = ent->client->topMarker; do { @@ -51,42 +131,142 @@ return; } - // save current position to backup - if( ent->client->backupMarker.time != level.time ) { - VectorCopy( ent->r.currentOrigin, ent->client->backupMarker.origin ); - VectorCopy( ent->r.mins, ent->client->backupMarker.mins ); - VectorCopy( ent->r.maxs, ent->client->backupMarker.maxs ); + // josh: from unlagged make sure it doesn't get backed-up twice + if ( ent->client->backupMarker.time != level.time ) { + VectorCopy(ent->r.currentOrigin, ent->client->backupMarker.origin); + VectorCopy(ent->r.mins, ent->client->backupMarker.mins); + VectorCopy(ent->r.maxs, ent->client->backupMarker.maxs); + //josh: for Head, Legs + VectorCopy(ent->client->ps.viewangles, + ent->client->backupMarker.viewangles); + ent->client->backupMarker.eFlags = ent->client->ps.eFlags; + ent->client->backupMarker.pm_flags = ent->client->ps.pm_flags; + ent->client->backupMarker.viewheight = ent->client->ps.viewheight; + // josh: This was missing, but needed or else no readjustment ent->client->backupMarker.time = level.time; } - if( i != ent->client->topMarker ) { - float frac = (float)(time - ent->client->clientMarkers[i].time) / - (float)(ent->client->clientMarkers[j].time - ent->client->clientMarkers[i].time); - - LerpPosition( ent->client->clientMarkers[i].origin, ent->client->clientMarkers[j].origin, frac, ent->r.currentOrigin ); - LerpPosition( ent->client->clientMarkers[i].mins, ent->client->clientMarkers[j].mins, frac, ent->r.mins ); - LerpPosition( ent->client->clientMarkers[i].maxs, ent->client->clientMarkers[j].maxs, frac, ent->r.maxs ); + if(i != ent->client->topMarker) { + //float frac = ((float)(ent->client->clientMarkers[j].time - time)) / (ent->client->clientMarkers[j].time - ent->client->clientMarkers[i].time); + //josh: reversing the order to better match the client and prevent + //roundoff error + float frac = (float)(time - ent->client->clientMarkers[i].time) / + (float)(ent->client->clientMarkers[j].time - ent->client->clientMarkers[i].time); + + //LerpPosition(ent->client->clientMarkers[i].origin, ent->client->clientMarkers[j].origin, frac, ent->r.currentOrigin); + //LerpPosition(ent->client->clientMarkers[i].mins, ent->client->clientMarkers[j].mins, frac, ent->r.mins); + //LerpPosition(ent->client->clientMarkers[i].maxs, ent->client->clientMarkers[j].maxs, frac, ent->r.maxs); + //josh: Using TimeShiftLerp since it follows the client exactly + //meaning less roundoff error + TimeShiftLerp( + ent->client->clientMarkers[i].origin, + ent->client->clientMarkers[j].origin, + frac, + ent->r.currentOrigin); + TimeShiftLerp( + ent->client->clientMarkers[i].mins, + ent->client->clientMarkers[j].mins, + frac, + ent->r.mins); + TimeShiftLerp( + ent->client->clientMarkers[i].maxs, + ent->client->clientMarkers[j].maxs, + frac, + ent->r.maxs); + + // These are for Head / Legs + ent->client->ps.viewangles[0] = LerpAngle( + ent->client->clientMarkers[i].viewangles[0], + ent->client->clientMarkers[j].viewangles[0], + frac); + ent->client->ps.viewangles[1] = LerpAngle( + ent->client->clientMarkers[i].viewangles[1], + ent->client->clientMarkers[j].viewangles[1], + frac); + ent->client->ps.viewangles[2] = LerpAngle( + ent->client->clientMarkers[i].viewangles[2], + ent->client->clientMarkers[j].viewangles[2], + frac); + // josh: Set the ints to the closest ones in time since you can't + // lerp them. + if((ent->client->clientMarkers[j].time - time) + < (time - ent->client->clientMarkers[i].time)) { + + ent->client->ps.eFlags = + ent->client->clientMarkers[j].eFlags; + ent->client->ps.pm_flags = + ent->client->clientMarkers[j].pm_flags; + ent->client->ps.viewheight = + ent->client->clientMarkers[j].viewheight; + } else { + ent->client->ps.eFlags = + ent->client->clientMarkers[i].eFlags; + ent->client->ps.pm_flags = + ent->client->clientMarkers[i].pm_flags; + ent->client->ps.viewheight = + ent->client->clientMarkers[i].viewheight; + } + if ( debugger && debugger->client) { + // print some debugging stuff exactly like what the client does + // it starts with "Rec:" to let you know it backward-reconciled + char msg[2048]; + Com_sprintf( msg, sizeof(msg), + "print \"^1Rec: time: %d, j: %d, k: %d, origin: %0.2f %0.2f %0.2f\n" + "^2frac: %0.4f, origin1: %0.2f %0.2f %0.2f, origin2: %0.2f %0.2f %0.2f\n" + "^7level.time: %d, est time: %d, level.time delta: %d, est real ping: %d\n\"", + time, ent->client->clientMarkers[i].time, ent->client->clientMarkers[j].time, + ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2], + frac, + ent->client->clientMarkers[i].origin[0], + ent->client->clientMarkers[i].origin[1], + ent->client->clientMarkers[i].origin[2], + ent->client->clientMarkers[j].origin[0], + ent->client->clientMarkers[j].origin[1], + ent->client->clientMarkers[j].origin[2], + level.time, level.time + debugger->client->frameOffset, + level.time - time, level.time + debugger->client->frameOffset - time); + + trap_SendServerCommand( debugger - g_entities, msg ); + } } else { VectorCopy( ent->client->clientMarkers[j].origin, ent->r.currentOrigin ); VectorCopy( ent->client->clientMarkers[j].mins, ent->r.mins ); VectorCopy( ent->client->clientMarkers[j].maxs, ent->r.maxs ); + //// josh: BuildHead/Legs uses these + VectorCopy(ent->client->clientMarkers[j].viewangles, + ent->client->ps.viewangles); + ent->client->ps.eFlags = + ent->client->clientMarkers[j].eFlags; + ent->client->ps.pm_flags = + ent->client->clientMarkers[j].pm_flags; + ent->client->ps.viewheight = + ent->client->clientMarkers[j].viewheight; } trap_LinkEntity( ent ); } -static void G_ReAdjustSingleClientPosition( gentity_t* ent ) { - if( !ent || !ent->client ) { - return; - } +void G_ReAdjustSingleClientPosition( gentity_t* ent ) { - // restore from backup - if( ent->client->backupMarker.time == level.time ) { - VectorCopy( ent->client->backupMarker.origin, ent->r.currentOrigin ); - VectorCopy( ent->client->backupMarker.mins, ent->r.mins ); - VectorCopy( ent->client->backupMarker.maxs, ent->r.maxs ); - ent->client->backupMarker.time = 0; + if(!G_AntilagSafe(ent)) return; + if( ent->client->backupMarker.time == level.time) { + VectorCopy( ent->client->backupMarker.origin, ent->r.currentOrigin ); + VectorCopy( ent->client->backupMarker.mins, ent->r.mins ); + VectorCopy( ent->client->backupMarker.maxs, ent->r.maxs ); + //josh: Head, Legs stuff + VectorCopy(ent->client->backupMarker.viewangles, + ent->client->ps.viewangles); + + ent->client->ps.eFlags = + ent->client->backupMarker.eFlags; + ent->client->ps.pm_flags = + ent->client->backupMarker.pm_flags; + ent->client->ps.viewheight = + ent->client->backupMarker.viewheight; + + //ent->client->backupMarker.servertime = 0; + ent->client->backupMarker.time = 0; trap_LinkEntity( ent ); } } @@ -97,38 +277,33 @@ for( i = 0; i < level.numConnectedClients; i++, list++ ) { list = g_entities + level.sortedClients[i]; - // Gordon: ok lets test everything under the sun - if( list->client && - list->inuse && - (list->client->sess.sessionTeam == TEAM_AXIS || list->client->sess.sessionTeam == TEAM_ALLIES) && - (list != ent) && - list->r.linked && - (list->health > 0) && - !(list->client->ps.pm_flags & PMF_LIMBO) && - (list->client->ps.pm_type == PM_NORMAL) - ) { - if( forward ) { - G_AdjustSingleClientPosition( list, time ); - } else { - G_ReAdjustSingleClientPosition( list ); - } + // unlagged doesn't adjust the firing client + if (list == ent) continue; + if(forward) { + G_AdjustSingleClientPosition( list, time, NULL); + } else { + G_ReAdjustSingleClientPosition( list ); } + } } void G_ResetMarkers( gentity_t* ent ) { int i, time; - char buffer[ MAX_CVAR_VALUE_STRING ]; float period; + int eFlags; - trap_Cvar_VariableStringBuffer( "sv_fps", buffer, sizeof( buffer ) - 1 ); - - period = atoi( buffer ); + period = sv_fps.value; if( !period ) { period = 50; } else { period = 1000.f / period; } + eFlags = ent->client->ps.eFlags; + // tjw: don't save entity flags that are not allowed + // for clientMarkers + if((eFlags & EF_MOUNTEDTANK)) + eFlags &= ~EF_MOUNTEDTANK; ent->client->topMarker = MAX_CLIENT_MARKERS - 1; for( i = MAX_CLIENT_MARKERS - 1, time = level.time; i >= 0; i--, time -= period ) { @@ -136,6 +311,11 @@ VectorCopy( ent->r.maxs, ent->client->clientMarkers[i].maxs ); VectorCopy( ent->r.currentOrigin, ent->client->clientMarkers[i].origin ); ent->client->clientMarkers[i].time = time; + VectorCopy(ent->client->ps.viewangles, + ent->client->clientMarkers[i].viewangles); + ent->client->clientMarkers[i].eFlags = eFlags; + ent->client->clientMarkers[i].pm_flags = ent->client->ps.pm_flags; + ent->client->clientMarkers[i].viewheight = ent->client->ps.viewheight; } } @@ -150,9 +330,9 @@ (list->client->sess.sessionTeam == TEAM_AXIS || list->client->sess.sessionTeam == TEAM_ALLIES) && (list != ent) && list->r.linked && - (list->health > 0) && !(list->client->ps.pm_flags & PMF_LIMBO) && - (list->client->ps.pm_type == PM_NORMAL) + (list->client->ps.pm_type == PM_NORMAL || + list->client->ps.pm_type == PM_DEAD) ) { list->client->tempHead = G_BuildHead( list ); list->client->tempLeg = G_BuildLeg( list ); @@ -250,3 +430,134 @@ G_DettachBodyParts(); } + + +qboolean G_SkipCorrectionSafe(gentity_t *ent) +{ + if(!ent) + return qfalse; + + if(!ent->inuse) + return qfalse; + + if(!ent->r.linked) + return qfalse; + + if(!ent->client) + return qfalse; + + if(ent->client->sess.sessionTeam != TEAM_AXIS + && ent->client->sess.sessionTeam != TEAM_ALLIES) + return qfalse; + + if((ent->client->ps.pm_flags & PMF_LIMBO) || + (ent->client->ps.pm_flags & PMF_TIME_LOCKPLAYER)) + return qfalse; + + if(ent->health <= 0) + return qfalse; + + if(ent->client->ps.pm_type != PM_NORMAL) + return qfalse; + + // tjw: this causes segfault with new PM_TraceLegs() from + // 2.60 because it wants to update pm.pmext which + // we don't bother passing. + if((ent->client->ps.eFlags & EF_PRONE)) + return qfalse; + + if((ent->client->ps.eFlags & EF_MOUNTEDTANK)) + return qfalse; + + // tjw: perhaps 2 would be OK too? + if(ent->waterlevel > 1) + return qfalse; + + // tjw: don't bother with skip correction if the player + // isn't moving horizontally + if(!ent->client->ps.velocity[0] && !ent->client->ps.velocity[1]) + return qfalse; + + return qtrue; +} + + + +// G_PredictPmove +// +// Make use of the 'Pmove' functions to figure out where a player +// would have moved in the short amount of time indicated by frametime. +// +// After the Pmove is complete, copy the values to the player's entity +// state, but then copy the original player state values back to the +// player state so that the player's movements aren't effected in any way. +// +// The only Pmove functions used are PM_StepSlideMove() and +// PM_GroundTrace() since these are all that is needed for this +// short period of time. This means that a lot of movement types +// cannot be predicted and no prediction should be done for them +// See G_SkipCorrectionSafe() + +void G_PredictPmove(gentity_t *ent, float frametime) +{ + pmove_t pm; + vec3_t origin; + vec3_t velocity; + int groundEntityNum; + int pm_flags; + int pm_time; + int eFlags; + gclient_t *client; + + if(!G_SkipCorrectionSafe(ent)) + return; + + client = ent->client; + + // tjw: backup all the playerState values that may + // be altered by PmovePredict() + VectorCopy(client->ps.origin, origin); + VectorCopy(client->ps.velocity, velocity); + groundEntityNum = client->ps.groundEntityNum; + pm_flags = client->ps.pm_flags; + pm_time = client->ps.pm_time; + eFlags = client->ps.eFlags; + + memset(&pm, 0, sizeof(pm)); + pm.ps = &client->ps; + pm.character = client->pers.character; + pm.trace = trap_TraceCapsuleNoEnts; + pm.pointcontents = trap_PointContents; + pm.tracemask = MASK_PLAYERSOLID; + VectorCopy(ent->r.mins, pm.mins); + VectorCopy(ent->r.maxs, pm.maxs); + pm.predict = qtrue; + //pm.debugLevel = 1; + + PmovePredict(&pm, frametime); + + //G_Printf("origin: before %s after %s\n", + // vtos(ent->s.pos.trBase), + // vtos(client->ps.origin)); + + // tjw: update entity state with the resulting player state + VectorCopy(client->ps.origin, ent->s.pos.trBase); + VectorCopy(client->ps.velocity, ent->s.pos.trDelta); + ent->s.groundEntityNum = client->ps.groundEntityNum; + ent->s.eFlags = client->ps.eFlags; + + // tjw: this needs to be updated in case g_antilag is not enabled + VectorCopy(client->ps.origin, ent->r.currentOrigin); + // josh: link them to stop them from skipping over mines and being + // unhittable when antilag is 0 + trap_LinkEntity(ent); + + // tjw: restore player state so that their next command + // will process as if nothing has happened. + VectorCopy(origin, client->ps.origin); + VectorCopy(velocity, client->ps.velocity); + client->ps.groundEntityNum = groundEntityNum; + client->ps.pm_flags = pm_flags; + client->ps.pm_time = pm_time; + client->ps.eFlags = eFlags; +} diff -urN --exclude=.svn et260/src/game/g_bot.c etpub/src/game/g_bot.c --- et260/src/game/g_bot.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/g_bot.c 2005-05-11 17:36:06.000000000 -0500 @@ -622,7 +622,8 @@ char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt - botinfo = G_GetBotInfoByName( "wolfbot" ); + // CHRUKER: b070 - Was 'wolfbot' but this must match in case + botinfo = G_GetBotInfoByName( "Wolfbot" ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; @@ -805,7 +806,8 @@ trap_Printf( "Usage: Addbot [skill 1-4] [team (RED/BLUE)] [msec delay]\n" ); return; } - Q_strncpyz( name, "wolfbot", sizeof( name ) ); // RF, hard code the bots for wolf + // CHRUKER: b070 - Was 'wolfbot' but this must match in case + Q_strncpyz( name, "Wolfbot", sizeof( name ) ); // RF, hard code the bots for wolf if ( !name[0] ) { trap_Printf( "Usage: Addbot [skill 1-4] [team (RED/BLUE)] [msec delay]\n" ); return; diff -urN --exclude=.svn et260/src/game/g_client.c etpub/src/game/g_client.c --- et260/src/game/g_client.c 2005-04-12 12:11:50.000000000 -0500 +++ etpub/src/game/g_client.c 2005-05-26 09:10:53.000000000 -0500 @@ -297,11 +297,13 @@ */ void BodySink2( gentity_t *ent ) { ent->physicsObject = qfalse; - ent->nextthink = level.time + BODY_TIME(BODY_TEAM(ent))+1500; - ent->think = BodyUnlink; - ent->s.pos.trType = TR_LINEAR; - ent->s.pos.trTime = level.time; - VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); + // tjw: BODY_TIME is how long they last all together, not just sink anim + //ent->nextthink = level.time + BODY_TIME(BODY_TEAM(ent))+1500; + ent->nextthink = level.time + 4000; + ent->think = BodyUnlink; + ent->s.pos.trType = TR_LINEAR; + ent->s.pos.trTime = level.time; + VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); VectorSet( ent->s.pos.trDelta, 0, 0, -8 ); } @@ -418,10 +420,11 @@ body->r.contents = CONTENTS_CORPSE; body->r.ownerNum = ent->r.ownerNum; - BODY_TEAM(body) = ent->client->sess.sessionTeam; - BODY_CLASS(body) = ent->client->sess.playerType; - BODY_CHARACTER(body) = ent->client->pers.characterIndex; - BODY_VALUE(body) = 0; + BODY_TEAM(body) = ent->client->sess.sessionTeam; + BODY_CLASS(body) = ent->client->sess.playerType; + BODY_CHARACTER(body) = ent->client->pers.characterIndex; + BODY_VALUE(body) = 0; + BODY_WEAPON(body) = ent->client->sess.playerWeapon; body->s.time2 = 0; @@ -478,6 +481,103 @@ VectorCopy( ent->s.angles, ent->client->ps.viewangles); } + +void G_DropLimboHealth(gentity_t *ent) +{ + vec3_t launchvel, launchspot; + int packs = 0; + int i; + int cwt; + + if(g_dropHealth.integer == 0) + return; + if(!ent->client) + return; + if(ent->client->sess.playerType != PC_MEDIC) + return; + + cwt = ent->client->ps.classWeaponTime; + packs = g_dropHealth.integer; + if(g_dropHealth.integer == -1) { + packs = 5; // max packs + ent->client->ps.classWeaponTime += + (level.time + - ent->client->deathTime + + 10000); + } + for(i=0; iclient->ps.classWeaponTime >= level.time) { + + break; + } + launchvel[0] = crandom(); + launchvel[1] = crandom(); + launchvel[2] = 0; + VectorScale(launchvel, 100, launchvel); + launchvel[2] = (100 + (g_tossDistance.integer * 10)); + VectorCopy(ent->r.currentOrigin, launchspot); + + Weapon_Medic_Ext(ent, + launchspot, + launchspot, + launchvel); + } + ent->client->ps.classWeaponTime = cwt; +} + +void G_DropLimboAmmo(gentity_t *ent) +{ + vec3_t launchvel, launchspot; + int packs = 0; + int i; + int cwt; + + if(g_dropAmmo.integer == 0) + return; + if(!ent->client) + return; + if(ent->client->sess.playerType != PC_FIELDOPS) + return; + + cwt = ent->client->ps.classWeaponTime; + packs = g_dropAmmo.integer; + if(g_dropAmmo.integer == -1) { + packs = 5; // max packs + // adjust the charge timer so that the + // fops only drops as much ammo as he + // could have before he died. + // don't ask me where the 10000 comes + // from, it just makes this more + // accurate. + ent->client->ps.classWeaponTime += + (level.time + - ent->client->deathTime + + 10000); + } + for(i=0; iclient->ps.classWeaponTime >= level.time) { + + break; + } + launchvel[0] = crandom(); + launchvel[1] = crandom(); + launchvel[2] = 0; + + VectorScale(launchvel, 100, launchvel); + launchvel[2] = (100 + (g_tossDistance.integer * 10)); + VectorCopy(ent->r.currentOrigin, launchspot); + + Weapon_MagicAmmo_Ext(ent, + launchspot, + launchspot, + launchvel); + } + ent->client->ps.classWeaponTime = cwt; +} + + /* JPW NERVE ================ limbo @@ -513,6 +613,8 @@ if( makeCorpse ) { + G_DropLimboHealth(ent); + G_DropLimboAmmo(ent); CopyToBodyQue (ent); // make a nice looking corpse } else { trap_UnlinkEntity (ent); @@ -552,10 +654,29 @@ for(i=0; ips.pm_flags & PMF_LIMBO) || - (cl->sess.sessionTeam == TEAM_SPECTATOR && cl->sess.spectatorState == SPECTATOR_FOLLOW)) && - cl->sess.spectatorClient == ent - g_entities) {//ent->s.number ) { - Cmd_FollowCycle_f( &g_entities[level.sortedClients[i]], 1 ); + + if(cl->sess.spectatorClient != (ent - g_entities) || + cl->sess.spectatorState != SPECTATOR_FOLLOW || + (g_spectator.integer & SPECF_PERSIST_FOLLOW)) { + + continue; + } + + if((cl->ps.pm_flags & PMF_LIMBO)) { + + Cmd_FollowCycle_f( + &g_entities[level.sortedClients[i]], 1); + } + else if(cl->sess.sessionTeam == TEAM_SPECTATOR) { + + if((g_spectator.integer & SPECF_FL_ON_LIMBO)) { + cl->sess.spectatorState = SPECTATOR_FREE; + ClientBegin(cl - level.clients); + } + else { + Cmd_FollowCycle_f( + &g_entities[level.sortedClients[i]], 1); + } } } } @@ -648,7 +769,7 @@ G_DPrintf( "Respawning %s, %i lives left\n", ent->client->pers.netname, ent->client->ps.persistant[PERS_RESPAWNS_LEFT]); - ClientSpawn(ent, qfalse); + ClientSpawn(ent, qfalse, qfalse, qtrue); // DHM - Nerve :: Add back if we decide to have a spawn effect // add a teleportation effect @@ -788,325 +909,618 @@ void BotSetPOW(int entityNum, qboolean isPOW); -/* -=========== -SetWolfSpawnWeapons -=========== -*/ -void SetWolfSpawnWeapons( gclient_t *client ) -{ - int pc = client->sess.playerType; - qboolean isBot = (g_entities[client->ps.clientNum].r.svFlags & SVF_BOT) ? qtrue : qfalse; - qboolean isPOW = (g_entities[client->ps.clientNum].r.svFlags & SVF_POW) ? qtrue : qfalse; - if ( client->sess.sessionTeam == TEAM_SPECTATOR ) - return; - // Reset special weapon time - client->ps.classWeaponTime = -999999; +/* + G_AddClassSpecificTools //tjw + This stuff was stripped out of SetWolfSpawnWeapons() so it can + be used for class stealing. It needed cleaning up badly anyway. + It still does. + */ +void G_AddClassSpecificTools(gclient_t *client) +{ + int pc = client->sess.playerType; + qboolean add_binocs = qfalse; - // Communicate it to cgame - client->ps.stats[STAT_PLAYER_CLASS] = pc; + if(client->sess.skill[SK_BATTLE_SENSE] >= 1) + add_binocs = qtrue; - // Abuse teamNum to store player class as well (can't see stats for all clients in cgame) - client->ps.teamNum = pc; + switch(client->sess.playerType) { + case PC_ENGINEER: + AddWeaponToPlayer( client, WP_DYNAMITE, 0, 1, qfalse ); + AddWeaponToPlayer( client, WP_PLIERS, 0, 1, qfalse ); + AddWeaponToPlayer( client, + WP_LANDMINE, + GetAmmoTableData(WP_LANDMINE)->defaultStartingAmmo, + GetAmmoTableData(WP_LANDMINE)->defaultStartingClip, + qfalse ); + break; + case PC_COVERTOPS: + add_binocs = qtrue; + AddWeaponToPlayer(client, + WP_SMOKE_BOMB, + GetAmmoTableData(WP_SMOKE_BOMB)->defaultStartingAmmo, + GetAmmoTableData(WP_SMOKE_BOMB)->defaultStartingClip, + qfalse); + // See if we already have a satchel charge placed + // this needs to be here for class stealing + if( G_FindSatchel( &g_entities[client->ps.clientNum] ) ) { + AddWeaponToPlayer( client, WP_SATCHEL, 0, 0, qfalse ); + AddWeaponToPlayer( client, WP_SATCHEL_DET, 0, 1, qfalse ); + } else { + AddWeaponToPlayer( client, WP_SATCHEL, 0, 1, qfalse ); + AddWeaponToPlayer( client, WP_SATCHEL_DET, 0, 0, qfalse ); + } + break; + case PC_FIELDOPS: + add_binocs = qtrue; + AddWeaponToPlayer(client, WP_AMMO, 0, 1, qfalse); + AddWeaponToPlayer( client, + WP_SMOKE_MARKER, + GetAmmoTableData(WP_SMOKE_MARKER)->defaultStartingAmmo, + GetAmmoTableData(WP_SMOKE_MARKER)->defaultStartingClip, + qfalse); + break; + case PC_MEDIC: + AddWeaponToPlayer(client, + WP_MEDIC_SYRINGE, + GetAmmoTableData(WP_MEDIC_SYRINGE)->defaultStartingAmmo, + GetAmmoTableData(WP_MEDIC_SYRINGE)->defaultStartingClip, + qfalse); + if( client->sess.skill[SK_FIRST_AID] >= 4 ) + AddWeaponToPlayer(client, + WP_MEDIC_ADRENALINE, + GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingAmmo, + GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingClip, + qfalse); + + AddWeaponToPlayer(client, + WP_MEDKIT, + GetAmmoTableData(WP_MEDKIT)->defaultStartingAmmo, + GetAmmoTableData(WP_MEDKIT)->defaultStartingClip, + qfalse); + break; + case PC_SOLDIER: + break; + } + if(add_binocs) { + if(AddWeaponToPlayer(client, WP_BINOCULARS, 1, 0, qfalse)) { + client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); + } + } + //// Josh: adren in different classes when level 4 + if( (client->sess.playerType != PC_MEDIC + && (g_skills.integer & SKILLS_ADREN)) + && client->sess.skill[SK_FIRST_AID] >= 4 ) + AddWeaponToPlayer(client, + WP_MEDIC_ADRENALINE, + GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingAmmo, + GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingClip, + qfalse); - // JPW NERVE -- zero out all ammo counts - memset(client->ps.ammo, 0, MAX_WEAPONS * sizeof(int)); +} - // All players start with a knife (not OR-ing so that it clears previous weapons) - client->ps.weapons[0] = 0; - client->ps.weapons[1] = 0; +qboolean _SetSoldierSpawnWeapons(gclient_t *client) +{ + weapon_t w; + weapon_t w2, w3 = 0; + weapon_t ws = 0; + + w = client->sess.latchPlayerWeapon; + w2 = client->sess.latchPlayerWeapon2; + if(client->sess.sessionTeam == TEAM_AXIS) { + switch(w) { + case WP_MP40: + case WP_PANZERFAUST: + case WP_MORTAR: + case WP_FLAMETHROWER: + case WP_MOBILE_MG42: + break; + default: + w = WP_MP40; + } + AddWeaponToPlayer(client, WP_GRENADE_LAUNCHER, 0, 4, qfalse); + switch(w2) { + case WP_LUGER: + case WP_AKIMBO_LUGER: + case WP_MP40: + break; + default: + w2 = WP_LUGER; + } + if(client->sess.skill[SK_LIGHT_WEAPONS] < 4) { + if(w2 == WP_AKIMBO_LUGER) + w2 = WP_LUGER; + w3 = WP_LUGER; + } + else { + w3 = WP_AKIMBO_LUGER; + } - // Gordon: set up pow status - if( isBot ) { - if( isPOW ) { - BotSetPOW( client->ps.clientNum, qtrue ); - return; - } else { - BotSetPOW( client->ps.clientNum, qfalse ); + if(client->sess.skill[SK_HEAVY_WEAPONS] < 4) { + w3 = 0; + if(w2 == WP_MP40) + w2 = WP_LUGER; + } + else { + if(!(g_weapons.integer & WPF_L4HW_KEEPS_PISTOL)) + w3 = 0; } } + else if(client->sess.sessionTeam == TEAM_ALLIES) { + switch(w) { + case WP_THOMPSON: + case WP_PANZERFAUST: + case WP_MORTAR: + case WP_FLAMETHROWER: + case WP_MOBILE_MG42: + break; + default: + w = WP_THOMPSON; + } + AddWeaponToPlayer(client, WP_GRENADE_PINEAPPLE, 0, 4, qfalse); - AddWeaponToPlayer( client, WP_KNIFE, 1, 0, qtrue ); - - client->ps.weaponstate = WEAPON_READY; + switch(w2) { + case WP_COLT: + case WP_AKIMBO_COLT: + case WP_THOMPSON: + break; + default: + w2 = WP_LUGER; + } + if(client->sess.skill[SK_LIGHT_WEAPONS] < 4) { + if(w2 == WP_AKIMBO_COLT) + w2 = WP_COLT; + w3 = WP_COLT; + } + else { + w3 = WP_AKIMBO_COLT; + } - // Engineer gets dynamite - if ( pc == PC_ENGINEER ) { - AddWeaponToPlayer( client, WP_DYNAMITE, 0, 1, qfalse ); - AddWeaponToPlayer( client, WP_PLIERS, 0, 1, qfalse ); - - if( g_knifeonly.integer != 1 ) { - if( client->sess.skill[SK_BATTLE_SENSE] >= 1 ) { - if( AddWeaponToPlayer( client, WP_BINOCULARS, 1, 0, qfalse ) ) { - client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); - } - } + if(client->sess.skill[SK_HEAVY_WEAPONS] < 4) { + w3 = 0; + if(w2 == WP_THOMPSON) + w2 = WP_COLT; + } + else { + if(!(g_weapons.integer & WPF_L4HW_KEEPS_PISTOL)) + w3 = 0; + } + } + else { + return qfalse; + } - if (client->sess.sessionTeam == TEAM_AXIS) { - switch( client->sess.playerWeapon ) { - case WP_KAR98: - if( AddWeaponToPlayer( client, WP_KAR98, GetAmmoTableData(WP_KAR98)->defaultStartingAmmo, GetAmmoTableData(WP_KAR98)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_GPG40, GetAmmoTableData(WP_GPG40)->defaultStartingAmmo, GetAmmoTableData(WP_GPG40)->defaultStartingClip, qfalse ); - } - break; - default: - AddWeaponToPlayer( client, WP_MP40, GetAmmoTableData(WP_MP40)->defaultStartingAmmo, GetAmmoTableData(WP_MP40)->defaultStartingClip, qtrue ); - break; - } - AddWeaponToPlayer( client, WP_LANDMINE, GetAmmoTableData(WP_LANDMINE)->defaultStartingAmmo, GetAmmoTableData(WP_LANDMINE)->defaultStartingClip, qfalse ); - AddWeaponToPlayer( client, WP_GRENADE_LAUNCHER, 0, 4, qfalse ); + AddWeaponToPlayer(client, + w, + GetAmmoTableData(w)->defaultStartingAmmo, + GetAmmoTableData(w)->defaultStartingClip, + qtrue); + + if(w == WP_MOBILE_MG42) { + ws = WP_MOBILE_MG42_SET; + } + else if(w == WP_MORTAR) { + ws = WP_MORTAR_SET; + } - } else { - switch( client->sess.playerWeapon ) { - case WP_CARBINE: - if( AddWeaponToPlayer( client, WP_CARBINE, GetAmmoTableData(WP_CARBINE)->defaultStartingAmmo, GetAmmoTableData(WP_CARBINE)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_M7, GetAmmoTableData(WP_M7)->defaultStartingAmmo, GetAmmoTableData(WP_M7)->defaultStartingClip, qfalse ); - } - break; - default: - AddWeaponToPlayer( client, WP_THOMPSON, GetAmmoTableData(WP_THOMPSON)->defaultStartingAmmo, GetAmmoTableData(WP_THOMPSON)->defaultStartingClip, qtrue ); - break; - } - AddWeaponToPlayer( client, WP_LANDMINE, GetAmmoTableData(WP_LANDMINE)->defaultStartingAmmo, GetAmmoTableData(WP_LANDMINE)->defaultStartingClip, qfalse ); - AddWeaponToPlayer( client, WP_GRENADE_PINEAPPLE, 0, 4, qfalse ); - } - } + if(ws) { + AddWeaponToPlayer(client, + ws, + GetAmmoTableData(ws)->defaultStartingAmmo, + GetAmmoTableData(ws)->defaultStartingClip, + qfalse); + } + + if(w2 == WP_AKIMBO_LUGER || w2 == WP_AKIMBO_COLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w2))] = + GetAmmoTableData(w2)->defaultStartingClip; + } + AddWeaponToPlayer(client, + w2, + GetAmmoTableData(w2)->defaultStartingAmmo, + GetAmmoTableData(w2)->defaultStartingClip, + qfalse); + + if(w2 == w3) + return qtrue; + + if(w3 == WP_AKIMBO_LUGER || w3 == WP_AKIMBO_COLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w3))] = + GetAmmoTableData(w3)->defaultStartingClip; + } + if(w3) { + AddWeaponToPlayer(client, + w3, + GetAmmoTableData(w3)->defaultStartingAmmo, + GetAmmoTableData(w3)->defaultStartingClip, + qfalse); } + return qtrue; +} - if ( g_knifeonly.integer != 1 ) { - // Field ops gets binoculars, ammo pack, artillery, and a grenade - if ( pc == PC_FIELDOPS ) { - AddWeaponToPlayer( client, WP_AMMO, 0, 1, qfalse ); +qboolean _SetMedicSpawnWeapons(gclient_t *client) +{ + weapon_t w; + weapon_t w2; - if( AddWeaponToPlayer( client, WP_BINOCULARS, 1, 0, qfalse ) ) { - client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); - } + if(client->sess.sessionTeam == TEAM_AXIS) { + w = WP_MP40; + AddWeaponToPlayer(client, WP_GRENADE_LAUNCHER, 0, 1, qfalse); + w2 = WP_LUGER; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_LUGER; + } + } + else if(client->sess.sessionTeam == TEAM_ALLIES) { + w = WP_THOMPSON; + AddWeaponToPlayer(client, WP_GRENADE_PINEAPPLE, 0, 1, qfalse); + w2 = WP_COLT; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_COLT; + } + } + else { + return qfalse; + } + + AddWeaponToPlayer(client, + w, + 0, + GetAmmoTableData(w)->defaultStartingClip, + qtrue); + + if(w2 == WP_AKIMBO_LUGER || w2 == WP_AKIMBO_COLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w2))] = + GetAmmoTableData(w2)->defaultStartingClip; + } + AddWeaponToPlayer(client, + w2, + GetAmmoTableData(w2)->defaultStartingAmmo, + GetAmmoTableData(w2)->defaultStartingClip, + qfalse); + return qtrue; +} - AddWeaponToPlayer( client, WP_SMOKE_MARKER, GetAmmoTableData(WP_SMOKE_MARKER)->defaultStartingAmmo, GetAmmoTableData(WP_SMOKE_MARKER)->defaultStartingClip, qfalse ); +qboolean _SetEngineerSpawnWeapons(gclient_t *client) +{ + weapon_t w; + weapon_t w2; - if( client->sess.sessionTeam == TEAM_AXIS ) { - AddWeaponToPlayer( client, WP_MP40, GetAmmoTableData(WP_MP40)->defaultStartingAmmo, GetAmmoTableData(WP_MP40)->defaultStartingClip, qtrue ); - AddWeaponToPlayer( client, WP_GRENADE_LAUNCHER, 0, 1, qfalse ); - } else { - AddWeaponToPlayer( client, WP_THOMPSON, GetAmmoTableData(WP_THOMPSON)->defaultStartingAmmo, GetAmmoTableData(WP_THOMPSON)->defaultStartingClip, qtrue ); - AddWeaponToPlayer( client, WP_GRENADE_PINEAPPLE, 0, 1, qfalse ); - } - } else if( pc == PC_MEDIC ) { - if( client->sess.skill[SK_BATTLE_SENSE] >= 1 ) { - if( AddWeaponToPlayer( client, WP_BINOCULARS, 1, 0, qfalse ) ) { - client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); - } - } + w = client->sess.latchPlayerWeapon; + if(client->sess.sessionTeam == TEAM_AXIS) { + switch(w) { + case WP_KAR98: + case WP_MP40: + break; + default: + w = WP_MP40; + } + AddWeaponToPlayer(client, WP_GRENADE_LAUNCHER, 0, 4, qfalse); + w2 = WP_LUGER; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_LUGER; + } + } + else if(client->sess.sessionTeam == TEAM_ALLIES) { + switch(w) { + case WP_CARBINE: + case WP_THOMPSON: + break; + default: + w = WP_THOMPSON; + } + AddWeaponToPlayer(client, WP_GRENADE_PINEAPPLE, 0, 4, qfalse); + w2 = WP_COLT; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_COLT; + } + } + else { + return qfalse; + } + + AddWeaponToPlayer(client, + w, + GetAmmoTableData(w)->defaultStartingAmmo, + GetAmmoTableData(w)->defaultStartingClip, + qtrue); + + if(w == WP_KAR98) { + AddWeaponToPlayer(client, + WP_GPG40, + GetAmmoTableData(WP_GPG40)->defaultStartingAmmo, + GetAmmoTableData(WP_GPG40)->defaultStartingClip, + qfalse); + } + else if(w == WP_CARBINE) { + AddWeaponToPlayer(client, + WP_M7, + GetAmmoTableData(WP_M7)->defaultStartingAmmo, + GetAmmoTableData(WP_M7)->defaultStartingClip, + qfalse); + } + if(w2 == WP_AKIMBO_LUGER || w2 == WP_AKIMBO_COLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w2))] = + GetAmmoTableData(w2)->defaultStartingClip; + } + AddWeaponToPlayer(client, + w2, + GetAmmoTableData(w2)->defaultStartingAmmo, + GetAmmoTableData(w2)->defaultStartingClip, + qfalse); + return qtrue; +} - AddWeaponToPlayer( client, WP_MEDIC_SYRINGE, GetAmmoTableData(WP_MEDIC_SYRINGE)->defaultStartingAmmo, GetAmmoTableData(WP_MEDIC_SYRINGE)->defaultStartingClip, qfalse ); - if( client->sess.skill[SK_FIRST_AID] >= 4 ) - AddWeaponToPlayer( client, WP_MEDIC_ADRENALINE, GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingAmmo, GetAmmoTableData(WP_MEDIC_ADRENALINE)->defaultStartingClip, qfalse ); +qboolean _SetFieldOpSpawnWeapons(gclient_t *client) +{ + weapon_t w; + weapon_t w2; - AddWeaponToPlayer( client, WP_MEDKIT, GetAmmoTableData(WP_MEDKIT)->defaultStartingAmmo, GetAmmoTableData(WP_MEDKIT)->defaultStartingClip, qfalse ); + if(client->sess.sessionTeam == TEAM_AXIS) { + w = WP_MP40; + AddWeaponToPlayer(client, WP_GRENADE_LAUNCHER, 0, 1, qfalse); + w2 = WP_LUGER; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_LUGER; + } + } + else if(client->sess.sessionTeam == TEAM_ALLIES) { + w = WP_THOMPSON; + AddWeaponToPlayer(client, WP_GRENADE_PINEAPPLE, 0, 1, qfalse); + w2 = WP_COLT; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_COLT; + } + } + else { + return qfalse; + } + + AddWeaponToPlayer(client, + w, + GetAmmoTableData(w)->defaultStartingAmmo, + GetAmmoTableData(w)->defaultStartingClip, + qtrue); + + if(w2 == WP_AKIMBO_LUGER || w2 == WP_AKIMBO_COLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w2))] = + GetAmmoTableData(w2)->defaultStartingClip; + } + AddWeaponToPlayer(client, + w2, + GetAmmoTableData(w2)->defaultStartingAmmo, + GetAmmoTableData(w2)->defaultStartingClip, + qfalse); + return qtrue; - if (client->sess.sessionTeam == TEAM_AXIS) { - AddWeaponToPlayer( client, WP_MP40, 0, GetAmmoTableData(WP_MP40)->defaultStartingClip, qtrue ); - AddWeaponToPlayer( client, WP_GRENADE_LAUNCHER, 0, 1, qfalse ); - } else { - AddWeaponToPlayer( client, WP_THOMPSON, 0, GetAmmoTableData(WP_THOMPSON)->defaultStartingClip, qtrue ); - AddWeaponToPlayer( client, WP_GRENADE_PINEAPPLE, 0, 1, qfalse ); - } - } else if ( pc == PC_SOLDIER ) { - if( client->sess.skill[SK_BATTLE_SENSE] >= 1 ) { - if( AddWeaponToPlayer( client, WP_BINOCULARS, 1, 0, qfalse ) ) { - client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); - } - } +} - switch( client->sess.sessionTeam ) { - case TEAM_AXIS: - switch( client->sess.playerWeapon ) { - default: - case WP_MP40: - AddWeaponToPlayer( client, WP_MP40, 2*(GetAmmoTableData(WP_MP40)->defaultStartingAmmo), GetAmmoTableData(WP_MP40)->defaultStartingClip, qtrue ); - break; - case WP_PANZERFAUST: - AddWeaponToPlayer( client, WP_PANZERFAUST, GetAmmoTableData(WP_PANZERFAUST)->defaultStartingAmmo, GetAmmoTableData(WP_PANZERFAUST)->defaultStartingClip, qtrue ); - break; - case WP_FLAMETHROWER: - AddWeaponToPlayer( client, WP_FLAMETHROWER, GetAmmoTableData(WP_FLAMETHROWER)->defaultStartingAmmo, GetAmmoTableData(WP_FLAMETHROWER)->defaultStartingClip, qtrue ); - break; - case WP_MOBILE_MG42: - if( AddWeaponToPlayer( client, WP_MOBILE_MG42, GetAmmoTableData(WP_MOBILE_MG42)->defaultStartingAmmo, GetAmmoTableData(WP_MOBILE_MG42)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_MOBILE_MG42_SET, GetAmmoTableData(WP_MOBILE_MG42_SET)->defaultStartingAmmo, GetAmmoTableData(WP_MOBILE_MG42_SET)->defaultStartingClip, qfalse ); - } - break; - case WP_MORTAR: - if( AddWeaponToPlayer( client, WP_MORTAR, GetAmmoTableData(WP_MORTAR)->defaultStartingAmmo, GetAmmoTableData(WP_MORTAR)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_MORTAR_SET, GetAmmoTableData(WP_MORTAR_SET)->defaultStartingAmmo, GetAmmoTableData(WP_MORTAR_SET)->defaultStartingClip, qfalse ); - } - break; - } - break; - case TEAM_ALLIES: - switch( client->sess.playerWeapon ) { - default: - case WP_THOMPSON: - AddWeaponToPlayer( client, WP_THOMPSON, 2*(GetAmmoTableData(WP_THOMPSON)->defaultStartingAmmo), GetAmmoTableData(WP_THOMPSON)->defaultStartingClip, qtrue ); - break; - case WP_PANZERFAUST: - AddWeaponToPlayer( client, WP_PANZERFAUST, GetAmmoTableData(WP_PANZERFAUST)->defaultStartingAmmo, GetAmmoTableData(WP_PANZERFAUST)->defaultStartingClip, qtrue ); - break; - case WP_FLAMETHROWER: - AddWeaponToPlayer( client, WP_FLAMETHROWER, GetAmmoTableData(WP_FLAMETHROWER)->defaultStartingAmmo, GetAmmoTableData(WP_FLAMETHROWER)->defaultStartingClip, qtrue ); - break; - case WP_MOBILE_MG42: - if( AddWeaponToPlayer( client, WP_MOBILE_MG42, GetAmmoTableData(WP_MOBILE_MG42)->defaultStartingAmmo, GetAmmoTableData(WP_MOBILE_MG42)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_MOBILE_MG42_SET, GetAmmoTableData(WP_MOBILE_MG42_SET)->defaultStartingAmmo, GetAmmoTableData(WP_MOBILE_MG42_SET)->defaultStartingClip, qfalse ); - } - break; - case WP_MORTAR: - if( AddWeaponToPlayer( client, WP_MORTAR, GetAmmoTableData(WP_MORTAR)->defaultStartingAmmo, GetAmmoTableData(WP_MORTAR)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_MORTAR_SET, GetAmmoTableData(WP_MORTAR_SET)->defaultStartingAmmo, GetAmmoTableData(WP_MORTAR_SET)->defaultStartingClip, qfalse ); - } - break; - } - break; - default: - break; - } - } else if( pc == PC_COVERTOPS ) { - switch( client->sess.playerWeapon ) { +qboolean _SetCovertSpawnWeapons(gclient_t *client) +{ + weapon_t w; + weapon_t w2, w3; + weapon_t ws = 0; + + w = client->sess.latchPlayerWeapon; + if(client->sess.sessionTeam== TEAM_AXIS) { + switch(w) { case WP_K43: - case WP_GARAND: - if( client->sess.sessionTeam == TEAM_AXIS ) { - if( AddWeaponToPlayer( client, WP_K43, GetAmmoTableData(WP_K43)->defaultStartingAmmo, GetAmmoTableData(WP_K43)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_K43_SCOPE, GetAmmoTableData(WP_K43_SCOPE)->defaultStartingAmmo, GetAmmoTableData(WP_K43_SCOPE)->defaultStartingClip, qfalse ); - } - break; - } else { - if( AddWeaponToPlayer( client, WP_GARAND, GetAmmoTableData(WP_GARAND)->defaultStartingAmmo, GetAmmoTableData(WP_GARAND)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_GARAND_SCOPE, GetAmmoTableData(WP_GARAND_SCOPE)->defaultStartingAmmo, GetAmmoTableData(WP_GARAND_SCOPE)->defaultStartingClip, qfalse ); - } - break; - } case WP_FG42: - if( AddWeaponToPlayer( client, WP_FG42, GetAmmoTableData(WP_FG42)->defaultStartingAmmo, GetAmmoTableData(WP_FG42)->defaultStartingClip, qtrue ) ) { - AddWeaponToPlayer( client, WP_FG42SCOPE, GetAmmoTableData(WP_FG42SCOPE)->defaultStartingAmmo, GetAmmoTableData(WP_FG42SCOPE)->defaultStartingClip, qfalse ); - } + case WP_STEN: break; default: - AddWeaponToPlayer( client, WP_STEN, 2*(GetAmmoTableData(WP_STEN)->defaultStartingAmmo), GetAmmoTableData(WP_STEN)->defaultStartingClip, qtrue ); + w = WP_STEN; + } + AddWeaponToPlayer(client, WP_GRENADE_LAUNCHER, 0, 2, qfalse); + w2 = WP_SILENCER; + w3 = WP_LUGER; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_SILENCEDLUGER; + w3 = 0; + } + } + else if(client->sess.sessionTeam == TEAM_ALLIES) { + switch(w) { + case WP_GARAND: + case WP_FG42: + case WP_STEN: break; - } + default: + w = WP_STEN; + } + AddWeaponToPlayer(client, WP_GRENADE_PINEAPPLE, 0, 2, qfalse); + w2 = WP_SILENCED_COLT; + w3 = WP_COLT; + if(client->sess.skill[SK_LIGHT_WEAPONS] >= 4) { + w2 = WP_AKIMBO_SILENCEDCOLT; + w3 = 0; + } + } + else { + return qfalse; + } - if( AddWeaponToPlayer( client, WP_BINOCULARS, 1, 0, qfalse ) ) { - client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); - } + AddWeaponToPlayer(client, + w, + GetAmmoTableData(w)->defaultStartingAmmo, + GetAmmoTableData(w)->defaultStartingClip, + qtrue); + + if(w == WP_K43) { + ws = WP_K43_SCOPE; + } + else if(w == WP_GARAND) { + ws = WP_GARAND_SCOPE; + } + else if(w == WP_FG42) { + ws = WP_FG42SCOPE; + } - AddWeaponToPlayer( client, WP_SMOKE_BOMB, GetAmmoTableData(WP_SMOKE_BOMB)->defaultStartingAmmo, GetAmmoTableData(WP_SMOKE_BOMB)->defaultStartingClip, qfalse ); + if(ws) { + AddWeaponToPlayer(client, + ws, + GetAmmoTableData(ws)->defaultStartingAmmo, + GetAmmoTableData(ws)->defaultStartingClip, + qfalse); + } + if(w2 == WP_AKIMBO_SILENCEDLUGER || w2 == WP_AKIMBO_SILENCEDCOLT) { + client->ps.ammoclip[ + BG_FindClipForWeapon(BG_AkimboSidearm(w2))] = + GetAmmoTableData(w2)->defaultStartingClip; + } + AddWeaponToPlayer(client, + w2, + GetAmmoTableData(w2)->defaultStartingAmmo, + GetAmmoTableData(w2)->defaultStartingClip, + qfalse); + if(w3) { + AddWeaponToPlayer(client, + w3, + GetAmmoTableData(w3)->defaultStartingAmmo, + GetAmmoTableData(w3)->defaultStartingClip, + qfalse); + } + client->pmext.silencedSideArm = 1; + return qtrue; +} - // See if we already have a satchel charge placed - NOTE: maybe we want to change this so the thing voids on death - if( G_FindSatchel( &g_entities[client->ps.clientNum] ) ) { - AddWeaponToPlayer( client, WP_SATCHEL, 0, 0, qfalse ); // Big Bang \o/ - AddWeaponToPlayer( client, WP_SATCHEL_DET, 0, 1, qfalse ); // Big Red Button for tha Big Bang - } else { - AddWeaponToPlayer( client, WP_SATCHEL, 0, 1, qfalse ); // Big Bang \o/ - AddWeaponToPlayer( client, WP_SATCHEL_DET, 0, 0, qfalse ); // Big Red Button for tha Big Bang - } - } +/* +=========== +SetWolfSpawnWeapons +=========== +*/ +void SetWolfSpawnWeapons( gclient_t *client ) +{ + int pc; + qboolean isBot = qfalse; + qboolean isPOW = qfalse; - switch( client->sess.sessionTeam ) { - case TEAM_AXIS: - switch( pc ) { - case PC_SOLDIER: - if( client->sess.skill[SK_HEAVY_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_MP40 ) { - AddWeaponToPlayer( client, WP_MP40, 2*(GetAmmoTableData(WP_MP40)->defaultStartingAmmo), GetAmmoTableData(WP_MP40)->defaultStartingClip, qtrue ); - } else if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_AKIMBO_LUGER ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_LUGER))] = GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_LUGER, GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_LUGER, GetAmmoTableData(WP_LUGER)->defaultStartingAmmo, GetAmmoTableData(WP_LUGER)->defaultStartingClip, qfalse ); - } - break; + pc = client->sess.playerType; + + if(g_entities[client->ps.clientNum].r.svFlags & SVF_BOT) + isBot = qtrue; + if(g_entities[client->ps.clientNum].r.svFlags & SVF_POW) + isPOW = qtrue; - case PC_COVERTOPS: - if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && ( client->sess.playerWeapon2 == WP_AKIMBO_SILENCEDLUGER || client->sess.playerWeapon2 == WP_AKIMBO_LUGER ) ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_SILENCEDLUGER))] = GetAmmoTableData(WP_AKIMBO_SILENCEDLUGER)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_SILENCEDLUGER, GetAmmoTableData(WP_AKIMBO_SILENCEDLUGER)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_SILENCEDLUGER)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_LUGER, GetAmmoTableData(WP_LUGER)->defaultStartingAmmo, GetAmmoTableData(WP_LUGER)->defaultStartingClip, qfalse ); - AddWeaponToPlayer( client, WP_SILENCER, GetAmmoTableData(WP_SILENCER)->defaultStartingAmmo, GetAmmoTableData(WP_SILENCER)->defaultStartingClip, qfalse ); - client->pmext.silencedSideArm = 1; - } - break; + if ( client->sess.sessionTeam == TEAM_SPECTATOR ) + return; - default: - if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_AKIMBO_LUGER ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_LUGER))] = GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_LUGER, GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_LUGER)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_LUGER, GetAmmoTableData(WP_LUGER)->defaultStartingAmmo, GetAmmoTableData(WP_LUGER)->defaultStartingClip, qfalse ); - } - break; - } - break; - default: - switch( pc ) { - case PC_SOLDIER: - if( client->sess.skill[SK_HEAVY_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_THOMPSON ) { - AddWeaponToPlayer( client, WP_THOMPSON, 2*(GetAmmoTableData(WP_THOMPSON)->defaultStartingAmmo), GetAmmoTableData(WP_THOMPSON)->defaultStartingClip, qtrue ); - } else if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_AKIMBO_COLT ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_COLT))] = GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_COLT, GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_COLT, GetAmmoTableData(WP_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_COLT)->defaultStartingClip, qfalse ); - } - break; + // Reset special weapon time + if((client->ps.eFlags & EF_SLASHKILL) && + (g_slashKill.integer & SLASHKILL_HALFCHARGE)) { - case PC_COVERTOPS: - if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && ( client->sess.playerWeapon2 == WP_AKIMBO_SILENCEDCOLT || client->sess.playerWeapon2 == WP_AKIMBO_COLT ) ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_SILENCEDCOLT))] = GetAmmoTableData(WP_AKIMBO_SILENCEDCOLT)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_SILENCEDCOLT, GetAmmoTableData(WP_AKIMBO_SILENCEDCOLT)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_SILENCEDCOLT)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_COLT, GetAmmoTableData(WP_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_COLT)->defaultStartingClip, qfalse ); - AddWeaponToPlayer( client, WP_SILENCED_COLT, GetAmmoTableData(WP_SILENCED_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_SILENCED_COLT)->defaultStartingClip, qfalse ); - client->pmext.silencedSideArm = 1; - } - break; + int cteam = (client->sess.sessionTeam - 1); + int ctime = 0; - default: - if( client->sess.skill[SK_LIGHT_WEAPONS] >= 4 && client->sess.playerWeapon2 == WP_AKIMBO_COLT ) { - client->ps.ammoclip[BG_FindClipForWeapon(BG_AkimboSidearm(WP_AKIMBO_COLT))] = GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingClip; - AddWeaponToPlayer( client, WP_AKIMBO_COLT, GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_AKIMBO_COLT)->defaultStartingClip, qfalse ); - } else { - AddWeaponToPlayer( client, WP_COLT, GetAmmoTableData(WP_COLT)->defaultStartingAmmo, GetAmmoTableData(WP_COLT)->defaultStartingClip, qfalse ); - } - break; - } + switch(client->sess.playerType) { + case PC_MEDIC: + ctime = level.medicChargeTime[cteam]; + break; + case PC_ENGINEER: + ctime = level.engineerChargeTime[cteam]; + break; + case PC_FIELDOPS: + ctime = level.lieutenantChargeTime[cteam]; + break; + case PC_COVERTOPS: + ctime = level.covertopsChargeTime[cteam]; + break; + default: + ctime = level.soldierChargeTime[cteam]; } + client->ps.classWeaponTime = level.time - (.5f * ctime); + } + else if((client->ps.eFlags & EF_SLASHKILL) && + (g_slashKill.integer & SLASHKILL_ZEROCHARGE)) { - if( pc == PC_SOLDIER ) { - if( client->sess.sessionTeam == TEAM_AXIS ) { - AddWeaponToPlayer( client, WP_GRENADE_LAUNCHER, 0, 4, qfalse ); - } else { - AddWeaponToPlayer( client, WP_GRENADE_PINEAPPLE, 0, 4, qfalse ); - } + client->ps.classWeaponTime = level.time; + } + else if((client->ps.eFlags & EF_SLASHKILL) && + (g_slashKill.integer & SLASHKILL_SAMECHARGE)) { + + // tjw: classWeaponTime preserved in ClientSpawn() + // adjust it to compensate for the time of /kill + client->ps.classWeaponTime += level.time - client->deathTime; + } + else { + // tjw: give full charge bar + client->ps.classWeaponTime = -999999; + } + + client->ps.eFlags &= ~EF_SLASHKILL; + + // Communicate it to cgame + client->ps.stats[STAT_PLAYER_CLASS] = pc; + + // Abuse teamNum to store player class as well (can't see stats + // for all clients in cgame) + client->ps.teamNum = pc; + + // JPW NERVE -- zero out all ammo counts + memset(client->ps.ammo, 0, MAX_WEAPONS * sizeof(int)); + + // All players start with a knife (not OR-ing so that it clears + // previous weapons) + client->ps.weapons[0] = 0; + client->ps.weapons[1] = 0; + + // Gordon: set up pow status + if( isBot ) { + if( isPOW ) { + BotSetPOW( client->ps.clientNum, qtrue ); + return; + } else { + BotSetPOW( client->ps.clientNum, qfalse ); } - if( pc == PC_COVERTOPS ) { - if( client->sess.sessionTeam == TEAM_AXIS ) { - AddWeaponToPlayer( client, WP_GRENADE_LAUNCHER, 0, 2, qfalse ); - } else { - AddWeaponToPlayer( client, WP_GRENADE_PINEAPPLE, 0, 2, qfalse ); + } + + AddWeaponToPlayer( client, WP_KNIFE, 1, 0, qtrue ); + + client->ps.weaponstate = WEAPON_READY; + + if(g_knifeonly.integer) { + switch(pc) { + case PC_MEDIC: + AddWeaponToPlayer(client, + WP_MEDIC_SYRINGE, 0, 20, qfalse); + if(client->sess.skill[SK_FIRST_AID] >= 4) { + AddWeaponToPlayer(client, + WP_MEDIC_ADRENALINE, 0, 10, qfalse); } + break; + case PC_ENGINEER: + AddWeaponToPlayer(client, WP_DYNAMITE, 0, 1, qfalse); + AddWeaponToPlayer(client, WP_PLIERS, 0, 1, qfalse); + break; } - } else { - // Knifeonly block - if( pc == PC_MEDIC ) { - AddWeaponToPlayer( client, WP_MEDIC_SYRINGE, 0, 20, qfalse ); - if( client->sess.skill[SK_FIRST_AID] >= 4 ) - AddWeaponToPlayer( client, WP_MEDIC_ADRENALINE, 0, 10, qfalse ); + return; + } - } - // End Knifeonly stuff -- Ensure that medics get their basic stuff + switch(pc) { + case PC_SOLDIER: + _SetSoldierSpawnWeapons(client); + break; + case PC_MEDIC: + _SetMedicSpawnWeapons(client); + break; + case PC_ENGINEER: + _SetEngineerSpawnWeapons(client); + break; + case PC_FIELDOPS: + _SetFieldOpSpawnWeapons(client); + break; + case PC_COVERTOPS: + _SetCovertSpawnWeapons(client); + break; } + + G_AddClassSpecificTools(client); } int G_CountTeamMedics( team_t team, qboolean alivecheck ) { @@ -1327,6 +1741,11 @@ } } + // look for cg_hitsounds + s = Info_ValueForKey(userinfo, "cg_hitsounds"); + if(*s) client->pers.hitsounds = atoi(s); + else client->pers.hitsounds = 1; + // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); @@ -1417,8 +1836,15 @@ return; } - G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); - G_DPrintf( "ClientUserinfoChanged: %i :: %s\n", clientNum, s ); + if (g_logOptions.integer & LOGOPTS_GUID) // Log the GUIDs? + { + char *guid = Info_ValueForKey ( userinfo, "cl_guid" ); + G_LogPrintf( "ClientUserinfoChangedGUID: %i %s %s\n", clientNum, guid, s ); + G_DPrintf( "ClientUserinfoChangedGUID: %i :: %s %s\n", clientNum, guid, s ); + } else { + G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); + G_DPrintf( "ClientUserinfoChanged: %i :: %s\n", clientNum, s ); + } } @@ -1446,11 +1872,12 @@ char *value; gclient_t *client; char userinfo[MAX_INFO_STRING]; + char userinfo2[MAX_INFO_STRING]; gentity_t *ent; -#ifdef USEXPSTORAGE - ipXPStorage_t* xpBackup; - int i; -#endif // USEXPSTORAGE + int i; + int clientNum2; + char guid[33]; + ent = &g_entities[ clientNum ]; @@ -1465,6 +1892,28 @@ return "You are banned from this server."; } + // josh: censor names + if (g_censorNames.string[0]) { + char censoredName[MAX_NETNAME]; + char name[MAX_NETNAME]; + value = Info_ValueForKey (userinfo, "name"); + Q_strncpyz( name, value, sizeof(name) ); + SanitizeString(name, censoredName, qtrue); + if (G_CensorText(censoredName,&censorNamesDictionary)) { + Info_SetValueForKey( userinfo, "name", censoredName); + trap_SetUserinfo( clientNum, userinfo ); + value = Info_ValueForKey (userinfo, "name"); + if (g_censorPenalty.integer & CNSRPNLTY_KICK) { + return "Name censor: Please change your name."; + } + } + } + + // check for shrubbot ban + if(G_shrubbot_ban_check(userinfo)) + return "You are banned from this server."; + + // Xian - check for max lives enforcement ban if( g_gametype.integer != GT_WOLF_LMS ) { if( g_enforcemaxlives.integer && (g_maxlives.integer > 0 || g_axismaxlives.integer > 0 || g_alliedmaxlives.integer > 0) ) { @@ -1504,6 +1953,36 @@ ClientDisconnect( ent-g_entities ); } + // tjw: if the client has crashed, force disconnect of previous + // session so that the XP can be reclaimed + // tjw: this interferes with sv_wwwDlDisconnected + if((g_XPSave.integer & XPSF_ENABLE) && + firstTime && + (!trap_Cvar_VariableIntegerValue("sv_wwwDlDisconnected") || + (g_XPSave.integer & XPSF_WIPE_DUP_GUID))) { + + value = Info_ValueForKey (userinfo, "cl_guid"); + Q_strncpyz(guid, value, sizeof(guid)); + for (i = 0; i < level.numConnectedClients; i++) { + clientNum2 = level.sortedClients[i]; + if(clientNum == clientNum2) continue; + trap_GetUserinfo(clientNum2, + userinfo2, + sizeof(userinfo2)); + value = Info_ValueForKey (userinfo2, "cl_guid"); + if(!Q_stricmp(guid, value)) { + G_LogPrintf( "Forcing disconnect of " + "duplicate guid %s for client %i\n", + guid, + clientNum2); + return va( + "Duplicate guid %s for client %i", + guid, + clientNum2); + } + } + } + // they can connect ent->client = level.clients + clientNum; client = ent->client; @@ -1515,11 +1994,10 @@ client->pers.connected = CON_CONNECTING; client->pers.connectTime = level.time; //