]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_weaponsystem.qc
Use only WR_SWITCHABLE to check if we can select our weapon. The ammo checks still...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_weaponsystem.qc
index 907f1788196e56adc947c9a0180fb00694231790..a6841d2d785d5eed7f7976644e98032cf1c5b497 100644 (file)
@@ -19,13 +19,13 @@ float W_WeaponRateFactor()
                if(self.runes & RUNE_SPEED)
                {
                        if(self.runes & CURSE_SLOW)
-                               t = t * cvar("g_balance_rune_speed_combo_atkrate");
+                               t = t * autocvar_g_balance_rune_speed_combo_atkrate;
                        else
-                               t = t * cvar("g_balance_rune_speed_atkrate");
+                               t = t * autocvar_g_balance_rune_speed_atkrate;
                }
                else if(self.runes & CURSE_SLOW)
                {
-                       t = t * cvar("g_balance_curse_slow_atkrate");
+                       t = t * autocvar_g_balance_curse_slow_atkrate;
                }
        }
 
@@ -132,13 +132,13 @@ vector w_shotend;
 // make sure you call makevectors first (FIXME?)
 .float prevstrengthsound;
 .float prevstrengthsoundattempt;
-void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float maxdamage, float range)
+void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range)
 {
        float nudge = 1; // added to traceline target and subtracted from result
        local float oldsolid;
        vector vecs, dv;
        oldsolid = ent.dphitcontentsmask;
-       if(ent.weapon == WEP_CAMPINGRIFLE)
+       if(ent.weapon == WEP_SNIPERRIFLE)
                ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
        else
                ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
@@ -158,15 +158,13 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
        v_right = vr;
        v_up = vu;
 
+       // un-adjust trueaim if shotend is too close
+       if(vlen(w_shotend - (ent.origin + ent.view_ofs)) < autocvar_g_trueaim_minrange)
+               w_shotend = ent.origin + ent.view_ofs + s_forward * autocvar_g_trueaim_minrange;
+
        // track max damage
-       if not(inWarmupStage) {
-               entity w;
-               w = get_weaponinfo(ent.weapon);
-               if(w.spawnflags & WEP_TYPE_SPLASH) {  // splash damage
-                       ent.stats_fired[ent.weapon - 1] += maxdamage;
-                       ent.stat_fired = ent.weapon + 64 * floor(ent.stats_fired[ent.weapon - 1]);
-               }
-       }
+       if(accuracy_canbegooddamage(ent))
+               accuracy_add(ent, ent.weapon, maxdamage, 0);
 
        W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
 
@@ -197,7 +195,7 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
        if (antilag)
        if (!ent.cvar_cl_noantilag)
        {
-               if (cvar("g_antilag") == 1) // switch to "ghost" if not hitting original
+               if (autocvar_g_antilag == 1) // switch to "ghost" if not hitting original
                {
                        traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent);
                        if (!trace_ent.takedamage)
@@ -213,7 +211,7 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
                                }
                        }
                }
