]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/cl_weapons.qc
Allow setting the volume of a global sound
[voretournament/voretournament.git] / data / qcsrc / server / cl_weapons.qc
1 void W_TriggerReload()\r
2 {\r
3         // don't trigger reload if holding the weapon attack button\r
4         if(self.BUTTON_ATCK2)\r
5                 return;\r
6 \r
7     weapon_action(self.weapon, WR_RELOAD);\r
8 }\r
9 \r
10 // switch between weapons\r
11 void W_SwitchWeapon(float imp)\r
12 {\r
13         if (self.switchweapon != imp)\r
14         {\r
15                 if (client_hasweapon(self, imp, TRUE, TRUE))\r
16                         W_SwitchWeapon_Force(self, imp);\r
17         }\r
18         else\r
19         {\r
20                 W_TriggerReload();\r
21         }\r
22 };\r
23 \r
24 .float weaponcomplainindex;\r
25 float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain)\r
26 {\r
27         // We cannot tokenize in this function, as GiveItems calls this\r
28         // function. Thus we must use car/cdr.\r
29         float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c;\r
30         string rest;\r
31         switchtonext = switchtolast = 0;\r
32         first_valid = prev_valid = 0;\r
33 \r
34         if(dir == 0)\r
35                 switchtonext = 1;\r
36 \r
37         c = 0;\r
38 \r
39         rest = weaponorder;\r
40         while(rest != "")\r
41         {\r
42                 weaponwant = stof(car(rest)); rest = cdr(rest);\r
43                 if(imp >= 0)\r
44                         if((get_weaponinfo(weaponwant)).impulse != imp)\r
45                                 continue;\r
46 \r
47                 ++c;\r
48 \r
49                 if(client_hasweapon(pl, weaponwant, TRUE, FALSE))\r
50                 {\r
51                         if(switchtonext)\r
52                                 return weaponwant;\r
53                         if(!first_valid)\r
54                                 first_valid = weaponwant;\r
55                         if(weaponwant == pl.switchweapon)\r
56                         {\r
57                                 if(dir >= 0)\r
58                                         switchtonext = 1;\r
59                                 else if(prev_valid)\r
60                                         return prev_valid;\r
61                                 else\r
62                                         switchtolast = 1;\r
63                         }\r
64                         prev_valid = weaponwant;\r
65                 }\r
66         }\r
67         if(first_valid)\r
68         {\r
69                 if(switchtolast)\r
70                         return prev_valid;\r
71                 else\r
72                         return first_valid;\r
73         }\r
74         // complain (but only for one weapon on the button that has been pressed)\r
75         if(complain)\r
76         {\r
77                 self.weaponcomplainindex += 1;\r
78                 c = mod(self.weaponcomplainindex, c) + 1;\r
79                 rest = weaponorder;\r
80                 while(rest != "")\r
81                 {\r
82                         weaponwant = stof(car(rest)); rest = cdr(rest);\r
83                         if(imp >= 0)\r
84                                 if((get_weaponinfo(weaponwant)).impulse != imp)\r
85                                         continue;\r
86 \r
87                         --c;\r
88                         if(c == 0)\r
89                         {\r
90                                 client_hasweapon(pl, weaponwant, TRUE, TRUE);\r
91                                 break;\r
92                         }\r
93                 }\r
94         }\r
95         return 0;\r
96 }\r
97 \r
98 void W_CycleWeapon(string weaponorder, float dir)\r
99 {\r
100         float w;\r
101         w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1);\r
102         if(w > 0)\r
103                 W_SwitchWeapon(w);\r
104 }\r
105 \r
106 void W_NextWeaponOnImpulse(float imp)\r
107 {\r
108         float w;\r
109         w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1);\r
110         if(w > 0)\r
111                 W_SwitchWeapon(w);\r
112 }\r
113 \r
114 // next weapon\r
115 void W_NextWeapon(float list)\r
116 {\r
117         if(list == 0)\r
118                 W_CycleWeapon(weaponpriority_hudselector_0, -1);\r
119         else if(list == 1)\r
120                 W_CycleWeapon(weaponpriority_hudselector_1, -1);\r
121         else if(list == 2)\r
122                 W_CycleWeapon(self.cvar_cl_weaponpriority, -1);\r
123 }\r
124 \r
125 // prev weapon\r
126 void W_PreviousWeapon(float list)\r
127 {\r
128         if(list == 0)\r
129                 W_CycleWeapon(weaponpriority_hudselector_0, +1);\r
130         else if(list == 1)\r
131                 W_CycleWeapon(weaponpriority_hudselector_1, +1);\r
132         else if(list == 2)\r
133                 W_CycleWeapon(self.cvar_cl_weaponpriority, +1);\r
134 }\r
135 \r
136 string W_FixWeaponOrder_AllowIncomplete(string order)\r
137 {\r
138         return W_FixWeaponOrder(order, 0);\r
139 }\r
140 \r
141 string W_FixWeaponOrder_ForceComplete(string order)\r
142 {\r
143         if(order == "")\r
144                 order = W_NumberWeaponOrder(cvar_string("cl_weaponpriority"));\r
145         return W_FixWeaponOrder(order, 1);\r
146 }\r
147 \r
148 float w_getbestweapon(entity e)\r
149 {\r
150         return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, 0);\r
151 };\r
152 \r
153 // generic weapons table\r
154 // TODO should they be macros instead?\r
155 float weapon_action(float wpn, float wrequest)\r
156 {\r
157         return (get_weaponinfo(wpn)).weapon_func(wrequest);\r
158 };\r
159 \r
160 string W_Name(float weaponid)\r
161 {\r
162         return (get_weaponinfo(weaponid)).message;\r
163 }\r
164 \r
165 float W_WeaponBit(float wpn)\r
166 {\r
167         return (get_weaponinfo(wpn)).weapons;\r
168 }\r
169 \r
170 float W_AmmoItemCode(float wpn)\r
171 {\r
172         return (get_weaponinfo(wpn)).items;\r
173 }\r
174 \r
175 void thrown_wep_think()\r
176 {\r
177         self.solid = SOLID_TRIGGER;\r
178         self.owner = world;\r
179         SUB_SetFade(self, time + 20, 1);\r
180 }\r
181 \r
182 // returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count\r
183 string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)\r
184 {\r
185         entity oldself, wep;\r
186         float wa, thisammo, i, j;\r
187         string s;\r
188         var .float ammofield;\r
189 \r
190         wep = spawn();\r
191 \r
192         setorigin(wep, org);\r
193         wep.classname = "droppedweapon";\r
194         wep.velocity = velo;\r
195         wep.owner = wep.enemy = own;\r
196         wep.flags |= FL_TOSSED;\r
197         wep.colormap = own.colormap;\r
198 \r
199         wa = W_AmmoItemCode(wpn);\r
200         if(wa == IT_SUPERWEAPON || wa == 0)\r
201         {\r
202                 oldself = self;\r
203                 self = wep;\r
204                 weapon_defaultspawnfunc(wpn);\r
205                 self = oldself;\r
206                 if(startitem_failed)\r
207                         return string_null;\r
208                 wep.colormod = wep.owner.colormod; // used by the regurgitating colors\r
209                 wep.glowmod = own.weaponentity_glowmod;\r
210                 wep.think = thrown_wep_think;\r
211                 wep.nextthink = time + 0.5;\r
212                 return "";\r
213         }\r
214         else\r
215         {\r
216                 s = "";\r
217                 oldself = self;\r
218                 self = wep;\r
219                 weapon_defaultspawnfunc(wpn);\r
220                 self = oldself;\r
221                 if(startitem_failed)\r
222                         return string_null;\r
223                 if(doreduce)\r
224                 {\r
225                         for(i = 0, j = 1; i < 24; ++i, j *= 2)\r
226                         {\r
227                                 if(wa & j)\r
228                                 {\r
229                                         ammofield = Item_CounterField(j);\r
230                                         thisammo = min(own.ammofield, wep.ammofield);\r
231                                         wep.ammofield = thisammo;\r
232                                         own.ammofield -= thisammo;\r
233                                         s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));\r
234 \r
235                                         // if our weapon is loaded, give its load back to the player\r
236                                         if(self.weapon_load[self.weapon] > 0)\r
237                                         {\r
238                                                 own.ammofield += self.weapon_load[self.weapon];\r
239                                                 self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading\r
240                                         }\r
241                                 }\r
242                         }\r
243                         s = substring(s, 5, -1);\r
244                 }\r
245                 wep.colormod = wep.owner.colormod; // used by the regurgitating colors\r
246                 wep.glowmod = own.weaponentity_glowmod;\r
247                 wep.think = thrown_wep_think;\r
248                 wep.nextthink = time + 0.5;\r
249                 return s;\r
250         }\r
251 }\r
252 \r
253 float W_IsWeaponThrowable(float w)\r
254 {\r
255         float wb, wa;\r
256         wb = W_WeaponBit(w);\r
257         if(!wb)\r
258                 return 0;\r
259         wa = W_AmmoItemCode(w);\r
260         if(start_weapons & wb)\r
261         {\r
262                 if(wa == IT_SUPERWEAPON && start_items & IT_UNLIMITED_SUPERWEAPONS)\r
263                         return 0;\r
264                 if(wa != IT_SUPERWEAPON && start_items & IT_UNLIMITED_WEAPON_AMMO)\r
265                         return 0;\r
266                 // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)\r
267                 if(wa == 0)\r
268                         return 0;\r
269         }\r
270 \r
271         return 1;\r
272 }\r
273 \r
274 // toss current weapon\r
275 void W_ThrowWeapon(vector velo, vector delta, float doreduce)\r
276 {\r
277         local float w, wb;\r
278         string a;\r
279 \r
280         w = self.weapon;\r
281         if (w == 0)\r
282                 return; // just in case\r
283         if(self.stat_eaten && self.dropweapon_check)\r
284                 return; // can't drop weapons from the stomach\r
285         if (g_lms)\r
286                 return;\r
287         if (!cvar("g_pickup_items"))\r
288                 return;\r
289         if(!cvar("g_weapon_throwable"))\r
290                 return;\r
291         if(cvar("g_weapon_stay") == 1)\r
292                 return;\r
293         if(!W_IsWeaponThrowable(w))\r
294                 return;\r
295 \r
296         wb = W_WeaponBit(w);\r
297         if(self.weapons & wb != wb)\r
298                 return;\r
299 \r
300         self.weapons &~= wb;\r
301         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
302         a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);\r
303         if not(a)\r
304                 return;\r
305         if(self.health >= 1)\r
306         {\r
307                 if(a == "")\r
308                         sprint(self, strcat("You dropped the ^2", W_Name(w), "\n"));\r
309                 else\r
310                         sprint(self, strcat("You dropped the ^2", W_Name(w), " with ", a, "\n"));\r
311         }\r
312 };\r
313 \r
314 // Bringed back weapon frame\r
315 void W_WeaponFrame()\r
316 {\r
317         vector fo, ri, up;\r
318 \r
319         if (frametime)\r
320                 self.weapon_frametime = frametime;\r
321 \r
322         if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))\r
323                 return;\r
324 \r
325         if (!self.weaponentity || self.health < 1)\r
326                 return; // Dead player can't use weapons and injure impulse commands\r
327 \r
328         if(!self.switchweapon)\r
329         {\r
330                 self.weapon = 0;\r
331                 self.weaponentity.state = WS_CLEAR;\r
332                 self.weaponname = "";\r
333                 self.items &~= IT_AMMO;\r
334                 return;\r
335         }\r
336 \r
337         makevectors(self.v_angle);\r
338         fo = v_forward; // save them in case the weapon think functions change it\r
339         ri = v_right;\r
340         up = v_up;\r
341 \r
342         // Change weapon\r
343         if (self.weapon != self.switchweapon)\r
344         {\r
345                 if (self.weaponentity.state == WS_CLEAR)\r
346                 {\r
347                         setanim(self, self.anim_draw, FALSE, TRUE, TRUE);\r
348                         self.weaponentity.state = WS_RAISE;\r
349                         weapon_action(self.switchweapon, WR_SETUP);\r
350 \r
351                         // set our clip load to the load of the weapon we switched to, if it's reloadable\r
352                         entity e;\r
353                         e = get_weaponinfo(self.switchweapon);\r
354                         if(e.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", e.netname, "_reload_ammo"))) // prevent accessing undefined cvars\r
355                         {\r
356                                 self.clip_load = self.weapon_load[self.switchweapon];\r
357                                 self.clip_size = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));\r
358                         }\r
359                         else\r
360                                 self.clip_load = self.clip_size = 0;\r
361 \r
362                         // VorteX: add player model weapon select frame here\r
363                         // setcustomframe(PlayerWeaponRaise);\r
364                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_weaponswitchdelay"), w_ready);\r
365                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');\r
366                 }\r
367                 else if (self.weaponentity.state == WS_READY)\r
368                 {\r
369 #ifndef INDEPENDENT_ATTACK_FINISHED\r
370                         if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)\r
371                         {\r
372 #endif\r
373                         // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds\r
374                         sound (self, CHAN_WEAPON2, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);\r
375                         self.weaponentity.state = WS_DROP;\r
376                         // set up weapon switch think in the future, and start drop anim\r
377                         weapon_thinkf(WFRAME_DONTCHANGE, cvar("g_balance_weaponswitchdelay"), w_clear);\r
378                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);\r
379 #ifndef INDEPENDENT_ATTACK_FINISHED\r
380                         }\r
381 #endif\r
382                 }\r
383         }\r
384 \r
385         // LordHavoc: network timing test code\r
386         //if (self.button0)\r
387         //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");\r
388 \r
389         float wb;\r
390         wb = W_WeaponBit(self.weapon);\r
391 \r
392         // call the think code which may fire the weapon\r
393         // and do so multiple times to resolve framerate dependency issues if the\r
394         // server framerate is very low and the weapon fire rate very high\r
395         local float c;\r
396         c = 0;\r
397         while (c < 5)\r
398         {\r
399                 c = c + 1;\r
400                 if(wb && ((self.weapons & wb) == 0))\r
401                 {\r
402                         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
403                         wb = 0;\r
404                 }\r
405                 if(wb)\r
406                 {\r
407                         v_forward = fo;\r
408                         v_right = ri;\r
409                         v_up = up;\r
410                         weapon_action(self.weapon, WR_THINK);\r
411                 }\r
412                 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)\r
413                 {\r
414                         if(self.weapon_think)\r
415                         {\r
416                                 v_forward = fo;\r
417                                 v_right = ri;\r
418                                 v_up = up;\r
419                                 self.weapon_think();\r
420                         }\r
421                         else\r
422                                 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");\r
423                 }\r
424         }\r
425 \r
426         // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!)\r
427         //if (ATTACK_FINISHED(self) < time)\r
428         //      ATTACK_FINISHED(self) = time;\r
429 \r
430         //if (self.weapon_nextthink < time)\r
431         //      self.weapon_nextthink = time;\r
432 };\r
433 \r