]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Split throwing and selection into their own files
authorSamual Lenks <samual@xonotic.org>
Mon, 1 Jul 2013 06:53:14 +0000 (02:53 -0400)
committerSamual Lenks <samual@xonotic.org>
Mon, 1 Jul 2013 06:53:14 +0000 (02:53 -0400)
qcsrc/server/weapons/cl_weapons.qc
qcsrc/server/weapons/main.qc
qcsrc/server/weapons/selection.qc [new file with mode: 0644]
qcsrc/server/weapons/throwing.qc [new file with mode: 0644]

index bf72ae15a739edf03900918c29bbc88658a98b0a..1091853af5887b959af62f5c97536050479bc296 100644 (file)
@@ -1,350 +1,3 @@
-// switch between weapons
-void W_SwitchWeapon(float imp)
-{
-       if (self.switchweapon != imp)
-       {
-               if (client_hasweapon(self, imp, TRUE, TRUE))
-                       W_SwitchWeapon_Force(self, imp);
-               else
-                       self.selectweapon = imp; // update selectweapon ANYWAY
-       }
-       else
-       {
-               WEP_ACTION(self.weapon, WR_RELOAD);
-       }
-}
-
-.float weaponcomplainindex;
-float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing)
-{
-       // We cannot tokenize in this function, as GiveItems calls this
-       // function. Thus we must use car/cdr.
-       float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c;
-       string rest;
-       switchtonext = switchtolast = 0;
-       first_valid = prev_valid = 0;
-       float weaponcur;
-
-       if(skipmissing || pl.selectweapon == 0)
-               weaponcur = pl.switchweapon;
-       else
-               weaponcur = pl.selectweapon;
-
-       if(dir == 0)
-               switchtonext = 1;
-
-       c = 0;
-
-       rest = weaponorder;
-       while(rest != "")
-       {
-               weaponwant = stof(car(rest)); rest = cdr(rest);
-               if(imp >= 0)
-                       if((get_weaponinfo(weaponwant)).impulse != imp)
-                               continue;
-
-               ++c;
-
-               if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE))
-               {
-                       if(switchtonext)
-                               return weaponwant;
-                       if(!first_valid)
-                               first_valid = weaponwant;
-                       if(weaponwant == weaponcur)
-                       {
-                               if(dir >= 0)
-                                       switchtonext = 1;
-                               else if(prev_valid)
-                                       return prev_valid;
-                               else
-                                       switchtolast = 1;
-                       }
-                       prev_valid = weaponwant;
-               }
-       }
-       if(first_valid)
-       {
-               if(switchtolast)
-                       return prev_valid;
-               else
-                       return first_valid;
-       }
-       // complain (but only for one weapon on the button that has been pressed)
-       if(complain)
-       {
-               self.weaponcomplainindex += 1;
-               c = mod(self.weaponcomplainindex, c) + 1;
-               rest = weaponorder;
-               while(rest != "")
-               {
-                       weaponwant = stof(car(rest)); rest = cdr(rest);
-                       if(imp >= 0)
-                               if((get_weaponinfo(weaponwant)).impulse != imp)
-                                       continue;
-
-                       --c;
-                       if(c == 0)
-                       {
-                               client_hasweapon(pl, weaponwant, TRUE, TRUE);
-                               break;
-                       }
-               }
-       }
-       return 0;
-}
-
-void W_CycleWeapon(string weaponorder, float dir)
-{
-       float w;
-       w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE);
-       if(w > 0)
-               W_SwitchWeapon(w);
-}
-
-void W_NextWeaponOnImpulse(float imp)
-{
-       float w;
-       w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0));
-       if(w > 0)
-               W_SwitchWeapon(w);
-}
-
-// next weapon
-void W_NextWeapon(float list)
-{
-       if(list == 0)
-               W_CycleWeapon(weaponorder_byid, -1);
-       else if(list == 1)
-               W_CycleWeapon(self.weaponorder_byimpulse, -1);
-       else if(list == 2)
-               W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
-}
-
-// prev weapon
-void W_PreviousWeapon(float list)
-{
-       if(list == 0)
-               W_CycleWeapon(weaponorder_byid, +1);
-       else if(list == 1)
-               W_CycleWeapon(self.weaponorder_byimpulse, +1);
-       else if(list == 2)
-               W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
-}
-
-// previously used if exists and has ammo, (second) best otherwise
-void W_LastWeapon()
-{
-       if(client_hasweapon(self, self.cnt, TRUE, FALSE))
-               W_SwitchWeapon(self.cnt);
-       else
-               W_SwitchToOtherWeapon(self);
-}
-
-float w_getbestweapon(entity e)
-{
-       return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE);
-}
-
-// generic weapons table
-// TODO should they be macros instead?
-//float WEP_ACTION(float wpn, float wrequest)
-//{
-//     return (get_weaponinfo(wpn)).weapon_func(wrequest);
-//}
-
-.float savenextthink;
-void thrown_wep_think()
-{
-       self.owner = world;
-       float timeleft = self.savenextthink - time;
-       if(timeleft > 1)
-               SUB_SetFade(self, self.savenextthink - 1, 1);
-       else if(timeleft > 0)
-               SUB_SetFade(self, time, timeleft);
-       else
-               SUB_VanishOrRemove(self);
-}
-
-// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
-string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
-{
-       entity oldself, wep;
-       float wa, thisammo, i, j;
-       string s;
-       var .float ammofield;
-
-       wep = spawn();
-
-       setorigin(wep, org);
-       wep.classname = "droppedweapon";
-       wep.velocity = velo;
-       wep.owner = wep.enemy = own;
-       wep.flags |= FL_TOSSED;
-       wep.colormap = own.colormap;
-
-       if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, wpn))
-       {
-               if(own.items & IT_UNLIMITED_SUPERWEAPONS)
-               {
-                       wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
-               }
-               else
-               {
-                       float superweapons = 1;
-                       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-                               if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, i))
-                                       if(WEPSET_CONTAINS_EW(own, i))
-                                               ++superweapons;
-                       if(superweapons <= 1)
-                       {
-                               wep.superweapons_finished = own.superweapons_finished;
-                               own.superweapons_finished = 0;
-                       }
-                       else
-                       {
-                               float timeleft = own.superweapons_finished - time;
-                               float weptimeleft = timeleft / superweapons;
-                               wep.superweapons_finished = time + weptimeleft;
-                               own.superweapons_finished -= weptimeleft;
-                       }
-               }
-       }
-
-       wa = W_AmmoItemCode(wpn);
-       if(wa == 0)
-       {
-               oldself = self;
-               self = wep;
-               weapon_defaultspawnfunc(wpn);
-               self = oldself;
-               if(startitem_failed)
-                       return string_null;
-               wep.glowmod = own.weaponentity_glowmod;
-               wep.think = thrown_wep_think;
-               wep.savenextthink = wep.nextthink;
-               wep.nextthink = min(wep.nextthink, time + 0.5);
-               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
-               return "";
-       }
-       else
-       {
-               s = "";
-               oldself = self;
-               self = wep;
-               weapon_defaultspawnfunc(wpn);
-               self = oldself;
-               if(startitem_failed)
-                       return string_null;
-               if(doreduce && g_weapon_stay == 2)
-               {
-                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
-                       {
-                               if(wa & j)
-                               {
-                                       ammofield = Item_CounterField(j);
-
-                                       // if our weapon is loaded, give its load back to the player
-                                       if(self.(weapon_load[self.weapon]) > 0)
-                                       {
-                                               own.ammofield += self.(weapon_load[self.weapon]);
-                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
-                                       }
-
-                                       wep.ammofield = 0;
-                               }
-                       }
-               }
-               else if(doreduce)
-               {
-                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
-                       {
-                               if(wa & j)
-                               {
-                                       ammofield = Item_CounterField(j);
-
-                                       // if our weapon is loaded, give its load back to the player
-                                       if(self.(weapon_load[self.weapon]) > 0)
-                                       {
-                                               own.ammofield += self.(weapon_load[self.weapon]);
-                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
-                                       }
-
-                                       thisammo = min(own.ammofield, wep.ammofield);
-                                       wep.ammofield = thisammo;
-                                       own.ammofield -= thisammo;
-                                       s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));
-                               }
-                       }
-                       s = substring(s, 5, -1);
-               }
-               wep.glowmod = own.weaponentity_glowmod;
-               wep.think = thrown_wep_think;
-               wep.savenextthink = wep.nextthink;
-               wep.nextthink = min(wep.nextthink, time + 0.5);
-               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
-
-               return s;
-       }
-}
-
-float W_IsWeaponThrowable(float w)
-{
-       float wa;
-
-       if (!autocvar_g_pickup_items)
-               return 0;
-       if (g_weaponarena)
-               return 0;
-       if (g_cts)
-               return 0;
-       if (g_nexball && w == WEP_GRENADE_LAUNCHER)
-               return 0;
-    if(w == 0)
-        return 0;
-       
-       wa = W_AmmoItemCode(w);
-       if(WEPSET_CONTAINS_AW(start_weapons, w))
-       {
-               // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)
-               if(start_items & IT_UNLIMITED_WEAPON_AMMO)
-                       return 0;
-               if(wa == 0)
-                       return 0;
-       }
-
-       return 1;
-}
-
-// toss current weapon
-void W_ThrowWeapon(vector velo, vector delta, float doreduce)
-{
-       float w;
-       string a;
-
-       w = self.weapon;
-       if (w == 0)
-               return; // just in case
-       if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
-               return;
-       if(!autocvar_g_weapon_throwable)
-               return;
-       if(self.weaponentity.state != WS_READY)
-               return;
-       if(!W_IsWeaponThrowable(w))
-               return;
-
-       if(!WEPSET_CONTAINS_EW(self, w))
-               return;
-       WEPSET_ANDNOT_EW(self, w);
-
-       W_SwitchWeapon_Force(self, w_getbestweapon(self));
-       a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
-       
-       if not(a) return;
-       Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
-}
-
 float forbidWeaponUse()
 {
        if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
index c6e9e30a90f39c5a85be60f93571281e9dcaeb3a..be5eca13b81318497e7d6fa8266599ee4c46758f 100644 (file)
@@ -573,114 +573,6 @@ void CL_SpawnWeaponentity()
        }
 }
 
