]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/weapons/weaponsystem.qc
Offhand: fix machinegun (still needs .clip_load > 0)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / weaponsystem.qc
index a2d924c95146cebdef5579e0dc8cffca8b419971..a6961aec87196383bb63f383e850fc873c7f92fb 100644 (file)
 
 float W_WeaponRateFactor()
 {
-       float t;
-       t = 1.0 / g_weaponratefactor;
+       float t = 1.0 / g_weaponratefactor;
 
-       weapon_rate = t;
-       MUTATOR_CALLHOOK(WeaponRateFactor);
+       MUTATOR_CALLHOOK(WeaponRateFactor, t);
        t = weapon_rate;
 
        return t;
@@ -40,21 +38,19 @@ float W_WeaponRateFactor()
 
 float W_WeaponSpeedFactor()
 {
-       float t;
-       t = 1.0 * g_weaponspeedfactor;
+       float t = 1.0 * g_weaponspeedfactor;
 
-       ret_float = t;
-       MUTATOR_CALLHOOK(WeaponSpeedFactor);
+       MUTATOR_CALLHOOK(WeaponSpeedFactor, t);
        t = ret_float;
 
        return t;
 }
 
 
-void(float fr, float t, void() func) weapon_thinkf;
+void weapon_thinkf(float fr, float t, void(Weapon thiswep, bool fire1, bool fire2) func);
 
 float CL_Weaponentity_CustomizeEntityForClient()
-{
+{SELFPARAM();
        self.viewmodelforclient = self.owner;
        if(IS_SPEC(other))
                if(other.enemy == self.owner)
@@ -114,19 +110,19 @@ float CL_Weaponentity_CustomizeEntityForClient()
 //   call again with ""
 //   remove the ent
 void CL_WeaponEntity_SetModel(string name)
-{
+{SELFPARAM();
        float v_shot_idx;
        if (name != "")
        {
                // if there is a child entity, hide it until we're sure we use it
                if (self.weaponentity)
                        self.weaponentity.model = "";
-               setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
+               _setmodel(self, W_Model(strcat("v_", name, ".md3")));
                v_shot_idx = gettagindex(self, "shot"); // used later
                if(!v_shot_idx)
                        v_shot_idx = gettagindex(self, "tag_shot");
 
-               setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
+               _setmodel(self, W_Model(strcat("h_", name, ".iqm")));
                // preset some defaults that work great for renamed zym files (which don't need an animinfo)
                self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
                self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
@@ -139,14 +135,14 @@ void CL_WeaponEntity_SetModel(string name)
                {
                        if (!self.weaponentity)
                                self.weaponentity = spawn();
-                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+                       _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
                        setattachment(self.weaponentity, self, "weapon");
                }
                else if(gettagindex(self, "tag_weapon"))
                {
                        if (!self.weaponentity)
                                self.weaponentity = spawn();
-                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+                       _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
                        setattachment(self.weaponentity, self, "tag_weapon");
                }
                else
@@ -176,7 +172,7 @@ void CL_WeaponEntity_SetModel(string name)
                                self.movedir = gettaginfo(self, idx);
                        else
                        {
-                               print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
+                               LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
                                self.movedir = '0 0 0';
                        }
                }
@@ -200,7 +196,7 @@ void CL_WeaponEntity_SetModel(string name)
                                self.spawnorigin = gettaginfo(self, idx);
                        else
                        {
-                               print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
+                               LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
                                self.spawnorigin = self.movedir;
                        }
                }
@@ -229,7 +225,7 @@ void CL_WeaponEntity_SetModel(string name)
                        }
                        else
                        {
-                               print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
+                               LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
                                self.oldorigin = '0 0 0'; // there is no way to recover from this
                        }
                }
@@ -273,22 +269,19 @@ void CL_WeaponEntity_SetModel(string name)
 }
 
 vector CL_Weapon_GetShotOrg(float wpn)
-{
-       entity wi, oldself;
-       vector ret;
-       wi = get_weaponinfo(wpn);
-       oldself = self;
-       self = spawn();
+{SELFPARAM();
+       entity wi = get_weaponinfo(wpn);
+       setself(spawn());
        CL_WeaponEntity_SetModel(wi.mdl);
-       ret = self.movedir;
+       vector ret = self.movedir;
        CL_WeaponEntity_SetModel("");
        remove(self);
-       self = oldself;
+       setself(this);
        return ret;
 }
 
 void CL_Weaponentity_Think()
-{
+{SELFPARAM();
        int tb;
        self.nextthink = time;
        if (intermission_running)
@@ -364,7 +357,7 @@ void CL_Weaponentity_Think()
 }
 
 void CL_ExteriorWeaponentity_Think()
-{
+{SELFPARAM();
        float tag_found;
        self.nextthink = time;
        if (self.owner.exteriorweaponentity != self)
@@ -383,7 +376,7 @@ void CL_ExteriorWeaponentity_Think()
                self.dmg = self.owner.modelindex;
                self.deadflag = self.owner.deadflag;
                if (self.owner.weaponname != "")
-                       setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
+                       _setmodel(self, W_Model(strcat("v_", self.owner.weaponname, ".md3")));
                else
                        self.model = "";
 
@@ -408,46 +401,41 @@ void CL_ExteriorWeaponentity_Think()
        self.glowmod = self.owner.weaponentity_glowmod;
        self.colormap = self.owner.colormap;
 
-       CSQCMODEL_AUTOUPDATE();
+       CSQCMODEL_AUTOUPDATE(self);
 }
 
 // spawning weaponentity for client
-void CL_SpawnWeaponentity()
+void CL_SpawnWeaponentity(entity e)
 {
-       self.weaponentity = spawn();
-       self.weaponentity.classname = "weaponentity";
-       self.weaponentity.solid = SOLID_NOT;
-       self.weaponentity.owner = self;
-       setmodel(self.weaponentity, ""); // precision set when changed
-       setorigin(self.weaponentity, '0 0 0');
-       self.weaponentity.angles = '0 0 0';
-       self.weaponentity.viewmodelforclient = self;
-       self.weaponentity.flags = 0;
-       self.weaponentity.think = CL_Weaponentity_Think;
-       self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
-       self.weaponentity.nextthink = time;
-
-       self.exteriorweaponentity = spawn();
-       self.exteriorweaponentity.classname = "exteriorweaponentity";
-       self.exteriorweaponentity.solid = SOLID_NOT;
-       self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
-       self.exteriorweaponentity.owner = self;
-       setorigin(self.exteriorweaponentity, '0 0 0');
-       self.exteriorweaponentity.angles = '0 0 0';
-       self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
-       self.exteriorweaponentity.nextthink = time;
-
-       {
-               entity oldself = self;
-               self = self.exteriorweaponentity;
-               CSQCMODEL_AUTOINIT();
-               self = oldself;
-       }
+       entity view = e.weaponentity = spawn();
+       view.classname = "weaponentity";
+       view.solid = SOLID_NOT;
+       view.owner = e;
+       setmodel(view, MDL_Null); // precision set when changed
+       setorigin(view, '0 0 0');
+       view.angles = '0 0 0';
+       view.viewmodelforclient = e;
+       view.flags = 0;
+       view.think = CL_Weaponentity_Think;
+       view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
+       view.nextthink = time;
+
+       entity exterior = e.exteriorweaponentity = spawn();
+       exterior.classname = "exteriorweaponentity";
+       exterior.solid = SOLID_NOT;
+       exterior.exteriorweaponentity = exterior;
+       exterior.owner = e;
+       setorigin(exterior, '0 0 0');
+       exterior.angles = '0 0 0';
+       exterior.think = CL_ExteriorWeaponentity_Think;
+       exterior.nextthink = time;
+
+       CSQCMODEL_AUTOINIT(exterior);
 }
 
 // Weapon subs
-void w_clear()
-{
+void w_clear(Weapon thiswep, bool fire1, bool fire2)
+{SELFPARAM();
        if (self.weapon != -1)
        {
                self.weapon = 0;
@@ -460,8 +448,8 @@ void w_clear()
        }
 }
 
-void w_ready()
-{
+void w_ready(Weapon thiswep, bool fire1, bool fire2)
+{SELFPARAM();
        if (self.weaponentity)
                self.weaponentity.state = WS_READY;
        weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
@@ -470,9 +458,9 @@ void w_ready()
 .float prevdryfire;
 .float prevwarntime;
 float weapon_prepareattack_checkammo(float secondary)
-{
+{SELFPARAM();
        if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-       if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
+       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;
@@ -486,11 +474,11 @@ float weapon_prepareattack_checkammo(float secondary)
 
                if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
                {
-                       sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
+                       sound (self, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM);
                        self.prevdryfire = time;
                }
 
-               if(WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
+               if(_WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
                {
                        if(time - self.prevwarntime > 1)
                        {
@@ -517,7 +505,7 @@ float weapon_prepareattack_checkammo(float secondary)
 }
 .float race_penalty;
 float weapon_prepareattack_check(float secondary, float attacktime)
-{
+{SELFPARAM();
        if(!weapon_prepareattack_checkammo(secondary))
                return false;
 
@@ -547,7 +535,7 @@ float weapon_prepareattack_check(float secondary, float attacktime)
        return true;
 }
 float weapon_prepareattack_do(float secondary, float attacktime)
-{
+{SELFPARAM();
        self.weaponentity.state = WS_INUSE;
 
        self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
@@ -577,8 +565,8 @@ float weapon_prepareattack(float secondary, float attacktime)
                return false;
 }
 
-void weapon_thinkf(float fr, float t, void() func)
-{
+void weapon_thinkf(float fr, float t, void(Weapon thiswep, bool fire1, bool fire2) func)
+{SELFPARAM();
        vector a;
        vector of, or, ou;
        float restartanim;
@@ -653,23 +641,23 @@ void weapon_thinkf(float fr, float t, void() func)
        }
 }
 
-float forbidWeaponUse()
+float forbidWeaponUse(entity player)
 {
        if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
                return 1;
        if(round_handler_IsActive() && !round_handler_IsRoundStarted())
                return 1;
-       if(self.player_blocked)
+       if(player.player_blocked)
                return 1;
-       if(self.frozen)
+       if(player.frozen)
                return 1;
-       if(self.weapon_blocked)
+       if(player.weapon_blocked)
                return 1;
        return 0;
 }
 
 void W_WeaponFrame()
-{
+{SELFPARAM();
        vector fo, ri, up;
 
        if (frametime)
@@ -678,10 +666,11 @@ void W_WeaponFrame()
        if (!self.weaponentity || self.health < 1)
                return; // Dead player can't use weapons and injure impulse commands
 
-       if(forbidWeaponUse())
+       if(forbidWeaponUse(self))
        if(self.weaponentity.state != WS_CLEAR)
        {
-               w_ready();
+               Weapon wpn = get_weaponinfo(self.weapon);
+               w_ready(wpn, self.BUTTON_ATCK, self.BUTTON_ATCK2);
                return;
        }
 
@@ -714,7 +703,7 @@ void W_WeaponFrame()
                        self.weaponname = newwep.mdl;
                        self.bulletcounter = 0;
                        self.ammo_field = newwep.ammo_field;
-                       WEP_ACTION(self.switchweapon, WR_SETUP);
+                       _WEP_ACTION(self.switchweapon, WR_SETUP);
                        self.weaponentity.state = WS_RAISE;
 
                        // set our clip load to the load of the weapon we switched to, if it's reloadable
@@ -744,7 +733,7 @@ void W_WeaponFrame()
                        if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
                        {
                        #endif
-                               sound(self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
+                               sound(self, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM);
                                self.weaponentity.state = WS_DROP;
                                weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
                        #ifndef INDEPENDENT_ATTACK_FINISHED
@@ -779,10 +768,18 @@ void W_WeaponFrame()
                v_right = ri;
                v_up = up;
 
-               if(w)
-                       WEP_ACTION(self.weapon, WR_THINK);
-               else
-                       WEP_ACTION(self.weapon, WR_GONETHINK);
+               {
+                       bool key_pressed = self.BUTTON_HOOK;
+               Weapon wpn = self.offhand;
+               if (wpn.offhand_think) wpn.offhand_think(wpn, self, key_pressed);
+        }
+
+               if (w) {
+                       entity e = get_weaponinfo(self.weapon);
+                       if (e.wr_think) e.wr_think(e, self.BUTTON_ATCK, self.BUTTON_ATCK2);
+               } else {
+                       _WEP_ACTION(self.weapon, WR_GONETHINK);
+               }
 
                if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
                {
@@ -791,7 +788,8 @@ void W_WeaponFrame()
                                v_forward = fo;
                                v_right = ri;
                                v_up = up;
-                               self.weapon_think();
+                               Weapon wpn = get_weaponinfo(self.weapon);
+                               self.weapon_think(wpn, self.BUTTON_ATCK, self.BUTTON_ATCK2);
                        }
                        else
                                bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
@@ -800,7 +798,7 @@ void W_WeaponFrame()
 }
 
 void W_AttachToShotorg(entity flash, vector offset)
-{
+{SELFPARAM();
        entity xflash;
        flash.owner = self;
        flash.angles_z = random() * 360;
@@ -831,9 +829,8 @@ void W_AttachToShotorg(entity flash, vector offset)
        }
 }
 
-void W_DecreaseAmmo(float ammo_use)
-{
-       entity wep = get_weaponinfo(self.weapon);
+void W_DecreaseAmmo(Weapon wep, float ammo_use)
+{SELFPARAM();
 
        if(cvar("g_overkill"))
        if(self.ok_use_ammocharge)
@@ -875,8 +872,8 @@ void W_DecreaseAmmo(float ammo_use)
 .float reload_complain;
 .string reload_sound;
 
-void W_ReloadedAndReady()
-{
+void W_ReloadedAndReady(Weapon thiswep, bool fire1, bool fire2)
+{SELFPARAM();
        // finish the reloading process, and do the ammo transfer
 
        self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
@@ -899,11 +896,12 @@ void W_ReloadedAndReady()
 
        //ATTACK_FINISHED(self) -= self.reload_time - 1;
 
-       w_ready();
+       Weapon wpn = get_weaponinfo(self.weapon);
+       w_ready(wpn, self.BUTTON_ATCK, self.BUTTON_ATCK2);
 }
 
 void W_Reload(float sent_ammo_min, string sent_sound)
-{
+{SELFPARAM();
        // set global values to work with
        entity e;
        e = get_weaponinfo(self.weapon);
@@ -920,7 +918,7 @@ void W_Reload(float sent_ammo_min, string sent_sound)
        // don't reload weapons that don't have the RELOADABLE flag
        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");
+               LOG_TRACE("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
                return;
        }
 
@@ -939,12 +937,12 @@ void W_Reload(float sent_ammo_min, string sent_sound)
        {
                if(IS_REAL_CLIENT(self) && self.reload_complain < time)
                {
-                       play2(self, "weapons/unavailable.wav");
+                       play2(self, SND(UNAVAILABLE));
                        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 (!(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);
@@ -963,7 +961,7 @@ void W_Reload(float sent_ammo_min, string sent_sound)
 
        // now begin the reloading process
 
-       sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
+       _sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
 
        // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
        // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
@@ -980,10 +978,9 @@ void W_Reload(float sent_ammo_min, string sent_sound)
 }
 
 void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item)
-{
-       entity oldself = self;
-       self = player;
+{SELFPARAM();
+       setself(player);
        weapon_dropevent_item = weapon_item;
-       WEP_ACTION(weapon_type, event);
-       self = oldself;
+       _WEP_ACTION(weapon_type, event);
+       setself(this);
 }