]> git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/cl_weapons.qc
Lastly, show portraits when they need to be shown, and sent the right player. Current...
[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.glowmod = own.weaponentity_glowmod;\r
206                 wep.think = thrown_wep_think;\r
207                 wep.nextthink = time + 0.5;\r
208                 return "";\r
209         }\r
210         else\r
211         {\r
212                 s = "";\r
213                 oldself = self;\r
214                 self = wep;\r
215                 weapon_defaultspawnfunc(wpn);\r
216                 self = oldself;\r
217                 if(startitem_failed)\r
218                         return string_null;\r
219                 if(doreduce)\r
220                 {\r
221                         for(i = 0, j = 1; i < 24; ++i, j *= 2)\r
222                         {\r
223                                 if(wa & j)\r
224                                 {\r
225                                         ammofield = Item_CounterField(j);\r
226                                         thisammo = min(own.ammofield, wep.ammofield);\r
227                                         wep.ammofield = thisammo;\r
228                                         own.ammofield -= thisammo;\r
229                                         s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));\r
230 \r
231                                         // if our weapon is loaded, give its load back to the player\r
232                                         if(self.weapon_load[self.weapon] > 0)\r
233                                         {\r
234                                                 own.ammofield += self.weapon_load[self.weapon];\r
235                                                 self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading\r
236                                         }\r
237                                 }\r
238                         }\r
239                         s = substring(s, 5, -1);\r
240                 }\r
241                 wep.colormod = wep.owner.colormod; // used by the regurgitating colors\r
242                 wep.glowmod = own.weaponentity_glowmod;\r
243                 wep.think = thrown_wep_think;\r
244                 wep.nextthink = time + 0.5;\r
245                 return s;\r
246         }\r
247 }\r
248 \r
249 float W_IsWeaponThrowable(float w)\r
250 {\r
251         float wb, wa;\r
252         wb = W_WeaponBit(w);\r
253         if(!wb)\r
254                 return 0;\r
255         wa = W_AmmoItemCode(w);\r
256         if(start_weapons & wb)\r
257         {\r
258                 if(wa == IT_SUPERWEAPON && start_items & IT_UNLIMITED_SUPERWEAPONS)\r
259                         return 0;\r
260                 if(wa != IT_SUPERWEAPON && start_items & IT_UNLIMITED_WEAPON_AMMO)\r
261                         return 0;\r
262                 // 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
263                 if(wa == 0)\r
264                         return 0;\r
265         }\r
266 \r
267         return 1;\r
268 }\r
269 \r
270 // toss current weapon\r
271 void W_ThrowWeapon(vector velo, vector delta, float doreduce)\r
272 {\r
273         local float w, wb;\r
274         string a;\r
275 \r
276         w = self.weapon;\r
277         if (w == 0)\r
278                 return; // just in case\r
279         if(self.stat_eaten && self.dropweapon_check)\r
280                 return; // can't drop weapons from the stomach\r
281         if (g_lms)\r
282                 return;\r
283         if (!cvar("g_pickup_items"))\r
284                 return;\r
285         if(!cvar("g_weapon_throwable"))\r
286                 return;\r
287         if(cvar("g_weapon_stay") == 1)\r
288                 return;\r
289         if(!W_IsWeaponThrowable(w))\r
290                 return;\r
291 \r
292         wb = W_WeaponBit(w);\r
293         if(self.weapons & wb != wb)\r
294                 return;\r
295 \r
296         self.weapons &~= wb;\r
297         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
298         a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);\r
299         if not(a)\r
300                 return;\r
301         if(self.health >= 1)\r
302         {\r
303                 if(a == "")\r
304                         sprint(self, strcat("You dropped the ^2", W_Name(w), "\n"));\r
305                 else\r
306                         sprint(self, strcat("You dropped the ^2", W_Name(w), " with ", a, "\n"));\r
307         }\r
308 };\r
309 \r
310 // Bringed back weapon frame\r
311 void W_WeaponFrame()\r
312 {\r
313         vector fo, ri, up;\r
314 \r
315         if (frametime)\r
316                 self.weapon_frametime = frametime;\r
317 \r
318         if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))\r
319                 return;\r
320 \r
321         if (!self.weaponentity || self.health < 1)\r
322                 return; // Dead player can't use weapons and injure impulse commands\r
323 \r
324         if(!self.switchweapon)\r
325         {\r
326                 self.weapon = 0;\r
327                 self.weaponentity.state = WS_CLEAR;\r
328                 self.weaponname = "";\r
329                 self.items &~= IT_AMMO;\r
330                 return;\r
331         }\r
332 \r
333         makevectors(self.v_angle);\r
334         fo = v_forward; // save them in case the weapon think functions change it\r
335         ri = v_right;\r
336         up = v_up;\r
337 \r
338         // Change weapon\r
339         if (self.weapon != self.switchweapon)\r
340         {\r
341                 if (self.weaponentity.state == WS_CLEAR)\r
342                 {\r
343                         setanim(self, self.anim_draw, FALSE, TRUE, TRUE);\r
344                         self.weaponentity.state = WS_RAISE;\r
345                         weapon_action(self.switchweapon, WR_SETUP);\r
346 \r
347                         // set our clip load to the load of the weapon we switched to, if it's reloadable\r
348                         entity e;\r
349                         e = get_weaponinfo(self.switchweapon);\r
350                         if(e.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", e.netname, "_reload_ammo"))) // prevent accessing undefined cvars\r
351                         {\r
352                                 self.clip_load = self.weapon_load[self.switchweapon];\r
353                                 self.clip_size = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));\r
354                         }\r
355                         else\r
356                                 self.clip_load = self.clip_size = 0;\r
357 \r
358                         // VorteX: add player model weapon select frame here\r
359                         // setcustomframe(PlayerWeaponRaise);\r
360                         weapon_thinkf(WFRAME_IDLE, cvar("g_balance_weaponswitchdelay"), w_ready);\r
361                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0');\r
362                 }\r
363                 else if (self.weaponentity.state == WS_READY)\r
364                 {\r
365 #ifndef INDEPENDENT_ATTACK_FINISHED\r
366                         if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)\r
367                         {\r
368 #endif\r
369                         // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds\r
370                         sound (self, CHAN_WEAPON2, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);\r
371                         self.weaponentity.state = WS_DROP;\r
372                         // set up weapon switch think in the future, and start drop anim\r
373                         weapon_thinkf(WFRAME_DONTCHANGE, cvar("g_balance_weaponswitchdelay"), w_clear);\r
374                         weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE);\r
375 #ifndef INDEPENDENT_ATTACK_FINISHED\r
376                         }\r
377 #endif\r
378                 }\r
379         }\r
380 \r
381         // LordHavoc: network timing test code\r
382         //if (self.button0)\r
383         //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");\r
384 \r
385         float wb;\r
386         wb = W_WeaponBit(self.weapon);\r
387 \r
388         // call the think code which may fire the weapon\r
389         // and do so multiple times to resolve framerate dependency issues if the\r
390         // server framerate is very low and the weapon fire rate very high\r
391         local float c;\r
392         c = 0;\r
393         while (c < 5)\r
394         {\r
395                 c = c + 1;\r
396                 if(wb && ((self.weapons & wb) == 0))\r
397                 {\r
398                         W_SwitchWeapon_Force(self, w_getbestweapon(self));\r
399                         wb = 0;\r
400                 }\r
401                 if(wb)\r
402                 {\r
403                         v_forward = fo;\r
404                         v_right = ri;\r
405                         v_up = up;\r
406                         weapon_action(self.weapon, WR_THINK);\r
407                 }\r
408                 if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)\r
409                 {\r
410                         if(self.weapon_think)\r
411                         {\r
412                                 v_forward = fo;\r
413                                 v_right = ri;\r
414                                 v_up = up;\r
415                                 self.weapon_think();\r
416                         }\r
417                         else\r
418                                 bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");\r
419                 }\r
420         }\r
421 \r
422         // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!)\r
423         //if (ATTACK_FINISHED(self) < time)\r
424         //      ATTACK_FINISHED(self) = time;\r
425 \r
426         //if (self.weapon_nextthink < time)\r
427         //      self.weapon_nextthink = time;\r
428 };\r
429 \r