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