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 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, (msg_entity.origin - start) * dir);
144 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * f);
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)
193 endzcurveparticles();
197 damage_headshotbonus = self.dmg_edge;
198 railgun_start = self.origin - 2 * frametime * self.velocity;
199 railgun_end = self.origin + 2 * frametime * self.velocity;
201 if(other.flags & FL_CLIENT)
202 if(other.deadflag == DEAD_NO)
206 if(other.team == self.owner.team)
209 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
210 damage_headshotbonus = 0;
212 if(self.dmg_edge != 0)
215 announce(self.owner, "announcer/male/headshot.wav");
217 announce(self.owner, "announcer/male/awesome.wav");
220 //calculate hits for ballistic weapons
221 if not(inWarmupStage)
222 if not(self.owner.isbot)
225 self.owner.bullets_hit[self.owner.weapon] += 1;
227 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
230 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
233 self.enemy = other; // don't hit the same player twice with the same bullet
236 .void(void) W_BallisticBullet_LeaveSolid_think_save;
237 .float W_BallisticBullet_LeaveSolid_nextthink_save;
238 .vector W_BallisticBullet_LeaveSolid_origin;
239 .vector W_BallisticBullet_LeaveSolid_velocity;
241 void W_BallisticBullet_LeaveSolid_think()
243 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
244 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
246 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
247 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
248 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
250 self.flags &~= FL_ONGROUND;
252 if(self.enemy.solid == SOLID_BSP)
255 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
256 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
259 UpdateCSQCProjectile(self);
262 // a fake logarithm function
267 if(x > 0.9 && x < 1.1)
269 return 2 * log(sqrt(x));
272 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
274 // move the entity along its velocity until it's out of solid, then let it resume
276 float dt, dst, velfactor, v0, vs;
280 // outside the world? forget it
281 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)
284 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
287 E0_m = 0.5 * v0 * v0;
288 maxdist = E0_m / constant;
289 // maxdist = 0.5 * v0 * v0 / constant
290 // dprint("max dist = ", ftos(maxdist), "\n");
292 if(maxdist <= cvar("g_ballistics_mindistance"))
295 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
297 if(trace_fraction == 1) // 1: we never got out of solid
300 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
302 dst = vlen(trace_endpos - self.origin);
303 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
304 Es_m = E0_m - constant * dst;
307 // roundoff errors got us
313 dt = dst / (0.5 * (v0 + vs));
314 // this is not correct, but the differential equations have no analytic
315 // solution - and these times are very small anyway
316 //print("dt = ", ftos(dt), "\n");
318 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
319 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
320 self.think = W_BallisticBullet_LeaveSolid_think;
321 self.nextthink = time + dt;
323 vel = vel * velfactor;
325 self.velocity = '0 0 0';
326 self.flags |= FL_ONGROUND; // prevent moving
327 self.W_BallisticBullet_LeaveSolid_velocity = vel;
332 void W_BallisticBullet_Touch (void)
334 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
338 W_BallisticBullet_Hit ();
341 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
347 self.projectiledeathtype |= HITTYPE_BOUNCE;
350 void endFireBallisticBullet()
352 endzcurveparticles();
354 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)
356 float lag, dt, savetime;
361 proj.classname = "bullet";
363 PROJECTILE_MAKETRIGGER(proj);
364 if(gravityfactor > 0)
366 proj.movetype = MOVETYPE_TOSS;
367 proj.gravity = gravityfactor;
370 proj.movetype = MOVETYPE_FLY;
371 proj.think = SUB_Remove;
372 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
373 proj.velocity = (dir + randomvec() * spread) * pSpeed;
374 W_SetupProjectileVelocity(proj);
375 proj.angles = vectoangles(proj.velocity);
376 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
377 // so: bulletconstant = bullet mass / area of bullet circle
378 setorigin(proj, start);
379 proj.flags = FL_PROJECTILE;
381 proj.touch = W_BallisticBullet_Touch;
383 proj.dmg_edge = headshotbonus;
384 proj.dmg_force = force;
385 proj.projectiledeathtype = dtype;
387 proj.oldvelocity = proj.velocity;
389 //calculate fired bullets for ballistics
390 if not(inWarmupStage)
393 self.bullets_fired[self.weapon] += 1;
394 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
397 if(cvar("g_antilag_bullets"))
398 if(pSpeed >= cvar("g_antilag_bullets"))
402 if(tracereffects & EF_RED)
403 eff = particleeffectnum("tr_rifle");
405 eff = particleeffectnum("tr_bullet");
407 // NOTE: this may severely throw off weapon balance
408 lag = ANTILAG_LATENCY(self);
411 if(clienttype(self) != CLIENTTYPE_REAL)
413 if(cvar("g_antilag") == 0)
414 lag = 0; // only do hitscan, but no antilag
418 antilag_takeback(pl, time - lag);
423 savetime = frametime;
428 // DP tracetoss is stupid and always traces in 0.05s
429 // ticks. This makes it trace in 0.05*0.125s ticks
435 self.velocity = self.velocity * 0.125;
436 self.gravity *= 0.125 * 0.125;
438 tracetoss(self, oldself);
442 if not(inWarmupStage)
445 self.bullets_fired[self.weapon] += 1;
446 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
449 if(vlen(trace_endpos - self.origin) > 16)
450 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
451 if(trace_fraction == 1)
453 // won't hit anything anytime soon (DP's
454 // tracetoss does 200 tics of, here,
455 // 0.05*0.125s, that is, 1.25 seconds
458 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
459 setorigin(self, trace_endpos);
460 self.velocity_z -= sv_gravity * dt;
462 if(!SUB_OwnerCheck())
464 if(SUB_NoImpactCheck())
468 W_BallisticBullet_Hit ();
472 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
475 W_BallisticBullet_LeaveSolid_think();
477 frametime = savetime;
489 if(tracereffects & EF_RED)
490 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
492 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
496 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
501 dir = normalize(dir + randomvec() * spread);
502 end = start + dir * MAX_SHOT_DISTANCE;
503 if(self.antilag_debug)
504 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
506 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
510 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
512 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
513 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
514 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
515 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
516 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
522 void W_PrepareExplosionByDamage(entity attacker, void() explode)
524 self.takedamage = DAMAGE_NO;
525 self.event_damage = SUB_Null;
526 self.owner = attacker;
528 // do not explode NOW but in the NEXT FRAME!
529 // because recursive calls to RadiusDamage are not allowed
530 self.nextthink = time;
531 self.think = explode;