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);
20 if (other.classname == "player")
22 sprint (other, "You got the ^2");
30 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
32 local vector hitloc, force, endpoint, dir;
33 local entity ent, endent;
34 local float endq3surfaceflags;
35 //local entity explosion;
40 entity pseudoprojectile;
45 railgun_start = start;
48 dir = normalize(end - start);
49 length = vlen(end - start);
52 // go a little bit into the wall because we need to hit this wall later
55 // trace multiple times until we hit a wall, each obstacle will be made
56 // non-solid so we can hit the next, while doing this we spawn effects and
57 // note down which entities were hit so we can damage them later
60 if(self.antilag_debug)
61 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
63 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
65 // if it is world we can't hurt it so stop now
66 if (trace_ent == world || trace_fraction == 1)
69 // make the entity non-solid so we can hit the next one
70 trace_ent.railgunhit = TRUE;
71 trace_ent.railgunhitloc = end;
72 trace_ent.railgunhitsolidbackup = trace_ent.solid;
74 // stop if this is a wall
75 if (trace_ent.solid == SOLID_BSP)
78 // make the entity non-solid
79 trace_ent.solid = SOLID_NOT;
82 endpoint = trace_endpos;
84 endq3surfaceflags = trace_dphitq3surfaceflags;
86 // find all the entities the railgun hit and restore their solid state
87 ent = findfloat(world, railgunhit, TRUE);
90 // restore their solid type
91 ent.solid = ent.railgunhitsolidbackup;
92 ent = findfloat(ent, railgunhit, TRUE);
95 // spawn a temporary explosion entity for RadiusDamage calls
96 //explosion = spawn();
98 // Find all non-hit players the beam passed close by
99 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
101 FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) if not(msg_entity.classname == "spectator" && msg_entity.enemy == self) // we use realclient, so spectators can hear the whoosh too
103 // nearest point on the beam
104 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
106 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
110 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
112 if(!pseudoprojectile)
113 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
114 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
118 remove(pseudoprojectile);
121 // find all the entities the railgun hit and hurt them
122 ent = findfloat(world, railgunhit, TRUE);
125 // get the details we need to call the damage function
126 hitloc = ent.railgunhitloc;
127 ent.railgunhitloc = '0 0 0';
128 ent.railgunhitsolidbackup = SOLID_NOT;
129 ent.railgunhit = FALSE;
131 //for stats so that team hit will count as a miss
132 if(ent.flags & FL_CLIENT)
133 if(ent.deadflag == DEAD_NO)
137 if(ent.team == self.team)
140 f = ExponentialFalloff(mindist, maxdist, halflifedist, (ent.origin - start) * dir);
141 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, (ent.origin - start) * dir);
145 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
147 // create a small explosion to throw gibs around (if applicable)
148 //setorigin (explosion, hitloc);
149 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
151 // advance to the next entity
152 ent = findfloat(ent, railgunhit, TRUE);
155 //calculate hits and fired shots for hitscan
156 if not(inWarmupStage)
159 self.bullets_fired[self.weapon] += 1;
162 self.bullets_hit[self.weapon] += 1;
164 // update the client and store in addstat() in g_world
165 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
166 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
169 // we're done with the explosion entity, remove it
172 trace_endpos = endpoint;
174 trace_dphitq3surfaceflags = endq3surfaceflags;
180 void W_BallisticBullet_Hit (void)
187 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
189 if(other.solid == SOLID_BSP)
190 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
192 if(other && other != self.enemy)
194 endzcurveparticles();
198 damage_headshotbonus = self.dmg_edge;
199 railgun_start = self.origin - 2 * frametime * self.velocity;
200 railgun_end = self.origin + 2 * frametime * self.velocity;
202 if(other.flags & FL_CLIENT)
203 if(other.deadflag == DEAD_NO)
207 if(other.team == self.owner.team)
210 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
211 damage_headshotbonus = 0;
213 if(self.dmg_edge != 0)
216 announce(self.owner, "announcer/male/headshot.wav");
218 announce(self.owner, "announcer/male/awesome.wav");
221 //calculate hits for ballistic weapons
222 if not(inWarmupStage)
223 if not(self.owner.isbot)
226 self.owner.bullets_hit[self.owner.weapon] += 1;
228 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
231 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
234 self.enemy = other; // don't hit the same player twice with the same bullet
237 .void(void) W_BallisticBullet_LeaveSolid_think_save;
238 .float W_BallisticBullet_LeaveSolid_nextthink_save;
239 .vector W_BallisticBullet_LeaveSolid_origin;
240 .vector W_BallisticBullet_LeaveSolid_velocity;
242 void W_BallisticBullet_LeaveSolid_think()
244 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
245 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
247 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
248 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
249 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
251 self.flags &~= FL_ONGROUND;
253 if(self.enemy.solid == SOLID_BSP)
256 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
257 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
260 UpdateCSQCProjectile(self);
263 // a fake logarithm function
268 if(x > 0.9 && x < 1.1)
270 return 2 * log(sqrt(x));
273 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
275 // move the entity along its velocity until it's out of solid, then let it resume
277 float dt, dst, velfactor, v0, vs;
281 // outside the world? forget it
282 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)
285 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
288 E0_m = 0.5 * v0 * v0;
289 maxdist = E0_m / constant;
290 // maxdist = 0.5 * v0 * v0 / constant
291 // dprint("max dist = ", ftos(maxdist), "\n");
293 if(maxdist <= cvar("g_ballistics_mindistance"))
296 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
298 if(trace_fraction == 1) // 1: we never got out of solid
301 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
303 dst = vlen(trace_endpos - self.origin);
304 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
305 Es_m = E0_m - constant * dst;
308 // roundoff errors got us
314 dt = dst / (0.5 * (v0 + vs));
315 // this is not correct, but the differential equations have no analytic
316 // solution - and these times are very small anyway
317 //print("dt = ", ftos(dt), "\n");
319 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
320 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
321 self.think = W_BallisticBullet_LeaveSolid_think;
322 self.nextthink = time + dt;
324 vel = vel * velfactor;
326 self.velocity = '0 0 0';
327 self.flags |= FL_ONGROUND; // prevent moving
328 self.W_BallisticBullet_LeaveSolid_velocity = vel;
333 void W_BallisticBullet_Touch (void)
335 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
339 W_BallisticBullet_Hit ();
342 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
348 self.projectiledeathtype |= HITTYPE_BOUNCE;
351 void endFireBallisticBullet()
353 endzcurveparticles();
355 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)
357 float lag, dt, savetime;
362 proj.classname = "bullet";
364 PROJECTILE_MAKETRIGGER(proj);
365 if(gravityfactor > 0)
367 proj.movetype = MOVETYPE_TOSS;
368 proj.gravity = gravityfactor;
371 proj.movetype = MOVETYPE_FLY;
372 proj.think = SUB_Remove;
373 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
374 proj.velocity = (dir + randomvec() * spread) * pSpeed;
375 W_SetupProjectileVelocity(proj);
376 proj.angles = vectoangles(proj.velocity);
377 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
378 // so: bulletconstant = bullet mass / area of bullet circle
379 setorigin(proj, start);
380 proj.flags = FL_PROJECTILE;
382 proj.touch = W_BallisticBullet_Touch;
384 proj.dmg_edge = headshotbonus;
385 proj.dmg_force = force;
386 proj.projectiledeathtype = dtype;
388 proj.oldvelocity = proj.velocity;
390 //calculate fired bullets for ballistics
391 if not(inWarmupStage)
394 self.bullets_fired[self.weapon] += 1;
395 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
398 if(cvar("g_antilag_bullets"))
399 if(pSpeed >= cvar("g_antilag_bullets"))
403 if(tracereffects & EF_RED)
404 eff = particleeffectnum("tr_rifle");
406 eff = particleeffectnum("tr_bullet");
408 // NOTE: this may severely throw off weapon balance
409 lag = ANTILAG_LATENCY(self);
412 if(clienttype(self) != CLIENTTYPE_REAL)
414 if(cvar("g_antilag") == 0)
415 lag = 0; // only do hitscan, but no antilag
419 antilag_takeback(pl, time - lag);
424 savetime = frametime;
429 // DP tracetoss is stupid and always traces in 0.05s
430 // ticks. This makes it trace in 0.05*0.125s ticks
436 self.velocity = self.velocity * 0.125;
437 self.gravity *= 0.125 * 0.125;
439 tracetoss(self, oldself);
443 if not(inWarmupStage)
446 self.bullets_fired[self.weapon] += 1;
447 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
450 if(vlen(trace_endpos - self.origin) > 16)
451 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
452 if(trace_fraction == 1)
454 // won't hit anything anytime soon (DP's
455 // tracetoss does 200 tics of, here,
456 // 0.05*0.125s, that is, 1.25 seconds
459 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
460 setorigin(self, trace_endpos);
461 self.velocity_z -= sv_gravity * dt;
463 if(!SUB_OwnerCheck())
465 if(SUB_NoImpactCheck())
469 W_BallisticBullet_Hit ();
473 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
476 W_BallisticBullet_LeaveSolid_think();
478 frametime = savetime;
490 if(tracereffects & EF_RED)
491 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
493 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
497 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
502 dir = normalize(dir + randomvec() * spread);
503 end = start + dir * MAX_SHOT_DISTANCE;
504 if(self.antilag_debug)
505 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
507 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
511 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
513 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
514 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
515 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
516 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
517 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
523 void W_PrepareExplosionByDamage(entity attacker, void() explode)
525 self.takedamage = DAMAGE_NO;
526 self.event_damage = SUB_Null;
527 self.owner = attacker;
529 // do not explode NOW but in the NEXT FRAME!
530 // because recursive calls to RadiusDamage are not allowed
531 self.nextthink = time;
532 self.think = explode;