2 ===========================================================================
4 CLIENT WEAPONSYSTEM CODE
5 Bring back W_Weaponframe
7 ===========================================================================
10 void W_SwitchWeapon_Force(entity e, float w)
12 e.cnt = e.switchweapon;
18 // VorteX: static frame globals
19 float WFRAME_FIRE1 = 0;
20 float WFRAME_FIRE2 = 1;
21 float WFRAME_IDLE = 2;
22 float WFRAME_RELOAD = 3;
24 void(float fr, float t, void() func) weapon_thinkf;
29 // this function calculates w_shotorg and w_shotdir based on the weapon model
30 // offset, trueaim and antilag, and won't put w_shotorg inside a wall.
31 // make sure you call makevectors first (FIXME?)
32 void W_SetupShot(entity ent, vector vecs, float antilag, float recoil, string snd)
34 float nudge = 1; // added to traceline target and subtracted from result
35 local vector trueaimpoint;
37 oldsolid = self.dphitcontentsmask;
38 self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
39 traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * MAX_SHOT_DISTANCE, MOVE_NOMONSTERS, self);
40 trueaimpoint = trace_endpos;
42 if(debug_shotorg != '0 0 0')
45 if (cvar("g_shootfromeye"))
46 w_shotorg = ent.origin + ent.view_ofs;
47 else if (cvar("g_shootfromcenter"))
48 w_shotorg = ent.origin + ent.view_ofs + v_up * vecs_z;
50 w_shotorg = ent.origin + ent.view_ofs + v_right * vecs_y + v_up * vecs_z;
51 // now move the shotorg forward as much as requested if possible
52 traceline(w_shotorg, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, self);
53 w_shotorg = trace_endpos - v_forward * nudge;
54 // calculate the shotdir from the chosen shotorg
55 w_shotdir = normalize(trueaimpoint - w_shotorg);
58 // explanation of g_antilag:
59 // if client reports it was aiming at a player, and the serverside trace
60 // says it would miss, change the aim point to the player's new origin,
61 // but only if the shot at the player's new origin would hit of course
63 // FIXME: a much better method for bullet weapons would be to leave a
64 // trail of lagged 'ghosts' behind players, and see if the bullet hits the
65 // ghost corresponding to this player's ping time, and if so it would do
66 // damage to the real player
68 if (self.cursor_trace_ent) // client was aiming at someone
69 if (self.cursor_trace_ent != self) // just to make sure
70 if (self.cursor_trace_ent.takedamage) // and that person is killable
71 if (self.cursor_trace_ent.classname == "player") // and actually a player
72 if (cvar("g_antilag") == 1)
74 // verify that the shot would miss without antilag
75 // (avoids an issue where guns would always shoot at their origin)
76 traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
77 if (!trace_ent.takedamage)
79 // verify that the shot would hit if altered
80 traceline(w_shotorg, self.cursor_trace_ent.origin, MOVE_NORMAL, self);
81 if (trace_ent == self.cursor_trace_ent)
83 // verify that the shot would hit in the past
84 if(self.antilag_debug)
85 antilag_takeback(self.cursor_trace_ent, time - self.antilag_debug);
87 antilag_takeback(self.cursor_trace_ent, time - ANTILAG_LATENCY(self));
89 traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
90 antilag_restore(self.cursor_trace_ent);
92 if(trace_ent == self.cursor_trace_ent)
95 w_shotdir = normalize(self.cursor_trace_ent.origin - w_shotorg);
96 dprint("ANTILAG HIT for ", self.netname, "\n");
100 // prydon cursor aimbot or odd network conditions
101 dprint("WARNING: antilag ghost trace for ", self.netname, " failed!\n");
104 if(cvar("developer") >= 2)
106 vector v, vplus, vel;
108 v = antilag_takebackorigin(self.cursor_trace_ent, time - (ANTILAG_LATENCY(self) ));
109 vplus = antilag_takebackorigin(self.cursor_trace_ent, time - (ANTILAG_LATENCY(self) + 0.01));
110 vel = (vplus - v) * (1 / 0.01);
111 // solve: v + X * vel = closest to self.origin + self.view_ofs, v_forward axis
112 v -= (self.origin + self.view_ofs);
113 // solve: v + X * vel = closest to v_forward axis
114 // project into 2D by subtracting v_forward components:
115 v -= (v * v_forward) * v_forward;
116 vel -= (vel * v_forward) * v_forward;
117 // solve: v + X * vel = closest to origin
118 // (v + X * vel)^2 closest to 0
119 // v^2 + 2 * X * (v * vel) + X^2 * vel^2 closest to 0
120 X = -(v * vel) / (vel * vel);
121 dprint("dead center needs adjustment of ", ftos(X), " (that is, ", ftos(ANTILAG_LATENCY(self) + X), " instead of ", ftos(ANTILAG_LATENCY(self)), "\n");
129 if (cvar("g_antilag") == 1) // switch to "ghost" if not hitting original
131 traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
132 if (!trace_ent.takedamage)
134 traceline_antilag_force (self, w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self, ANTILAG_LATENCY(self));
135 if (trace_ent.takedamage && trace_ent.classname == "player")
139 traceline(w_shotorg, e.origin, MOVE_NORMAL, self);
141 w_shotdir = normalize(trace_ent.origin - w_shotorg);
145 else if(cvar("g_antilag") == 3) // client side hitscan
147 if (self.cursor_trace_ent) // client was aiming at someone
148 if (self.cursor_trace_ent != self) // just to make sure
149 if (self.cursor_trace_ent.takedamage) // and that person is killable
150 if (self.cursor_trace_ent.classname == "player") // and actually a player
152 // verify that the shot would miss without antilag
153 // (avoids an issue where guns would always shoot at their origin)
154 traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
155 if (!trace_ent.takedamage)
157 // verify that the shot would hit if altered
158 traceline(w_shotorg, self.cursor_trace_ent.origin, MOVE_NORMAL, self);
159 if (trace_ent == self.cursor_trace_ent)
160 w_shotdir = normalize(self.cursor_trace_ent.origin - w_shotorg);
162 print("antilag fail\n");
169 self.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX)
172 self.punchangle_x = recoil * -1;
176 sound (self, CHAN_WEAPON, snd, VOL_BASE, ATTN_NORM);
179 if (self.items & IT_STRENGTH)
181 sound (self, CHAN_AUTO, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM);
184 void LaserTarget_Think()
191 // list of weapons that will use the laser, and the options that enable it
192 if(self.owner.laser_on && self.owner.weapon == WEP_ROCKET_LAUNCHER && g_laserguided_missile)
195 //if(self.owner.weapon == WEP_ELECTRO && cvar("g_laserguided_electro"))
200 // if a laser-enabled weapon isn't selected, delete any existing laser and quit
203 // rocket launcher isn't selected, so no laser target.
204 if(self.lasertarget != world)
206 remove(self.lasertarget);
207 self.lasertarget = world;
212 if(!self.lasertarget)
214 // we don't have a lasertarget entity, so spawn one
215 //bprint("create laser target\n");
216 e = self.lasertarget = spawn();
217 e.owner = self.owner; // Its owner is my owner
218 e.classname = "laser_target";
219 e.movetype = MOVETYPE_NOCLIP; // don't touch things
220 setmodel(e, "models/laser_dot.mdl"); // what it looks like, precision set below
221 e.scale = 1.25; // make it larger
222 e.alpha = 0.25; // transparency
223 e.colormod = '255 0 0' * (1/255) * 8; // change colors
224 e.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
225 // make it dynamically glow
226 // you should avoid over-using this, as it can slow down the player's computer.
227 e.glow_color = 251; // red color
231 e = self.lasertarget;
233 // move the laser dot to where the player is looking
235 makevectors(self.owner.v_angle); // set v_forward etc to the direction the player is looking
236 offset = '0 0 26' + v_right*3;
237 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
238 setorigin(e, trace_endpos + v_forward*8); // move me to where the traceline ended
239 if(trace_plane_normal != '0 0 0')
240 e.angles = vectoangles(trace_plane_normal);
242 e.angles = vectoangles(v_forward);
245 float CL_Weaponentity_CustomizeEntityForClient()
247 self.viewmodelforclient = self.owner;
248 if(other.classname == "spectator")
249 if(other.enemy == self.owner)
250 self.viewmodelforclient = other;
254 float qcweaponanimation;
255 vector weapon_offset = '0 -10 0';
256 vector weapon_adjust = '10 0 -15';
257 .vector weapon_morph0origin;
258 .vector weapon_morph0angles;
259 .float weapon_morph0time;
260 .vector weapon_morph1origin;
261 .vector weapon_morph1angles;
262 .float weapon_morph1time;
263 .vector weapon_morph2origin;
264 .vector weapon_morph2angles;
265 .float weapon_morph2time;
266 .vector weapon_morph3origin;
267 .vector weapon_morph3angles;
268 .float weapon_morph3time;
269 .vector weapon_morph4origin;
270 .vector weapon_morph4angles;
271 .float weapon_morph4time;
274 void CL_Weaponentity_Think()
277 self.nextthink = time;
278 if (intermission_running)
279 self.frame = self.anim_idle_x;
280 if (self.owner.weaponentity != self)
285 if (self.owner.deadflag != DEAD_NO)
290 if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
292 self.cnt = self.owner.weapon;
293 self.dmg = self.owner.modelindex;
294 self.deadflag = self.owner.deadflag;
298 if (self.owner.weaponname != "")
300 if (qcweaponanimation)
301 setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
304 animfilename = strcat("models/weapons/w_", self.owner.weaponname, ".dpm.animinfo");
305 animfile = fopen(animfilename, FILE_READ);
308 animparseerror = FALSE;
309 self.anim_fire1 = animparseline(animfile);
310 self.anim_fire2 = animparseline(animfile);
311 self.anim_idle = animparseline(animfile);
312 self.anim_reload = animparseline(animfile);
315 print("Parse error in ", animfilename, ", some player animations are broken\n");
316 setmodel(self, strcat("models/weapons/w_", self.owner.weaponname, ".dpm")); // precision set below
320 self.anim_fire1 = '0 1 10';
321 self.anim_fire2 = '1 1 10';
322 self.anim_idle = '2 1 10';
323 self.anim_reload = '3 1 10';
324 setmodel(self, strcat("models/weapons/w_", self.owner.weaponname, ".zym")); // precision set below
331 // reset animstate now
332 setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
334 if(cvar("g_shootfromcenter") || cvar("g_shootfromeye"))
338 e.modelindex = self.modelindex;
341 idx = gettagindex(e, "bone02");
344 v = gettaginfo(e, idx);
345 setorigin(self, '0 -1 0' * v_y);
348 setorigin(self, '0 0 0');
352 setorigin(self, '0 0 0');
355 tb = (self.effects & EF_TELEPORT_BIT);
356 self.effects = self.owner.effects - (self.owner.effects & EF_LOWPRECISION);// | EF_LOWPRECISION;
357 self.effects &~= EF_FULLBRIGHT; // can mask team color, so get rid of it
358 self.effects &~= EF_TELEPORT_BIT;
361 if(self.owner.alpha != 0)
362 self.alpha = self.owner.alpha;
366 self.colormap = self.owner.colormap;
368 self.angles = '0 0 0';
371 if (self.state == WS_RAISE)
373 f = (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
374 self.angles_x = -90 * f * f;
375 makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1');
376 setorigin(self, weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust);
378 else if (self.state == WS_DROP)
380 f = 1 - (self.owner.weapon_nextthink - time) / cvar("g_balance_weaponswitchdelay");
381 self.angles_x = -90 * f * f;
382 makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1');
383 setorigin(self, weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust);
385 else if (self.state == WS_CLEAR)
388 self.angles_x = -90 * f * f;
389 makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0' + self.angles_z * '0 0 1');
390 setorigin(self, weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust);
392 else if (time < self.owner.weapon_morph1time)
394 f = (time - self.owner.weapon_morph0time) / (self.owner.weapon_morph1time - self.owner.weapon_morph0time);
395 self.angles = self.owner.weapon_morph0angles * (1 - f) + self.owner.weapon_morph1angles * f;
396 setorigin(self, self.owner.weapon_morph0origin * (1 - f) + self.owner.weapon_morph1origin * f);
398 else if (time < self.owner.weapon_morph2time)
400 f = (time - self.owner.weapon_morph1time) / (self.owner.weapon_morph2time - self.owner.weapon_morph1time);
401 self.angles = self.owner.weapon_morph1angles * (1 - f) + self.owner.weapon_morph2angles * f;
402 setorigin(self, self.owner.weapon_morph1origin * (1 - f) + self.owner.weapon_morph2origin * f);
404 else if (time < self.owner.weapon_morph3time)
406 f = (time - self.owner.weapon_morph2time) / (self.owner.weapon_morph3time - self.owner.weapon_morph2time);
407 self.angles = self.owner.weapon_morph2angles * (1 - f) + self.owner.weapon_morph3angles * f;
408 setorigin(self, self.owner.weapon_morph2origin * (1 - f) + self.owner.weapon_morph3origin * f);
410 else if (time < self.owner.weapon_morph4time)
412 f = (time - self.owner.weapon_morph3time) / (self.owner.weapon_morph4time - self.owner.weapon_morph3time);
413 self.angles = self.owner.weapon_morph3angles * (1 - f) + self.owner.weapon_morph4angles * f;
414 setorigin(self, self.owner.weapon_morph3origin * (1 - f) + self.owner.weapon_morph4origin * f);
416 else if (qcweaponanimation)
418 // begin a new idle morph
419 self.owner.weapon_morph0time = time;
420 self.owner.weapon_morph0angles = self.angles;
421 self.owner.weapon_morph0origin = self.origin;
429 // turn gun to the left to look at it
431 self.owner.weapon_morph1time = time + t * 0.2;
432 self.owner.weapon_morph1angles = randomvec() * 3 + '-5 30 0';
433 makevectors(self.owner.weapon_morph1angles_x * '-1 0 0' + self.owner.weapon_morph1angles_y * '0 1 0' + self.owner.weapon_morph1angles_z * '0 0 1');
434 self.owner.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
436 self.owner.weapon_morph2time = time + t * 0.6;
437 self.owner.weapon_morph2angles = randomvec() * 3 + '-5 30 0';
438 makevectors(self.owner.weapon_morph2angles_x * '-1 0 0' + self.owner.weapon_morph2angles_y * '0 1 0' + self.owner.weapon_morph2angles_z * '0 0 1');
439 self.owner.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
441 self.owner.weapon_morph3time = time + t;
442 self.owner.weapon_morph3angles = '0 0 0';
443 makevectors(self.owner.weapon_morph3angles_x * '-1 0 0' + self.owner.weapon_morph3angles_y * '0 1 0' + self.owner.weapon_morph3angles_z * '0 0 1');
444 self.owner.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
448 // raise the gun a bit
450 self.owner.weapon_morph1time = time + t * 0.2;
451 self.owner.weapon_morph1angles = randomvec() * 3 + '30 -10 0';
452 makevectors(self.owner.weapon_morph1angles_x * '-1 0 0' + self.owner.weapon_morph1angles_y * '0 1 0' + self.owner.weapon_morph1angles_z * '0 0 1');
453 self.owner.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
455 self.owner.weapon_morph2time = time + t * 0.5;
456 self.owner.weapon_morph2angles = randomvec() * 3 + '30 -10 5';
457 makevectors(self.owner.weapon_morph2angles_x * '-1 0 0' + self.owner.weapon_morph2angles_y * '0 1 0' + self.owner.weapon_morph2angles_z * '0 0 1');
458 self.owner.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
460 self.owner.weapon_morph3time = time + t;
461 self.owner.weapon_morph3angles = '0 0 0';
462 makevectors(self.owner.weapon_morph3angles_x * '-1 0 0' + self.owner.weapon_morph3angles_y * '0 1 0' + self.owner.weapon_morph3angles_z * '0 0 1');
463 self.owner.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
469 self.owner.weapon_morph1time = time + t * 0.3;
470 self.owner.weapon_morph1angles = randomvec() * 6;
471 makevectors(self.owner.weapon_morph1angles_x * '-1 0 0' + self.owner.weapon_morph1angles_y * '0 1 0' + self.owner.weapon_morph1angles_z * '0 0 1');
472 self.owner.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
474 self.owner.weapon_morph2time = time + t * 0.7;
475 self.owner.weapon_morph2angles = randomvec() * 6;
476 makevectors(self.owner.weapon_morph2angles_x * '-1 0 0' + self.owner.weapon_morph2angles_y * '0 1 0' + self.owner.weapon_morph2angles_z * '0 0 1');
477 self.owner.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
479 self.owner.weapon_morph3time = time + t;
480 self.owner.weapon_morph3angles = '0 0 0';
481 makevectors(self.owner.weapon_morph3angles_x * '-1 0 0' + self.owner.weapon_morph3angles_y * '0 1 0' + self.owner.weapon_morph3angles_z * '0 0 1');
482 self.owner.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
486 // hold it mostly steady
487 t = random() * 6 + 4;
488 self.owner.weapon_morph1time = time + t * 0.2;
489 self.owner.weapon_morph1angles = randomvec() * 1;
490 makevectors(self.owner.weapon_morph1angles_x * '-1 0 0' + self.owner.weapon_morph1angles_y * '0 1 0' + self.owner.weapon_morph1angles_z * '0 0 1');
491 self.owner.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
493 self.owner.weapon_morph2time = time + t * 0.5;
494 self.owner.weapon_morph2angles = randomvec() * 1;
495 makevectors(self.owner.weapon_morph2angles_x * '-1 0 0' + self.owner.weapon_morph2angles_y * '0 1 0' + self.owner.weapon_morph2angles_z * '0 0 1');
496 self.owner.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
498 self.owner.weapon_morph3time = time + t * 0.7;
499 self.owner.weapon_morph3angles = randomvec() * 1;
500 makevectors(self.owner.weapon_morph3angles_x * '-1 0 0' + self.owner.weapon_morph3angles_y * '0 1 0' + self.owner.weapon_morph3angles_z * '0 0 1');
501 self.owner.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
504 self.owner.weapon_morph4time = time + t;
505 self.owner.weapon_morph4angles = '0 0 0';
506 makevectors(self.owner.weapon_morph4angles_x * '-1 0 0' + self.owner.weapon_morph4angles_y * '0 1 0' + self.owner.weapon_morph4angles_z * '0 0 1');
507 self.owner.weapon_morph4origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
511 // create or update the lasertarget entity
515 void CL_ExteriorWeaponentity_Think()
518 self.nextthink = time;
519 if (self.owner.exteriorweaponentity != self)
524 if (self.owner.deadflag != DEAD_NO)
529 if (self.cnt != self.owner.weapon || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
531 self.cnt = self.owner.weapon;
532 self.dmg = self.owner.modelindex;
533 self.deadflag = self.owner.deadflag;
534 if (self.owner.weaponname != "")
535 setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
539 if((tag_found = gettagindex(self.owner, "tag_weapon")))
541 self.tag_index = tag_found;
542 self.tag_entity = self.owner;
545 setattachment(self, self.owner, "bip01 r hand");
547 // if that didn't find a tag, hide the exterior weapon model
551 self.effects = self.owner.effects | EF_LOWPRECISION;
552 self.effects = self.effects & EFMASK_CHEAP; // eat performance
553 if(self.owner.alpha != 0)
554 self.alpha = self.owner.alpha;
558 self.colormap = self.owner.colormap;
561 // spawning weaponentity for client
562 void CL_SpawnWeaponentity()
564 self.weaponentity = spawn();
565 self.weaponentity.classname = "weaponentity";
566 self.weaponentity.solid = SOLID_NOT;
567 self.weaponentity.owner = self;
568 self.weaponentity.weaponentity = self.weaponentity;
569 setmodel(self.weaponentity, ""); // precision set when changed
570 self.weaponentity.origin = '0 0 0';
571 self.weaponentity.angles = '0 0 0';
572 self.weaponentity.viewmodelforclient = self;
573 self.weaponentity.flags = 0;
574 self.weaponentity.think = CL_Weaponentity_Think;
575 self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
576 self.weaponentity.nextthink = time;
578 self.exteriorweaponentity = spawn();
579 self.exteriorweaponentity.classname = "exteriorweaponentity";
580 self.exteriorweaponentity.solid = SOLID_NOT;
581 self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
582 self.exteriorweaponentity.owner = self;
583 self.exteriorweaponentity.origin = '0 0 0';
584 self.exteriorweaponentity.angles = '0 0 0';
585 self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
586 self.exteriorweaponentity.nextthink = time;
589 .float hasweapon_complain_spam;
591 float client_hasweapon(entity cl, float wpn, float andammo, float complain)
593 local float weaponbit, f;
594 local entity oldself;
596 if(time < self.hasweapon_complain_spam)
599 self.hasweapon_complain_spam = time + 0.2;
601 if (wpn < WEP_FIRST || wpn > WEP_LAST)
604 sprint(self, "Invalid weapon\n");
607 weaponbit = W_WeaponBit(wpn);
608 if (cl.weapons & weaponbit)
612 if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
620 f = weapon_action(wpn, WR_CHECKAMMO1);
621 f = f + weapon_action(wpn, WR_CHECKAMMO2);
627 sprint(cl, strcat("You don't have any ammo for the ^2", W_Name(wpn), "\n"));
636 // Report Proper Weapon Status / Modified Weapon Ownership Message
637 if(weaponsInMap & weaponbit)
639 sprint(cl, strcat("You do not have the ^2", W_Name(wpn), "\n") );
641 if(cvar("g_showweaponspawns"))
646 e = get_weaponinfo(wpn);
649 for(e = world; (e = findfloat(e, weapons, weaponbit)); )
651 if(e.classname == "droppedweapon")
653 if not(e.flags & FL_ITEM)
655 WaypointSprite_Spawn(
667 sprint(cl, strcat("The ^2", W_Name(wpn), "^7 is ^1NOT AVAILABLE^7 in this map\n") );
675 if (self.weapon != -1)
677 if (self.weaponentity)
679 self.weaponentity.state = WS_CLEAR;
680 self.weaponentity.effects = 0;
686 if (self.weaponentity)
687 self.weaponentity.state = WS_READY;
688 weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
691 // Setup weapon for client (after this raise frame will be launched)
692 void weapon_setup(float windex)
695 qcweaponanimation = cvar("sv_qcweaponanimation");
696 e = get_weaponinfo(windex);
697 self.items &~= IT_AMMO;
698 self.items = self.items | e.items;
700 // the two weapon entities will notice this has changed and update their models
701 self.weapon = windex;
702 self.weaponname = e.mdl;
703 self.bulletcounter = 0;
706 // perform weapon to attack (weaponstate and attack_finished check is here)
708 float weapon_prepareattack(float secondary, float attacktime)
710 //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
711 //if all players readied up and the countdown is running
712 if (cvar("sv_ready_restart_after_countdown"))
713 if(time < game_starttime || time < self.race_penalty) {
717 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
718 if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
720 W_SwitchWeapon_Force(self, w_getbestweapon(self));
724 if (timeoutStatus == 2) //don't allow the player to shoot while game is paused
727 // do not even think about shooting if switching
728 if(self.switchweapon != self.weapon)
731 // don't fire if previous attack is not finished
733 if (ATTACK_FINISHED(self) > time + frametime * 0.5)
735 // don't fire while changing weapon
736 if (self.weaponentity.state != WS_READY)
738 self.weaponentity.state = WS_INUSE;
740 self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
742 // if the weapon hasn't been firing continuously, reset the timer
745 if (ATTACK_FINISHED(self) < time - frametime * 1.5)
747 ATTACK_FINISHED(self) = time;
748 //dprint("resetting attack finished to ", ftos(time), "\n");
750 ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime;
752 self.bulletcounter += 1;
753 //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
757 void weapon_thinkf(float fr, float t, void() func)
762 if (fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2)
767 if (self.weaponentity)
769 if (qcweaponanimation)
771 self.weaponentity.frame = fr;
772 if (fr != WFRAME_IDLE)
778 self.weaponentity.frame = fr;
779 self.weapon_morph0time = time;
780 self.weapon_morph0angles = self.weaponentity.angles;
781 self.weapon_morph0origin = self.weaponentity.origin;
783 self.weapon_morph1angles = '0 0 0';
784 self.weapon_morph1time = time + t;
785 makevectors(self.weapon_morph1angles_x * '-1 0 0' + self.weapon_morph1angles_y * '0 1 0' + self.weapon_morph1angles_z * '0 0 1');
786 self.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
788 self.weapon_morph2angles = '0 0 0';
789 self.weapon_morph2time = time + t;
790 makevectors(self.weapon_morph2angles_x * '-1 0 0' + self.weapon_morph2angles_y * '0 1 0' + self.weapon_morph2angles_z * '0 0 1');
791 self.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
793 self.weapon_morph3angles = '0 0 0';
794 self.weapon_morph3time = time + t;
795 makevectors(self.weapon_morph3angles_x * '-1 0 0' + self.weapon_morph3angles_y * '0 1 0' + self.weapon_morph3angles_z * '0 0 1');
796 self.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
798 self.weapon_morph4angles = '0 0 0';
799 self.weapon_morph4time = time + t + 1;
800 makevectors(self.weapon_morph4angles_x * '-1 0 0' + self.weapon_morph4angles_y * '0 1 0' + self.weapon_morph4angles_z * '0 0 1');
801 self.weapon_morph4origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
803 if (fr == WFRAME_FIRE1)
805 self.weapon_morph1angles = '5 0 0';
806 self.weapon_morph1time = time + t * 0.1;
807 makevectors(self.weapon_morph1angles_x * '-1 0 0' + self.weapon_morph1angles_y * '0 1 0' + self.weapon_morph1angles_z * '0 0 1');
808 self.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
810 else if (fr == WFRAME_FIRE2)
812 self.weapon_morph1angles = '10 0 0';
813 self.weapon_morph1time = time + t * 0.1;
814 makevectors(self.weapon_morph1angles_x * '-1 0 0' + self.weapon_morph1angles_y * '0 1 0' + self.weapon_morph1angles_z * '0 0 1');
815 self.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
817 else if (fr == WFRAME_RELOAD)
819 self.weapon_morph1time = time + t * 0.05;
820 self.weapon_morph1angles = '-10 40 0';
821 makevectors(self.weapon_morph1angles_x * '-1 0 0' + self.weapon_morph1angles_y * '0 1 0' + self.weapon_morph1angles_z * '0 0 1');
822 self.weapon_morph1origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
824 self.weapon_morph2time = time + t * 0.15;
825 self.weapon_morph2angles = '-10 40 5';
826 makevectors(self.weapon_morph2angles_x * '-1 0 0' + self.weapon_morph2angles_y * '0 1 0' + self.weapon_morph2angles_z * '0 0 1');
827 self.weapon_morph2origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
829 self.weapon_morph3time = time + t * 0.25;
830 self.weapon_morph3angles = '-10 40 0';
831 makevectors(self.weapon_morph3angles_x * '-1 0 0' + self.weapon_morph3angles_y * '0 1 0' + self.weapon_morph3angles_z * '0 0 1');
832 self.weapon_morph3origin = weapon_offset_x * v_forward - weapon_offset_y * v_right + weapon_offset_z * v_up + weapon_adjust;
834 self.weapon_morph4time = time + t;
843 BITXOR_ASSIGN(self.weaponentity.effects, EF_TELEPORT_BIT);
849 if (fr == WFRAME_IDLE)
850 a = self.weaponentity.anim_idle;
851 else if (fr == WFRAME_FIRE1)
852 a = self.weaponentity.anim_fire1;
853 else if (fr == WFRAME_FIRE2)
854 a = self.weaponentity.anim_fire2;
855 else if (fr == WFRAME_RELOAD)
856 a = self.weaponentity.anim_reload;
857 setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
861 BITXOR_ASSIGN(self.weaponentity.effects, EF_TELEPORT_BIT);
866 if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
868 backtrace("Tried to override initial weapon think function - should this really happen?");
873 if(self.runes & RUNE_SPEED)
875 if(self.runes & CURSE_SLOW)
876 t = t * cvar("g_balance_rune_speed_combo_atkrate");
878 t = t * cvar("g_balance_rune_speed_atkrate");
880 else if(self.runes & CURSE_SLOW)
882 t = t * cvar("g_balance_curse_slow_atkrate");
886 // VorteX: haste can be added here
887 if (self.weapon_think == w_ready)
889 self.weapon_nextthink = time;
890 //dprint("started firing at ", ftos(time), "\n");
892 if (self.weapon_nextthink < time - frametime * 1.5 || self.weapon_nextthink > time + frametime * 1.5)
894 self.weapon_nextthink = time;
895 //dprint("reset weapon animation timer at ", ftos(time), "\n");
897 self.weapon_nextthink = self.weapon_nextthink + t;
898 self.weapon_think = func;
899 //dprint("next ", ftos(self.weapon_nextthink), "\n");
903 if (!self.crouch) // shoot anim stands up, this looks bad
906 anim = self.anim_shoot;
908 setanim(self, anim, FALSE, TRUE, TRUE);
912 void weapon_boblayer1(float spd, vector org)
914 // VorteX: haste can be added here
917 vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity)
925 mdirection = normalize(mvelocity);
926 mspeed = vlen(mvelocity);
928 nstyle = cvar("g_projectiles_newton_style");
932 outvelocity = mvelocity;
936 // true Newtonian projectiles
937 outvelocity = pvelocity + mvelocity;
941 // true Newtonian projectiles with automatic aim adjustment
943 // solve: |outspeed * mdirection - pvelocity| = mspeed
944 // outspeed^2 - 2 * outspeed * (mdirection * pvelocity) + pvelocity^2 - mspeed^2 = 0
945 // outspeed = (mdirection * pvelocity) +- sqrt((mdirection * pvelocity)^2 - pvelocity^2 + mspeed^2)
949 // pvelocity^2 - (mdirection * pvelocity)^2 > mspeed^2
950 // velocity without mdirection component > mspeed
951 // fire at smallest possible mspeed that works?
952 // |(mdirection * pvelocity) * pvelocity - pvelocity| = mspeed
957 p = mdirection * pvelocity;
958 q = pvelocity * pvelocity - mspeed * mspeed;
962 //dprint("impossible shot, adjusting\n");
965 outspeed = p + sqrt(D);
966 outspeed = bound(mspeed * 0.7, outspeed, mspeed * 5.0);
967 outvelocity = mdirection * outspeed;
972 outspeed = mspeed + mdirection * pvelocity;
973 outspeed = bound(mspeed * 0.7, outspeed, mspeed * 5.0);
974 outvelocity = mdirection * outspeed;
979 outspeed = mspeed + vlen(pvelocity);
980 outvelocity = mdirection * outspeed;
983 error("g_projectiles_newton_style must be 0 (absolute), 1 (Newtonian), 2 (Newtonian + aimfix), 3 (pseudo Newtonian) or 4 (tZorkian)!");
988 void W_SetupProjectileVelocity(entity missile)
990 if(missile.owner == world)
991 error("Unowned missile");
993 missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, missile.velocity);