-void Send_WeaponComplain (entity e, float wpn, string wpnname, float type)
-{
-       msg_entity = e;
-       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-       WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
-       WriteByte(MSG_ONE, wpn);
-       WriteString(MSG_ONE, wpnname);
-       WriteByte(MSG_ONE, type);
-}
-
-.float hasweapon_complain_spam;
-
-float client_hasweapon(entity cl, float wpn, float andammo, float complain)
-{
-       float f;
-       entity oldself;
-
-       if(time < self.hasweapon_complain_spam)
-               complain = 0;
-       if(complain)
-               self.hasweapon_complain_spam = time + 0.2;
-
-       if (wpn < WEP_FIRST || wpn > WEP_LAST)
-       {
-               if (complain)
-                       sprint(self, "Invalid weapon\n");
-               return FALSE;
-       }
-       if (WEPSET_CONTAINS_EW(cl, wpn))
-       {
-               if (andammo)
-               {
-                       if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
-                       {
-                               f = 1;
-                       }
-                       else
-                       {
-                               oldself = self;
-                               self = cl;
-                               f = WEP_ACTION(wpn, WR_CHECKAMMO1);
-                               f = f + WEP_ACTION(wpn, WR_CHECKAMMO2);
-
-                               // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
-                               entity mine;
-                               if(wpn == WEP_MINE_LAYER)
-                               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
-                                       f = 1;
-
-                               self = oldself;
-                       }
-                       if (!f)
-                       {
-                               if (complain)
-                               if(IS_REAL_CLIENT(cl))
-                               {
-                                       play2(cl, "weapons/unavailable.wav");
-                                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 0);
-                               }
-                               return FALSE;
-                       }
-               }
-               return TRUE;
-       }
-       if (complain)
-       {
-               // DRESK - 3/16/07
-               // Report Proper Weapon Status / Modified Weapon Ownership Message
-               if (WEPSET_CONTAINS_AW(weaponsInMap, wpn))
-               {
-                       Send_WeaponComplain(cl, wpn, W_Name(wpn), 1);
-
-                       if(autocvar_g_showweaponspawns)
-                       {
-                               entity e;
-                               string s;
-
-                               e = get_weaponinfo(wpn);
-                               s = e.model2;
-
-                               for(e = world; (e = findfloat(e, weapon, wpn)); )
-                               {
-                                       if(e.classname == "droppedweapon")
-                                               continue;
-                                       if not(e.flags & FL_ITEM)
-                                               continue;
-                                       WaypointSprite_Spawn(
-                                               s,
-                                               1, 0,
-                                               world, e.origin,
-                                               self, 0,
-                                               world, enemy,
-                                               0,
-                                               RADARICON_NONE, '0 0 0'
-                                       );
-                               }
-                       }
-               }
-               else
-               {
-                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 2);
-               }
-
-               play2(cl, "weapons/unavailable.wav");
-       }
-       return FALSE;
-}
-
 // Weapon subs
 void w_clear()
 {
@@ -703,24 +595,6 @@ void w_ready()
        weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
 }
 
