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 proj.velocity = (dir + randomvec() * spread) * pSpeed;
365 W_SetupProjectileVelocity(proj);
366 proj.angles = vectoangles(proj.velocity);
367 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
368 // so: bulletconstant = bullet mass / area of bullet circle
369 setorigin(proj, start);
370 proj.flags = FL_PROJECTILE;
372 proj.touch = W_BallisticBullet_Touch;
374 proj.dmg_edge = headshotbonus;
375 proj.dmg_force = force;
376 proj.projectiledeathtype = dtype;
378 proj.oldvelocity = proj.velocity;
380 //calculate fired bullets for ballistics
381 if not(inWarmupStage)
384 self.bullets_fired[self.weapon] += 1;
385 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
388 if(cvar("g_antilag_bullets"))
389 if(pSpeed >= cvar("g_antilag_bullets"))
393 if(tracereffects & EF_RED)
394 eff = particleeffectnum("tr_rifle");
396 eff = particleeffectnum("tr_bullet");
398 // NOTE: this may severely throw off weapon balance
399 lag = ANTILAG_LATENCY(self);
402 if(clienttype(self) != CLIENTTYPE_REAL)
404 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
405 lag = 0; // only do hitscan, but no antilag
409 antilag_takeback(pl, time - lag);
414 savetime = frametime;
419 // DP tracetoss is stupid and always traces in 0.05s
420 // ticks. This makes it trace in 0.05*0.125s ticks
426 self.velocity = self.velocity * 0.125;
427 self.gravity *= 0.125 * 0.125;
429 tracetoss(self, oldself);
433 if not(inWarmupStage)
436 self.bullets_fired[self.weapon] += 1;
437 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
440 if(vlen(trace_endpos - self.origin) > 16)
441 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
442 if(trace_fraction == 1)
444 // won't hit anything anytime soon (DP's
445 // tracetoss does 200 tics of, here,
446 // 0.05*0.125s, that is, 1.25 seconds
449 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
450 setorigin(self, trace_endpos);
451 self.velocity_z -= sv_gravity * dt;
453 if(!SUB_OwnerCheck())
455 if(SUB_NoImpactCheck())
459 W_BallisticBullet_Hit ();
463 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
466 W_BallisticBullet_LeaveSolid_think();
468 frametime = savetime;
480 if(tracereffects & EF_RED)
481 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
482 else if(tracereffects & EF_BLUE)
483 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
485 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
489 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
494 dir = normalize(dir + randomvec() * spread);
495 end = start + dir * MAX_SHOT_DISTANCE;
496 if(self.antilag_debug)
497 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
499 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
503 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
505 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
506 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
507 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
508 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
509 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
515 void W_PrepareExplosionByDamage(entity attacker, void() explode)
517 self.takedamage = DAMAGE_NO;
518 self.event_damage = SUB_Null;
519 self.owner = attacker;
521 // do not explode NOW but in the NEXT FRAME!
522 // because recursive calls to RadiusDamage are not allowed
523 self.nextthink = time;
524 self.think = explode;