-               else if(cvar("g_antilag") == 3) // client side hitscan
+               else if(autocvar_g_antilag == 3) // client side hitscan
                {
                        // this part MUST use prydon cursor
                        if (ent.cursor_trace_ent)                 // client was aiming at someone
@@ -243,16 +241,14 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
                ent.punchangle_x = recoil * -1;
 
        if (snd != "")
-       {
-               sound (ent, CHAN_WEAPON, snd, VOL_BASE, ATTN_NORM);
-       }
+               sound (ent, chan, snd, VOL_BASE, ATTN_NORM);
 
        if(ent.items & IT_STRENGTH)
        if(!g_minstagib)
        if(
-               (time > ent.prevstrengthsound + cvar("sv_strengthsound_antispam_time"))
+               (time > ent.prevstrengthsound + autocvar_sv_strengthsound_antispam_time)
                ||
-               (time > ent.prevstrengthsoundattempt + cvar("sv_strengthsound_antispam_refire_threshold"))
+               (time > ent.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)
        ) // prevent insane sound spam
        {
                sound(ent, CHAN_AUTO, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM);
@@ -264,11 +260,11 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m
        w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge;
 };
 
-#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, maxdamage, MAX_SHOT_DISTANCE)
-#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, maxdamage)
-#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage)
-#define W_SetupShot(ent,antilag,recoil,snd,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage)
-#define W_SetupShot_Range(ent,antilag,recoil,snd,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, maxdamage, range)
+#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE)
+#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range)
 
 float CL_Weaponentity_CustomizeEntityForClient()
 {
@@ -611,12 +607,14 @@ void CL_Weaponentity_Think()
        else
                self.alpha = 1;
 
+       self.glowmod = self.owner.weaponentity_glowmod;
        self.colormap = self.owner.colormap;
        if (self.weaponentity)
        {
                self.weaponentity.effects = self.effects;
                self.weaponentity.alpha = self.alpha;
                self.weaponentity.colormap = self.colormap;
+               self.weaponentity.glowmod = self.glowmod;
        }
 
        self.angles = '0 0 0';
@@ -624,7 +622,7 @@ void CL_Weaponentity_Think()
        f = 0;
        if (self.state == WS_RAISE && !intermission_running)
        {
-               f = (self.owner.weapon_nextthink - time) * g_weaponratefactor / cvar("g_balance_weaponswitchdelay");
+               f = (self.owner.weapon_nextthink - time) * g_weaponratefactor / autocvar_g_balance_weaponswitchdelay;
                self.angles_x = -90 * f * f;
                if (qcweaponanimation)
                {
@@ -634,7 +632,7 @@ void CL_Weaponentity_Think()
        }
        else if (self.state == WS_DROP && !intermission_running)
        {
-               f = 1 - (self.owner.weapon_nextthink - time) * g_weaponratefactor / cvar("g_balance_weaponswitchdelay");
+               f = 1 - (self.owner.weapon_nextthink - time) * g_weaponratefactor / autocvar_g_balance_weaponswitchdelay;
                self.angles_x = -90 * f * f;
                if (qcweaponanimation)
                {
@@ -855,6 +853,7 @@ void CL_ExteriorWeaponentity_Think()
                self.angles = ang;
        }
 
+       self.glowmod = self.owner.weaponentity_glowmod;
        self.colormap = self.owner.colormap;
 };
 
@@ -926,8 +925,13 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain)
                        {
                                oldself = self;
                                self = cl;
-                               f = weapon_action(wpn, WR_CHECKAMMO1);
-                               f = f + weapon_action(wpn, WR_CHECKAMMO2);
+                               f = weapon_action(wpn, WR_SWITCHABLE);
+
+                               // allow switching to reloadable weapons, even if we're out of ammo, since the weapon itself
+                               // might still be loaded. The reload code takes care of complaining and forced switching
+                               entity e;
+                               e = get_weaponinfo(wpn);
+                               if(wpn != WEP_TUBA && wpn != WEP_PORTO && wpn != WEP_HOOK) // skip non-reloadable weapons, or we access undefined cvars
 
                                // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
                                local entity mine;
@@ -960,7 +964,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain)
                        sprint(cl, strcat("You do not have the ^2", W_Name(wpn), "\n") );
                        Send_WeaponComplain (cl, wpn, W_Name(wpn), 1);
 
-                       if(cvar("g_showweaponspawns"))
+                       if(autocvar_g_showweaponspawns)
                        {
                                entity e;
                                string s;
@@ -1019,7 +1023,7 @@ void w_ready()
 void weapon_setup(float windex)
 {
        entity e;
-       qcweaponanimation = cvar("sv_qcweaponanimation");
+       qcweaponanimation = autocvar_sv_qcweaponanimation;
        e = get_weaponinfo(windex);
        self.items &~= IT_AMMO;
        self.items = self.items | e.items;
@@ -1042,19 +1046,28 @@ void W_SwitchToOtherWeapon(entity pl)
        if(ww)
                W_SwitchWeapon_Force(pl, ww);
 }
+.float prevdryfire;
 float weapon_prepareattack_checkammo(float secondary)
 {
        if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
        if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
        {
+               // always allow reloadable weapons, even if we're out of ammo, since the weapon itself
+               // could still be loaded. The reload code takes care of the rest
+               if(weapon_action(self.weapon, WR_SWITCHABLE))
+                       return FALSE;
+
                // always keep the Mine Layer if we placed mines, so that we can detonate them
                local entity mine;
                if(self.weapon == WEP_MINE_LAYER)
                for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
                        return FALSE;
 
-               if(self.weapon == self.switchweapon) // only play once BEFORE starting to switch weapons
+               if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
+               {
                        sound (self, CHAN_AUTO, "weapons/dryfire.wav", VOL_BASE, ATTN_NORM);
+                       self.prevdryfire = time;
+               }
 
                W_SwitchToOtherWeapon(self);
                return FALSE;
@@ -1266,7 +1279,7 @@ void weapon_boblayer1(float spd, vector org)
        // VorteX: haste can be added here
 };
 
-vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity)
+vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute)
 {
        vector mdirection;
        float mspeed;
@@ -1279,8 +1292,8 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity)
        mdirection = normalize(mvelocity);
        mspeed = vlen(mvelocity);
 
-       nstyle = cvar("g_projectiles_newton_style");
-       if(nstyle == 0)
+       nstyle = autocvar_g_projectiles_newton_style;
+       if(nstyle == 0 || forceAbsolute)
        {
                // absolute velocity
                outvelocity = mvelocity;
@@ -1316,7 +1329,7 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity)
                        //dprint("impossible shot, adjusting\n");
                }
 
-               outspeed = bound(mspeed * cvar("g_projectiles_newton_style_2_minfactor"), outspeed, mspeed * cvar("g_projectiles_newton_style_2_maxfactor"));
+               outspeed = bound(mspeed * autocvar_g_projectiles_newton_style_2_minfactor, outspeed, mspeed * autocvar_g_projectiles_newton_style_2_maxfactor);
                outvelocity = mdirection * outspeed;
        }
        else if(nstyle == 3)
