zhakal@Posted: Sun Oct 31, 2010 7:00 am :
When I fall down onto a jumppad from solid ground the jumppad sound and fx get triggered.
But if i jump up and down on the jumppad i will after the first hit, suddenly start to land on the ground, trigger the crashland animation and sound before the jumppad gets triggered and throws me back up. So every second jump is a land.

So basically what i need help with is how to jump up and down on a jumppad without triggering a landing (crashland in player.cpp).

I can alter the fall deltas but then normal landing sound won't play when you jump.



zhakal@Posted: Tue Nov 02, 2010 11:24 pm :
First part shows the move () and second crashland ()

Code:
void idPlayer::Move( void ) {
   float newEyeOffset;
   idVec3 oldOrigin;
   idVec3 oldVelocity;
   idVec3 pushVelocity;

   // save old origin and velocity for crashlanding
   oldOrigin = physicsObj.GetOrigin();
   oldVelocity = physicsObj.GetLinearVelocity();
   pushVelocity = physicsObj.GetPushedLinearVelocity();

   // set physics variables
   physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
   physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );

   if ( noclip ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_NOCLIP );
   } else if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_SPECTATOR );
   } else if ( health <= 0 ) {
      physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
      physicsObj.SetMovementType( PM_DEAD );
   } else if ( gameLocal.GetCamera() || gameLocal.timeoutData.isTimeout ) { // XBM: timeout stuff (kaffeedoktor)
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_FREEZE );
   } else {
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_NORMAL );
   }

   if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else if ( health <= 0 ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else {
      physicsObj.SetClipMask( MASK_PLAYERSOLID );
   }

   physicsObj.SetPlayerInput( usercmd, viewAngles );

   // FIXME: physics gets disabled somehow
   BecomeActive( TH_PHYSICS );

   // If the player is dead then only run physics on new
   // frames since articulated figures are not synchronized over the network
   if ( health <= 0 ) {
      if ( gameLocal.isNewFrame ) {
         DeathPush();
         RunPhysics();
      }
   } else {
      RunPhysics();
   }

   // update our last valid AAS location for the AI
   // sb - AAS always on Fake Clients for TargetFilter - lets talk about how to eliminate the need for this, i don't want to leave it
   SetAASLocation();

   if ( gameLocal.isNewFrame ) {
      if ( spectating ) {
         newEyeOffset = 0.0f;
      } else if ( health <= 0 ) {
         newEyeOffset = pm_deadviewheight.GetFloat();
      } else if ( physicsObj.IsCrouching() ) {
         newEyeOffset = pm_crouchviewheight.GetFloat();
      }
      else {
         newEyeOffset = pm_normalviewheight.GetFloat();
      }

      if ( EyeHeight() != newEyeOffset ) {
         if ( spectating ) {
            SetEyeHeight( newEyeOffset );
         } else {
            // smooth out duck height changes
            SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
         }
      }
   }
   if ( noclip ) {
      pfl.crouch      = false;
      pfl.onGround   = false;
      pfl.jump      = false;
   } else {
      pfl.onGround   = physicsObj.HasGroundContacts();
      pfl.crouch   = physicsObj.IsCrouching();
      pfl.jump      = physicsObj.HasJumped();
   }

   if ( pfl.jump ) {
      loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
      currentLoggedAccel++;
      acc->time = gameLocal.time;
      acc->dir[2] = 200;
      acc->dir[0] = acc->dir[1] = 0;
   }

   BobCycle( pushVelocity );

   if( !noclip) {
      CrashLand( oldOrigin, oldVelocity );
   }
}


