Merge branch 'martin-t/jump' into 'master'
authorMario <mario.mario@y7mail.com>
Fri, 11 Sep 2020 08:31:01 +0000 (08:31 +0000)
committerMario <mario.mario@y7mail.com>
Fri, 11 Sep 2020 08:31:01 +0000 (08:31 +0000)
Fix bug allowing players to avoid falldamage (tiny MR)

See merge request xonotic/xonotic-data.pk3dir!497

1  2 
qcsrc/common/physics/player.qc

index ade9c4d6f8acc5a90f0411b680ab4ab34467dcf5,21c216b39e9236f71bd12407cf9e8c6824dc22aa..7e63442ca2bea6fb7bfeffb4b02f32107c9139b4
@@@ -1,35 -1,28 +1,35 @@@
  #include "player.qh"
 -#include "../triggers/include.qh"
 +#include "../mapobjects/_mod.qh"
  #include "../viewloc.qh"
  
  #ifdef SVQC
  
 +#include <server/client.qh>
  #include <server/miscfunctions.qh>
 -#include "../triggers/trigger/viewloc.qh"
 +#include <common/mapobjects/defs.qh>
 +#include "../mapobjects/trigger/viewloc.qh"
 +#include <server/main.qh>
  
  // client side physics
  bool Physics_Valid(string thecvar)
  {
 -      return autocvar_g_physics_clientselect && thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar);
 +      return thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar);
  }
  
  float Physics_ClientOption(entity this, string option, float defaultval)
  {
 +      if(!autocvar_g_physics_clientselect)
 +              return defaultval;
 +
        if(IS_REAL_CLIENT(this) && Physics_Valid(CS(this).cvar_cl_physics))
        {
                string s = strcat("g_physics_", CS(this).cvar_cl_physics, "_", option);
                if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
                        return cvar(s);
        }
 -      if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default)
 +      if(autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "" && autocvar_g_physics_clientselect_default != "default")
        {
 +              // NOTE: not using Physics_Valid here, so the default can be forced to something normally unavailable
                string s = strcat("g_physics_", autocvar_g_physics_clientselect_default, "_", option);
                if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
                        return cvar(s);
@@@ -43,37 -36,27 +43,37 @@@ void Physics_UpdateStats(entity this
        STAT(MOVEVARS_HIGHSPEED, this) = autocvar_g_movement_highspeed;
  
        MUTATOR_CALLHOOK(PlayerPhysics_UpdateStats, this);
 -      float maxspd_mod = PHYS_HIGHSPEED(this);
 -
 -      STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw), maxspd_mod);
 -      STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw))
 -              ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw), maxspd_mod)
 -              : 0;
 -      STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
 -      STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed", autocvar_sv_maxspeed) * maxspd_mod; // also slow walking
 -
 -      STAT(PL_MIN, this) = autocvar_sv_player_mins;
 -      STAT(PL_MAX, this) = autocvar_sv_player_maxs;
 -      STAT(PL_VIEW_OFS, this) = autocvar_sv_player_viewoffset;
 -      STAT(PL_CROUCH_MIN, this) = autocvar_sv_player_crouch_mins;
 -      STAT(PL_CROUCH_MAX, this) = autocvar_sv_player_crouch_maxs;
 -      STAT(PL_CROUCH_VIEW_OFS, this) = autocvar_sv_player_crouch_viewoffset;
 +      float maxspd_mod = PHYS_HIGHSPEED(this) * ((this.swampslug.active == ACTIVE_ACTIVE) ? this.swampslug.swamp_slowdown : 1);
 +        STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed", autocvar_sv_maxspeed) * maxspd_mod; // also slow walking
 +        if (autocvar_g_movement_highspeed_q3_compat) {
 +          STAT(MOVEVARS_AIRACCEL_QW, this) = Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw);
 +          STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw);
 +          STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw);
 +        } else {
 +          STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw), maxspd_mod);
 +          STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw))
 +            ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw), maxspd_mod)
 +            : 0;
 +          STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
 +        }
 +      bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox; // NOTE: these hitboxes are off by 1 due to engine differences
 +      STAT(PL_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_mins;
 +      STAT(PL_MAX, this) = (q3dfcompat) ? '15 15 36' : autocvar_sv_player_maxs;
 +      STAT(PL_VIEW_OFS, this) = (q3dfcompat) ? '0 0 30' : autocvar_sv_player_viewoffset;
 +      STAT(PL_CROUCH_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_crouch_mins;
 +      STAT(PL_CROUCH_MAX, this) = (q3dfcompat) ? '15 15 20' : autocvar_sv_player_crouch_maxs;
 +      STAT(PL_CROUCH_VIEW_OFS, this) = (q3dfcompat) ? '0 0 16' : autocvar_sv_player_crouch_viewoffset;
  
        // old stats
        // fix some new settings
        STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor", autocvar_sv_airaccel_qw_stretchfactor);
        STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed", autocvar_sv_maxairstrafespeed);
 -      STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed", autocvar_sv_maxairspeed);
 +        if (autocvar_g_movement_highspeed_q3_compat) {
 +          STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed", autocvar_sv_maxairspeed) * maxspd_mod;
 +        } else {
 +          STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed", autocvar_sv_maxairspeed);
 +        }
 +
        STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate", autocvar_sv_airstrafeaccelerate);
        STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel", autocvar_sv_warsowbunny_turnaccel);
        STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction", autocvar_sv_airaccel_sideways_friction);
        STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate);
        STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate);
        STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
 +      STAT(MOVEVARS_JUMPVELOCITY_CROUCH, this) = Physics_ClientOption(this, "jumpvelocity_crouch", autocvar_sv_jumpvelocity_crouch);
        STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
 +
 +      MUTATOR_CALLHOOK(PlayerPhysics_PostUpdateStats, this, maxspd_mod);
  }
  #endif
  
