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)
194 damage_headshotbonus = self.dmg_edge;
195 railgun_start = self.origin - 2 * frametime * self.velocity;
196 railgun_end = self.origin + 2 * frametime * self.velocity;
198 if(other.flags & FL_CLIENT)
199 if(other.deadflag == DEAD_NO)
203 if(other.team == self.owner.team)
206 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
207 damage_headshotbonus = 0;
209 if(self.dmg_edge != 0)
212 announce(self.owner, "announcer/male/headshot.wav");
214 announce(self.owner, "announcer/male/awesome.wav");
217 //calculate hits for ballistic weapons
218 if not(inWarmupStage)
219 if not(self.owner.isbot)
222 self.owner.bullets_hit[self.owner.weapon] += 1;
224 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
227 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
230 self.enemy = other; // don't hit the same player twice with the same bullet
233 .void(void) W_BallisticBullet_LeaveSolid_think_save;
234 .float W_BallisticBullet_LeaveSolid_nextthink_save;
235 .vector W_BallisticBullet_LeaveSolid_origin;
236 .vector W_BallisticBullet_LeaveSolid_velocity;
238 void W_BallisticBullet_LeaveSolid_think()
240 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
241 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
243 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
244 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
245 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
247 self.flags &~= FL_ONGROUND;
249 if(self.enemy.solid == SOLID_BSP)
252 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
253 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
256 UpdateCSQCProjectile(self);
259 // a fake logarithm function
264 if(x > 0.9 && x < 1.1)
266 return 2 * log(sqrt(x));
269 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
271 // move the entity along its velocity until it's out of solid, then let it resume
273 float dt, dst, velfactor, v0, vs;
277 // outside the world? forget it
278 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)
281 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
284 E0_m = 0.5 * v0 * v0;
285 maxdist = E0_m / constant;
286 // maxdist = 0.5 * v0 * v0 / constant
287 // dprint("max dist = ", ftos(maxdist), "\n");
292 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
294 if(trace_fraction == 1) // 1: we never got out of solid
297 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
299 dst = vlen(trace_endpos - self.origin);
300 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
301 Es_m = E0_m - constant * dst;
304 // roundoff errors got us
310 dt = dst / (0.5 * (v0 + vs));
311 // this is not correct, but the differential equations have no analytic
312 // solution - and these times are very small anyway
313 //print("dt = ", ftos(dt), "\n");
315 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
316 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
317 self.think = W_BallisticBullet_LeaveSolid_think;
318 self.nextthink = time + dt;
320 vel = vel * velfactor;
322 self.velocity = '0 0 0';
323 self.flags |= FL_ONGROUND; // prevent moving
324 self.W_BallisticBullet_LeaveSolid_velocity = vel;
329 void W_BallisticBullet_Touch (void)
331 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
335 W_BallisticBullet_Hit ();
338 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
344 self.projectiledeathtype |= HITTYPE_BOUNCE;
347 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)
349 float lag, dt, savetime;
354 proj.classname = "bullet";
356 PROJECTILE_MAKETRIGGER(proj);
357 if(gravityfactor > 0)
359 proj.movetype = MOVETYPE_TOSS;
360 proj.gravity = gravityfactor;
363 proj.movetype = MOVETYPE_FLY;
364 proj.think = SUB_Remove;
365 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
366 proj.velocity = (dir + randomvec() * spread) * pSpeed;
367 W_SetupProjectileVelocity(proj);
368 proj.angles = vectoangles(proj.velocity);
369 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
370 // so: bulletconstant = bullet mass / area of bullet circle
371 setorigin(proj, start);
372 proj.flags = FL_PROJECTILE;
374 proj.touch = W_BallisticBullet_Touch;
376 proj.dmg_edge = headshotbonus;
377 proj.dmg_force = force;
378 proj.projectiledeathtype = dtype;
380 proj.oldvelocity = proj.velocity;
382 //calculate fired bullets for ballistics
383 if not(inWarmupStage)
386 self.bullets_fired[self.weapon] += 1;
387 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
390 if(cvar("g_antilag_bullets"))
391 if(pSpeed >= cvar("g_antilag_bullets"))
393 // NOTE: this may severely throw off weapon balance
394 lag = ANTILAG_LATENCY(self);
397 if(clienttype(self) != CLIENTTYPE_REAL)
399 if(cvar("g_antilag") == 0)
400 lag = 0; // only do hitscan, but no antilag
404 antilag_takeback(pl, time - lag);
409 savetime = frametime;
414 // DP tracetoss is stupid and always traces in 0.05s
415 // ticks. This makes it trace in 0.05*0.125s ticks
421 self.velocity = self.velocity * 0.125;
422 self.gravity *= 0.125 * 0.125;
424 tracetoss(self, oldself);
428 if not(inWarmupStage)
431 self.bullets_fired[self.weapon] += 1;
432 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
435 if(vlen(trace_endpos - self.origin) > 32)
436 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
437 if(trace_fraction == 1)
439 // won't hit anything anytime soon (DP's
440 // tracetoss does 200 tics of, here,
441 // 0.05*0.125s, that is, 1.25 seconds
444 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
445 setorigin(self, trace_endpos);
446 self.velocity_z -= sv_gravity * dt;
448 if(!SUB_OwnerCheck())
450 if(SUB_NoImpactCheck())
454 W_BallisticBullet_Hit ();
458 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
461 W_BallisticBullet_LeaveSolid_think();
463 frametime = savetime;
475 if(tracereffects & EF_RED)
476 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
478 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
482 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
487 dir = normalize(dir + randomvec() * spread);
488 end = start + dir * MAX_SHOT_DISTANCE;
489 if(self.antilag_debug)
490 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
492 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
496 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
498 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
499 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
500 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
501 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
502 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
508 void W_PrepareExplosionByDamage(entity attacker, void() explode)
510 self.takedamage = DAMAGE_NO;
511 self.event_damage = SUB_Null;
512 self.owner = attacker;
514 // do not explode NOW but in the NEXT FRAME!
515 // because recursive calls to RadiusDamage are not allowed
516 self.nextthink = time;
517 self.think = explode;