-// perform weapon to attack (weaponstate and attack_finished check is here)
-void W_SwitchToOtherWeapon(entity pl)
-{
-       // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
-       float w, ww;
-       w = pl.weapon;
-       if(WEPSET_CONTAINS_EW(pl, w))
-       {
-               WEPSET_ANDNOT_EW(pl, w);
-               ww = w_getbestweapon(pl);
-               WEPSET_OR_EW(pl, w);
-       }
-       else
-               ww = w_getbestweapon(pl);
-       if(ww)
-               W_SwitchWeapon_Force(pl, ww);
-}
-
 .float prevdryfire;
 .float prevwarntime;
 float weapon_prepareattack_checkammo(float secondary)
diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc
new file mode 100644 (file)
index 0000000..95a145e
--- /dev/null
@@ -0,0 +1,266 @@
+// switch between weapons
+void Send_WeaponComplain(entity e, float wpn, string wpnname, float type)
+{
+       msg_entity = e;
+       WriteByte(MSG_ONE, SVC_TEMPENTITY);
+       WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
+       WriteByte(MSG_ONE, wpn);
+       WriteString(MSG_ONE, wpnname);
+       WriteByte(MSG_ONE, type);
+}
+
+.float hasweapon_complain_spam;
+float client_hasweapon(entity cl, float wpn, float andammo, float complain)
+{
+       float f;
+       entity oldself;
+
+       if(time < self.hasweapon_complain_spam)
+               complain = 0;
+       if(complain)
+               self.hasweapon_complain_spam = time + 0.2;
+
+       if (wpn < WEP_FIRST || wpn > WEP_LAST)
+       {
+               if (complain)
+                       sprint(self, "Invalid weapon\n");
+               return FALSE;
+       }
+       if (WEPSET_CONTAINS_EW(cl, wpn))
+       {
+               if (andammo)
+               {
+                       if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
+                       {
+                               f = 1;
+                       }
+                       else
+                       {
+                               oldself = self;
+                               self = cl;
+                               f = WEP_ACTION(wpn, WR_CHECKAMMO1);
+                               f = f + WEP_ACTION(wpn, WR_CHECKAMMO2);
+
+                               // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
+                               entity mine;
+                               if(wpn == WEP_MINE_LAYER)
+                               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
+                                       f = 1;
+
+                               self = oldself;
+                       }
+                       if (!f)
+                       {
+                               if (complain)
+                               if(IS_REAL_CLIENT(cl))
+                               {
+                                       play2(cl, "weapons/unavailable.wav");
+                                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 0);
+                               }
+                               return FALSE;
+                       }
+               }
+               return TRUE;
+       }
+       if (complain)
+       {
+               // DRESK - 3/16/07
+               // Report Proper Weapon Status / Modified Weapon Ownership Message
+               if (WEPSET_CONTAINS_AW(weaponsInMap, wpn))
+               {
+                       Send_WeaponComplain(cl, wpn, W_Name(wpn), 1);
+
+                       if(autocvar_g_showweaponspawns)
+                       {
+                               entity e;
+                               string s;
+
+                               e = get_weaponinfo(wpn);
+                               s = e.model2;
+
+                               for(e = world; (e = findfloat(e, weapon, wpn)); )
+                               {
+                                       if(e.classname == "droppedweapon")
+                                               continue;
+                                       if not(e.flags & FL_ITEM)
+                                               continue;
+                                       WaypointSprite_Spawn(
+                                               s,
+                                               1, 0,
+                                               world, e.origin,
+                                               self, 0,
+                                               world, enemy,
+                                               0,
+                                               RADARICON_NONE, '0 0 0'
+                                       );
+                               }
+                       }
+               }
+               else
+               {
+                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 2);
+               }
+
+               play2(cl, "weapons/unavailable.wav");
+       }
+       return FALSE;
+}
+
+.float weaponcomplainindex;
+float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing)
+{
+       // We cannot tokenize in this function, as GiveItems calls this
+       // function. Thus we must use car/cdr.
+       float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c;
+       string rest;
+       switchtonext = switchtolast = 0;
+       first_valid = prev_valid = 0;
+       float weaponcur;
+
+       if(skipmissing || pl.selectweapon == 0)
+               weaponcur = pl.switchweapon;
+       else
+               weaponcur = pl.selectweapon;
+
+       if(dir == 0)
+               switchtonext = 1;
+
+       c = 0;
+
+       rest = weaponorder;
+       while(rest != "")
+       {
+               weaponwant = stof(car(rest)); rest = cdr(rest);
+               if(imp >= 0)
+                       if((get_weaponinfo(weaponwant)).impulse != imp)
+                               continue;
+
+               ++c;
+
+               if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE))
+               {
+                       if(switchtonext)
+                               return weaponwant;
+                       if(!first_valid)
+                               first_valid = weaponwant;
+                       if(weaponwant == weaponcur)
+                       {
+                               if(dir >= 0)
+                                       switchtonext = 1;
+                               else if(prev_valid)
+                                       return prev_valid;
+                               else
+                                       switchtolast = 1;
+                       }
+                       prev_valid = weaponwant;
+               }
+       }
+       if(first_valid)
+       {
+               if(switchtolast)
+                       return prev_valid;
+               else
+                       return first_valid;
+       }
+       // complain (but only for one weapon on the button that has been pressed)
+       if(complain)
+       {
+               self.weaponcomplainindex += 1;
+               c = mod(self.weaponcomplainindex, c) + 1;
+               rest = weaponorder;
+               while(rest != "")
+               {
+                       weaponwant = stof(car(rest)); rest = cdr(rest);
+                       if(imp >= 0)
+                               if((get_weaponinfo(weaponwant)).impulse != imp)
+                                       continue;
+
+                       --c;
+                       if(c == 0)
+                       {
+                               client_hasweapon(pl, weaponwant, TRUE, TRUE);
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+#define w_getbestweapon(ent) W_GetCycleWeapon(ent, ent.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE)
+
+// perform weapon to attack (weaponstate and attack_finished check is here)
+void W_SwitchToOtherWeapon(entity pl)
+{
+       // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
+       float w, ww;
+       w = pl.weapon;
+       if(WEPSET_CONTAINS_EW(pl, w))
+       {
+               WEPSET_ANDNOT_EW(pl, w);
+               ww = w_getbestweapon(pl);
+               WEPSET_OR_EW(pl, w);
+       }
+       else
+               ww = w_getbestweapon(pl);
+       if(ww)
+               W_SwitchWeapon_Force(pl, ww);
+}
+
+void W_SwitchWeapon(float imp)
+{
+       if (self.switchweapon != imp)
+       {
+               if (client_hasweapon(self, imp, TRUE, TRUE))
+                       W_SwitchWeapon_Force(self, imp);
+               else
+                       self.selectweapon = imp; // update selectweapon ANYWAY
+       }
+       else { WEP_ACTION(self.weapon, WR_RELOAD); }
+}
+
+void W_CycleWeapon(string weaponorder, float dir)
+{
+       float w;
+       w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE);
+       if(w > 0)
+               W_SwitchWeapon(w);
+}
+
+void W_NextWeaponOnImpulse(float imp)
+{
+       float w;
+       w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0));
+       if(w > 0)
+               W_SwitchWeapon(w);
+}
+
+// next weapon
+void W_NextWeapon(float list)
+{
+       if(list == 0)
+               W_CycleWeapon(weaponorder_byid, -1);
+       else if(list == 1)
+               W_CycleWeapon(self.weaponorder_byimpulse, -1);
+       else if(list == 2)
+               W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
+}
+
+// prev weapon
+void W_PreviousWeapon(float list)
+{
+       if(list == 0)
+               W_CycleWeapon(weaponorder_byid, +1);
+       else if(list == 1)
+               W_CycleWeapon(self.weaponorder_byimpulse, +1);
+       else if(list == 2)
+               W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
+}
+
+// previously used if exists and has ammo, (second) best otherwise
+void W_LastWeapon(void)
+{
+       if(client_hasweapon(self, self.cnt, TRUE, FALSE))
+               W_SwitchWeapon(self.cnt);
+       else
+               W_SwitchToOtherWeapon(self);
+}
diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc
new file mode 100644 (file)
index 0000000..c5e21f2
--- /dev/null
@@ -0,0 +1,191 @@
+.float savenextthink;
+void thrown_wep_think()
+{
+       self.owner = world;
+       float timeleft = self.savenextthink - time;
+       if(timeleft > 1)
+               SUB_SetFade(self, self.savenextthink - 1, 1);
+       else if(timeleft > 0)
+               SUB_SetFade(self, time, timeleft);
+       else
+               SUB_VanishOrRemove(self);
+}
+
+// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
+string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
+{
+       entity oldself, wep;
+       float wa, thisammo, i, j;
+       string s;
+       var .float ammofield;
+
+       wep = spawn();
+
+       setorigin(wep, org);
+       wep.classname = "droppedweapon";
+       wep.velocity = velo;
+       wep.owner = wep.enemy = own;
+       wep.flags |= FL_TOSSED;
+       wep.colormap = own.colormap;
+
+       if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, wpn))
+       {
+               if(own.items & IT_UNLIMITED_SUPERWEAPONS)
+               {
+                       wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
+               }
+               else
+               {
+                       float superweapons = 1;
+                       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+                               if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, i))
+                                       if(WEPSET_CONTAINS_EW(own, i))
+                                               ++superweapons;
+                       if(superweapons <= 1)
+                       {
+                               wep.superweapons_finished = own.superweapons_finished;
+                               own.superweapons_finished = 0;
+                       }
+                       else
+                       {
+                               float timeleft = own.superweapons_finished - time;
+                               float weptimeleft = timeleft / superweapons;
+                               wep.superweapons_finished = time + weptimeleft;
+                               own.superweapons_finished -= weptimeleft;
+                       }
+               }
+       }
+
+       wa = W_AmmoItemCode(wpn);
+       if(wa == 0)
+       {
+               oldself = self;
+               self = wep;
+               weapon_defaultspawnfunc(wpn);
+               self = oldself;
+               if(startitem_failed)
+                       return string_null;
+               wep.glowmod = own.weaponentity_glowmod;
+               wep.think = thrown_wep_think;
+               wep.savenextthink = wep.nextthink;
+               wep.nextthink = min(wep.nextthink, time + 0.5);
+               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+               return "";
+       }
+       else
+       {
+               s = "";
+               oldself = self;
+               self = wep;
+               weapon_defaultspawnfunc(wpn);
+               self = oldself;
+               if(startitem_failed)
+                       return string_null;
+               if(doreduce && g_weapon_stay == 2)
+               {
+                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
+                       {
+                               if(wa & j)
+                               {
+                                       ammofield = Item_CounterField(j);
+
+                                       // if our weapon is loaded, give its load back to the player
+                                       if(self.(weapon_load[self.weapon]) > 0)
+                                       {
+                                               own.ammofield += self.(weapon_load[self.weapon]);
+                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
+                                       }
+
+                                       wep.ammofield = 0;
+                               }
+                       }
+               }
+               else if(doreduce)
+               {
+                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
+                       {
+                               if(wa & j)
+                               {
+                                       ammofield = Item_CounterField(j);
+
+                                       // if our weapon is loaded, give its load back to the player
+                                       if(self.(weapon_load[self.weapon]) > 0)
+                                       {
+                                               own.ammofield += self.(weapon_load[self.weapon]);
+                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
+                                       }
+
+                                       thisammo = min(own.ammofield, wep.ammofield);
+                                       wep.ammofield = thisammo;
+                                       own.ammofield -= thisammo;
+                                       s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));
+                               }
+                       }
+                       s = substring(s, 5, -1);
+               }
+               wep.glowmod = own.weaponentity_glowmod;
+               wep.think = thrown_wep_think;
+               wep.savenextthink = wep.nextthink;
+               wep.nextthink = min(wep.nextthink, time + 0.5);
+               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+
+               return s;
+       }
+}
+
+float W_IsWeaponThrowable(float w)
+{
+       float wa;
+
+       if (!autocvar_g_pickup_items)
+               return 0;
+       if (g_weaponarena)
+               return 0;
+       if (g_cts)
+               return 0;
+       if (g_nexball && w == WEP_GRENADE_LAUNCHER)
+               return 0;
+    if(w == 0)
+        return 0;
+       
+       wa = W_AmmoItemCode(w);
+       if(WEPSET_CONTAINS_AW(start_weapons, w))
+       {
+               // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)
+               if(start_items & IT_UNLIMITED_WEAPON_AMMO)
+                       return 0;
+               if(wa == 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+// toss current weapon
+void W_ThrowWeapon(vector velo, vector delta, float doreduce)
+{
+       float w;
+       string a;
+
+       w = self.weapon;
+       if (w == 0)
+               return; // just in case
+       if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
+               return;
+       if(!autocvar_g_weapon_throwable)
+               return;
+       if(self.weaponentity.state != WS_READY)
+               return;
+       if(!W_IsWeaponThrowable(w))
+               return;
+
+       if(!WEPSET_CONTAINS_EW(self, w))
+               return;
+       WEPSET_ANDNOT_EW(self, w);
+
+       W_SwitchWeapon_Force(self, w_getbestweapon(self));
+       a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
+       
+       if not(a) return;
+       Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
+}