]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/cl_weapons.qc
Include gmqcc binaries for Windows and Linux
[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 .float display_setup;\r
315 void W_DisplayDigitThink()\r
316 {\r
317         self.nextthink = time;\r
318         float w_load, w_ammo;\r
319         w_load = self.owner.weapon_load[self.owner.weapon];\r
320         w_ammo = self.owner.(self.owner.current_ammo);\r
321 \r
322         // the owner has switched to another weapon, remove the digits\r
323         if(!self.owner.display_setup || !self.owner.weapon || self.owner.deadflag != DEAD_NO)\r
324         {\r
325                 self.nextthink = 0;\r
326                 remove(self);\r
327                 self = world;\r
328                 return;\r
329         }\r
330 \r
331         // copy all properties of the weapon to the digit\r
332         self.origin = self.weaponentity.origin;\r
333         self.angles = self.weaponentity.angles;\r
334         self.scale = self.weaponentity.scale;\r
335         self.effects = self.weaponentity.effects;\r
336         self.alpha = self.weaponentity.alpha;\r
337         self.colormap = self.weaponentity.colormap;\r
338         self.colormod = self.weaponentity.colormod; // used by the regurgitating colors\r
339         self.glowmod = self.weaponentity.glowmod;\r
340 \r
341         string txt;\r
342         if(self.team) // weapon load display\r
343         {\r
344                 if(w_load <= 0)\r
345                 {\r
346                         self.skin = 11; // unavailable digit\r
347                         return;\r
348                 }\r
349                 else\r
350                 {\r
351                         txt = ftos(floor(w_load));\r
352                         txt = substring(txt, self.cnt - 1, 1);\r
353                 }\r
354 \r
355                 entity e;\r
356                 e = get_weaponinfo(self.owner.weapon);\r
357                 if(self.owner.weapon_load[self.owner.weapon] <= cvar(strcat("g_gundisplay_warn_weapon_", e.netname)))\r
358                 {\r
359                         // in warning mode, only keep red color\r
360                         if(!self.colormod)\r
361                                 self.colormod = '1 1 1';\r
362                         self.colormod_y = 0;\r
363                         self.colormod_z = 0;\r
364                 }\r
365         }\r
366         else // ammo display\r
367         {\r
368                 txt = ftos(floor(w_ammo));\r
369                 txt = substring(txt, self.cnt - 1, 1);\r
370 \r
371                 if(w_ammo <= cvar(strcat("g_gundisplay_warn_ammo_", Item_CounterFieldName(W_AmmoItemCode(self.owner.weapon)))))\r
372                 {\r
373                         // in warning mode, only keep red color\r
374                         if(!self.colormod)\r
375                                 self.colormod = '1 1 1';\r
376                         self.colormod_y = 0;\r
377                         self.colormod_z = 0;\r
378                 }\r
379         }\r
380 \r
381         if((!txt || txt == ""))\r
382                 self.skin = 10; // empty digit\r
383         else\r
384                 self.skin = stof(txt);\r
385 }\r
386 \r
387 void W_DisplayDigitSetup(entity own, float num, float load, float exterior)\r
388 {\r
389         entity digit, e;\r
390         digit = spawn();\r
391         digit.owner = own;\r
392         //digit.weapon = own.weapon;\r
393         digit.team = load;\r
394         digit.cnt = num;\r
395         e = get_weaponinfo(own.weapon);\r
396 \r
397         if(exterior) // exterior weapon\r
398         {\r
399                 // keep the digit attached to the same bone as the gun\r
400                 setattachment(digit, digit.owner, "bip01 r hand");\r
401                 digit.weaponentity = digit.owner.exteriorweaponentity;\r
402         }\r
403         else // view weapon\r
404         {\r
405                 // keep the digit attached to the same bone as the gun\r
406                 // TODO: Does this work with self-animated weapons too?\r
407                 if(gettagindex(digit.owner.weaponentity, "weapon"))\r
408                         setattachment(digit, digit.owner.weaponentity, "weapon");\r
409                 else if(gettagindex(digit.owner.weaponentity, "tag_weapon"))\r
410                         setattachment(digit, digit.owner.weaponentity, "tag_weapon");\r
411                 digit.weaponentity = digit.owner.weaponentity;\r
412         }\r
413 \r
414         if(load)\r
415         {\r
416                 // weapon load digit\r
417                 setmodel(digit, strcat("models/weapons/v_", e.netname, "_digit1-", ftos(num) , ".md3"));\r
418         }\r
419         else\r
420         {\r
421                 // ammo count digit\r
422                 setmodel(digit, strcat("models/weapons/v_", e.netname, "_digit2-", ftos(num) , ".md3"));\r
423         }\r
424         digit.think = W_DisplayDigitThink;\r
425         digit.nextthink = time;\r
426 }\r
427 \r
428 // Bringed back weapon frame\r
429 void W_WeaponFrame()\r
430 {\r
431         vector fo, ri, up;\r
432         entity e;\r
433 \r
434         if (frametime)\r
435                 self.weapon_frametime = frametime;\r
436 \r
437         if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))\r
438                 return;\r
439 \r
440         if (!self.weaponentity || self.health < 1)\r
441                 return; // Dead player can't use weapons and injure impulse commands\r
442 \r
443         if(!self.switchweapon)\r
444         {\r
445                 self.weapon = 0;\r
446                 self.weaponentity.state = WS_CLEAR;\r
447                 self.weaponname = "";\r
448                 self.items &~= IT_AMMO;\r
449                 return;\r
450         }\r
451 \r
452         makevectors(self.v_angle);\r
453         fo = v_forward; // save them in case the weapon think functions change it\r
454         ri = v_right;\r
455         up = v_up;\r
456 \r
457         // setup weapon display digits\r
458         if(self.weapon != self.switchweapon || self.classname != "player")\r
459                 self.display_setup = FALSE;\r
460         else if(!self.display_setup)\r
461         {\r
462                 if(self.weaponentity.modelindex && self.exteriorweaponentity.modelindex)\r
463                 {\r
464                         float i;\r
465                         e = get_weaponinfo(self.weapon);\r
466 \r
467                         for(i = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit1-", ftos(i) , ".md3")); i++)\r
468                         {\r
469                                 W_DisplayDigitSetup(self, i, TRUE, FALSE); // weapon load digit, view model\r
470                                 W_DisplayDigitSetup(self, i, TRUE, TRUE); // weapon load digit, exterior model\r
471                         }\r
472                         for(i = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit2-", ftos(i) , ".md3")); i++)\r
473                         {\r
474                                 W_DisplayDigitSetup(self, i, FALSE, FALSE); // ammo count digit, view model\r
475                                 W_DisplayDigitSetup(self, i, FALSE, TRUE); // ammo count digit, exterior model\r
476                         }\r
477 \r
478                         self.display_setup = TRUE;\r
479                 }\r
480         }\r
481 \r
482         // Change weapon\r
483         if (self.weapon != self.switchweapon)\r
484         {\r
485                 if (self.weaponentity.state == WS_CLEAR)\r
486                 {\r
487                         setanim(self, self.anim_draw, FALSE, TRUE, TRUE);\r
488                         self.weaponentity.state = WS_RAISE;\r
489                         weapon_action(self.switchweapon, WR_SETUP);\r
490 \r
491                         // set our clip load to the load of the weapon we switched to, if it's reloadable\r
492                         e = get_weaponinfo(self.switchweapon);\r
493                         if(e.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", e.netname, "_reload_ammo"))) // prevent accessing undefined cvars\r
494                         {\r
495                                 self.clip_load = self.weapon_load[self.switchweapon];\r
496                                 self.clip_size = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));\r
497                         }\r
498                         else\r
499                                 self.clip_load = self.clip_size = 0;\r
500 \r
501                         // VorteX: add player model weapon select frame here\r
502                         // setcustomframe(PlayerWeaponRaise);\r
503                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_weaponswitchdelay"), w_ready);\r
504                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');\r
505                 }\r
506                 else if (self.weaponentity.state == WS_READY)\r
507                 {\r
508 #ifndef INDEPENDENT_ATTACK_FINISHED\r
509                         if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)\r
510                         {\r
511 #endif\r
512                         // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds\r
513                         sound (self, CHAN_WEAPON2, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);\r
514                         self.weaponentity.state = WS_DROP;\r
515                         // set up weapon switch think in the future, and start drop anim\r
516                         weapon_thinkf(WFRAME_DONTCHANGE, cvar("g_balance_weaponswitchdelay"), w_clear);\r
517                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);\r
518 #ifndef INDEPENDENT_ATTACK_FINISHED\r
519                         }\r
520 #endif\r
521                 }\r
522         }\r
523 \r
524         // LordHavoc: network timing test code\r
525         //if (self.button0)\r
526         //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");\r
527 \r
528         float wb;\r
529         wb = W_WeaponBit(self.weapon);\r
530 \r
531         // call the think code which may fire the weapon\r
532         // and do so multiple times to resolve framerate dependency issues if the\r
533         // server framerate is very low and the weapon fire rate very high\r
534         local float c;\r
535         c = 0;\r
536         while (c < 5)\r
537         {\r
538                 c = c + 1;\r
539                 if(wb && ((self.weapons & wb) == 0))\r
540                 {\r
541                         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
542                         wb = 0;\r
543                 }\r
544                 if(wb)\r
545                 {\r
546                         v_forward = fo;\r
547                         v_right = ri;\r
548                         v_up = up;\r
549                         weapon_action(self.weapon, WR_THINK);\r
550                 }\r
551                 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)\r
552                 {\r
553                         if(self.weapon_think)\r
554                         {\r
555                                 v_forward = fo;\r
556                                 v_right = ri;\r
557                                 v_up = up;\r
558                                 self.weapon_think();\r
559                         }\r
560                         else\r
561                                 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");\r
562                 }\r
563         }\r
564 \r
565         // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!)\r
566         //if (ATTACK_FINISHED(self) < time)\r
567         //      ATTACK_FINISHED(self) = time;\r
568 \r
569         //if (self.weapon_nextthink < time)\r
570         //      self.weapon_nextthink = time;\r
571 };\r
572 \r