Code:
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
   idVec3      origin, velocity;
   idVec3      gravityVector, gravityNormal;
   float      delta;
   float      dist;
   float      vel, acc;
   float      t;
   float      a, b, c, den;
   waterLevel_t waterLevel;
   bool      noDamage;

   pfl.softLanding = false;
   pfl.hardLanding = false;

   // if the player is not on the ground
   if ( !physicsObj.HasGroundContacts() ) {
      return;
   }

   gravityNormal = physicsObj.GetGravityNormal();

   // if the player wasn't going down
   if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
      return;
   }

   waterLevel = physicsObj.GetWaterLevel();

   // never take falling damage if completely underwater
   if ( waterLevel == WATERLEVEL_HEAD ) {
      return;
   }


   // no falling damage if touching a nodamage surface
   noDamage = false;
   for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
      const contactInfo_t &contact = physicsObj.GetContact( i );
      if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
         noDamage = true;
         break;
      }
   }

   origin = GetPhysics()->GetOrigin();
   gravityVector = physicsObj.GetGravity();

   // calculate the exact velocity on landing
   dist = ( origin - oldOrigin ) * -gravityNormal;
   vel = oldVelocity * -gravityNormal;
   acc = -gravityVector.Length();

   a = acc / 2.0f;
   b = vel;
   c = -dist;

   den = b * b - 4.0f * a * c;
   if ( den < 0 ) {
      return;
   }
   t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );

   delta = vel + t * acc;
   delta = delta * delta * 0.0001;

   // reduce falling damage if there is standing water
   if ( waterLevel == WATERLEVEL_WAIST ) {
      delta *= 0.25f;
   }
   if ( waterLevel == WATERLEVEL_FEET ) {
      delta *= 0.5f;
   }

   if ( delta < 1.0f ) {
      return;
   }

   if ( gameLocal.time - lastFallDmgTime < 100 )
      return;

   lastFallDmgTime = gameLocal.time;

   // ddynerman: moved height delta selection to player def
   if ( delta > playerCfg.fatalFallDelta && playerCfg.fatalFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange = -32;
      landTime = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f );
      }
   } else if ( delta > playerCfg.hardFallDelta && playerCfg.hardFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange   = -24;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f );
      }
   } else if ( delta > playerCfg.softFallDelta && playerCfg.softFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -16;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f );
      }
   } else if ( delta > playerCfg.noFallDelta  && playerCfg.noFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -8;
      landTime   = gameLocal.time;
   } else {
      return;
   }
}



zhakal@Posted: Sun Oct 31, 2010 7:00 am :
When I fall down onto a jumppad from solid ground the jumppad sound and fx get triggered.
But if i jump up and down on the jumppad i will after the first hit, suddenly start to land on the ground, trigger the crashland animation and sound before the jumppad gets triggered and throws me back up. So every second jump is a land.

So basically what i need help with is how to jump up and down on a jumppad without triggering a landing (crashland in player.cpp).

I can alter the fall deltas but then normal landing sound won't play when you jump.



zhakal@Posted: Tue Nov 02, 2010 11:24 pm :
First part shows the move () and second crashland ()

Code:
void idPlayer::Move( void ) {
   float newEyeOffset;
   idVec3 oldOrigin;
   idVec3 oldVelocity;
   idVec3 pushVelocity;

   // save old origin and velocity for crashlanding
   oldOrigin = physicsObj.GetOrigin();
   oldVelocity = physicsObj.GetLinearVelocity();
   pushVelocity = physicsObj.GetPushedLinearVelocity();

   // set physics variables
   physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
   physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );

   if ( noclip ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_NOCLIP );
   } else if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_SPECTATOR );
   } else if ( health <= 0 ) {
      physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
      physicsObj.SetMovementType( PM_DEAD );
   } else if ( gameLocal.GetCamera() || gameLocal.timeoutData.isTimeout ) { // XBM: timeout stuff (kaffeedoktor)
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_FREEZE );
   } else {
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_NORMAL );
   }

   if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else if ( health <= 0 ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else {
      physicsObj.SetClipMask( MASK_PLAYERSOLID );
   }

   physicsObj.SetPlayerInput( usercmd, viewAngles );

   // FIXME: physics gets disabled somehow
   BecomeActive( TH_PHYSICS );

   // If the player is dead then only run physics on new
   // frames since articulated figures are not synchronized over the network
   if ( health <= 0 ) {
      if ( gameLocal.isNewFrame ) {
         DeathPush();
         RunPhysics();
      }
   } else {
      RunPhysics();
   }

   // update our last valid AAS location for the AI
   // sb - AAS always on Fake Clients for TargetFilter - lets talk about how to eliminate the need for this, i don't want to leave it
   SetAASLocation();

   if ( gameLocal.isNewFrame ) {
      if ( spectating ) {
         newEyeOffset = 0.0f;
      } else if ( health <= 0 ) {
         newEyeOffset = pm_deadviewheight.GetFloat();
      } else if ( physicsObj.IsCrouching() ) {
         newEyeOffset = pm_crouchviewheight.GetFloat();
      }
      else {
         newEyeOffset = pm_normalviewheight.GetFloat();
      }

      if ( EyeHeight() != newEyeOffset ) {
         if ( spectating ) {
            SetEyeHeight( newEyeOffset );
         } else {
            // smooth out duck height changes
            SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
         }
      }
   }
   if ( noclip ) {
      pfl.crouch      = false;
      pfl.onGround   = false;
      pfl.jump      = false;
   } else {
      pfl.onGround   = physicsObj.HasGroundContacts();
      pfl.crouch   = physicsObj.IsCrouching();
      pfl.jump      = physicsObj.HasJumped();
   }

   if ( pfl.jump ) {
      loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
      currentLoggedAccel++;
      acc->time = gameLocal.time;
      acc->dir[2] = 200;
      acc->dir[0] = acc->dir[1] = 0;
   }

   BobCycle( pushVelocity );

   if( !noclip) {
      CrashLand( oldOrigin, oldVelocity );
   }
}


