]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into samual/weapons
authorSamual Lenks <samual@xonotic.org>
Fri, 28 Jun 2013 22:05:26 +0000 (18:05 -0400)
committerSamual Lenks <samual@xonotic.org>
Fri, 28 Jun 2013 22:05:26 +0000 (18:05 -0400)
Conflicts:
qcsrc/server/t_items.qc

1  2 
qcsrc/server/weapons/cl_weapons.qc

index 1b47fef3b95584890a0cded9d3737d886ed5d90b,0000000000000000000000000000000000000000..8870edf3ad8745d054873fcbb0463bfdd46641d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,669 -1,0 +1,666 @@@
-                       print("Attempted to spawn a mutator-blocked weapon; these guns will in the future require a mutator\n");
-                       /*
 +void W_TriggerReload()
 +{
 +    weapon_action(self.weapon, WR_RELOAD);
 +}
 +
 +// 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
 +      {
 +              W_TriggerReload();
 +      }
 +}
 +
 +.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 weapon_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)
 +              return 1;
 +      if(round_handler_IsActive() && !round_handler_IsRoundStarted())
 +              return 1;
 +      if(self.player_blocked)
 +              return 1;
 +      if(self.freezetag_frozen)
 +              return 1;
 +      return 0;
 +}
 +
 +void W_WeaponFrame()
 +{
 +      vector fo, ri, up;
 +
 +      if (frametime)
 +              self.weapon_frametime = frametime;
 +
 +      if (!self.weaponentity || self.health < 1)
 +              return; // Dead player can't use weapons and injure impulse commands
 +
 +      if(forbidWeaponUse())
 +      if(self.weaponentity.state != WS_CLEAR)
 +      {
 +              w_ready();
 +              return;
 +      }
 +
 +      if(!self.switchweapon)
 +      {
 +              self.weapon = 0;
 +              self.switchingweapon = 0;
 +              self.weaponentity.state = WS_CLEAR;
 +              self.weaponname = "";
 +              self.items &~= IT_AMMO;
 +              return;
 +      }
 +
 +      makevectors(self.v_angle);
 +      fo = v_forward; // save them in case the weapon think functions change it
 +      ri = v_right;
 +      up = v_up;
 +
 +      // Change weapon
 +      if (self.weapon != self.switchweapon)
 +      {
 +              if (self.weaponentity.state == WS_CLEAR)
 +              {
 +                      // end switching!
 +                      self.switchingweapon = self.switchweapon;
 +
 +                      entity newwep = get_weaponinfo(self.switchweapon);
 +
 +                      //setanim(self, self.anim_draw, FALSE, TRUE, TRUE);
 +                      self.weaponentity.state = WS_RAISE;
 +                      weapon_action(self.switchweapon, WR_SETUP);
 +
 +                      // set our clip load to the load of the weapon we switched to, if it's reloadable
 +                      if(newwep.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"))) // prevent accessing undefined cvars
 +                      {
 +                              self.clip_load = self.(weapon_load[self.switchweapon]);
 +                              self.clip_size = cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"));
 +                      }
 +                      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))));
 +                      weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');
 +              }
 +              else if (self.weaponentity.state == WS_DROP)
 +              {
 +                      // in dropping phase we can switch at any time
 +                      self.switchingweapon = self.switchweapon;
 +              }
 +              else if (self.weaponentity.state == WS_READY)
 +              {
 +                      // start switching!
 +                      self.switchingweapon = self.switchweapon;
 +
 +                      entity oldwep = get_weaponinfo(self.weapon);
 +                      
 +#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))));
 +                      weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);
 +#ifndef INDEPENDENT_ATTACK_FINISHED
 +                      }
 +#endif
 +              }
 +      }
 +
 +      // LordHavoc: network timing test code
 +      //if (self.button0)
 +      //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");
 +
 +      float w;
 +      w = self.weapon;
 +
 +      // call the think code which may fire the weapon
 +      // and do so multiple times to resolve framerate dependency issues if the
 +      // server framerate is very low and the weapon fire rate very high
 +      float c;
 +      c = 0;
 +      while (c < W_TICSPERFRAME)
 +      {
 +              c = c + 1;
 +              if(w && !WEPSET_CONTAINS_EW(self, w))
 +              {
 +                      if(self.weapon == self.switchweapon)
 +                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +                      w = 0;
 +              }
 +
 +              v_forward = fo;
 +              v_right = ri;
 +              v_up = up;
 +
 +              if(w)
 +                      weapon_action(self.weapon, WR_THINK);
 +              else
 +                      weapon_action(self.weapon, WR_GONETHINK);
 +
 +              if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
 +              {
 +                      if(self.weapon_think)
 +                      {
 +                              v_forward = fo;
 +                              v_right = ri;
 +                              v_up = up;
 +                              self.weapon_think();
 +                      }
 +                      else
 +                              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
 +}
 +
 +string W_Apply_Weaponreplace(string in)
 +{
 +      float n = tokenize_console(in);
 +      string out = "";
 +      float i;
 +      for(i = 0; i < n; ++i)
 +      {
 +              string s = argv(i);
 +              string r = cvar_string(strcat("g_weaponreplace_", s));
 +              if(r == "")
 +                      out = strcat(out, " ", s);
 +              else if(r != "0")
 +                      out = strcat(out, " ", r);
 +      }
 +      return substring(out, 1, -1);
 +}
 +
 +void weapon_defaultspawnfunc(float wpn)
 +{
 +      entity e;
 +      float t;
 +      var .float ammofield;
 +      string s;
 +      entity oldself;
 +      float i, j;
 +      float f;
 +
 +      if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
 +      {
 +              e = get_weaponinfo(wpn);
 +
 +              if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
 +              {
-                       */
 +                      objerror("Attempted to spawn a mutator-blocked weapon rejected");
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +
 +              s = W_Apply_Weaponreplace(e.netname);
 +              ret_string = s;
 +              other = e;
 +              MUTATOR_CALLHOOK(SetWeaponreplace);
 +              s = ret_string;
 +              if(s == "")
 +              {
 +                      remove(self);
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +              t = tokenize_console(s);
 +              if(t >= 2)
 +              {
 +                      self.team = --internalteam;
 +                      oldself = self;
 +                      for(i = 1; i < t; ++i)
 +                      {
 +                              s = argv(i);
 +                              for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 +                              {
 +                                      e = get_weaponinfo(j);
 +                                      if(e.netname == s)
 +                                      {
 +                                              self = spawn();
 +                                              copyentity(oldself, self);
 +                                              self.classname = "replacedweapon";
 +                                              weapon_defaultspawnfunc(j);
 +                                              break;
 +                                      }
 +                              }
 +                              if(j > WEP_LAST)
 +                              {
 +                                      print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 +                              }
 +                      }
 +                      self = oldself;
 +              }
 +              if(t >= 1) // always the case!
 +              {
 +                      s = argv(0);
 +                      wpn = 0;
 +                      for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 +                      {
 +                              e = get_weaponinfo(j);
 +                              if(e.netname == s)
 +                              {
 +                                      wpn = j;
 +                                      break;
 +                              }
 +                      }
 +                      if(j > WEP_LAST)
 +                      {
 +                              print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 +                      }
 +              }
 +              if(wpn == 0)
 +              {
 +                      remove(self);
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +      }
 +
 +      e = get_weaponinfo(wpn);
 +
 +      if(!self.respawntime)
 +      {
 +              if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
 +              {
 +                      self.respawntime = g_pickup_respawntime_superweapon;
 +                      self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
 +              }
 +              else
 +              {
 +                      self.respawntime = g_pickup_respawntime_weapon;
 +                      self.respawntimejitter = g_pickup_respawntimejitter_weapon;
 +              }
 +      }
 +
 +      if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
 +              if(!self.superweapons_finished)
 +                      self.superweapons_finished = autocvar_g_balance_superweapons_time;
 +
 +      if(e.items)
 +      {
 +              for(i = 0, j = 1; i < 24; ++i, j *= 2)
 +              {
 +                      if(e.items & j)
 +                      {
 +                              ammofield = Item_CounterField(j);
 +                              if(!self.ammofield)
 +                                      self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
 +                      }
 +              }
 +      }
 +
 +      // pickup anyway
 +      if(g_pickup_weapons_anyway)
 +              self.pickup_anyway = TRUE;
 +
 +      f = FL_WEAPON;
 +
 +      // no weapon-stay on superweapons
 +      if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
 +              f |= FL_NO_WEAPON_STAY;
 +
 +      // weapon stay isn't supported for teamed weapons
 +      if(self.team)
 +              f |= FL_NO_WEAPON_STAY;
 +
 +      StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
 +      if (self.modelindex) // don't precache if self was removed
 +              weapon_action(e.weapon, WR_PRECACHE);
 +}