@@@ -117,57 -97,46 +117,57 @@@ float GeomLerp(float a, float _lerp, fl
  
  void PM_ClientMovement_UpdateStatus(entity this)
  {
 -#ifdef CSQC
        if(!IS_PLAYER(this))
                return;
  
 -      // set crouched
 -      bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
 +      bool have_hook = false;
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
 -              entity wep = viewmodels[slot];
 -              if(wep.hook && !wasfreed(wep.hook))
 +      #if defined(CSQC)
 +              entity wepent = viewmodels[slot];
 +      #elif defined(SVQC)
 +              .entity weaponentity = weaponentities[slot];
 +              entity wepent = this.(weaponentity);
 +      #endif
 +              if(wepent.hook && !wasfreed(wepent.hook))
                {
 -                      do_crouch = false;
 -                      break; // don't bother checking the others
 +                      have_hook = true;
 +                      break;
                }
        }
 -      if(this.waterlevel >= WATERLEVEL_SWIMMING)
 +      bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
 +      if(this.viewloc && !(this.viewloc.spawnflags & VIEWLOC_FREEMOVE) && PHYS_CS(this).movement.x < 0)
 +              do_crouch = true;
 +      if (have_hook) {
                do_crouch = false;
 -      if(hud != HUD_NORMAL)
 +      //} else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
 +              //do_crouch = false;
 +      } else if (PHYS_INVEHICLE(this)) {
                do_crouch = false;
 -      if(STAT(FROZEN, this))
 +      } else if (STAT(FROZEN, this) || IS_DEAD(this)) {
                do_crouch = false;
 +    }
  
 -      if (do_crouch)
 -      {
 -              // wants to crouch, this always works
 -              if (!IS_DUCKED(this)) SET_DUCKED(this);
 -      }
 -      else
 -      {
 -              // wants to stand, if currently crouching we need to check for a low ceiling first
 -              if (IS_DUCKED(this))
 -              {
 -                      tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this);
 -                      if (!trace_startsolid) UNSET_DUCKED(this);
 +    MUTATOR_CALLHOOK(PlayerCanCrouch, this, do_crouch);
 +    do_crouch = M_ARGV(1, bool);
 +
 +      if (do_crouch) {
 +              if (!IS_DUCKED(this)) {
 +                      SET_DUCKED(this);
 +                      this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
 +                      setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
 +                      // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
                }
 +      } else if (IS_DUCKED(this)) {
 +        tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
 +        if (!trace_startsolid) {
 +            UNSET_DUCKED(this);
 +            this.view_ofs = STAT(PL_VIEW_OFS, this);
 +            setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
 +        }
        }
  
 -      if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0)
 -              PHYS_WATERJUMP_TIME(this) = 0;
 -#endif
 +      _Movetype_CheckWater(this); // needs to be run on the client, might as well use the latest on the server too!
  }
  
  void CPM_PM_Aircontrol(entity this, float dt, vector wishdir, float wishspeed)
@@@ -328,16 -297,13 +328,16 @@@ bool PlayerJump(entity this
        if (PHYS_FROZEN(this))
                return true; // no jumping in freezetag when frozen
  
 +      if(PHYS_INPUT_BUTTON_CHAT(this) || PHYS_INPUT_BUTTON_MINIGAME(this))
 +              return true; // no jumping while typing
 +
  #ifdef SVQC
        if (this.player_blocked)
                return true; // no jumping while blocked
  #endif
  
        bool doublejump = false;
 -      float mjumpheight = PHYS_JUMPVELOCITY(this);
 +      float mjumpheight = ((PHYS_JUMPVELOCITY_CROUCH(this) && IS_DUCKED(this)) ? PHYS_JUMPVELOCITY_CROUCH(this) : PHYS_JUMPVELOCITY(this));
        bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
  
        if (MUTATOR_CALLHOOK(PlayerJump, this, mjumpheight, doublejump))
        }
  
        if (!doublejump)
 -              if (!IS_ONGROUND(this) && !IS_ONSLICK(this))
 +              if (!IS_ONGROUND(this))
                        return IS_JUMP_HELD(this);
  
        if(PHYS_TRACK_CANJUMP(this))
        SET_JUMP_HELD(this);
  
  #ifdef SVQC