@@ -1459,7 +1472,7 @@ vector W_CalculateProjectileSpread(vector forward, float spread)
        spread *= g_weaponspreadfactor;
        if(spread <= 0)
                return forward;
-       sstyle = cvar("g_projectiles_spread_style");
+       sstyle = autocvar_g_projectiles_spread_style;
        
        if(sstyle == 0)
        {
@@ -1573,7 +1586,7 @@ float mspercallsum;
 float mspercallsstyle;
 float mspercallcount;
 #endif
-void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread)
+void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
 {
        if(missile.owner == world)
                error("Unowned missile");
@@ -1584,10 +1597,10 @@ void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float
        dir = normalize(dir);
 
 #if 0
-       if(cvar("g_projectiles_spread_style") != mspercallsstyle)
+       if(autocvar_g_projectiles_spread_style != mspercallsstyle)
        {
                mspercallsum = mspercallcount = 0;
-               mspercallsstyle = cvar("g_projectiles_spread_style");
+               mspercallsstyle = autocvar_g_projectiles_spread_style;
        }
        mspercallsum -= gettime(GETTIME_HIRES);
 #endif
@@ -1598,13 +1611,54 @@ void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float
        print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n");
 #endif
 
-       missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir);
+       missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute);
 }
 
 void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread)
 {
-       W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread);
+       W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE);
 }
 
-#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"))
-#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"))
+#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"), FALSE)
+#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"), FALSE)
+
+// shared weapon reload code
+.float reload_complain;
+float W_ReloadCheck(float ammo_amount, float ammo_shot)
+{
+       entity e;
+       e = get_weaponinfo(self.weapon);
+
+       // our weapon is fully loaded, no need to reload
+       if (self.clip_load >= cvar(strcat("g_balance_", e.netname, "_reload_ammo")))
+               return 0;
+
+       // no ammo, so nothing to load
+       if(!ammo_amount)
+       {
+               if(clienttype(self) == CLIENTTYPE_REAL && self.reload_complain < time)
+               {
+                       play2(self, "weapons/unavailable.wav");
+                       sprint(self, strcat("You don't have enough ammo to reload the ^2", W_Name(self.weapon), "\n"));
+                       self.reload_complain = time + 1;
+               }
+               // switch away if the loaded amount of ammo is not enough to keep using this weapon
+               if(self.clip_load < ammo_shot)
+               {
+                       self.clip_load = -1; // reload later
+                       W_SwitchToOtherWeapon(self);
+               }
+               return 0;
+       }
+
+       if (self.weaponentity)
+       {
+               if (self.weaponentity.wframe == WFRAME_RELOAD)
+                       return 0;
+
+               // allow switching away while reloading, but this will cause a new reload!
+               self.weaponentity.state = WS_READY;
+       }
+
+       return 1;
+}
\ No newline at end of file