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, max(1, 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, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
260 UpdateCSQCProjectile(self);
263 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
265 // move the entity along its velocity until it's out of solid, then let it resume
267 float dt, dst, velfactor, v0, vs;
271 // outside the world? forget it
272 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)
275 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
278 E0_m = 0.5 * v0 * v0;
279 maxdist = E0_m / constant;
280 // maxdist = 0.5 * v0 * v0 / constant
281 // dprint("max dist = ", ftos(maxdist), "\n");
283 if(maxdist <= cvar("g_ballistics_mindistance"))
286 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
288 if(trace_fraction == 1) // 1: we never got out of solid
291 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
293 dst = vlen(trace_endpos - self.origin);
294 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
295 Es_m = E0_m - constant * dst;
298 // roundoff errors got us
304 dt = dst / (0.5 * (v0 + vs));
305 // this is not correct, but the differential equations have no analytic
306 // solution - and these times are very small anyway
307 //print("dt = ", ftos(dt), "\n");
309 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
310 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
311 self.think = W_BallisticBullet_LeaveSolid_think;
312 self.nextthink = time + dt;
314 vel = vel * velfactor;
316 self.velocity = '0 0 0';
317 self.flags |= FL_ONGROUND; // prevent moving
318 self.W_BallisticBullet_LeaveSolid_velocity = vel;
323 void W_BallisticBullet_Touch (void)
325 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
329 W_BallisticBullet_Hit ();
332 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
338 self.projectiledeathtype |= HITTYPE_BOUNCE;
341 void endFireBallisticBullet()
343 endzcurveparticles();
345 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)
347 float lag, dt, savetime;
352 proj.classname = "bullet";
354 PROJECTILE_MAKETRIGGER(proj);
355 if(gravityfactor > 0)
357 proj.movetype = MOVETYPE_TOSS;
358 proj.gravity = gravityfactor;
361 proj.movetype = MOVETYPE_FLY;
362 proj.think = SUB_Remove;
363 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
364 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, spread);
365 proj.angles = vectoangles(proj.velocity);
366 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
367 // so: bulletconstant = bullet mass / area of bullet circle
368 setorigin(proj, start);
369 proj.flags = FL_PROJECTILE;
371 proj.touch = W_BallisticBullet_Touch;
373 proj.dmg_edge = headshotbonus;
374 proj.dmg_force = force;
375 proj.projectiledeathtype = dtype;
377 proj.oldvelocity = proj.velocity;
379 //calculate fired bullets for ballistics
380 if not(inWarmupStage)
383 self.bullets_fired[self.weapon] += 1;
384 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
387 if(cvar("g_antilag_bullets"))
388 if(pSpeed >= cvar("g_antilag_bullets"))
392 if(tracereffects & EF_RED)
393 eff = particleeffectnum("tr_rifle");
395 eff = particleeffectnum("tr_bullet");
397 // NOTE: this may severely throw off weapon balance
398 lag = ANTILAG_LATENCY(self);
401 if(clienttype(self) != CLIENTTYPE_REAL)
403 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
404 lag = 0; // only do hitscan, but no antilag
408 antilag_takeback(pl, time - lag);
413 savetime = frametime;
418 // DP tracetoss is stupid and always traces in 0.05s
419 // ticks. This makes it trace in 0.05*0.125s ticks
425 self.velocity = self.velocity * 0.125;
426 self.gravity *= 0.125 * 0.125;
428 tracetoss(self, oldself);
432 if not(inWarmupStage)
435 self.bullets_fired[self.weapon] += 1;
436 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
439 if(vlen(trace_endpos - self.origin) > 16)
440 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
441 if(trace_fraction == 1)
443 // won't hit anything anytime soon (DP's
444 // tracetoss does 200 tics of, here,
445 // 0.05*0.125s, that is, 1.25 seconds
448 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
449 setorigin(self, trace_endpos);
450 self.velocity_z -= sv_gravity * dt;
452 if(!SUB_OwnerCheck())
454 if(SUB_NoImpactCheck())
458 W_BallisticBullet_Hit ();
462 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
465 W_BallisticBullet_LeaveSolid_think();
467 frametime = savetime;
479 if(tracereffects & EF_RED)
480 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
481 else if(tracereffects & EF_BLUE)
482 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
484 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
488 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
493 dir = normalize(dir + randomvec() * spread);
494 end = start + dir * MAX_SHOT_DISTANCE;
495 if(self.antilag_debug)
496 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
498 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
502 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
504 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
505 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
506 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
507 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
508 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
514 void W_PrepareExplosionByDamage(entity attacker, void() explode)
516 self.takedamage = DAMAGE_NO;
517 self.event_damage = SUB_Null;
518 self.owner = attacker;
520 // do not explode NOW but in the NEXT FRAME!
521 // because recursive calls to RadiusDamage are not allowed
522 self.nextthink = time;
523 self.think = explode;