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");
31 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype)
33 local vector hitloc, force, endpoint, dir;
34 local entity ent, endent;
35 local float endq3surfaceflags;
36 //local entity explosion;
41 entity pseudoprojectile;
46 railgun_start = start;
49 dir = normalize(end - start);
50 length = vlen(end - start);
53 // go a little bit into the wall because we need to hit this wall later
56 // trace multiple times until we hit a wall, each obstacle will be made
57 // non-solid so we can hit the next, while doing this we spawn effects and
58 // note down which entities were hit so we can damage them later
61 if(self.antilag_debug)
62 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
64 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
66 // if it is world we can't hurt it so stop now
67 if (trace_ent == world || trace_fraction == 1)
70 // make the entity non-solid so we can hit the next one
71 trace_ent.railgunhit = TRUE;
72 trace_ent.railgunhitloc = end;
73 trace_ent.railgunhitsolidbackup = trace_ent.solid;
75 // stop if this is a wall
76 if (trace_ent.solid == SOLID_BSP)
79 // make the entity non-solid
80 trace_ent.solid = SOLID_NOT;
83 endpoint = trace_endpos;
85 endq3surfaceflags = trace_dphitq3surfaceflags;
87 // find all the entities the railgun hit and restore their solid state
88 ent = findfloat(world, railgunhit, TRUE);
91 // restore their solid type
92 ent.solid = ent.railgunhitsolidbackup;
93 ent = findfloat(ent, railgunhit, TRUE);
96 // spawn a temporary explosion entity for RadiusDamage calls
97 //explosion = spawn();
99 // Find all non-hit players the beam passed close by
100 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
102 FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) // we use realclient, so spectators can hear the whoosh too
104 // nearest point on the beam
105 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
107 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
111 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
113 if(!pseudoprojectile)
114 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
115 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
119 remove(pseudoprojectile);
122 // find all the entities the railgun hit and hurt them
123 ent = findfloat(world, railgunhit, TRUE);
126 // get the details we need to call the damage function
127 hitloc = ent.railgunhitloc;
128 ent.railgunhitloc = '0 0 0';
129 ent.railgunhitsolidbackup = SOLID_NOT;
130 ent.railgunhit = FALSE;
132 //for stats so that team hit will count as a miss
133 if(ent.flags & FL_CLIENT)
134 if(ent.deadflag == DEAD_NO)
138 if(ent.team == self.team)
142 if (ent.takedamage || ent.classname == "case")
143 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
145 // create a small explosion to throw gibs around (if applicable)
146 //setorigin (explosion, hitloc);
147 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
149 // advance to the next entity
150 ent = findfloat(ent, railgunhit, TRUE);
153 //calculate hits and fired shots for hitscan
154 if not(inWarmupStage)
157 self.bullets_fired[self.weapon] += 1;
160 self.bullets_hit[self.weapon] += 1;
162 // update the client and store in addstat() in g_world
163 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
164 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
167 // we're done with the explosion entity, remove it
170 trace_endpos = endpoint;
172 trace_dphitq3surfaceflags = endq3surfaceflags;
178 void W_BallisticBullet_Hit (void)
185 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
187 if(other.solid == SOLID_BSP)
188 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
190 if(other && other != self.enemy)
192 endzcurveparticles();
196 damage_headshotbonus = self.dmg_edge;
197 railgun_start = self.origin - 2 * frametime * self.velocity;
198 railgun_end = self.origin + 2 * frametime * self.velocity;
200 if(other.flags & FL_CLIENT)
201 if(other.deadflag == DEAD_NO)
205 if(other.team == self.owner.team)
208 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
209 damage_headshotbonus = 0;
211 if(self.dmg_edge != 0)
214 announce(self.owner, "announcer/male/headshot.wav");
216 announce(self.owner, "announcer/male/awesome.wav");
219 //calculate hits for ballistic weapons
220 if not(inWarmupStage)
221 if not(self.owner.isbot)
224 self.owner.bullets_hit[self.owner.weapon] += 1;
226 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
229 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
232 self.enemy = other; // don't hit the same player twice with the same bullet
235 .void(void) W_BallisticBullet_LeaveSolid_think_save;
236 .float W_BallisticBullet_LeaveSolid_nextthink_save;
237 .vector W_BallisticBullet_LeaveSolid_origin;
238 .vector W_BallisticBullet_LeaveSolid_velocity;
240 void W_BallisticBullet_LeaveSolid_think()
242 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
243 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
245 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
246 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
247 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
249 self.flags &~= FL_ONGROUND;
251 if(self.enemy.solid == SOLID_BSP)
254 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
255 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
258 UpdateCSQCProjectile(self);
261 // a fake logarithm function
266 if(x > 0.9 && x < 1.1)
268 return 2 * log(sqrt(x));
271 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
273 // move the entity along its velocity until it's out of solid, then let it resume
275 float dt, dst, velfactor, v0, vs;
279 // outside the world? forget it
280 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)
283 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
286 E0_m = 0.5 * v0 * v0;
287 maxdist = E0_m / constant;
288 // maxdist = 0.5 * v0 * v0 / constant
289 // dprint("max dist = ", ftos(maxdist), "\n");
294 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
296 if(trace_fraction == 1) // 1: we never got out of solid
299 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
301 dst = vlen(trace_endpos - self.origin);
302 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
303 Es_m = E0_m - constant * dst;
306 // roundoff errors got us
312 dt = dst / (0.5 * (v0 + vs));
313 // this is not correct, but the differential equations have no analytic
314 // solution - and these times are very small anyway
315 //print("dt = ", ftos(dt), "\n");
317 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
318 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
319 self.think = W_BallisticBullet_LeaveSolid_think;
320 self.nextthink = time + dt;
322 vel = vel * velfactor;
324 self.velocity = '0 0 0';
325 self.flags |= FL_ONGROUND; // prevent moving
326 self.W_BallisticBullet_LeaveSolid_velocity = vel;
331 void W_BallisticBullet_Touch (void)
333 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
337 W_BallisticBullet_Hit ();
340 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
346 self.projectiledeathtype |= HITTYPE_BOUNCE;
349 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)
351 float lag, dt, savetime;
356 proj.classname = "bullet";
358 PROJECTILE_MAKETRIGGER(proj);
359 if(gravityfactor > 0)
361 proj.movetype = MOVETYPE_TOSS;
362 proj.gravity = gravityfactor;
365 proj.movetype = MOVETYPE_FLY;
366 proj.think = SUB_Remove;
367 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
368 proj.velocity = (dir + randomvec() * spread) * pSpeed;
369 W_SetupProjectileVelocity(proj);
370 proj.angles = vectoangles(proj.velocity);
371 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
372 // so: bulletconstant = bullet mass / area of bullet circle
373 setorigin(proj, start);
374 proj.flags = FL_PROJECTILE;
376 proj.touch = W_BallisticBullet_Touch;
378 proj.dmg_edge = headshotbonus;
379 proj.dmg_force = force;
380 proj.projectiledeathtype = dtype;
382 proj.oldvelocity = proj.velocity;
384 //calculate fired bullets for ballistics
385 if not(inWarmupStage)
388 self.bullets_fired[self.weapon] += 1;
389 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
392 if(cvar("g_antilag_bullets"))
393 if(pSpeed >= cvar("g_antilag_bullets"))
395 // NOTE: this may severely throw off weapon balance
396 lag = ANTILAG_LATENCY(self);
399 if(clienttype(self) != CLIENTTYPE_REAL)
401 if(cvar("g_antilag") == 0)
402 lag = 0; // only do hitscan, but no antilag
406 antilag_takeback(pl, time - lag);
411 savetime = frametime;
416 // DP tracetoss is stupid and always traces in 0.05s
417 // ticks. This makes it trace in 0.05*0.125s ticks
423 self.velocity = self.velocity * 0.125;
424 self.gravity *= 0.125 * 0.125;
426 tracetoss(self, oldself);
430 if not(inWarmupStage)
433 self.bullets_fired[self.weapon] += 1;
434 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
437 if(vlen(trace_endpos - self.origin) > 32)
438 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
439 if(trace_fraction == 1)
441 // won't hit anything anytime soon (DP's
442 // tracetoss does 200 tics of, here,
443 // 0.05*0.125s, that is, 1.25 seconds
446 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
447 setorigin(self, trace_endpos);
448 self.velocity_z -= sv_gravity * dt;
450 if(!SUB_OwnerCheck())
452 if(SUB_NoImpactCheck())
456 W_BallisticBullet_Hit ();
460 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
463 W_BallisticBullet_LeaveSolid_think();
465 frametime = savetime;
468 endzcurveparticles();
479 if(tracereffects & EF_RED)
480 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
482 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
486 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
491 dir = normalize(dir + randomvec() * spread);
492 end = start + dir * MAX_SHOT_DISTANCE;
493 if(self.antilag_debug)
494 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
496 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
500 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
502 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
503 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
504 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
505 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
506 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
512 void W_PrepareExplosionByDamage(entity attacker, void() explode)
514 self.takedamage = DAMAGE_NO;
515 self.event_damage = SUB_Null;
516 self.owner = attacker;
518 // do not explode NOW but in the NEXT FRAME!
519 // because recursive calls to RadiusDamage are not allowed
520 self.nextthink = time;
521 self.think = explode;