]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/cl_weapons.c
fix weapon bobbing/pitching code to take into account the weapon entity
[divverent/nexuiz.git] / data / qcsrc / server / gamec / cl_weapons.c
1 // generic weapons table
2 // add new weapons here
3 void(float wpn, float wrequest) weapon_action =
4 {
5         if (wpn == WEP_LASER)
6                 w_laser(wrequest);
7         else if (wpn == WEP_SHOTGUN)
8                 w_shotgun(wrequest);
9         else if (wpn == WEP_UZI)
10                 w_uzi(wrequest);
11         else if (wpn == WEP_GRENADE_LAUNCHER)
12                 w_glauncher(wrequest);
13         else if (wpn == WEP_ELECTRO)
14                 w_electro(wrequest);
15         else if (wpn == WEP_CRYLINK)
16                 w_crylink(wrequest);
17         else if (wpn == WEP_NEX)
18                 w_nex(wrequest);
19         else if (wpn == WEP_HAGAR)
20                 w_hagar(wrequest);
21         else if (wpn == WEP_ROCKET_LAUNCHER)
22                 w_rlauncher(wrequest);
23 };
24
25 // think function for tossed weapons
26 void() thrown_wep_think
27 {
28         self.solid = SOLID_TRIGGER;
29         self.owner = world;
30         SUB_SetFade(self, time + 20, 1);
31         setorigin(self, self.origin);
32 };
33
34 // toss current weapon
35 void() W_ThrowWeapon
36 {
37         local float w, ammo;
38         local entity wep, e;
39
40         e = self;
41         wep = spawn();
42         self = wep;
43         w = e.weapon;
44         setorigin(wep, e.origin);
45         makevectors(e.angles);
46         wep.classname = "droppedweapon";
47         wep.velocity = e.velocity * 0.5 + v_forward * 750;
48         SUB_SetFade(wep, time + 20, 1);
49
50         if(w == WEP_SHOTGUN)
51         {
52                 w = IT_SHOTGUN;
53                 if(!(e.items & w))
54                 {
55                         remove(wep);
56                         return;
57                 }
58                 weapon_shotgun();
59                 ammo = min(e.ammo_shells, wep.ammo_shells);
60                 wep.ammo_shells = ammo;
61                 e.ammo_shells -= ammo;
62         }
63         else if(w == WEP_UZI)
64         {
65                 w = IT_UZI;
66                 if(!(e.items & w))
67                 {
68                         remove(wep);
69                         return;
70                 }
71                 weapon_uzi();
72                 ammo = min(e.ammo_nails, wep.ammo_nails);
73                 wep.ammo_nails = ammo;
74                 e.ammo_nails -= ammo;
75         }
76         else if(w == WEP_GRENADE_LAUNCHER)
77         {
78                 w = IT_GRENADE_LAUNCHER;
79                 if(!(e.items & w))
80                 {
81                         remove(wep);
82                         return;
83                 }
84                 weapon_grenadelauncher();
85                 ammo = min(e.ammo_rockets, wep.ammo_rockets);
86                 wep.ammo_rockets = ammo;
87                 e.ammo_rockets -= ammo;
88         }
89         else if(w == WEP_ELECTRO)
90         {
91                 w = IT_ELECTRO;
92                 if(!(e.items & w))
93                 {
94                         remove(wep);
95                         return;
96                 }
97                 weapon_electro();
98                 ammo = min(e.ammo_cells, wep.ammo_cells);
99                 wep.ammo_cells = ammo;
100                 e.ammo_cells -= ammo;
101         }
102         else if(w == WEP_CRYLINK)
103         {
104                 w = IT_CRYLINK;
105                 if(!(e.items & w))
106                 {
107                         remove(wep);
108                         return;
109                 }
110                 weapon_crylink();
111                 ammo = min(e.ammo_cells, wep.ammo_cells);
112                 wep.ammo_cells = ammo;
113                 e.ammo_cells -= ammo;
114         }
115         else if(w == WEP_NEX)
116         {
117                 w = IT_NEX;
118                 if(!(e.items & w))
119                 {
120                         remove(wep);
121                         return;
122                 }
123                 weapon_nex();
124                 ammo = min(e.ammo_cells, wep.ammo_cells);
125                 wep.ammo_cells = ammo;
126                 e.ammo_cells -= ammo;
127         }
128         else if(w == WEP_HAGAR)
129         {
130                 w = IT_HAGAR;
131                 if(!(e.items & w))
132                 {
133                         remove(wep);
134                         return;
135                 }
136                 weapon_hagar();
137                 ammo = min(e.ammo_rockets, wep.ammo_rockets);
138                 wep.ammo_rockets = ammo;
139                 e.ammo_rockets -= ammo;
140         }
141         else if(w == WEP_ROCKET_LAUNCHER)
142         {
143                 w = IT_ROCKET_LAUNCHER;
144                 if(!(e.items & w))
145                 {
146                         remove(wep);
147                         return;
148                 }
149                 weapon_rocketlauncher();
150                 ammo = min(e.ammo_rockets, wep.ammo_rockets);
151                 wep.ammo_rockets = ammo;
152                 e.ammo_rockets -= ammo;
153         }
154
155         if(e.items & w)
156                 sprint(e, strcat("You dropped the ^2", wep.netname, "\n"));
157         wep.owner = e;
158         setorigin(wep, wep.origin);
159         wep.nextthink = time + 0.5;
160         wep.think = thrown_wep_think;
161         wep.classname = "droppedweapon";
162         e.items = e.items - (e.items & w);
163         e.switchweapon = w_getbestweapon(e);
164         wep.colormap = e.colormap;
165         if (e.switchweapon != e.weapon)
166                 e.cnt = e.weapon;
167         self = e;
168 };
169
170 // switch between weapons
171 void(float imp) W_SwitchWeapon
172 {
173         weapon_hasammo = TRUE;
174         if (!client_hasweapon(self, imp, TRUE))
175         {
176                 if (!weapon_hasammo)
177                         sprint(self, "You don't have any ammo for that weapon\n");
178                 else
179                         sprint(self, "You don't own that weapon\n");
180         }
181         else
182         {
183                 self.cnt = self.weapon;
184                 self.switchweapon = imp;
185         }
186 };
187
188 // next weapon
189 void() W_NextWeapon =
190 {
191         local float weaponwant, maxtries;
192
193         maxtries = WEP_LAST;
194
195         weaponwant = self.switchweapon + 1;
196         if (weaponwant < WEP_FIRST)
197                 weaponwant = WEP_LAST;
198         if (weaponwant > WEP_LAST)
199                 weaponwant = WEP_FIRST;
200         weapon_hasammo = TRUE;
201         while(!client_hasweapon(self, weaponwant, TRUE))
202         {
203                 if(!maxtries)
204                         return;
205
206                 maxtries -= 1;
207                 weaponwant = weaponwant + 1;
208                 if (weaponwant < WEP_FIRST)
209                         weaponwant = WEP_LAST;
210                 if (weaponwant > WEP_LAST)
211                         weaponwant = WEP_FIRST;
212         }
213         self.cnt = self.weapon;
214         self.switchweapon = weaponwant;
215 };
216
217 // prev weapon
218 void() W_PreviousWeapon =
219 {
220         local float weaponwant, maxtries;
221
222         maxtries = WEP_LAST;
223
224         weaponwant = self.switchweapon - 1;
225         if (weaponwant < WEP_FIRST)
226                 weaponwant = WEP_LAST;
227         if (weaponwant > WEP_LAST)
228                 weaponwant = WEP_FIRST;
229         weapon_hasammo = TRUE;
230         while(!client_hasweapon(self, weaponwant, TRUE))
231         {
232                 if(!maxtries)
233                         return;
234
235                 maxtries -= 1;
236                 weaponwant = weaponwant - 1;
237                 if (weaponwant < WEP_FIRST)
238                         weaponwant = WEP_LAST;
239                 if (weaponwant > WEP_LAST)
240                         weaponwant = WEP_FIRST;
241         }
242         self.cnt = self.weapon;
243         self.switchweapon = weaponwant;
244 };
245
246 // Bringed back weapon frame
247 void() W_WeaponFrame =
248 {
249         if(arena_roundbased)
250         if(time < self.arena_warmup_end)
251                 return;
252
253         if (!self.weaponentity || self.health < 1)
254                 return; // Dead player can't use weapons and injure impulse commands
255
256         if(!self.switchweapon)
257         {
258                 self.weapon = 0;
259                 self.weaponentity.state = 0;
260                 return;
261         }
262
263         makevectors(self.v_angle);
264
265         // Change weapon
266         if (self.weapon != self.switchweapon)
267         {
268                 if (self.weaponentity.state == WS_CLEAR)
269                 {
270                         self.weaponentity.state = WS_RAISE;
271                         weapon_action(self.switchweapon, WR_SETUP);
272                         // VorteX: add player model weapon select frame here
273                         // setcustomframe(PlayerWeaponRaise);
274                         weapon_action(self.weapon, WR_UPDATECOUNTS);
275                         weapon_action(self.weapon, WR_RAISE);
276                 }
277                 else if (self.weaponentity.state == WS_READY)
278                 {
279                         sound (self, CHAN_WEAPON, "weapons/weapon_switch.ogg", 1, ATTN_NORM);
280                         self.weaponentity.state = WS_DROP;
281                         // VorteX: add player model weapon deselect frame here
282                         // setcustomframe(PlayerWeaponDrop);
283                         weapon_action(self.weapon, WR_DROP);
284                 }
285         }
286
287         if (self.button0)
288                 weapon_action(self.weapon, WR_FIRE1);
289         if (self.button3)
290                 weapon_action(self.weapon, WR_FIRE2);
291
292         // do weapon think
293         if (time >= self.weapon_nextthink)
294                 if (self.weapon_nextthink > 0)
295                         self.weapon_think();
296
297         // weapon bobbing and script actions
298         local float bobintensity, q1pitching, framespeed, diff, modelscale;
299         local vector vel, realorg, layer1, boblayer;
300
301         modelscale = self.weaponentity.scale;
302         if (!modelscale)
303                 modelscale = 1;
304         bobintensity = cvar("g_viewweapon_bobintensity") * modelscale; // weapon bob intensity
305         q1pitching = fabs(cvar("g_viewweapon_q1pitching")) * modelscale; // q1 style of "bob" when looking up and down
306
307         realorg = self.weaponentity.origin + self.weaponentity.view_ofs;
308         realorg = realorg - self.weaponentity.finaldest; // finaldest is last bob position
309
310         // VorteX: actually this is needed for weapon screen offset
311         if (q1pitching)
312         {
313                 self.weaponentity.view_ofs_x = q1pitching*bound(-5.5, self.v_angle_x/45, 5.5);
314                 self.weaponentity.view_ofs_z = q1pitching*bound(-1.5, self.v_angle_x/60, 1.5);
315         }
316
317         // weapon origin interpolation, layer 1
318         if (realorg != self.weaponentity.pos1)
319         {
320                 framespeed = frametime*self.weaponentity.lip*10; // lip is speed of origin changing (of layer1)
321                 diff = vlen(realorg - self.weaponentity.pos1);
322                 // VorteX: add speed modifier (haste)?
323                 layer1 = frametime*10*self.weaponentity.lip*normalize(self.weaponentity.pos1 - realorg);
324                 if (diff <= vlen(layer1))
325                         layer1 = normalize(self.weaponentity.pos1 - realorg)*diff;
326         }
327
328         // weapon bobbing (q3-style)
329         if (self.flags & FL_ONGROUND && self.waterlevel < 2)
330         {
331                 // VorteX: only xy velocity matters
332                 vel_x = self.velocity_x;
333                 vel_y = self.velocity_y;
334                 framespeed = vlen(vel);
335                 // Y axis
336                 diff = bobintensity*framespeed/300;
337                 self.weaponentity.destvec_y = self.weaponentity.destvec_y + frametime*10;
338                 boblayer_y = diff*cos(self.weaponentity.destvec_y + 90);
339                 // Z axis
340                 diff = bobintensity*framespeed/540;
341                 self.weaponentity.destvec_z = self.weaponentity.destvec_z + frametime*20;
342                 boblayer_z = diff*cos(self.weaponentity.destvec_z);
343                 self.weaponentity.finaldest = boblayer;
344         }
345         else if (self.waterlevel > 0)
346         {// swim, all velocity matters
347                 // X axis
348                 framespeed = vlen(self.velocity);
349                 diff = bobintensity*framespeed/100;
350                 self.weaponentity.destvec_x = self.weaponentity.destvec_x + frametime*6;
351                 boblayer_x = diff*cos(self.weaponentity.destvec_x);
352                 self.weaponentity.finaldest = boblayer;
353         }
354         else
355                 self.weaponentity.finaldest = '0 0 0';
356         self.weaponentity.origin = realorg + boblayer + layer1 - self.weaponentity.view_ofs;
357 };
358
359 float nixnex_weapon;
360 float nixnex_nextchange;
361 float nixnex_nextweapon;
362 .float nixnex_lastchange_id;
363 .float nixnex_lastinfotime;
364 .float nixnex_nextincr;
365
366 void Nixnex_ChooseNextWeapon()
367 {
368         float numberof, id;
369         numberof = WEP_LAST - WEP_FIRST; // all but the current one
370         if(cvar("g_nixnex_with_laser"))
371                 numberof = numberof - 1;
372         id = WEP_FIRST + ceil(random() * numberof) - 1;
373
374         if(cvar("g_nixnex_with_laser")) // skip the laser if needed
375                 id = id + 1;
376
377         if(id >= nixnex_weapon) // skip the current weapon
378                 id = id + 1;
379
380         if(id < WEP_FIRST) // can't happen, but to be sure...
381         {
382                 dprint("Won't happen (id < WEP_FIRST)\n");
383                 id = WEP_FIRST;
384         }
385         if(id > WEP_LAST) // either
386         {
387                 dprint("Won't happen (id > WEP_LAST)\n");
388                 id = WEP_LAST;
389         }
390
391         nixnex_nextweapon = id;
392 }
393
394 string W_Name(float weaponid)
395 {
396         if(weaponid == WEP_LASER)             return "Laser";
397         if(weaponid == WEP_UZI)               return "Machine Gun";
398         if(weaponid == WEP_SHOTGUN)           return "Shotgun";
399         if(weaponid == WEP_GRENADE_LAUNCHER)  return "Mortar";
400         if(weaponid == WEP_ELECTRO)           return "Electro";
401         if(weaponid == WEP_NEX)               return "Nex";
402         if(weaponid == WEP_HAGAR)             return "Hagar";
403         if(weaponid == WEP_ROCKET_LAUNCHER)   return "Rocket Launcher";
404         if(weaponid == WEP_CRYLINK)           return "Crylink";
405         return "@!#%'n Tuba";
406 }
407
408 void Nixnex_GiveCurrentWeapon()
409 {
410         float dt;
411         if(cvar("g_nixnex"))
412         {
413                 if(!nixnex_nextweapon)
414                         Nixnex_ChooseNextWeapon();
415
416                 dt = ceil(nixnex_nextchange - time);
417
418                 if(dt <= 0)
419                 {
420                         nixnex_weapon = nixnex_nextweapon;
421                         nixnex_nextweapon = 0;
422                         nixnex_nextchange = time + cvar("g_balance_nixnex_roundtime");
423                 }
424
425                 if(nixnex_nextchange != self.nixnex_lastchange_id) // this shall only be called once per round!
426                 {
427                         self.nixnex_lastchange_id = nixnex_nextchange;
428                         if (cvar("g_use_ammunition"))
429                         {
430                                 self.ammo_shells = cvar("g_balance_nixnex_ammo_shells");
431                                 self.ammo_nails = cvar("g_balance_nixnex_ammo_nails");
432                                 self.ammo_rockets = cvar("g_balance_nixnex_ammo_rockets");
433                                 self.ammo_cells = cvar("g_balance_nixnex_ammo_cells");
434                         }
435                         else
436                         {
437                                 self.ammo_shells = 999;
438                                 self.ammo_nails = 999;
439                                 self.ammo_rockets = 999;
440                                 self.ammo_cells = 999;
441                         }
442                         self.nixnex_nextincr = time + cvar("g_balance_nixnex_incrtime");
443                         if(dt >= 1 && dt <= 5)
444                                 self.nixnex_lastinfotime = -42;
445                         else
446                                 centerprint(self, strcat("\n\n^2Active weapon: ^3", W_Name(nixnex_weapon), "\n"));
447                 }
448                 if(self.nixnex_lastinfotime != dt)
449                 {
450                         self.nixnex_lastinfotime = dt; // initial value 0 should count as "not seen"
451                         if(dt >= 1 && dt <= 5)
452                                 centerprint(self, strcat("^3", ftos(dt), "^2 seconds until weapon change...\n\nNext weapon: ^3", W_Name(nixnex_nextweapon), "\n"));
453                 }
454
455                 if(cvar("g_use_ammunition") && time > self.nixnex_nextincr)
456                 {
457                         self.ammo_shells = self.ammo_shells + cvar("g_balance_nixnex_ammoincr_shells");
458                         self.ammo_nails = self.ammo_nails + cvar("g_balance_nixnex_ammoincr_nails");
459                         self.ammo_rockets = self.ammo_rockets + cvar("g_balance_nixnex_ammoincr_rockets");
460                         self.ammo_cells = self.ammo_cells + cvar("g_balance_nixnex_ammoincr_cells");
461                         weapon_action(self.weapon, WR_UPDATECOUNTS);
462                         self.nixnex_nextincr = time + cvar("g_balance_nixnex_incrtime");
463                 }
464
465                 self.items = self.items - (self.items & (IT_LASER | IT_SHOTGUN | IT_UZI | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER));
466                 if(cvar("g_nixnex_with_laser"))
467                         self.items = self.items + IT_LASER;
468                 self.items = self.items - (self.items & weapon_translateindextoflag(nixnex_weapon)) + weapon_translateindextoflag(nixnex_weapon);
469
470                 if(self.switchweapon != nixnex_weapon)
471                         if(!client_hasweapon(self, self.switchweapon, TRUE))
472                                 if(client_hasweapon(self, nixnex_weapon, TRUE))
473                                         W_SwitchWeapon(nixnex_weapon);
474         }
475 }
476