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