]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/cl_weaponsystem.qc
simplified weapon throw code by removing redundant code; added conversion functios...
[divverent/nexuiz.git] / data / qcsrc / server / cl_weaponsystem.qc
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 * MAX_SHOT_DISTANCE, 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 * MAX_SHOT_DISTANCE, 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 (intermission_running)
130                 self.frame = WFRAME_IDLE;
131         if (self.owner.weaponentity != self)
132         {
133                 remove(self);
134                 return;
135         }
136         if (self.owner.deadflag != DEAD_NO)
137         {
138                 self.model = "";
139                 return;
140         }
141         if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
142         {
143                 self.cnt = self.owner.weapon;
144                 self.dmg = self.owner.modelindex;
145                 self.deadflag = self.owner.deadflag;
146                 if (self.owner.weaponname != "")
147                         setmodel(self, strcat("models/weapons/w_", self.owner.weaponname, ".zym"));
148                 else
149                         self.model = "";
150         }
151         self.effects = self.owner.effects;
152
153         if (self.flags & FL_FLY)
154                 // owner is currently being teleported, so don't apply EF_NODRAW otherwise the viewmodel would "blink"
155                 self.effects = self.effects - (self.effects & EF_NODRAW);
156
157         self.alpha = self.owner.alpha;
158         self.colormap = self.owner.colormap;
159
160         self.angles = '0 0 0';
161         local float f;
162         f = 0;
163         if (self.state == WS_RAISE)
164                 f = (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
165         else if (self.state == WS_DROP)
166                 f = 1 - (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
167         else if (self.state == WS_CLEAR)
168                 f = 1;
169         self.angles_x = -90 * f * f;
170
171         // create or update the lasertarget entity
172         LaserTarget_Think();
173 };
174
175 void() CL_ExteriorWeaponentity_Think =
176 {
177         self.nextthink = time;
178         if (self.owner.exteriorweaponentity != self)
179         {
180                 remove(self);
181                 return;
182         }
183         if (self.owner.deadflag != DEAD_NO)
184         {
185                 self.model = "";
186                 return;
187         }
188         if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
189         {
190                 self.cnt = self.owner.weapon;
191                 self.dmg = self.owner.modelindex;
192                 self.deadflag = self.owner.deadflag;
193                 if (self.owner.weaponname != "")
194                         setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3"));
195                 else
196                         self.model = "";
197                 setattachment(self, self.owner, "bip01 r hand");
198                 // if that didn't find a tag, hide the exterior weapon model
199                 if (!self.tag_index)
200                         self.model = "";
201         }
202         self.effects = self.owner.effects;
203         self.colormap = self.owner.colormap;
204 };
205
206 // spawning weaponentity for client
207 void() CL_SpawnWeaponentity =
208 {
209         self.weaponentity = spawn();
210         self.weaponentity.solid = SOLID_NOT;
211         self.weaponentity.owner = self;
212         self.weaponentity.weaponentity = self.weaponentity;
213         setmodel(self.weaponentity, "");
214         self.weaponentity.origin = '0 0 0';
215         self.weaponentity.angles = '0 0 0';
216         self.weaponentity.viewmodelforclient = self;
217         self.weaponentity.flags = 0;
218         self.weaponentity.think = CL_Weaponentity_Think;
219         self.weaponentity.nextthink = time;
220         self.weaponentity.scale = 0.61;
221
222         self.exteriorweaponentity = spawn();
223         self.exteriorweaponentity.solid = SOLID_NOT;
224         self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
225         self.exteriorweaponentity.owner = self;
226         self.exteriorweaponentity.origin = '0 0 0';
227         self.exteriorweaponentity.angles = '0 0 0';
228         self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
229         self.exteriorweaponentity.nextthink = time;
230 };
231
232 float(entity cl, float wpn, float andammo, float complain) client_hasweapon =
233 {
234         local float itemcode, f;
235         local entity oldself;
236
237         if (wpn < WEP_FIRST || wpn > WEP_LAST)
238         {
239                 if (complain)
240                         sprint(self, "Invalid weapon\n");
241                 return FALSE;
242         }
243         itemcode = W_ItemCode(wpn);
244         if (cl.items & itemcode)
245         {
246                 if (andammo)
247                 {
248                         oldself = self;
249                         self = cl;
250                         f = weapon_action(wpn, WR_CHECKAMMO1);
251                         f = f + weapon_action(wpn, WR_CHECKAMMO2);
252                         self = oldself;
253                         if (!f)
254                         {
255                                 if (complain)
256                                         sprint(self, "You don't have any ammo for that weapon\n");
257                                 return FALSE;
258                         }
259                 }
260                 return TRUE;
261         }
262         if (complain)
263                 sprint(self, "You don't own that weapon\n");
264         return FALSE;
265 };
266
267 // Weapon subs
268 void() w_clear =
269 {
270         if (self.weapon != -1)
271                 self.weapon = 0;
272         if (self.weaponentity)
273         {
274                 self.weaponentity.state = WS_CLEAR;
275                 setmodel(self.weaponentity, "");
276                 self.weaponentity.effects = 0;
277         }
278 };
279
280 void() w_ready =
281 {
282         if (self.weaponentity)
283         {
284                 self.weaponentity.state = WS_READY;
285                 weapon_thinkf(WFRAME_IDLE, 0.1, w_ready);
286         }
287 };
288
289 // FIXME: add qw-style client-custom weaponrating (cl_weaponrating)?
290 float(entity e) w_getbestweapon
291 {// add new weapons here
292         if (client_hasweapon(e, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
293                 return WEP_ROCKET_LAUNCHER;
294         else if (client_hasweapon(e, WEP_NEX, TRUE, FALSE))
295                 return WEP_NEX;
296         else if (client_hasweapon(e, WEP_HAGAR, TRUE, FALSE))
297                 return WEP_HAGAR;
298         else if (client_hasweapon(e, WEP_GRENADE_LAUNCHER, TRUE, FALSE))
299                 return WEP_GRENADE_LAUNCHER;
300         else if (client_hasweapon(e, WEP_ELECTRO, TRUE, FALSE))
301                 return WEP_ELECTRO;
302         else if (client_hasweapon(e, WEP_CRYLINK, TRUE, FALSE))
303                 return WEP_CRYLINK;
304         else if (client_hasweapon(e, WEP_UZI, TRUE, FALSE))
305                 return WEP_UZI;
306         else if (client_hasweapon(e, WEP_SHOTGUN, TRUE, FALSE))
307                 return WEP_SHOTGUN;
308         else if (client_hasweapon(e, WEP_LASER, FALSE, FALSE))
309                 return WEP_LASER;
310         else
311                 return 0;
312 };
313
314 // Setup weapon for client (after this raise frame will be launched)
315 void(float windex, string wname, float hudammo) weapon_setup =
316 {
317         self.items = self.items - (self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS));
318         self.items = self.items | hudammo;
319
320         // the two weapon entities will notice this has changed and update their models
321         self.weapon = windex;
322         self.weaponname = wname;
323 };
324
325 // perform weapon to attack (weaponstate and attack_finished check is here)
326 float(float secondary, float attacktime) weapon_prepareattack =
327 {
328         if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
329         {
330                 self.switchweapon = w_getbestweapon(self);
331                 if (self.switchweapon != self.weapon)
332                         self.cnt = self.weapon;
333                 return FALSE;
334         }
335         // don't fire if previous attack is not finished
336         if (self.attack_finished > time)
337                 return FALSE;
338         // don't fire while changing weapon
339         if (self.weaponentity.state != WS_READY)
340                 return FALSE;
341         self.weaponentity.state = WS_INUSE;
342         self.attack_finished = max(time, self.attack_finished + attacktime);
343         return TRUE;
344 };
345
346 void(float fr, float t, void() func) weapon_thinkf =
347 {
348         if (fr >= 0)
349         {
350                 if (self.weaponentity != world)
351                         self.weaponentity.frame = fr;
352         }
353
354         if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
355         {
356                 backtrace("Tried to override initial weapon think function - should this really happen?");
357         }
358
359         if(cvar("g_runematch"))
360         {
361                 if(self.runes & RUNE_SPEED)
362                 {
363                         if(self.runes & CURSE_SLOW)
364                                 t = t * cvar("g_balance_rune_speed_combo_atkrate");
365                         else
366                                 t = t * cvar("g_balance_rune_speed_atkrate");
367                 }
368                 else if(self.runes & CURSE_SLOW)
369                 {
370                         t = t * cvar("g_balance_curse_slow_atkrate");
371                 }
372         }
373
374         // VorteX: haste can be added here
375         self.weapon_nextthink = max(time, self.weapon_nextthink_lastframe + t);
376         self.weapon_think = func;
377 };
378
379 void(float spd, vector org) weapon_boblayer1 =
380 {
381         // VorteX: haste can be added here
382 };
383