//NOTE NOTE NOTE NOTE NOTE NOTE //I want to keep this function BG too, because it's fairly generic already, and it //would be nice to have proper prediction of animations. -rww // This function makes sure that the rider's in this vehicle are properly animated. void AnimateRiders( Vehicle_t *pVeh ) { animNumber_t Anim = BOTH_VS_IDLE; float fSpeedPercToMax; int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300; playerState_t *pilotPS; playerState_t *parentPS; int curTime; // Boarding animation. if ( pVeh->m_iBoarding != 0 ) { // We've just started moarding, set the amount of time it will take to finish moarding. if ( pVeh->m_iBoarding < 0 ) { int iAnimLen; // Boarding from left... if ( pVeh->m_iBoarding == -1 ) { Anim = BOTH_VS_MOUNT_L; } else if ( pVeh->m_iBoarding == -2 ) { Anim = BOTH_VS_MOUNT_R; } else if ( pVeh->m_iBoarding == -3 ) { Anim = BOTH_VS_MOUNTJUMP_L; } else if ( pVeh->m_iBoarding == VEH_MOUNT_THROW_LEFT) { iBlend = 0; Anim = BOTH_VS_MOUNTTHROW_R; } else if ( pVeh->m_iBoarding == VEH_MOUNT_THROW_RIGHT) { iBlend = 0; Anim = BOTH_VS_MOUNTTHROW_L; } // Set the delay time (which happens to be the time it takes for the animation to complete). // NOTE: Here I made it so the delay is actually 40% (0.4f) of the animation time. #ifdef _JK2MP iAnimLen = BG_AnimLength( pVeh->m_pPilot->localAnimIndex, Anim ) * 0.4f; pVeh->m_iBoarding = BG_GetTime() + iAnimLen; #else iAnimLen = PM_AnimLength( pVeh->m_pPilot->client->clientInfo.animFileIndex, Anim );// * 0.4f; if (pVeh->m_iBoarding!=VEH_MOUNT_THROW_LEFT && pVeh->m_iBoarding!=VEH_MOUNT_THROW_RIGHT) { pVeh->m_iBoarding = level.time + (iAnimLen*0.4f); } else { pVeh->m_iBoarding = level.time + iAnimLen; } #endif // Set the animation, which won't be interrupted until it's completed. // TODO: But what if he's killed? Should the animation remain persistant??? iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; #ifdef _JK2MP BG_SetAnim(pVeh->m_pPilot->playerState, bgAllAnims[pVeh->m_pPilot->localAnimIndex].anims, SETANIM_BOTH, Anim, iFlags, iBlend); #else NPC_SetAnim( pVeh->m_pPilot, SETANIM_BOTH, Anim, iFlags, iBlend ); if (pVeh->m_pOldPilot) { iAnimLen = PM_AnimLength( pVeh->m_pPilot->client->clientInfo.animFileIndex, BOTH_VS_MOUNTTHROWEE); NPC_SetAnim( pVeh->m_pOldPilot, SETANIM_BOTH, BOTH_VS_MOUNTTHROWEE, iFlags, iBlend ); } #endif } #ifndef _JK2MP if (pVeh->m_pOldPilot && pVeh->m_pOldPilot->client->ps.torsoAnimTimer<=0) { if (Q_irand(0, player->count)==0) { player->count++; player->lastEnemy = pVeh->m_pOldPilot; G_StartMatrixEffect(player, MEF_LOOK_AT_ENEMY|MEF_NO_RANGEVAR|MEF_NO_VERTBOB|MEF_NO_SPIN, 1000); } gentity_t* oldPilot = pVeh->m_pOldPilot; pVeh->m_pVehicleInfo->Eject(pVeh, pVeh->m_pOldPilot, qtrue); // will set pointer to zero // Kill Him //---------- oldPilot->client->noRagTime = -1; // no ragdoll for you G_Damage(oldPilot, pVeh->m_pPilot, pVeh->m_pPilot, pVeh->m_pPilot->currentAngles, pVeh->m_pPilot->currentOrigin, 1000, 0, MOD_CRUSH); // Compute THe Throw Direction As Backwards From The Vehicle's Velocity //---------------------------------------------------------------------- vec3_t throwDir; VectorScale(pVeh->m_pParentEntity->client->ps.velocity, -1.0f, throwDir); VectorNormalize(throwDir); throwDir[2] += 0.3f; // up a little // Now Throw Him Out //------------------- G_Throw(oldPilot, throwDir, VectorLength(pVeh->m_pParentEntity->client->ps.velocity)/10.0f); NPC_SetAnim(oldPilot, SETANIM_BOTH, BOTH_DEATHBACKWARD1, SETANIM_FLAG_OVERRIDE, iBlend ); } #endif return; } #ifdef _JK2MP //fixme if (1) return; #endif #ifdef _JK2MP pilotPS = pVeh->m_pPilot->playerState; parentPS = pVeh->m_pPilot->playerState; #else pilotPS = &pVeh->m_pPilot->client->ps; parentPS = &pVeh->m_pParentEntity->client->ps; #endif #ifndef _JK2MP//SP curTime = level.time; #elif QAGAME//MP GAME curTime = level.time; #elif CGAME//MP CGAME //FIXME: pass in ucmd? Not sure if this is reliable... curTime = pm->cmd.serverTime; #endif // Percentage of maximum speed relative to current speed. fSpeedPercToMax = parentPS->speed / pVeh->m_pVehicleInfo->speedMax; // Going in reverse... #ifdef _JK2MP if ( pVeh->m_ucmd.forwardmove < 0 && !(pVeh->m_ulFlags & VEH_SLIDEBREAKING)) #else if ( fSpeedPercToMax < -0.018f && !(pVeh->m_ulFlags & VEH_SLIDEBREAKING)) #endif { Anim = BOTH_VS_REV; iBlend = 500; } else { bool HasWeapon = ((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE)); bool Attacking = (HasWeapon && !!(pVeh->m_ucmd.buttons&BUTTON_ATTACK)); #ifdef _JK2MP //fixme: flying tends to spaz out a lot bool Flying = false; bool Crashing = false; #else bool Flying = !!(pVeh->m_ulFlags & VEH_FLYING); bool Crashing = !!(pVeh->m_ulFlags & VEH_CRASHING); #endif bool Right = (pVeh->m_ucmd.rightmove>0); bool Left = (pVeh->m_ucmd.rightmove<0); bool Turbo = (curTime<pVeh->m_iTurboTime); EWeaponPose WeaponPose = WPOSE_NONE; // Remove Crashing Flag //---------------------- pVeh->m_ulFlags &= ~VEH_CRASHING; // Put Away Saber When It Is Not Active //-------------------------------------- #ifndef _JK2MP if (HasWeapon && (Turbo || (pilotPS->weapon==WP_SABER && !pilotPS->SaberActive()))) { if (pVeh->m_pPilot->s.number<MAX_CLIENTS) { CG_ChangeWeapon(WP_NONE); } pVeh->m_pPilot->client->ps.weapon = WP_NONE; G_RemoveWeaponModels(pVeh->m_pPilot); } #endif // Don't Interrupt Attack Anims //------------------------------ #ifdef _JK2MP if (pilotPS->weaponTime>0) { return; } #else if (pilotPS->torsoAnim>=BOTH_VS_ATL_S && pilotPS->torsoAnim<=BOTH_VS_ATF_G) { float bodyCurrent = 0.0f; int bodyEnd = 0; if (!!gi.G2API_GetBoneAnimIndex(&pVeh->m_pPilot->ghoul2[pVeh->m_pPilot->playerModel], pVeh->m_pPilot->rootBone, level.time, &bodyCurrent, NULL, &bodyEnd, NULL, NULL, NULL)) { if (bodyCurrent<=((float)(bodyEnd)-1.5f)) { return; } } } #endif // Compute The Weapon Pose //-------------------------- if (pilotPS->weapon==WP_BLASTER) { WeaponPose = WPOSE_BLASTER; } else if (pilotPS->weapon==WP_SABER) { if ( (pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VS_ATL_TO_R_S) { pVeh->m_ulFlags &= ~VEH_SABERINLEFTHAND; } if (!(pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VS_ATR_TO_L_S) { pVeh->m_ulFlags |= VEH_SABERINLEFTHAND; } WeaponPose = (pVeh->m_ulFlags&VEH_SABERINLEFTHAND)?(WPOSE_SABERLEFT):(WPOSE_SABERRIGHT); } if (Attacking && WeaponPose) {// Attack! iBlend = 100; iFlags = SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART; // Auto Aiming //=============================================== if (!Left && !Right) // Allow player strafe keys to override { #ifndef _JK2MP if (pVeh->m_pPilot->enemy) { vec3_t toEnemy; float toEnemyDistance; vec3_t actorRight; float actorRightDot; VectorSubtract(pVeh->m_pPilot->currentOrigin, pVeh->m_pPilot->enemy->currentOrigin, toEnemy); toEnemyDistance = VectorNormalize(toEnemy); AngleVectors(pVeh->m_pParentEntity->currentAngles, 0, actorRight, 0); actorRightDot = DotProduct(toEnemy, actorRight); if (fabsf(actorRightDot)>0.5f || pilotPS->weapon==WP_SABER) { Left = (actorRightDot>0.0f); Right = !Left; } else { Right = Left = false; } } else #endif if (pilotPS->weapon==WP_SABER && !Left && !Right) { Left = (WeaponPose==WPOSE_SABERLEFT); Right = !Left; } } if (Left) {// Attack Left switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATL_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_ATL_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_ATR_TO_L_S; break; default: assert(0); } } else if (Right) {// Attack Right switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_ATL_TO_R_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_ATR_S; break; default: assert(0); } } else {// Attack Ahead switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATF_G; break; default: assert(0); } } } else if (Left && pVeh->m_ucmd.buttons&BUTTON_USE) {// Look To The Left Behind iBlend = 400; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: Anim = BOTH_VS_LOOKLEFT; } } else if (Right && pVeh->m_ucmd.buttons&BUTTON_USE) {// Look To The Right Behind iBlend = 400; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: Anim = BOTH_VS_LOOKRIGHT; } } else if (Turbo) {// Kicked In Turbo iBlend = 50; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; Anim = BOTH_VS_TURBO; } else if (Flying) {// Off the ground in a jump iBlend = 800; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_AIR; break; case WPOSE_BLASTER: Anim = BOTH_VS_AIR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_AIR_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_AIR_SR; break; default: assert(0); } } else if (Crashing) {// Hit the ground! iBlend = 100; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LAND; break; case WPOSE_BLASTER: Anim = BOTH_VS_LAND_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LAND_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LAND_SR; break; default: assert(0); } } else {// No Special Moves iBlend = 300; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; if (pVeh->m_vOrientation[ROLL] <= -20) {// Lean Left switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LEANL; break; case WPOSE_BLASTER: Anim = BOTH_VS_LEANL_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LEANL_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LEANL_SR; break; default: assert(0); } } else if (pVeh->m_vOrientation[ROLL] >= 20) {// Lean Right switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LEANR; break; case WPOSE_BLASTER: Anim = BOTH_VS_LEANR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LEANR_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LEANR_SR; break; default: assert(0); } } else {// No Lean switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_IDLE; break; case WPOSE_BLASTER: Anim = BOTH_VS_IDLE_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: assert(0); } } }// No Special Moves }// Going backwards? #ifdef _JK2MP iFlags &= ~SETANIM_FLAG_OVERRIDE; if (pVeh->m_pPilot->playerState->torsoAnim == Anim) { pVeh->m_pPilot->playerState->torsoTimer = BG_AnimLength(pVeh->m_pPilot->localAnimIndex, Anim); } if (pVeh->m_pPilot->playerState->legsAnim == Anim) { pVeh->m_pPilot->playerState->legsTimer = BG_AnimLength(pVeh->m_pPilot->localAnimIndex, Anim); } BG_SetAnim(pVeh->m_pPilot->playerState, bgAllAnims[pVeh->m_pPilot->localAnimIndex].anims, SETANIM_BOTH, Anim, iFlags|SETANIM_FLAG_HOLD, iBlend); #else NPC_SetAnim( pVeh->m_pPilot, SETANIM_BOTH, Anim, iFlags, iBlend ); #endif }
void ExitEmplacedWeapon( gentity_t *ent ) { // requesting to unlock from the weapon // We'll leave the gun pointed in the direction it was last facing, though we'll cut out the pitch if ( ent->client ) { // if we are the player we will have put down a brush that blocks NPCs so that we have a clear spot to get back out. //gentity_t *place = G_Find( NULL, FOFS(classname), "emp_placeholder" ); if ( ent->health > 0 ) {//he's still alive, and we have a placeholder, so put him back if ( ent->owner->nextTrain ) { // reset the players position VectorCopy( ent->owner->nextTrain->currentOrigin, ent->client->ps.origin ); //reset ent's size to normal VectorCopy( ent->owner->nextTrain->mins, ent->mins ); VectorCopy( ent->owner->nextTrain->maxs, ent->maxs ); //free the placeholder G_FreeEntity( ent->owner->nextTrain ); //re-link the ent gi.linkentity( ent ); } else if ( ent->owner->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but... { // so give 'em a push away from us vec3_t backDir, start, end; trace_t trace; gentity_t *eweb = ent->owner; float curRadius = 0.0f; float minRadius, maxRadius; qboolean safeExit = qfalse; VectorSubtract( ent->currentOrigin, eweb->currentOrigin, backDir ); backDir[2] = 0; minRadius = VectorNormalize( backDir )-8.0f; maxRadius = (ent->maxs[0]+ent->maxs[1])*0.5f; maxRadius += (eweb->maxs[0]+eweb->maxs[1])*0.5f; maxRadius *= 1.5f; if ( minRadius >= maxRadius - 1.0f ) { maxRadius = minRadius + 8.0f; } ent->owner = NULL;//so his trace hits me for ( curRadius = minRadius; curRadius <= maxRadius; curRadius += 4.0f ) { VectorMA( ent->currentOrigin, curRadius, backDir, start ); //make sure they're not in the ground VectorCopy( start, end ); start[2] += 18; end[2] -= 18; gi.trace(&trace, start, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, (EG2_Collision)0, 0); if ( !trace.allsolid && !trace.startsolid ) { G_SetOrigin( ent, trace.endpos ); gi.linkentity( ent ); safeExit = qtrue; break; } } //Hmm... otherwise, don't allow them to get off? ent->owner = eweb; if ( !safeExit ) {//don't try again for a second ent->owner->delay = level.time + 500; return; } } } else if ( ent->health <= 0 ) { // dead, so give 'em a push out of the chair vec3_t dir; AngleVectors( ent->owner->s.angles, NULL, dir, NULL ); if ( rand() & 1 ) { VectorScale( dir, -1, dir ); } VectorMA( ent->client->ps.velocity, 75, dir, ent->client->ps.velocity ); } //don't let them move towards me for a couple frames so they don't step back into me while I'm becoming solid to them if ( ent->s.number < MAX_CLIENTS ) { if ( ent->client->ps.pm_time < 100 ) { ent->client->ps.pm_time = 100; } ent->client->ps.pm_flags |= (PMF_TIME_NOFRICTION|PMF_TIME_KNOCKBACK); } if ( !ent->owner->bounceCount ) {//not an EWeb - the overridden bone angles will remember the angle we left it at VectorCopy( ent->client->ps.viewangles, ent->owner->s.angles ); ent->owner->s.angles[PITCH] = 0; G_SetAngles( ent->owner, ent->owner->s.angles ); VectorCopy( ent->owner->s.angles, ent->owner->pos1 ); } } // Remove the emplaced gun from our inventory ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_EMPLACED_GUN ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); extern void CG_ChangeWeapon( int num ); if ( ent->health <= 0 ) {//when die, don't set weapon back on when ejected from emplaced/eweb //empty hands ent->client->ps.weapon = WP_NONE; if ( ent->NPC ) { ChangeWeapon( ent, ent->client->ps.weapon ); // should be OK actually. } else { CG_ChangeWeapon( ent->client->ps.weapon ); } if ( ent->s.number < MAX_CLIENTS ) { gi.cvar_set( "cg_thirdperson", "1" ); } } else { // when we lock or unlock from the the gun, we get our old weapon back ent->client->ps.weapon = ent->owner->s.weapon; if ( ent->NPC ) {//BTW, if a saber-using NPC ever gets off of an emplaced gun/eweb, this will not work, look at NPC_ChangeWeapon for the proper way ChangeWeapon( ent, ent->client->ps.weapon ); } else { G_RemoveWeaponModels( ent ); CG_ChangeWeapon( ent->client->ps.weapon ); if ( ent->client->ps.weapon == WP_SABER ) { WP_SaberAddG2SaberModels( ent ); } else { G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); } if ( ent->s.number < MAX_CLIENTS ) { if ( ent->client->ps.weapon == WP_SABER ) { gi.cvar_set( "cg_thirdperson", "1" ); } else if ( ent->client->ps.weapon != WP_SABER && cg_gunAutoFirst.integer ) { gi.cvar_set( "cg_thirdperson", "0" ); } } } if ( ent->client->ps.weapon == WP_SABER ) { if ( ent->owner->alt_fire ) { ent->client->ps.SaberActivate(); } else { ent->client->ps.SaberDeactivate(); } } } //set the emplaced gun/eweb's weapon back to the emplaced gun ent->owner->s.weapon = WP_EMPLACED_GUN; // gi.G2API_DetachG2Model( &ent->ghoul2[ent->playerModel] ); ent->s.eFlags &= ~EF_LOCKED_TO_WEAPON; ent->client->ps.eFlags &= ~EF_LOCKED_TO_WEAPON; ent->owner->noDamageTeam = TEAM_FREE; ent->owner->svFlags &= ~SVF_NONNPC_ENEMY; ent->owner->delay = level.time; ent->owner->activator = NULL; if ( !ent->NPC ) { // by keeping the owner, a dead npc can be pushed out of the chair without colliding with it ent->owner = NULL; } }
void eweb_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( !eweb_can_be_used( self, other, activator ) ) { return; } int oldWeapon = activator->s.weapon; if ( oldWeapon == WP_SABER ) { self->alt_fire = activator->client->ps.SaberActive(); } // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; G_RemoveWeaponModels( activator ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); if ( activator->NPC ) { ChangeWeapon( activator, WP_EMPLACED_GUN ); } else if ( activator->s.number == 0 ) { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); } VectorCopy( activator->currentOrigin, self->pos4 );//keep this around so we know when to make them play the strafe anim // the gun will track which weapon we used to have self->s.weapon = oldWeapon; // Lock the player activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON; activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; self->delay = level.time; // can't disconnect from the thing for half a second // Let the gun be considered an enemy //Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have self->svFlags |= SVF_NONNPC_ENEMY; self->noDamageTeam = activator->client->playerTeam; //FIXME: should really wait a bit after spawn and get this just once? self->waypoint = NAV::GetNearestNode(self); #ifdef _DEBUG if ( self->waypoint == -1 ) { gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif G_Sound( self, G_SoundIndex( "sound/weapons/eweb/eweb_mount.mp3" )); if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 ) {//player-only usescript or any usescript // Run use script G_ActivateBehavior( self, BSET_USE ); } }
//---------------------------------------------------------- void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { vec3_t fwd1, fwd2; if ( self->health <= 0 ) { // can't use a dead gun. return; } if ( self->svFlags & SVF_INACTIVE ) { return; // can't use inactive gun } if ( !activator->client ) { return; // only a client can use it. } if ( self->activator ) { // someone is already in the gun. return; } if ( other && other->client && G_IsRidingVehicle( other ) ) {//can't use eweb when on a vehicle return; } if ( activator && activator->client && G_IsRidingVehicle( activator ) ) {//can't use eweb when on a vehicle return; } // We'll just let the designers duke this one out....I mean, as to whether they even want to limit such a thing. if ( self->spawnflags & EMPLACED_FACING ) { // Let's get some direction vectors for the users AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL ); // Get the guns direction vector AngleVectors( self->pos1, fwd2, NULL, NULL ); float dot = DotProduct( fwd1, fwd2 ); // Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it. if ( dot < 0.0f ) { return; } } // don't allow using it again for half a second if ( self->delay + 500 < level.time ) { int oldWeapon = activator->s.weapon; if ( oldWeapon == WP_SABER ) { self->alt_fire = activator->client->ps.SaberActive(); } // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; G_RemoveWeaponModels( activator ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); if ( activator->NPC ) { ChangeWeapon( activator, WP_EMPLACED_GUN ); } else if ( activator->s.number == 0 ) { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); } // Since we move the activator inside of the gun, we reserve a solid spot where they were standing in order to be able to get back out without being in solid if ( self->nextTrain ) {//you never know G_FreeEntity( self->nextTrain ); } self->nextTrain = G_Spawn(); //self->nextTrain->classname = "emp_placeholder"; self->nextTrain->contents = CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP;//hmm... playerclip too now that we're doing it for NPCs? G_SetOrigin( self->nextTrain, activator->client->ps.origin ); VectorCopy( activator->mins, self->nextTrain->mins ); VectorCopy( activator->maxs, self->nextTrain->maxs ); gi.linkentity( self->nextTrain ); //need to inflate the activator's mins/maxs since the gunsit anim puts them outside of their bbox VectorSet( activator->mins, -24, -24, -24 ); VectorSet( activator->maxs, 24, 24, 40 ); // Move the activator into the center of the gun. For NPC's the only way the can get out of the gun is to die. VectorCopy( self->s.origin, activator->client->ps.origin ); activator->client->ps.origin[2] += 30; // move them up so they aren't standing in the floor gi.linkentity( activator ); // the gun will track which weapon we used to have self->s.weapon = oldWeapon; // Lock the player activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON; activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; self->delay = level.time; // can't disconnect from the thing for half a second // Let the gun be considered an enemy //Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have self->svFlags |= SVF_NONNPC_ENEMY; self->noDamageTeam = activator->client->playerTeam; // FIXME: don't do this, we'll try and actually put the player in this beast // move the player to the center of the gun // activator->contents = 0; // VectorCopy( self->currentOrigin, activator->client->ps.origin ); SetClientViewAngle( activator, self->pos1 ); //FIXME: should really wait a bit after spawn and get this just once? self->waypoint = NAV::GetNearestNode(self); #ifdef _DEBUG if ( self->waypoint == -1 ) { gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" )); if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 ) {//player-only usescript or any usescript // Run use script G_ActivateBehavior( self, BSET_USE ); } } }
//rwwFIXMEFIXME: This is all going to have to be predicted I think, or it will feel awful //and lagged // This function makes sure that the rider's in this vehicle are properly animated. static void AnimateRiders( Vehicle_t *pVeh ) { animNumber_t Anim = BOTH_VT_IDLE; int iFlags = SETANIM_FLAG_NORMAL, iBlend = 500; gentity_t *pilot = (gentity_t *)pVeh->m_pPilot; gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity; playerState_t *pilotPS; playerState_t *parentPS; float fSpeedPercToMax; #ifdef _JK2MP pilotPS = pVeh->m_pPilot->playerState; parentPS = pVeh->m_pPilot->playerState; #else pilotPS = &pVeh->m_pPilot->client->ps; parentPS = &pVeh->m_pParentEntity->client->ps; #endif // Boarding animation. if ( pVeh->m_iBoarding != 0 ) { return; } // Percentage of maximum speed relative to current speed. fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax; // Going in reverse... #ifdef _JK2MP //handled in pmove in mp if (0) #else if ( fSpeedPercToMax < -0.01f ) #endif { Anim = BOTH_VT_WALK_REV; iBlend = 600; } else { bool HasWeapon = (qboolean)((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE)); bool Attacking = (qboolean)(HasWeapon && !!(pVeh->m_ucmd.buttons&BUTTON_ATTACK)); bool Right = (qboolean)(pVeh->m_ucmd.rightmove>0); bool Left = (qboolean)(pVeh->m_ucmd.rightmove<0); bool Turbo = (qboolean)(fSpeedPercToMax>0.0f && level.time<pVeh->m_iTurboTime); bool Walking = (qboolean)(fSpeedPercToMax>0.0f && ((pVeh->m_ucmd.buttons&BUTTON_WALKING) || fSpeedPercToMax<=0.275f)); bool Running = (qboolean)(fSpeedPercToMax>0.275f); EWeaponPose WeaponPose = WPOSE_NONE; // Remove Crashing Flag //---------------------- pVeh->m_ulFlags &= ~VEH_CRASHING; // Put Away Saber When It Is Not Active //-------------------------------------- #ifndef _JK2MP if (HasWeapon && (Turbo || (pilotPS->weapon==WP_SABER && !pilotPS->SaberActive()))) { if (pVeh->m_pPilot->s.number<MAX_CLIENTS) { CG_ChangeWeapon(WP_NONE); } pVeh->m_pPilot->client->ps.weapon = WP_NONE; G_RemoveWeaponModels(pVeh->m_pPilot); } #endif // Don't Interrupt Attack Anims //------------------------------ #ifdef _JK2MP if (pilotPS->weaponTime>0) { return; } #else if (pilotPS->torsoAnim>=BOTH_VT_ATL_S && pilotPS->torsoAnim<=BOTH_VT_ATF_G) { float bodyCurrent = 0.0f; int bodyEnd = 0; if (!!gi.G2API_GetBoneAnimIndex(&pVeh->m_pPilot->ghoul2[pVeh->m_pPilot->playerModel], pVeh->m_pPilot->rootBone, level.time, &bodyCurrent, NULL, &bodyEnd, NULL, NULL, NULL)) { if (bodyCurrent<=((float)(bodyEnd)-1.5f)) { return; } } } #endif // Compute The Weapon Pose //-------------------------- if (pilotPS->weapon==WP_BLASTER) { WeaponPose = WPOSE_BLASTER; } else if (pilotPS->weapon==WP_SABER) { if ( (pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATL_TO_R_S) { pVeh->m_ulFlags &= ~VEH_SABERINLEFTHAND; } if (!(pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATR_TO_L_S) { pVeh->m_ulFlags |= VEH_SABERINLEFTHAND; } WeaponPose = (pVeh->m_ulFlags&VEH_SABERINLEFTHAND)?(WPOSE_SABERLEFT):(WPOSE_SABERRIGHT); } if (Attacking && WeaponPose) {// Attack! iBlend = 100; iFlags = SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART; if (Turbo) { Right = true; Left = false; } // Auto Aiming //=============================================== if (!Left && !Right) // Allow player strafe keys to override { #ifndef _JK2MP if (pVeh->m_pPilot->enemy) { vec3_t toEnemy; float toEnemyDistance; vec3_t actorRight; float actorRightDot; VectorSubtract(pVeh->m_pPilot->currentOrigin, pVeh->m_pPilot->enemy->currentOrigin, toEnemy); toEnemyDistance = VectorNormalize(toEnemy); AngleVectors(pVeh->m_pParentEntity->currentAngles, 0, actorRight, 0); actorRightDot = DotProduct(toEnemy, actorRight); if (fabsf(actorRightDot)>0.5f || pilotPS->weapon==WP_SABER) { Left = (actorRightDot>0.0f); Right = !Left; } else { Right = Left = false; } } else #endif if (pilotPS->weapon==WP_SABER && !Left && !Right) { Left = (qboolean)(WeaponPose==WPOSE_SABERLEFT); Right = (qboolean)(!Left); } } if (Left) {// Attack Left switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VT_ATL_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VT_ATL_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VT_ATR_TO_L_S; break; default: assert(0); } } else if (Right) {// Attack Right switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VT_ATR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VT_ATL_TO_R_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VT_ATR_S; break; default: assert(0); } } else {// Attack Ahead switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VT_ATF_G; break; default: assert(0); } } } else if (Turbo) {// Kicked In Turbo iBlend = 50; iFlags = SETANIM_FLAG_OVERRIDE; Anim = BOTH_VT_TURBO; } else {// No Special Moves iBlend = 300; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; if (WeaponPose==WPOSE_NONE) { if (Walking) { Anim = BOTH_VT_WALK_FWD; } else if (Running) { Anim = BOTH_VT_RUN_FWD; } else { Anim = BOTH_VT_IDLE1;//(Q_irand(0,1)==0)?(BOTH_VT_IDLE):(BOTH_VT_IDLE1); } } else { switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VT_IDLE_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VT_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VT_IDLE_SR; break; default: assert(0); } } }// No Special Moves } Vehicle_SetAnim( pilot, SETANIM_BOTH, Anim, iFlags, iBlend ); }