Code:
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
   idVec3      origin, velocity;
   idVec3      gravityVector, gravityNormal;
   float      delta;
   float      dist;
   float      vel, acc;
   float      t;
   float      a, b, c, den;
   waterLevel_t waterLevel;
   bool      noDamage;

   pfl.softLanding = false;
   pfl.hardLanding = false;

   // if the player is not on the ground
   if ( !physicsObj.HasGroundContacts() ) {
      return;
   }

   gravityNormal = physicsObj.GetGravityNormal();

   // if the player wasn't going down
   if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
      return;
   }

   waterLevel = physicsObj.GetWaterLevel();

   // never take falling damage if completely underwater
   if ( waterLevel == WATERLEVEL_HEAD ) {
      return;
   }


   // no falling damage if touching a nodamage surface
   noDamage = false;
   for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
      const contactInfo_t &contact = physicsObj.GetContact( i );
      if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
         noDamage = true;
         break;
      }
   }

   origin = GetPhysics()->GetOrigin();
   gravityVector = physicsObj.GetGravity();

   // calculate the exact velocity on landing
   dist = ( origin - oldOrigin ) * -gravityNormal;
   vel = oldVelocity * -gravityNormal;
   acc = -gravityVector.Length();

   a = acc / 2.0f;
   b = vel;
   c = -dist;

   den = b * b - 4.0f * a * c;
   if ( den < 0 ) {
      return;
   }
   t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );

   delta = vel + t * acc;
   delta = delta * delta * 0.0001;

   // reduce falling damage if there is standing water
   if ( waterLevel == WATERLEVEL_WAIST ) {
      delta *= 0.25f;
   }
   if ( waterLevel == WATERLEVEL_FEET ) {
      delta *= 0.5f;
   }

   if ( delta < 1.0f ) {
      return;
   }

   if ( gameLocal.time - lastFallDmgTime < 100 )
      return;

   lastFallDmgTime = gameLocal.time;

   // ddynerman: moved height delta selection to player def
   if ( delta > playerCfg.fatalFallDelta && playerCfg.fatalFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange = -32;
      landTime = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f );
      }
   } else if ( delta > playerCfg.hardFallDelta && playerCfg.hardFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange   = -24;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f );
      }
   } else if ( delta > playerCfg.softFallDelta && playerCfg.softFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -16;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f );
      }
   } else if ( delta > playerCfg.noFallDelta  && playerCfg.noFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -8;
      landTime   = gameLocal.time;
   } else {
      return;
   }
}



zhakal@Posted: Sun Oct 31, 2010 7:00 am :
When I fall down onto a jumppad from solid ground the jumppad sound and fx get triggered.
But if i jump up and down on the jumppad i will after the first hit, suddenly start to land on the ground, trigger the crashland animation and sound before the jumppad gets triggered and throws me back up. So every second jump is a land.

So basically what i need help with is how to jump up and down on a jumppad without triggering a landing (crashland in player.cpp).

I can alter the fall deltas but then normal landing sound won't play when you jump.



zhakal@Posted: Tue Nov 02, 2010 11:24 pm :
First part shows the move () and second crashland ()