-       this.oldvelocity_z = this.velocity_z;
        animdecide_setaction(this, ANIMACTION_JUMP, true);
  
        if (autocvar_g_jump_grunt)
@@@ -456,6 -419,11 +453,6 @@@ void CheckWaterJump(entity this
                        this.velocity_z = 225;
                        this.flags |= FL_WATERJUMP;
                        SET_JUMP_HELD(this);
 -              #ifdef SVQC
 -                      PHYS_TELEPORT_TIME(this) = time + 2;    // safety net
 -              #elif defined(CSQC)
 -                      PHYS_WATERJUMP_TIME(this) = 2;
 -              #endif
                }
        }
  }
@@@ -481,8 -449,8 +478,8 @@@ void CheckPlayerJump(entity this
                bool playerjump = PlayerJump(this); // required
  
                bool air_jump = !playerjump || M_ARGV(2, bool);
 -              bool activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this);
 -              bool has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || (ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO);
 +              bool activate = (JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this)) || PHYS_INPUT_BUTTON_JETPACK(this);
 +              bool has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || (ITEMS_STAT(this) & IT_UNLIMITED_AMMO);
  
                if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { }
                else if (this.jetpack_stopped) { }
@@@ -614,15 -582,27 +611,15 @@@ void PM_check_frozen(entity this
  {
        if (!PHYS_FROZEN(this))
                return;
 -      if (PHYS_DODGING_FROZEN(this)
 -#ifdef SVQC
 -      && IS_REAL_CLIENT(this)
 -#endif
 -      )
 +      if (PHYS_DODGING_FROZEN(this) && IS_CLIENT(this))
        {
 -              PHYS_CS(this).movement_x = bound(-5, PHYS_CS(this).movement.x, 5);
 -              PHYS_CS(this).movement_y = bound(-5, PHYS_CS(this).movement.y, 5);
 -              PHYS_CS(this).movement_z = bound(-5, PHYS_CS(this).movement.z, 5);
 +              // bind movement to a very slow speed so dodging can use .movement for directional calculations
 +              PHYS_CS(this).movement_x = bound(-2, PHYS_CS(this).movement.x, 2);
 +              PHYS_CS(this).movement_y = bound(-2, PHYS_CS(this).movement.y, 2);
 +              PHYS_CS(this).movement_z = bound(-2, PHYS_CS(this).movement.z, 2);
        }
        else
                PHYS_CS(this).movement = '0 0 0';
 -
 -      vector midpoint = ((this.absmin + this.absmax) * 0.5);
 -      if (pointcontents(midpoint) == CONTENT_WATER)
 -      {
 -              this.velocity = this.velocity * 0.5;
 -
 -              if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
 -                      this.velocity_z = 200;
 -      }
  }
  
  void PM_check_hitground(entity this)
        if (!this.wasFlying) return;
      this.wasFlying = false;
      if (this.waterlevel >= WATERLEVEL_SWIMMING) return;
 -    if (time < this.ladder_time) return;
 +    if (this.ladder_entity) return;
      for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
      {
        .entity weaponentity = weaponentities[slot];
@@@ -676,18 -656,21 +673,18 @@@ void PM_check_slick(entity this
        if(!IS_ONGROUND(this))
                return;
  
 -      if(!PHYS_SLICK_APPLYGRAVITY(this))
 -              return;
 -
 +      trace_dphitq3surfaceflags = 0;
        tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
        if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
 -      {
 -              UNSET_ONGROUND(this);
                SET_ONSLICK(this);
 -      }
        else
                UNSET_ONSLICK(this);
  }
  
  void PM_check_blocked(entity this)
  {
 +      if(PHYS_INPUT_BUTTON_CHAT(this) || PHYS_INPUT_BUTTON_MINIGAME(this))
 +              PHYS_CS(this).movement = '0 0 0';
  #ifdef SVQC
        if (!this.player_blocked)
                return;
@@@ -780,7 -763,7 +777,7 @@@ void PM_jetpack(entity this, float maxs
        wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this);
  
        fvel = min(1, vlen(wishvel) / best);
 -      if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
 +      if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_AMMO))
                f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * dt * fvel));
        else
                f = 1;
                UNSET_ONGROUND(this);
  
  #ifdef SVQC
 -              if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO))
 -                      this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f;
 +              if (!(ITEMS_STAT(this) & IT_UNLIMITED_AMMO))
 +                      TakeResource(this, RES_FUEL, PHYS_JETPACK_FUEL(this) * dt * fvel * f);
  
                ITEMS_STAT(this) |= IT_USING_JETPACK;
  
@@@ -811,8 -794,7 +808,8 @@@ bool IsFlying(entity this
                return false;
        if(this.waterlevel >= WATERLEVEL_SWIMMING)
                return false;
 -      traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
 +      tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 24', MOVE_NORMAL, this);
 +      //traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this);
        if(trace_fraction < 1)
                return false;
        return true;