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;
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 FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) // we use realclient, so spectators can hear the whoosh too
101 // nearest point on the beam
102 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
104 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
108 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
110 if(!pseudoprojectile)
111 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
112 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
116 remove(pseudoprojectile);
118 // find all the entities the railgun hit and hurt them
119 ent = findfloat(world, railgunhit, TRUE);
122 // get the details we need to call the damage function
123 hitloc = ent.railgunhitloc;
124 ent.railgunhitloc = '0 0 0';
125 ent.railgunhitsolidbackup = SOLID_NOT;
126 ent.railgunhit = FALSE;
128 //for stats so that team hit will count as a miss
129 if(ent.flags & FL_CLIENT)
130 if(ent.deadflag == DEAD_NO)
134 if(ent.team == self.team)
138 if (ent.takedamage || ent.classname == "case")
139 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
141 // create a small explosion to throw gibs around (if applicable)
142 //setorigin (explosion, hitloc);
143 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
145 // advance to the next entity
146 ent = findfloat(ent, railgunhit, TRUE);
149 //calculate hits and fired shots for hitscan
150 if not(inWarmupStage)
153 self.bullets_fired[self.weapon] += 1;
156 self.bullets_hit[self.weapon] += 1;
158 // update the client and store in addstat() in g_world
159 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
160 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
163 // we're done with the explosion entity, remove it
166 trace_endpos = endpoint;
168 trace_dphitq3surfaceflags = endq3surfaceflags;
174 void W_BallisticBullet_Hit (void)
181 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
183 if(other.solid == SOLID_BSP)
184 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
186 if(other && other != self.enemy)
190 damage_headshotbonus = self.dmg_edge;
191 railgun_start = self.origin - 2 * frametime * self.velocity;
192 railgun_end = self.origin + 2 * frametime * self.velocity;
194 if(other.flags & FL_CLIENT)
195 if(other.deadflag == DEAD_NO)
199 if(other.team == self.owner.team)
202 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
203 damage_headshotbonus = 0;
205 if(self.dmg_edge != 0)
208 announce(self.owner, "announcer/male/headshot.wav");
210 announce(self.owner, "announcer/male/awesome.wav");
213 //calculate hits for ballistic weapons
214 if not(self.owner.isbot)
217 self.owner.bullets_hit[self.owner.weapon] += 1;
219 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
222 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
225 self.enemy = other; // don't hit the same player twice with the same bullet
228 .void(void) W_BallisticBullet_LeaveSolid_think_save;
229 .float W_BallisticBullet_LeaveSolid_nextthink_save;
230 .vector W_BallisticBullet_LeaveSolid_origin;
231 .vector W_BallisticBullet_LeaveSolid_velocity;
233 void W_BallisticBullet_LeaveSolid_think()
235 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
236 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
238 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
239 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
240 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
242 self.flags &~= FL_ONGROUND;
244 if(self.enemy.solid == SOLID_BSP)
247 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
248 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
251 UpdateCSQCProjectile(self);
254 // a fake logarithm function
259 if(x > 0.9 && x < 1.1)
261 return 2 * log(sqrt(x));
264 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
266 // move the entity along its velocity until it's out of solid, then let it resume
268 float dt, dst, velfactor, v0, vs;
272 // outside the world? forget it
273 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)
276 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
279 E0_m = 0.5 * v0 * v0;
280 maxdist = E0_m / constant;
281 // maxdist = 0.5 * v0 * v0 / constant
282 // dprint("max dist = ", ftos(maxdist), "\n");
287 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
289 if(trace_fraction == 1) // 1: we never got out of solid
292 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
294 dst = vlen(trace_endpos - self.origin);
295 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
296 Es_m = E0_m - constant * dst;
299 // roundoff errors got us
305 dt = dst / (0.5 * (v0 + vs));
306 // this is not correct, but the differential equations have no analytic
307 // solution - and these times are very small anyway
308 //print("dt = ", ftos(dt), "\n");
310 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
311 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
312 self.think = W_BallisticBullet_LeaveSolid_think;
313 self.nextthink = time + dt;
315 vel = vel * velfactor;
317 self.velocity = '0 0 0';
318 self.flags |= FL_ONGROUND; // prevent moving
319 self.W_BallisticBullet_LeaveSolid_velocity = vel;
324 void W_BallisticBullet_Touch (void)
326 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
330 W_BallisticBullet_Hit ();
333 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
339 self.projectiledeathtype |= HITTYPE_BOUNCE;
342 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)
344 float lag, dt, savetime;
349 proj.classname = "bullet";
351 PROJECTILE_MAKETRIGGER(proj);
352 if(gravityfactor > 0)
354 proj.movetype = MOVETYPE_TOSS;
355 proj.gravity = gravityfactor;
358 proj.movetype = MOVETYPE_FLY;
359 proj.think = SUB_Remove;
360 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
361 proj.velocity = (dir + randomvec() * spread) * pSpeed;
362 W_SetupProjectileVelocity(proj);
363 proj.angles = vectoangles(proj.velocity);
364 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
365 // so: bulletconstant = bullet mass / area of bullet circle
366 setorigin(proj, start);
367 proj.flags = FL_PROJECTILE;
369 proj.touch = W_BallisticBullet_Touch;
371 proj.dmg_edge = headshotbonus;
372 proj.dmg_force = force;
373 proj.projectiledeathtype = dtype;
375 proj.oldvelocity = proj.velocity;
377 //calculate fired bullets for ballistics
380 self.bullets_fired[self.weapon] += 1;
381 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
384 if(cvar("g_antilag_bullets"))
385 if(pSpeed >= cvar("g_antilag_bullets"))
387 // NOTE: this may severely throw off weapon balance
388 lag = ANTILAG_LATENCY(self);
391 if(clienttype(self) != CLIENTTYPE_REAL)
393 if(cvar("g_antilag") == 0)
394 lag = 0; // only do hitscan, but no antilag
398 antilag_takeback(pl, time - lag);
403 savetime = frametime;
408 // DP tracetoss is stupid and always traces in 0.05s
409 // ticks. This makes it trace in 0.05*0.125s ticks
415 self.velocity = self.velocity * 0.125;
416 self.gravity *= 0.125 * 0.125;
418 tracetoss(self, oldself);
424 self.bullets_fired[self.weapon] += 1;
425 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
428 if(vlen(trace_endpos - self.origin) > 32)
429 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
430 if(trace_fraction == 1)
432 // won't hit anything anytime soon (DP's
433 // tracetoss does 200 tics of, here,
434 // 0.05*0.125s, that is, 1.25 seconds
437 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
438 setorigin(self, trace_endpos);
439 self.velocity_z -= sv_gravity * dt;
441 if(!SUB_OwnerCheck())
443 if(SUB_NoImpactCheck())
447 W_BallisticBullet_Hit ();
451 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
454 W_BallisticBullet_LeaveSolid_think();
456 frametime = savetime;
468 if(tracereffects & EF_RED)
469 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
471 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
475 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
480 dir = normalize(dir + randomvec() * spread);
481 end = start + dir * MAX_SHOT_DISTANCE;
482 if(self.antilag_debug)
483 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
485 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
489 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
491 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
492 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
493 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
494 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
495 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
501 void W_PrepareExplosionByDamage(entity attacker, void() explode)
503 self.takedamage = DAMAGE_NO;
504 self.event_damage = SUB_Null;
505 self.owner = attacker;
507 // do not explode NOW but in the NEXT FRAME!
508 // because recursive calls to RadiusDamage are not allowed
509 self.nextthink = time;
510 self.think = explode;