]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/cl_weaponsystem.c
changed shotorg positions for most weapons, they are now all inside the
[divverent/nexuiz.git] / data / qcsrc / server / gamec / cl_weaponsystem.c
1 /*
2 ===========================================================================
3
4   CLIENT WEAPONSYSTEM CODE
5   Bring back W_Weaponframe
6
7 ===========================================================================
8 */
9
10 // VorteX: static frame globals
11 float WFRAME_FIRE1 = 0;
12 float WFRAME_FIRE2 = 1;
13 float WFRAME_IDLE = 2;
14
15 void(float fr, float t, void() func) weapon_thinkf;
16
17 vector w_shotorg;
18 vector w_shotdir;
19
20 // this function calculates w_shotorg and w_shotdir based on the weapon model
21 // offset, trueaim and antilag, and won't put w_shotorg inside a wall.
22 // make sure you call makevectors first (FIXME?)
23 void(entity ent, vector vecs, float antilag, float recoil, string snd) W_SetupShot =
24 {
25         local vector trueaimpoint;
26
27         traceline_hitcorpse(self, self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 8192, MOVE_NOMONSTERS, self);
28         trueaimpoint = trace_endpos;
29
30         // if aiming at a player and the original trace won't hit that player
31         // anymore, try aiming at the player's new position
32         if (antilag)
33         if (self.cursor_trace_ent)
34         if (self.cursor_trace_ent.takedamage)
35         if (trace_ent != self.cursor_trace_ent)
36         if (cvar("g_antilag"))
37                 trueaimpoint = self.cursor_trace_ent.origin;
38
39         /*
40         // don't allow the shot to start inside a wall
41         local vector startorg;
42         local vector idealorg;
43         startorg = ent.origin + ent.view_ofs;// - '0 0 8';
44         idealorg = ent.origin + ent.view_ofs + v_forward * vecs_x + v_right * vecs_y + v_up * vecs_z;
45         traceline_hitcorpse (ent, startorg, idealorg, MOVE_NOMONSTERS, ent);
46         w_shotorg = trace_endpos;
47         */
48         // all positions in the code are now required to be inside the player box
49         w_shotorg = ent.origin + ent.view_ofs + v_forward * vecs_x + v_right * vecs_y + v_up * vecs_z;
50
51         w_shotdir = normalize(trueaimpoint - w_shotorg);
52
53         if (!cvar("g_norecoil"))
54                 self.punchangle_x = recoil * -1;
55
56         if (snd != "")
57                 sound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);
58
59         if (self.items & IT_STRENGTH)
60         if (!cvar("g_minstagib"))
61                 sound (self, CHAN_AUTO, "weapons/strength_fire.ogg", 1, ATTN_NORM);
62 };
63
64 void LaserTarget_Think()
65 {
66         entity e;
67         vector offset;
68         float uselaser;
69         uselaser = 0;
70
71         // list of weapons that will use the laser, and the options that enable it
72         if(self.owner.laser_on && self.owner.weapon == WEP_ROCKET_LAUNCHER && cvar("g_laserguided_missile"))
73                 uselaser = 1;
74         // example
75         //if(self.owner.weapon == WEP_ELECTRO && cvar("g_laserguided_electro"))
76         //      uselaser = 1;
77
78
79
80         // if a laser-enabled weapon isn't selected, delete any existing laser and quit
81         if(!uselaser)
82         {
83                 // rocket launcher isn't selected, so no laser target.
84                 if(self.lasertarget != world)
85                 {
86                         remove(self.lasertarget);
87                         self.lasertarget = world;
88                 }
89                 return;
90         }
91
92         if(!self.lasertarget)
93         {
94                 // we don't have a lasertarget entity, so spawn one
95                 //bprint("create laser target\n");
96                 e = self.lasertarget = spawn();
97                 e.owner = self.owner;                   // Its owner is my owner
98                 e.classname = "laser_target";
99                 e.movetype = MOVETYPE_NOCLIP;   // don't touch things
100                 setmodel(e, "models/laser_dot.mdl");    // what it looks like
101                 e.scale = 1.25;                         // make it larger
102                 e.alpha = 0.25;                         // transparency
103                 e.colormod = '255 0 0' * (1/255) * 8;   // change colors
104                 e.effects = EF_FULLBRIGHT;
105                 // make it dynamically glow
106                 // you should avoid over-using this, as it can slow down the player's computer.
107                 e.glow_color = 251; // red color
108                 e.glow_size = 12;
109         }
110         else
111                 e = self.lasertarget;
112
113         // move the laser dot to where the player is looking
114
115         makevectors(self.owner.v_angle); // set v_forward etc to the direction the player is looking
116         offset = '0 0 26' + v_right*3;
117         traceline(self.owner.origin + offset, self.owner.origin + offset + v_forward * 8192, FALSE, self); // trace forward until you hit something, like a player or wall
118         setorigin(e, trace_endpos + v_forward*8); // move me to where the traceline ended
119         if(trace_plane_normal != '0 0 0')
120                 e.angles = vectoangles(trace_plane_normal);
121         else
122                 e.angles = vectoangles(v_forward);
123 }
124
125 .string weaponname;
126 void() CL_Weaponentity_Think =
127 {
128         self.nextthink = time;
129         if (self.owner.weaponentity != self)
130         {
131                 remove(self);
132                 return;
133         }
134         if (self.owner.deadflag != DEAD_NO)
135         {
136                 self.model = "";
137                 return;
138         }
139         if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
140         {
141                 self.cnt = self.owner.weapon;
142                 self.dmg = self.owner.modelindex;
143                 self.deadflag = self.owner.deadflag;
144                 if (self.owner.weaponname != "")
145                         setmodel(self, strcat("models/weapons/w_", self.owner.weaponname, ".zym"));
146                 else
147                         self.model = "";
148         }
149         self.effects = self.owner.effects;
150
151         if (self.flags & FL_FLY)
152                 // owner is currently being teleported, so don't apply EF_NODRAW otherwise the viewmodel would "blink"
153                 self.effects = self.effects - (self.effects & EF_NODRAW);
154
155         self.alpha = self.owner.alpha;
156
157         self.angles = '0 0 0';
158         local float f;
159         f = 0;
160         if (self.state == WS_RAISE)
161                 f = (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
162         else if (self.state == WS_DROP)
163                 f = 1 - (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
164         else if (self.state == WS_CLEAR)
165                 f = 1;
166         self.angles_x = -90 * f * f;
167
168         // create or update the lasertarget entity
169         LaserTarget_Think();
170 };
171
172 void() CL_ExteriorWeaponentity_Think =
173 {
174         self.nextthink = time;
175         if (self.owner.exteriorweaponentity != self)
176         {
177                 remove(self);
178                 return;
179         }
180         if (self.owner.deadflag != DEAD_NO)
181         {
182                 self.model = "";
183                 return;
184         }
185         if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
186         {
187                 self.cnt = self.owner.weapon;
188                 self.dmg = self.owner.modelindex;
189                 self.deadflag = self.owner.deadflag;
190                 if (self.owner.weaponname != "")
191                         setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3"));
192                 else
193                         self.model = "";
194                 setattachment(self, self.owner, "bip01 r hand");
195                 // if that didn't find a tag, hide the exterior weapon model
196                 if (!self.tag_index)
197                         self.model = "";
198         }
199         self.effects = self.owner.effects;
200 };
201
202 // spawning weaponentity for client
203 void() CL_SpawnWeaponentity =
204 {
205         if (self.weaponentity)
206         {
207                 w_clear();
208                 return;
209         }
210         self.weaponentity = spawn();
211         self.weaponentity.solid = SOLID_NOT;
212         self.weaponentity.owner = self;
213         self.weaponentity.weaponentity = self.weaponentity;
214         setmodel(self.weaponentity, "");
215         self.weaponentity.origin = '0 0 0';
216         self.weaponentity.angles = '0 0 0';
217         self.weaponentity.viewmodelforclient = self;
218         self.weaponentity.flags = 0;
219         self.weaponentity.think = CL_Weaponentity_Think;
220         self.weaponentity.nextthink = time;
221         self.weaponentity.scale = 0.61;
222
223         self.exteriorweaponentity = spawn();
224         self.exteriorweaponentity.solid = SOLID_NOT;
225         self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
226         self.exteriorweaponentity.owner = self;
227         self.exteriorweaponentity.origin = '0 0 0';
228         self.exteriorweaponentity.angles = '0 0 0';
229         self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
230         self.exteriorweaponentity.nextthink = time;
231 };
232
233 // convertion from index (= impulse) to flag in .items
234 float(float index) weapon_translateindextoflag =
235 {
236         if (index == WEP_LASER)
237                 return IT_LASER;
238         else if (index == WEP_SHOTGUN)
239                 return IT_SHOTGUN;
240         else if (index == WEP_UZI)
241                 return IT_UZI;
242         else if (index == WEP_GRENADE_LAUNCHER)
243                 return IT_GRENADE_LAUNCHER;
244         else if (index == WEP_ELECTRO)
245                 return IT_ELECTRO;
246         else if (index == WEP_CRYLINK)
247                 return IT_CRYLINK;
248         else if (index == WEP_NEX)
249                 return IT_NEX;
250         else if (index == WEP_HAGAR)
251                 return IT_HAGAR;
252         else if (index == WEP_ROCKET_LAUNCHER)
253                 return IT_ROCKET_LAUNCHER;
254         else if (index == WEP_LASER)
255                 return IT_LASER;
256         else
257                 return 0;
258 };
259
260 float(entity cl, float wpn, float andammo, float complain) client_hasweapon =
261 {
262         local float itemcode, f;
263         local entity oldself;
264
265         if (wpn < WEP_FIRST || wpn > WEP_LAST)
266         {
267                 if (complain)
268                         sprint(self, "Invalid weapon\n");
269                 return FALSE;
270         }
271         itemcode = weapon_translateindextoflag(wpn);
272         if (cl.items & itemcode)
273         {
274                 if (andammo)
275                 {
276                         oldself = self;
277                         self = cl;
278                         f = weapon_action(wpn, WR_CHECKAMMO1);
279                         f = f + weapon_action(wpn, WR_CHECKAMMO2);
280                         self = oldself;
281                         if (!f)
282                         {
283                                 if (complain)
284                                         sprint(self, "You don't have any ammo for that weapon\n");
285                                 return FALSE;
286                         }
287                 }
288                 return TRUE;
289         }
290         if (complain)
291                 sprint(self, "You don't own that weapon\n");
292         return FALSE;
293 };
294
295 // Weapon subs
296 void() w_clear =
297 {
298         if (self.weapon != -1)
299                 self.weapon = 0;
300         if (self.weaponentity)
301         {
302                 self.weaponentity.state = WS_CLEAR;
303                 setmodel(self.weaponentity, "");
304                 self.weaponentity.effects = 0;
305         }
306 };
307
308 void() w_ready =
309 {
310         self.weaponentity.state = WS_READY;
311         weapon_thinkf(WFRAME_IDLE, 0.1, w_ready);
312 };
313
314 // FIXME: add qw-style client-custom weaponrating (cl_weaponrating)?
315 float(entity e) w_getbestweapon
316 {// add new weapons here
317         if (client_hasweapon(e, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
318                 return WEP_ROCKET_LAUNCHER;
319         else if (client_hasweapon(e, WEP_NEX, TRUE, FALSE))
320                 return WEP_NEX;
321         else if (client_hasweapon(e, WEP_HAGAR, TRUE, FALSE))
322                 return WEP_HAGAR;
323         else if (client_hasweapon(e, WEP_GRENADE_LAUNCHER, TRUE, FALSE))
324                 return WEP_GRENADE_LAUNCHER;
325         else if (client_hasweapon(e, WEP_ELECTRO, TRUE, FALSE))
326                 return WEP_ELECTRO;
327         else if (client_hasweapon(e, WEP_CRYLINK, TRUE, FALSE))
328                 return WEP_CRYLINK;
329         else if (client_hasweapon(e, WEP_UZI, TRUE, FALSE))
330                 return WEP_UZI;
331         else if (client_hasweapon(e, WEP_SHOTGUN, TRUE, FALSE))
332                 return WEP_SHOTGUN;
333         else if (client_hasweapon(e, WEP_LASER, FALSE, FALSE))
334                 return WEP_LASER;
335         else
336                 return 0;
337 };
338
339 // Setup weapon for client (after this raise frame will be launched)
340 void(float windex, string wname, float hudammo) weapon_setup =
341 {
342         self.items = self.items - (self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS));
343         self.items = self.items | hudammo;
344
345         // the two weapon entities will notice this has changed and update their models
346         self.weapon = windex;
347         self.weaponname = wname;
348 };
349
350 // perform weapon to attack (weaponstate and attack_finished check is here)
351 float(float secondary, float attacktime) weapon_prepareattack =
352 {
353         if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
354         {
355                 self.switchweapon = w_getbestweapon(self);
356                 if (self.switchweapon != self.weapon)
357                         self.cnt = self.weapon;
358                 return FALSE;
359         }
360         // don't fire if previous attack is not finished
361         if (time < self.attack_finished)
362                 return FALSE;
363         // don't fire while changing weapon
364         if (self.weaponentity.state != WS_READY)
365                 return FALSE;
366         self.weaponentity.state = WS_INUSE;
367         // FIXME: this does not allow multiple shots per frame and disgards cumulative error
368         self.attack_finished = time + attacktime;
369         return TRUE;
370 };
371
372 void(float fr, float t, void() func) weapon_thinkf =
373 {
374         if (fr >= 0)
375         {
376                 if (self.weaponentity != world)
377                         self.weaponentity.frame = fr;
378         }
379
380         if(cvar("g_runematch"))
381         {
382                 if(self.runes & RUNE_SPEED)
383                 {
384                         if(self.runes & CURSE_SLOW)
385                                 t = t * cvar("g_balance_rune_speed_combo_atkrate");
386                         else
387                                 t = t * cvar("g_balance_rune_speed_atkrate");
388                 }
389                 else if(self.runes & CURSE_SLOW)
390                 {
391                         t = t * cvar("g_balance_curse_slow_atkrate");
392                 }
393         }
394
395         // VorteX: haste can be added here
396         self.weapon_nextthink = time + t;
397         self.weapon_think = func;
398 };
399
400 void(float spd, vector org) weapon_boblayer1 =
401 {
402         // VorteX: haste can be added here
403 };
404