]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/weapons/weaponsystem.qc
Merge branch 'master' into Mario/qc_updates
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / weaponsystem.qc
index 2586c2ee49a2c23dcdcae491efdb8911fb7f9511..ab85389c5c9dc675e84f4917e21a6c08cf47eb03 100644 (file)
@@ -1,3 +1,24 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../dpdefs/progsdefs.qh"
+    #include "../../dpdefs/dpextensions.qh"
+    #include "../../common/constants.qh"
+    #include "../../common/util.qh"
+    #include "../../common/animdecide.qh"
+    #include "../../common/monsters/monsters.qh"
+    #include "../../common/weapons/weapons.qh"
+    #include "weaponsystem.qh"
+    #include "../t_items.qh"
+    #include "../autocvars.qh"
+    #include "../defs.qh"
+    #include "../../common/notifications.qh"
+    #include "../mutators/mutators_include.qh"
+    #include "../command/common.qh"
+    #include "../../csqcmodellib/sv_model.qh"
+    #include "../round_handler.qh"
+#endif
+
 /*
 ===========================================================================
 
@@ -14,16 +35,13 @@ float W_WeaponRateFactor()
        float t;
        t = 1.0 / g_weaponratefactor;
 
+       weapon_rate = t;
+       MUTATOR_CALLHOOK(WeaponRateFactor);
+       t = weapon_rate;
+
        return t;
 }
 
-// VorteX: static frame globals
-const float WFRAME_DONTCHANGE = -1;
-const float WFRAME_FIRE1 = 0;
-const float WFRAME_FIRE2 = 1;
-const float WFRAME_IDLE = 2;
-const float WFRAME_RELOAD = 3;
-.float wframe;
 
 void(float fr, float t, void() func) weapon_thinkf;
 
@@ -33,7 +51,7 @@ float CL_Weaponentity_CustomizeEntityForClient()
        if(IS_SPEC(other))
                if(other.enemy == self.owner)
                        self.viewmodelforclient = other;
-       return TRUE;
+       return true;
 }
 
 /*
@@ -227,12 +245,12 @@ void CL_WeaponEntity_SetModel(string name)
 
        self.view_ofs = '0 0 0';
 
-       if(self.movedir_x >= 0)
+       if(self.movedir.x >= 0)
        {
                vector v0;
                v0 = self.movedir;
-               self.movedir = shotorg_adjust(v0, FALSE, FALSE);
-               self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
+               self.movedir = shotorg_adjust(v0, false, false);
+               self.view_ofs = shotorg_adjust(v0, false, true) - v0;
        }
        self.owner.stat_shotorg = compressShotOrigin(self.movedir);
        self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
@@ -243,7 +261,7 @@ void CL_WeaponEntity_SetModel(string name)
        setorigin(self, self.view_ofs);
        // reset animstate now
        self.wframe = WFRAME_IDLE;
-       setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
+       setanim(self, self.anim_idle, true, false, true);
 }
 
 vector CL_Weapon_GetShotOrg(float wpn)
@@ -263,10 +281,10 @@ vector CL_Weapon_GetShotOrg(float wpn)
 
 void CL_Weaponentity_Think()
 {
-       float tb;
+       int tb;
        self.nextthink = time;
        if (intermission_running)
-               self.frame = self.anim_idle_x;
+               self.frame = self.anim_idle.x;
        if (self.owner.weaponentity != self)
        {
                if (self.weaponentity)
@@ -316,20 +334,18 @@ void CL_Weaponentity_Think()
        }
 
        self.angles = '0 0 0';
-       
+
        float f = (self.owner.weapon_nextthink - time);
        if (self.state == WS_RAISE && !intermission_running)
        {
                entity newwep = get_weaponinfo(self.owner.switchweapon);
                f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
-               //print(sprintf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)), (self.owner.weapon_nextthink - time)));
                self.angles_x = -90 * f * f;
        }
        else if (self.state == WS_DROP && !intermission_running)
        {
                entity oldwep = get_weaponinfo(self.owner.weapon);
                f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
-               //print(sprintf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)), (self.owner.weapon_nextthink - time)));
                self.angles_x = -90 * f * f;
        }
        else if (self.state == WS_CLEAR)
@@ -447,14 +463,18 @@ void w_ready()
 .float prevwarntime;
 float weapon_prepareattack_checkammo(float secondary)
 {
-       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
        if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
        {
                // always keep the Mine Layer if we placed mines, so that we can detonate them
                entity mine;
                if(self.weapon == WEP_MINE_LAYER)
                for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
-                       return FALSE;
+                       return false;
+
+               if(self.weapon == WEP_SHOTGUN)
+               if(!secondary && WEP_CVAR(shotgun, secondary) == 1)
+                       return false; // no clicking, just allow
 
                if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
                {
@@ -482,41 +502,41 @@ float weapon_prepareattack_checkammo(float secondary)
                {
                        W_SwitchToOtherWeapon(self);
                }
-               
-               return FALSE;
+
+               return false;
        }
-       return TRUE;
+       return true;
 }
 .float race_penalty;
 float weapon_prepareattack_check(float secondary, float attacktime)
 {
        if(!weapon_prepareattack_checkammo(secondary))
-               return FALSE;
+               return false;
 
        //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
        //if all players readied up and the countdown is running
        if(time < game_starttime || time < self.race_penalty) {
-               return FALSE;
+               return false;
        }
 
        if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
-               return FALSE;
+               return false;
 
        // do not even think about shooting if switching
        if(self.switchweapon != self.weapon)
-               return FALSE;
+               return false;
 
        if(attacktime >= 0)
        {
                // don't fire if previous attack is not finished
                if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
-                       return FALSE;
+                       return false;
                // don't fire while changing weapon
                if (self.weaponentity.state != WS_READY)
-                       return FALSE;
+                       return false;
        }
 
-       return TRUE;
+       return true;
 }
 float weapon_prepareattack_do(float secondary, float attacktime)
 {
@@ -536,17 +556,17 @@ float weapon_prepareattack_do(float secondary, float attacktime)
        }
        self.bulletcounter += 1;
        //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
-       return TRUE;
+       return true;
 }
 float weapon_prepareattack(float secondary, float attacktime)
 {
        if(weapon_prepareattack_check(secondary, attacktime))
        {
                weapon_prepareattack_do(secondary, attacktime);
-               return TRUE;
+               return true;
        }
        else
-               return FALSE;
+               return false;
 }
 
 void weapon_thinkf(float fr, float t, void() func)
@@ -558,12 +578,12 @@ void weapon_thinkf(float fr, float t, void() func)
        if(fr == WFRAME_DONTCHANGE)
        {
                fr = self.weaponentity.wframe;
-               restartanim = FALSE;
+               restartanim = false;
        }
        else if (fr == WFRAME_IDLE)
-               restartanim = FALSE;
+               restartanim = false;
        else
-               restartanim = TRUE;
+               restartanim = true;
 
        of = v_forward;
        or = v_right;
@@ -581,8 +601,8 @@ void weapon_thinkf(float fr, float t, void() func)
                        a = self.weaponentity.anim_fire2;
                else // if (fr == WFRAME_RELOAD)
                        a = self.weaponentity.anim_reload;
-               a_z *= g_weaponratefactor;
-               setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
+               a.z *= g_weaponratefactor;
+               setanim(self.weaponentity, a, restartanim == false, restartanim, restartanim);
        }
 
        v_forward = of;
@@ -613,7 +633,7 @@ void weapon_thinkf(float fr, float t, void() func)
 
        if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
        {
-               if(self.weapon == WEP_SHOTGUN && fr == WFRAME_FIRE2)
+               if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2)
                        animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
                else
                        animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
@@ -633,7 +653,9 @@ float forbidWeaponUse()
                return 1;
        if(self.player_blocked)
                return 1;
-       if(self.freezetag_frozen)
+       if(self.frozen)
+               return 1;
+       if(self.weapon_blocked)
                return 1;
        return 0;
 }
@@ -661,7 +683,7 @@ void W_WeaponFrame()
                self.switchingweapon = 0;
                self.weaponentity.state = WS_CLEAR;
                self.weaponname = "";
-               self.items &= ~IT_AMMO;
+               //self.items &= ~IT_AMMO;
                return;
        }
 
@@ -679,13 +701,11 @@ void W_WeaponFrame()
                        self.switchingweapon = self.switchweapon;
                        entity newwep = get_weaponinfo(self.switchweapon);
 
-                       self.items &= ~IT_AMMO;
-                       self.items = self.items | (newwep.items & IT_AMMO);
-
                        // the two weapon entities will notice this has changed and update their models
                        self.weapon = self.switchweapon;
                        self.weaponname = newwep.mdl;
-                       self.bulletcounter = 0; // WEAPONTODO
+                       self.bulletcounter = 0;
+                       self.ammo_field = newwep.ammo_field;
                        WEP_ACTION(self.switchweapon, WR_SETUP);
                        self.weaponentity.state = WS_RAISE;
 
@@ -698,10 +718,7 @@ void W_WeaponFrame()
                        else
                                self.clip_load = self.clip_size = 0;
 
-                       // VorteX: add player model weapon select frame here
-                       // setcustomframe(PlayerWeaponRaise);
                        weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
-                       //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))));
                }
                else if (self.weaponentity.state == WS_DROP)
                {
@@ -712,21 +729,19 @@ void W_WeaponFrame()
                {
                        // start switching!
                        self.switchingweapon = self.switchweapon;
-
                        entity oldwep = get_weaponinfo(self.weapon);
-                       
-#ifndef INDEPENDENT_ATTACK_FINISHED
+
+                       // set up weapon switch think in the future, and start drop anim
+                       #ifndef INDEPENDENT_ATTACK_FINISHED
                        if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
                        {
-#endif
-                       sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
-                       self.weaponentity.state = WS_DROP;
-                       // set up weapon switch think in the future, and start drop anim
-                       weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
-                       //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))));
-#ifndef INDEPENDENT_ATTACK_FINISHED
+                       #endif
+                               sound(self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
+                               self.weaponentity.state = WS_DROP;
+                               weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
+                       #ifndef INDEPENDENT_ATTACK_FINISHED
                        }
-#endif
+                       #endif
                }
        }
 
@@ -774,19 +789,6 @@ void W_WeaponFrame()
                                bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
                }
        }
-
-#if 0
-       if (self.items & IT_CELLS)
-               self.currentammo = self.ammo_cells;
-       else if (self.items & IT_ROCKETS)
-               self.currentammo = self.ammo_rockets;
-       else if (self.items & IT_NAILS)
-               self.currentammo = self.ammo_nails;
-       else if (self.items & IT_SHELLS)
-               self.currentammo = self.ammo_shells;
-       else
-               self.currentammo = 1;
-#endif
 }
 
 void W_AttachToShotorg(entity flash, vector offset)
@@ -806,7 +808,7 @@ void W_AttachToShotorg(entity flash, vector offset)
 
        flash.viewmodelforclient = self;
 
-       if(self.weaponentity.oldorigin_x > 0)
+       if(self.weaponentity.oldorigin.x > 0)
        {
                setattachment(xflash, self.exteriorweaponentity, "");
                setorigin(xflash, self.weaponentity.oldorigin + offset);
@@ -821,19 +823,42 @@ void W_AttachToShotorg(entity flash, vector offset)
        }
 }
 
-void W_DecreaseAmmo(.float ammo_type, float ammo_use, float ammo_reload) // WEAPONTODO: why does this have ammo_type?
+void W_DecreaseAmmo(float ammo_use)
 {
-       if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !ammo_reload)
+       entity wep = get_weaponinfo(self.weapon);
+
+       if(cvar("g_overkill"))
+       if(self.ok_use_ammocharge)
+       {
+               ok_DecreaseCharge(self, self.weapon);
+               return; // TODO
+       }
+
+       if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo)
                return;
 
        // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
-       if(ammo_reload)
+       if(wep.reloading_ammo)
        {
                self.clip_load -= ammo_use;
                self.(weapon_load[self.weapon]) = self.clip_load;
        }
-       else
-               self.(self.current_ammo) -= ammo_use;
+       else if(wep.ammo_field != ammo_none)
+       {
+               self.(wep.ammo_field) -= ammo_use;
+               if(self.(wep.ammo_field) < 0)
+               {
+                       backtrace(sprintf(
+                               "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... "
+                               "Please notify Samual immediately with a copy of this backtrace!\n",
+                               ammo_use,
+                               wep.netname,
+                               GetAmmoPicture(wep.ammo_field),
+                               self.netname,
+                               self.(wep.ammo_field)
+                       ));
+               }
+       }
 }
 
 // weapon reloading code
@@ -849,15 +874,14 @@ void W_ReloadedAndReady()
        self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
 
        // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
-       if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO)
+       if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO || self.ammo_field == ammo_none)
                self.clip_load = self.reload_ammo_amount;
        else
        {
-               while(self.clip_load < self.reload_ammo_amount && self.(self.current_ammo)) // make sure we don't add more ammo than we have
-               {
-                       self.clip_load += 1;
-                       self.(self.current_ammo) -= 1;
-               }
+               // make sure we don't add more ammo than we have
+               float load = min(self.reload_ammo_amount - self.clip_load, self.(self.ammo_field));
+        self.clip_load += load;
+        self.(self.ammo_field) -= load;
        }
        self.(weapon_load[self.weapon]) = self.clip_load;
 
@@ -876,13 +900,17 @@ void W_Reload(float sent_ammo_min, string sent_sound)
        entity e;
        e = get_weaponinfo(self.weapon);
 
+       if(cvar("g_overkill"))
+       if(self.ok_use_ammocharge)
+               return; // TODO
+
        self.reload_ammo_min = sent_ammo_min;
-       self.reload_ammo_amount = e.reloading_ammo;;
+       self.reload_ammo_amount = e.reloading_ammo;
        self.reload_time = e.reloading_time;
        self.reload_sound = sent_sound;
 
        // don't reload weapons that don't have the RELOADABLE flag
-       if not(e.spawnflags & WEP_FLAG_RELOADABLE)
+       if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
        {
                dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
                return;
@@ -897,17 +925,18 @@ void W_Reload(float sent_ammo_min, string sent_sound)
                return;
 
        // no ammo, so nothing to load
-       if(!self.(self.current_ammo) && self.reload_ammo_min)
-       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+       if(self.ammo_field != ammo_none)
+       if(!self.(self.ammo_field) && self.reload_ammo_min)
+       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
        {
                if(IS_REAL_CLIENT(self) && 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"));
+                       sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n"));
                        self.reload_complain = time + 1;
                }
                // switch away if the amount of ammo is not enough to keep using this weapon
-               if not(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2))
+               if (!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2)))
                {
                        self.clip_load = -1; // reload later
                        W_SwitchToOtherWeapon(self);
@@ -941,3 +970,12 @@ void W_Reload(float sent_ammo_min, string sent_sound)
        self.old_clip_load = self.clip_load;
        self.clip_load = self.(weapon_load[self.weapon]) = -1;
 }
+
+void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item)
+{
+       entity oldself = self;
+       self = player;
+       weapon_dropevent_item = weapon_item;
+       WEP_ACTION(weapon_type, event);
+       self = oldself;
+}