1 .float bullets_hit[WEP_COUNT]; //for hitscan bullets hit
2 .float bullets_fired[WEP_COUNT]; //for hitscan bullets fired
4 FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bullets_hit);
5 FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bullets_fired);
7 void W_GiveWeapon (entity e, float wep, string name)
14 e.weapons = e.weapons | W_WeaponBit(wep);
19 if (other.classname == "player")
21 sprint (other, "You got the ^2");
30 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype)
32 local vector hitloc, force, endpoint, dir;
33 local entity ent, endent;
34 local float endq3surfaceflags;
35 //local entity explosion;
42 railgun_start = start;
45 dir = normalize(end - start);
48 // go a little bit into the wall because we need to hit this wall later
51 // trace multiple times until we hit a wall, each obstacle will be made
52 // non-solid so we can hit the next, while doing this we spawn effects and
53 // note down which entities were hit so we can damage them later
56 if(self.antilag_debug)
57 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
59 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
61 // if it is world we can't hurt it so stop now
62 if (trace_ent == world || trace_fraction == 1)
65 // make the entity non-solid so we can hit the next one
66 trace_ent.railgunhit = TRUE;
67 trace_ent.railgunhitloc = end;
68 trace_ent.railgunhitsolidbackup = trace_ent.solid;
70 // stop if this is a wall
71 if (trace_ent.solid == SOLID_BSP)
74 // make the entity non-solid
75 trace_ent.solid = SOLID_NOT;
78 endpoint = trace_endpos;
80 endq3surfaceflags = trace_dphitq3surfaceflags;
82 // Find all players the beam passed close by
83 length = vlen(start - endpoint);
84 for(step = 0; step < length; step += 16)
86 ent = findradius(start + dir * step,128);
89 if(clienttype(ent) == CLIENTTYPE_REAL)
91 if(ent.health != -666)
99 // And play a flyby sound to them
100 ent = findchainfloat(ticrate,1);
103 snd = "weapons/nexwhoosh";
104 snd = strcat(snd,ftos(rint(random() * 2) +1));
105 snd = strcat(snd,".ogg");
111 // find all the entities the railgun hit and restore their solid state
112 ent = findfloat(world, railgunhit, TRUE);
115 // restore their solid type
116 ent.solid = ent.railgunhitsolidbackup;
117 ent = findfloat(ent, railgunhit, TRUE);
120 // spawn a temporary explosion entity for RadiusDamage calls
121 //explosion = spawn();
123 // find all the entities the railgun hit and hurt them
124 ent = findfloat(world, railgunhit, TRUE);
127 // get the details we need to call the damage function
128 hitloc = ent.railgunhitloc;
129 ent.railgunhitloc = '0 0 0';
130 ent.railgunhitsolidbackup = SOLID_NOT;
131 ent.railgunhit = FALSE;
133 //for stats so that team hit will count as a miss
134 if(ent.flags & FL_CLIENT)
135 if(ent.deadflag == DEAD_NO)
139 if(ent.team == self.team)
143 if (ent.takedamage || ent.classname == "case")
144 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
146 // create a small explosion to throw gibs around (if applicable)
147 //setorigin (explosion, hitloc);
148 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
150 // advance to the next entity
151 ent = findfloat(ent, railgunhit, TRUE);
154 //calculate hits and fired shots for hitscan
155 if not(inWarmupStage)
158 self.bullets_fired[self.weapon] += 1;
161 self.bullets_hit[self.weapon] += 1;
163 // update the client and store in addstat() in g_world
164 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
165 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
168 // we're done with the explosion entity, remove it
171 trace_endpos = endpoint;
173 trace_dphitq3surfaceflags = endq3surfaceflags;
179 void W_BallisticBullet_Hit (void)
186 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
188 if(other.solid == SOLID_BSP)
189 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
191 if(other && other != self.enemy)
195 damage_headshotbonus = self.dmg_edge;
196 railgun_start = self.origin - 2 * frametime * self.velocity;
197 railgun_end = self.origin + 2 * frametime * self.velocity;
199 if(other.flags & FL_CLIENT)
200 if(other.deadflag == DEAD_NO)
204 if(other.team == self.owner.team)
207 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
208 damage_headshotbonus = 0;
210 if(self.dmg_edge != 0)
213 announce(self.owner, "announcer/male/headshot.wav");
215 announce(self.owner, "announcer/male/awesome.wav");
218 //calculate hits for ballistic weapons
219 if not(self.owner.isbot)
222 self.owner.bullets_hit[self.owner.weapon] += 1;
224 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
227 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
230 self.enemy = other; // don't hit the same player twice with the same bullet
233 .void(void) W_BallisticBullet_LeaveSolid_think_save;
234 .float W_BallisticBullet_LeaveSolid_nextthink_save;
235 .vector W_BallisticBullet_LeaveSolid_origin;
236 .vector W_BallisticBullet_LeaveSolid_velocity;
238 void W_BallisticBullet_LeaveSolid_think()
240 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
241 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
243 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
244 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
245 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
247 self.flags &~= FL_ONGROUND;
249 if(self.enemy.solid == SOLID_BSP)
252 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
253 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
256 UpdateCSQCProjectile(self);
259 // a fake logarithm function
264 if(x > 0.9 && x < 1.1)
266 return 2 * log(sqrt(x));
269 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
271 // move the entity along its velocity until it's out of solid, then let it resume
273 float dt, dst, velfactor, v0, vs;
277 // outside the world? forget it
278 if(self.origin_x > world.maxs_x || self.origin_y > world.maxs_y || self.origin_z > world.maxs_z || self.origin_x < world.mins_x || self.origin_y < world.mins_y || self.origin_z < world.mins_z)
281 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
284 E0_m = 0.5 * v0 * v0;
285 maxdist = E0_m / constant;
286 // maxdist = 0.5 * v0 * v0 / constant
287 // dprint("max dist = ", ftos(maxdist), "\n");
292 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
294 if(trace_fraction == 1) // 1: we never got out of solid
297 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
299 dst = vlen(trace_endpos - self.origin);
300 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
301 Es_m = E0_m - constant * dst;
304 // roundoff errors got us
310 dt = dst / (0.5 * (v0 + vs));
311 // this is not correct, but the differential equations have no analytic
312 // solution - and these times are very small anyway
313 //print("dt = ", ftos(dt), "\n");
315 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
316 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
317 self.think = W_BallisticBullet_LeaveSolid_think;
318 self.nextthink = time + dt;
320 vel = vel * velfactor;
322 self.velocity = '0 0 0';
323 self.flags |= FL_ONGROUND; // prevent moving
324 self.W_BallisticBullet_LeaveSolid_velocity = vel;
329 void W_BallisticBullet_Touch (void)
331 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
335 W_BallisticBullet_Hit ();
338 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
344 self.projectiledeathtype |= HITTYPE_BOUNCE;
347 void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant)
349 float lag, dt, savetime;
354 proj.classname = "bullet";
356 PROJECTILE_MAKETRIGGER(proj);
357 if(gravityfactor > 0)
359 proj.movetype = MOVETYPE_TOSS;
360 proj.gravity = gravityfactor;
363 proj.movetype = MOVETYPE_FLY;
364 proj.think = SUB_Remove;
365 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
366 proj.velocity = (dir + randomvec() * spread) * pSpeed;
367 W_SetupProjectileVelocity(proj);
368 proj.angles = vectoangles(proj.velocity);
369 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
370 // so: bulletconstant = bullet mass / area of bullet circle
371 setorigin(proj, start);
372 proj.flags = FL_PROJECTILE;
374 proj.touch = W_BallisticBullet_Touch;
376 proj.dmg_edge = headshotbonus;
377 proj.dmg_force = force;
378 proj.projectiledeathtype = dtype;
380 proj.oldvelocity = proj.velocity;
382 //calculate fired bullets for ballistics
385 self.bullets_fired[self.weapon] += 1;
386 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
389 if(cvar("g_antilag_bullets"))
390 if(pSpeed >= cvar("g_antilag_bullets"))
392 // NOTE: this may severely throw off weapon balance
393 lag = ANTILAG_LATENCY(self);
396 if(clienttype(self) != CLIENTTYPE_REAL)
398 if(cvar("g_antilag") == 0)
399 lag = 0; // only do hitscan, but no antilag
403 antilag_takeback(pl, time - lag);
408 savetime = frametime;
413 // DP tracetoss is stupid and always traces in 0.05s
414 // ticks. This makes it trace in 0.05*0.125s ticks
420 self.velocity = self.velocity * 0.125;
421 self.gravity *= 0.125 * 0.125;
423 tracetoss(self, oldself);
429 self.bullets_fired[self.weapon] += 1;
430 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
433 if(vlen(trace_endpos - self.origin) > 32)
434 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
435 if(trace_fraction == 1)
437 // won't hit anything anytime soon (DP's
438 // tracetoss does 200 tics of, here,
439 // 0.05*0.125s, that is, 1.25 seconds
442 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
443 setorigin(self, trace_endpos);
444 self.velocity_z -= sv_gravity * dt;
446 if(!SUB_OwnerCheck())
448 if(SUB_NoImpactCheck())
452 W_BallisticBullet_Hit ();
456 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
459 W_BallisticBullet_LeaveSolid_think();
461 frametime = savetime;
473 if(tracereffects & EF_RED)
474 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
476 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
480 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
485 dir = normalize(dir + randomvec() * spread);
486 end = start + dir * MAX_SHOT_DISTANCE;
487 if(self.antilag_debug)
488 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
490 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
494 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
496 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
497 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
498 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
499 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
500 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
506 void W_PrepareExplosionByDamage(entity attacker, void() explode)
508 self.takedamage = DAMAGE_NO;
509 self.event_damage = SUB_Null;
510 self.owner = attacker;
512 // do not explode NOW but in the NEXT FRAME!
513 // because recursive calls to RadiusDamage are not allowed
514 self.nextthink = time;
515 self.think = explode;