Code:
void idPlayer::Move( void ) {
   float newEyeOffset;
   idVec3 oldOrigin;
   idVec3 oldVelocity;
   idVec3 pushVelocity;

   // save old origin and velocity for crashlanding
   oldOrigin = physicsObj.GetOrigin();
   oldVelocity = physicsObj.GetLinearVelocity();
   pushVelocity = physicsObj.GetPushedLinearVelocity();

   // set physics variables
   physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
   physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );

   if ( noclip ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_NOCLIP );
   } else if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetContents( 0 );
      physicsObj.SetMovementType( PM_SPECTATOR );
   } else if ( health <= 0 ) {
      physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
      physicsObj.SetMovementType( PM_DEAD );
   } else if ( gameLocal.GetCamera() || gameLocal.timeoutData.isTimeout ) { // XBM: timeout stuff (kaffeedoktor)
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_FREEZE );
   } else {
      physicsObj.SetContents( CONTENTS_BODY | CONTENTS_SOLID );
      physicsObj.SetMovementType( PM_NORMAL );
   }

   if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else if ( health <= 0 ) {
      physicsObj.SetClipMask( MASK_DEADSOLID );
   } else {
      physicsObj.SetClipMask( MASK_PLAYERSOLID );
   }

   physicsObj.SetPlayerInput( usercmd, viewAngles );

   // FIXME: physics gets disabled somehow
   BecomeActive( TH_PHYSICS );

   // If the player is dead then only run physics on new
   // frames since articulated figures are not synchronized over the network
   if ( health <= 0 ) {
      if ( gameLocal.isNewFrame ) {
         DeathPush();
         RunPhysics();
      }
   } else {
      RunPhysics();
   }

   // update our last valid AAS location for the AI
   // sb - AAS always on Fake Clients for TargetFilter - lets talk about how to eliminate the need for this, i don't want to leave it
   SetAASLocation();

   if ( gameLocal.isNewFrame ) {
      if ( spectating ) {
         newEyeOffset = 0.0f;
      } else if ( health <= 0 ) {
         newEyeOffset = pm_deadviewheight.GetFloat();
      } else if ( physicsObj.IsCrouching() ) {
         newEyeOffset = pm_crouchviewheight.GetFloat();
      }
      else {
         newEyeOffset = pm_normalviewheight.GetFloat();
      }

      if ( EyeHeight() != newEyeOffset ) {
         if ( spectating ) {
            SetEyeHeight( newEyeOffset );
         } else {
            // smooth out duck height changes
            SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
         }
      }
   }
   if ( noclip ) {
      pfl.crouch      = false;
      pfl.onGround   = false;
      pfl.jump      = false;
   } else {
      pfl.onGround   = physicsObj.HasGroundContacts();
      pfl.crouch   = physicsObj.IsCrouching();
      pfl.jump      = physicsObj.HasJumped();
   }

   if ( pfl.jump ) {
      loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
      currentLoggedAccel++;
      acc->time = gameLocal.time;
      acc->dir[2] = 200;
      acc->dir[0] = acc->dir[1] = 0;
   }

   BobCycle( pushVelocity );

   if( !noclip) {
      CrashLand( oldOrigin, oldVelocity );
   }
}


Code:
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
   idVec3      origin, velocity;
   idVec3      gravityVector, gravityNormal;
   float      delta;
   float      dist;
   float      vel, acc;
   float      t;
   float      a, b, c, den;
   waterLevel_t waterLevel;
   bool      noDamage;

   pfl.softLanding = false;
   pfl.hardLanding = false;

   // if the player is not on the ground
   if ( !physicsObj.HasGroundContacts() ) {
      return;
   }

   gravityNormal = physicsObj.GetGravityNormal();

   // if the player wasn't going down
   if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
      return;
   }

   waterLevel = physicsObj.GetWaterLevel();

   // never take falling damage if completely underwater
   if ( waterLevel == WATERLEVEL_HEAD ) {
      return;
   }


   // no falling damage if touching a nodamage surface
   noDamage = false;
   for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
      const contactInfo_t &contact = physicsObj.GetContact( i );
      if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
         noDamage = true;
         break;
      }
   }

   origin = GetPhysics()->GetOrigin();
   gravityVector = physicsObj.GetGravity();

   // calculate the exact velocity on landing
   dist = ( origin - oldOrigin ) * -gravityNormal;
   vel = oldVelocity * -gravityNormal;
   acc = -gravityVector.Length();

   a = acc / 2.0f;
   b = vel;
   c = -dist;

   den = b * b - 4.0f * a * c;
   if ( den < 0 ) {
      return;
   }
   t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );

   delta = vel + t * acc;
   delta = delta * delta * 0.0001;

   // reduce falling damage if there is standing water
   if ( waterLevel == WATERLEVEL_WAIST ) {
      delta *= 0.25f;
   }
   if ( waterLevel == WATERLEVEL_FEET ) {
      delta *= 0.5f;
   }

   if ( delta < 1.0f ) {
      return;
   }

   if ( gameLocal.time - lastFallDmgTime < 100 )
      return;

   lastFallDmgTime = gameLocal.time;

   // ddynerman: moved height delta selection to player def
   if ( delta > playerCfg.fatalFallDelta && playerCfg.fatalFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange = -32;
      landTime = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f );
      }
   } else if ( delta > playerCfg.hardFallDelta && playerCfg.hardFallDelta > 0.0f ) {
      pfl.hardLanding = true;
      landChange   = -24;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f );
      }
   } else if ( delta > playerCfg.softFallDelta && playerCfg.softFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -16;
      landTime   = gameLocal.time;
      if ( !noDamage ) {
         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f );
      }
   } else if ( delta > playerCfg.noFallDelta  && playerCfg.noFallDelta > 0.0f ) {
      pfl.softLanding = true;
      landChange   = -8;
      landTime   = gameLocal.time;
   } else {
      return;
   }
}