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