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 FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) // we use realclient, so spectators can hear the whoosh too
102 // nearest point on the beam
103 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
105 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
109 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
111 if(!pseudoprojectile)
112 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
113 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
117 remove(pseudoprojectile);
119 // find all the entities the railgun hit and hurt them
120 ent = findfloat(world, railgunhit, TRUE);
123 // get the details we need to call the damage function
124 hitloc = ent.railgunhitloc;
125 ent.railgunhitloc = '0 0 0';
126 ent.railgunhitsolidbackup = SOLID_NOT;
127 ent.railgunhit = FALSE;
129 //for stats so that team hit will count as a miss
130 if(ent.flags & FL_CLIENT)
131 if(ent.deadflag == DEAD_NO)
135 if(ent.team == self.team)
139 if (ent.takedamage || ent.classname == "case")
140 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
142 // create a small explosion to throw gibs around (if applicable)
143 //setorigin (explosion, hitloc);
144 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
146 // advance to the next entity
147 ent = findfloat(ent, railgunhit, TRUE);
150 //calculate hits and fired shots for hitscan
151 if not(inWarmupStage)
154 self.bullets_fired[self.weapon] += 1;
157 self.bullets_hit[self.weapon] += 1;
159 // update the client and store in addstat() in g_world
160 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
161 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
164 // we're done with the explosion entity, remove it
167 trace_endpos = endpoint;
169 trace_dphitq3surfaceflags = endq3surfaceflags;
175 void W_BallisticBullet_Hit (void)
182 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
184 if(other.solid == SOLID_BSP)
185 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
187 if(other && other != self.enemy)
191 damage_headshotbonus = self.dmg_edge;
192 railgun_start = self.origin - 2 * frametime * self.velocity;
193 railgun_end = self.origin + 2 * frametime * self.velocity;
195 if(other.flags & FL_CLIENT)
196 if(other.deadflag == DEAD_NO)
200 if(other.team == self.owner.team)
203 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
204 damage_headshotbonus = 0;
206 if(self.dmg_edge != 0)
209 announce(self.owner, "announcer/male/headshot.wav");
211 announce(self.owner, "announcer/male/awesome.wav");
214 //calculate hits for ballistic weapons
215 if not(self.owner.isbot)
218 self.owner.bullets_hit[self.owner.weapon] += 1;
220 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
223 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
226 self.enemy = other; // don't hit the same player twice with the same bullet
229 .void(void) W_BallisticBullet_LeaveSolid_think_save;
230 .float W_BallisticBullet_LeaveSolid_nextthink_save;
231 .vector W_BallisticBullet_LeaveSolid_origin;
232 .vector W_BallisticBullet_LeaveSolid_velocity;
234 void W_BallisticBullet_LeaveSolid_think()
236 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
237 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
239 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
240 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
241 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
243 self.flags &~= FL_ONGROUND;
245 if(self.enemy.solid == SOLID_BSP)
248 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
249 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
252 UpdateCSQCProjectile(self);
255 // a fake logarithm function
260 if(x > 0.9 && x < 1.1)
262 return 2 * log(sqrt(x));
265 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
267 // move the entity along its velocity until it's out of solid, then let it resume
269 float dt, dst, velfactor, v0, vs;
273 // outside the world? forget it
274 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)
277 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
280 E0_m = 0.5 * v0 * v0;
281 maxdist = E0_m / constant;
282 // maxdist = 0.5 * v0 * v0 / constant
283 // dprint("max dist = ", ftos(maxdist), "\n");
288 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
290 if(trace_fraction == 1) // 1: we never got out of solid
293 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
295 dst = vlen(trace_endpos - self.origin);
296 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
297 Es_m = E0_m - constant * dst;
300 // roundoff errors got us
306 dt = dst / (0.5 * (v0 + vs));
307 // this is not correct, but the differential equations have no analytic
308 // solution - and these times are very small anyway
309 //print("dt = ", ftos(dt), "\n");
311 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
312 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
313 self.think = W_BallisticBullet_LeaveSolid_think;
314 self.nextthink = time + dt;
316 vel = vel * velfactor;
318 self.velocity = '0 0 0';
319 self.flags |= FL_ONGROUND; // prevent moving
320 self.W_BallisticBullet_LeaveSolid_velocity = vel;
325 void W_BallisticBullet_Touch (void)
327 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
331 W_BallisticBullet_Hit ();
334 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
340 self.projectiledeathtype |= HITTYPE_BOUNCE;
343 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)
345 float lag, dt, savetime;
350 proj.classname = "bullet";
352 PROJECTILE_MAKETRIGGER(proj);
353 if(gravityfactor > 0)
355 proj.movetype = MOVETYPE_TOSS;
356 proj.gravity = gravityfactor;
359 proj.movetype = MOVETYPE_FLY;
360 proj.think = SUB_Remove;
361 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
362 proj.velocity = (dir + randomvec() * spread) * pSpeed;
363 W_SetupProjectileVelocity(proj);
364 proj.angles = vectoangles(proj.velocity);
365 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
366 // so: bulletconstant = bullet mass / area of bullet circle
367 setorigin(proj, start);
368 proj.flags = FL_PROJECTILE;
370 proj.touch = W_BallisticBullet_Touch;
372 proj.dmg_edge = headshotbonus;
373 proj.dmg_force = force;
374 proj.projectiledeathtype = dtype;
376 proj.oldvelocity = proj.velocity;
378 //calculate fired bullets for ballistics
381 self.bullets_fired[self.weapon] += 1;
382 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
385 if(cvar("g_antilag_bullets"))
386 if(pSpeed >= cvar("g_antilag_bullets"))
388 // NOTE: this may severely throw off weapon balance
389 lag = ANTILAG_LATENCY(self);
392 if(clienttype(self) != CLIENTTYPE_REAL)
394 if(cvar("g_antilag") == 0)
395 lag = 0; // only do hitscan, but no antilag
399 antilag_takeback(pl, time - lag);
404 savetime = frametime;
409 // DP tracetoss is stupid and always traces in 0.05s
410 // ticks. This makes it trace in 0.05*0.125s ticks
416 self.velocity = self.velocity * 0.125;
417 self.gravity *= 0.125 * 0.125;
419 tracetoss(self, oldself);
425 self.bullets_fired[self.weapon] += 1;
426 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
429 if(vlen(trace_endpos - self.origin) > 32)
430 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
431 if(trace_fraction == 1)
433 // won't hit anything anytime soon (DP's
434 // tracetoss does 200 tics of, here,
435 // 0.05*0.125s, that is, 1.25 seconds
438 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
439 setorigin(self, trace_endpos);
440 self.velocity_z -= sv_gravity * dt;
442 if(!SUB_OwnerCheck())
444 if(SUB_NoImpactCheck())
448 W_BallisticBullet_Hit ();
452 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
455 W_BallisticBullet_LeaveSolid_think();
457 frametime = savetime;
469 if(tracereffects & EF_RED)
470 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
472 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
476 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
481 dir = normalize(dir + randomvec() * spread);
482 end = start + dir * MAX_SHOT_DISTANCE;
483 if(self.antilag_debug)
484 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
486 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
490 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
492 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
493 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
494 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
495 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
496 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
502 void W_PrepareExplosionByDamage(entity attacker, void() explode)
504 self.takedamage = DAMAGE_NO;
505 self.event_damage = SUB_Null;
506 self.owner = attacker;
508 // do not explode NOW but in the NEXT FRAME!
509 // because recursive calls to RadiusDamage are not allowed
510 self.nextthink = time;
511 self.think = explode;