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 railgun_start = start;
43 dir = normalize(end - start);
46 // go a little bit into the wall because we need to hit this wall later
49 // trace multiple times until we hit a wall, each obstacle will be made
50 // non-solid so we can hit the next, while doing this we spawn effects and
51 // note down which entities were hit so we can damage them later
54 if(self.antilag_debug)
55 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
57 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
59 // if it is world we can't hurt it so stop now
60 if (trace_ent == world || trace_fraction == 1)
63 // make the entity non-solid so we can hit the next one
64 trace_ent.railgunhit = TRUE;
65 trace_ent.railgunhitloc = end;
66 trace_ent.railgunhitsolidbackup = trace_ent.solid;
68 // stop if this is a wall
69 if (trace_ent.solid == SOLID_BSP)
72 // make the entity non-solid
73 trace_ent.solid = SOLID_NOT;
76 endpoint = trace_endpos;
78 endq3surfaceflags = trace_dphitq3surfaceflags;
80 // find all the entities the railgun hit and restore their solid state
81 ent = findfloat(world, railgunhit, TRUE);
84 // restore their solid type
85 ent.solid = ent.railgunhitsolidbackup;
86 ent = findfloat(ent, railgunhit, TRUE);
89 // spawn a temporary explosion entity for RadiusDamage calls
90 //explosion = spawn();
92 // find all the entities the railgun hit and hurt them
93 ent = findfloat(world, railgunhit, TRUE);
96 // get the details we need to call the damage function
97 hitloc = ent.railgunhitloc;
98 ent.railgunhitloc = '0 0 0';
99 ent.railgunhitsolidbackup = SOLID_NOT;
100 ent.railgunhit = FALSE;
102 //for stats so that team hit will count as a miss
103 if(ent.flags & FL_CLIENT)
104 if(ent.deadflag == DEAD_NO)
108 if(ent.team == self.team)
112 if (ent.takedamage || ent.classname == "case")
113 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
115 // create a small explosion to throw gibs around (if applicable)
116 //setorigin (explosion, hitloc);
117 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
119 // advance to the next entity
120 ent = findfloat(ent, railgunhit, TRUE);
123 //calculate hits and fired shots for hitscan
124 if not(inWarmupStage)
127 self.bullets_fired[self.weapon] += 1;
130 self.bullets_hit[self.weapon] += 1;
132 // update the client and store in addstat() in g_world
133 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
134 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
137 // we're done with the explosion entity, remove it
140 trace_endpos = endpoint;
142 trace_dphitq3surfaceflags = endq3surfaceflags;
148 void W_BallisticBullet_Hit (void)
155 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
157 if(other.solid == SOLID_BSP)
158 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
160 if(other && other != self.enemy)
164 damage_headshotbonus = self.dmg_edge;
165 railgun_start = self.origin - 2 * frametime * self.velocity;
166 railgun_end = self.origin + 2 * frametime * self.velocity;
168 if(other.flags & FL_CLIENT)
169 if(other.deadflag == DEAD_NO)
173 if(other.team == self.owner.team)
176 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
177 damage_headshotbonus = 0;
179 if(self.dmg_edge != 0)
182 announce(self.owner, "announcer/male/headshot.wav");
184 announce(self.owner, "announcer/male/awesome.wav");
187 //calculate hits for ballistic weapons
188 if not(self.owner.isbot)
191 self.owner.bullets_hit[self.owner.weapon] += 1;
193 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
196 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
199 self.enemy = other; // don't hit the same player twice with the same bullet
202 .void(void) W_BallisticBullet_LeaveSolid_think_save;
203 .float W_BallisticBullet_LeaveSolid_nextthink_save;
204 .vector W_BallisticBullet_LeaveSolid_origin;
205 .vector W_BallisticBullet_LeaveSolid_velocity;
207 void W_BallisticBullet_LeaveSolid_think()
209 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
210 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
212 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
213 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
214 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
216 self.flags &~= FL_ONGROUND;
218 if(self.enemy.solid == SOLID_BSP)
221 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
222 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
225 UpdateCSQCProjectile(self);
228 // a fake logarithm function
233 if(x > 0.9 && x < 1.1)
235 return 2 * log(sqrt(x));
238 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
240 // move the entity along its velocity until it's out of solid, then let it resume
242 float dt, dst, velfactor, v0, vs;
246 // outside the world? forget it
247 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)
250 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
253 E0_m = 0.5 * v0 * v0;
254 maxdist = E0_m / constant;
255 // maxdist = 0.5 * v0 * v0 / constant
256 // dprint("max dist = ", ftos(maxdist), "\n");
261 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
263 if(trace_fraction == 1) // 1: we never got out of solid
266 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
268 dst = vlen(trace_endpos - self.origin);
269 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
270 Es_m = E0_m - constant * dst;
273 // roundoff errors got us
279 dt = dst / (0.5 * (v0 + vs));
280 // this is not correct, but the differential equations have no analytic
281 // solution - and these times are very small anyway
282 //print("dt = ", ftos(dt), "\n");
284 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
285 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
286 self.think = W_BallisticBullet_LeaveSolid_think;
287 self.nextthink = time + dt;
289 vel = vel * velfactor;
291 self.velocity = '0 0 0';
292 self.flags |= FL_ONGROUND; // prevent moving
293 self.W_BallisticBullet_LeaveSolid_velocity = vel;
298 void W_BallisticBullet_Touch (void)
300 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
304 W_BallisticBullet_Hit ();
307 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
313 self.projectiledeathtype |= HITTYPE_BOUNCE;
316 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)
318 float lag, dt, savetime;
323 proj.classname = "bullet";
325 PROJECTILE_MAKETRIGGER(proj);
326 if(gravityfactor > 0)
328 proj.movetype = MOVETYPE_TOSS;
329 proj.gravity = gravityfactor;
332 proj.movetype = MOVETYPE_FLY;
333 proj.think = SUB_Remove;
334 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
335 proj.velocity = (dir + randomvec() * spread) * pSpeed;
336 W_SetupProjectileVelocity(proj);
337 proj.angles = vectoangles(proj.velocity);
338 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
339 // so: bulletconstant = bullet mass / area of bullet circle
340 setorigin(proj, start);
341 proj.flags = FL_PROJECTILE;
343 proj.touch = W_BallisticBullet_Touch;
345 proj.dmg_edge = headshotbonus;
346 proj.dmg_force = force;
347 proj.projectiledeathtype = dtype;
349 proj.oldvelocity = proj.velocity;
351 //calculate fired bullets for ballistics
354 self.bullets_fired[self.weapon] += 1;
355 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
358 if(cvar("g_antilag_bullets"))
359 if(pSpeed >= cvar("g_antilag_bullets"))
361 // NOTE: this may severely throw off weapon balance
362 lag = ANTILAG_LATENCY(self);
365 if(clienttype(self) != CLIENTTYPE_REAL)
367 if(cvar("g_antilag") == 0)
368 lag = 0; // only do hitscan, but no antilag
372 antilag_takeback(pl, time - lag);
377 savetime = frametime;
382 // DP tracetoss is stupid and always traces in 0.05s
383 // ticks. This makes it trace in 0.05*0.125s ticks
389 self.velocity = self.velocity * 0.125;
390 self.gravity *= 0.125 * 0.125;
392 tracetoss(self, oldself);
398 self.bullets_fired[self.weapon] += 1;
399 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
402 if(vlen(trace_endpos - self.origin) > 32)
403 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
404 if(trace_fraction == 1)
406 // won't hit anything anytime soon (DP's
407 // tracetoss does 200 tics of, here,
408 // 0.05*0.125s, that is, 1.25 seconds
411 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
412 setorigin(self, trace_endpos);
413 self.velocity_z -= sv_gravity * dt;
415 if(!SUB_OwnerCheck())
417 if(SUB_NoImpactCheck())
421 W_BallisticBullet_Hit ();
425 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
428 W_BallisticBullet_LeaveSolid_think();
430 frametime = savetime;
442 if(tracereffects & EF_RED)
443 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
445 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
449 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
454 dir = normalize(dir + randomvec() * spread);
455 end = start + dir * MAX_SHOT_DISTANCE;
456 if(self.antilag_debug)
457 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
459 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
463 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
465 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
466 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
467 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
468 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
469 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
475 void W_PrepareExplosionByDamage(entity attacker, void() explode)
477 self.takedamage = DAMAGE_NO;
478 self.event_damage = SUB_Null;
479 self.owner = attacker;
481 // do not explode NOW but in the NEXT FRAME!
482 // because recursive calls to RadiusDamage are not allowed
483 self.nextthink = time;
484 self.think = explode;