]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/cl_weapons.qc
0cd918caae428cef85f8c7320833753e3d5bd0cd
[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(!cvar("g_weapon_throwable"))\r
266                 return;\r
267         if(cvar("g_weapon_stay") == 1)\r
268                 return;\r
269         if(!W_IsWeaponThrowable(w))\r
270                 return;\r
271 \r
272         wb = W_WeaponBit(w);\r
273         if(self.weapons & wb != wb)\r
274                 return;\r
275 \r
276         self.weapons &~= wb;\r
277         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
278         a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);\r
279         if not(a)\r
280                 return;\r
281         if(self.health >= 1)\r
282         {\r
283                 if(a == "")\r
284                         sprint(self, strcat("You dropped the ^2", W_Name(w), "\n"));\r
285                 else\r
286                         sprint(self, strcat("You dropped the ^2", W_Name(w), " with ", a, "\n"));\r
287         }\r
288 };\r
289 \r
290 // Bringed back weapon frame\r
291 void W_WeaponFrame()\r
292 {\r
293         vector fo, ri, up;\r
294 \r
295         if (frametime)\r
296                 self.weapon_frametime = frametime;\r
297 \r
298         if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))\r
299                 return;\r
300 \r
301         if (!self.weaponentity || self.health < 1)\r
302                 return; // Dead player can't use weapons and injure impulse commands\r
303 \r
304         if(!self.switchweapon)\r
305         {\r
306                 self.weapon = 0;\r
307                 self.weaponentity.state = WS_CLEAR;\r
308                 self.weaponname = "";\r
309                 self.items &~= IT_AMMO;\r
310                 return;\r
311         }\r
312 \r
313         makevectors(self.v_angle);\r
314         fo = v_forward; // save them in case the weapon think functions change it\r
315         ri = v_right;\r
316         up = v_up;\r
317 \r
318         // Change weapon\r
319         if (self.weapon != self.switchweapon)\r
320         {\r
321                 if (self.weaponentity.state == WS_CLEAR)\r
322                 {\r
323                         setanim(self, self.anim_draw, FALSE, TRUE, TRUE);\r
324                         self.weaponentity.state = WS_RAISE;\r
325                         weapon_action(self.switchweapon, WR_SETUP);\r
326                         // VorteX: add player model weapon select frame here\r
327                         // setcustomframe(PlayerWeaponRaise);\r
328                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_weaponswitchdelay"), w_ready);\r
329                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');\r
330                 }\r
331                 else if (self.weaponentity.state == WS_READY)\r
332                 {\r
333 #ifndef INDEPENDENT_ATTACK_FINISHED\r
334                         if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)\r
335                         {\r
336 #endif\r
337                         // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds\r
338                         sound (self, CHAN_WEAPON2, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);\r
339                         self.weaponentity.state = WS_DROP;\r
340                         // set up weapon switch think in the future, and start drop anim\r
341                         weapon_thinkf(WFRAME_DONTCHANGE, cvar("g_balance_weaponswitchdelay"), w_clear);\r
342                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);\r
343 #ifndef INDEPENDENT_ATTACK_FINISHED\r
344                         }\r
345 #endif\r
346                 }\r
347         }\r
348 \r
349         // LordHavoc: network timing test code\r
350         //if (self.button0)\r
351         //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");\r
352 \r
353         float wb;\r
354         wb = W_WeaponBit(self.weapon);\r
355 \r
356         // call the think code which may fire the weapon\r
357         // and do so multiple times to resolve framerate dependency issues if the\r
358         // server framerate is very low and the weapon fire rate very high\r
359         local float c;\r
360         c = 0;\r
361         while (c < 5)\r
362         {\r
363                 c = c + 1;\r
364                 if(wb && ((self.weapons & wb) == 0))\r
365                 {\r
366                         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
367                         wb = 0;\r
368                 }\r
369                 if(wb)\r
370                 {\r
371                         v_forward = fo;\r
372                         v_right = ri;\r
373                         v_up = up;\r
374                         weapon_action(self.weapon, WR_THINK);\r
375                 }\r
376                 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)\r
377                 {\r
378                         if(self.weapon_think)\r
379                         {\r
380                                 v_forward = fo;\r
381                                 v_right = ri;\r
382                                 v_up = up;\r
383                                 self.weapon_think();\r
384                         }\r
385                         else\r
386                                 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");\r
387                 }\r
388         }\r
389 \r
390         // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!)\r
391         //if (ATTACK_FINISHED(self) < time)\r
392         //      ATTACK_FINISHED(self) = time;\r
393 \r
394         //if (self.weapon_nextthink < time)\r
395         //      self.weapon_nextthink = time;\r
396 };\r
397 \r