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);
122 //calculate hits and fired shots for hitscan
125 self.bullets_fired[self.weapon] += 1;
127 self.bullets_hit[self.weapon] += 1;
129 // update the client and store in addstat() in g_world
130 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
131 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
134 // we're done with the explosion entity, remove it
137 trace_endpos = endpoint;
139 trace_dphitq3surfaceflags = endq3surfaceflags;
145 void W_BallisticBullet_Hit (void)
152 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
154 if(other.solid == SOLID_BSP)
155 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
157 if(other && other != self.enemy)
161 damage_headshotbonus = self.dmg_edge;
162 railgun_start = self.origin - 2 * frametime * self.velocity;
163 railgun_end = self.origin + 2 * frametime * self.velocity;
165 if(other.flags & FL_CLIENT)
166 if(other.deadflag == DEAD_NO)
170 if(other.team == self.owner.team)
173 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
174 damage_headshotbonus = 0;
176 if(self.dmg_edge != 0)
179 announce(self.owner, "announcer/male/headshot.wav");
181 announce(self.owner, "announcer/male/awesome.wav");
184 //calculate hits for ballistic weapons
185 if not(self.owner.isbot)
188 self.owner.bullets_hit[self.owner.weapon] += 1;
190 self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
193 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
196 self.enemy = other; // don't hit the same player twice with the same bullet
199 .void(void) W_BallisticBullet_LeaveSolid_think_save;
200 .float W_BallisticBullet_LeaveSolid_nextthink_save;
201 .vector W_BallisticBullet_LeaveSolid_origin;
202 .vector W_BallisticBullet_LeaveSolid_velocity;
204 void W_BallisticBullet_LeaveSolid_think()
206 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
207 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
209 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
210 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
211 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
213 self.flags &~= FL_ONGROUND;
215 if(self.enemy.solid == SOLID_BSP)
218 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
219 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
222 UpdateCSQCProjectile(self);
225 // a fake logarithm function
230 if(x > 0.9 && x < 1.1)
232 return 2 * log(sqrt(x));
235 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
237 // move the entity along its velocity until it's out of solid, then let it resume
239 float dt, dst, velfactor, v0, vs;
243 // outside the world? forget it
244 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)
247 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
250 E0_m = 0.5 * v0 * v0;
251 maxdist = E0_m / constant;
252 // maxdist = 0.5 * v0 * v0 / constant
253 // dprint("max dist = ", ftos(maxdist), "\n");
258 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
260 if(trace_fraction == 1) // 1: we never got out of solid
263 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
265 dst = vlen(trace_endpos - self.origin);
266 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
267 Es_m = E0_m - constant * dst;
270 // roundoff errors got us
276 dt = dst / (0.5 * (v0 + vs));
277 // this is not correct, but the differential equations have no analytic
278 // solution - and these times are very small anyway
279 //print("dt = ", ftos(dt), "\n");
281 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
282 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
283 self.think = W_BallisticBullet_LeaveSolid_think;
284 self.nextthink = time + dt;
286 vel = vel * velfactor;
288 self.velocity = '0 0 0';
289 self.flags |= FL_ONGROUND; // prevent moving
290 self.W_BallisticBullet_LeaveSolid_velocity = vel;
295 void W_BallisticBullet_Touch (void)
297 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
301 W_BallisticBullet_Hit ();
304 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
310 self.projectiledeathtype |= HITTYPE_BOUNCE;
313 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)
315 float lag, dt, savetime;
320 proj.classname = "bullet";
322 proj.solid = SOLID_BBOX;
323 if(gravityfactor > 0)
325 proj.movetype = MOVETYPE_TOSS;
326 proj.gravity = gravityfactor;
329 proj.movetype = MOVETYPE_FLY;
330 proj.think = SUB_Remove;
331 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
332 proj.velocity = (dir + randomvec() * spread) * pSpeed;
333 W_SetupProjectileVelocity(proj);
334 proj.angles = vectoangles(proj.velocity);
335 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
336 // so: bulletconstant = bullet mass / area of bullet circle
337 setorigin(proj, start);
338 proj.flags = FL_PROJECTILE;
340 proj.touch = W_BallisticBullet_Touch;
342 proj.dmg_edge = headshotbonus;
343 proj.dmg_force = force;
344 proj.projectiledeathtype = dtype;
346 proj.oldvelocity = proj.velocity;
348 //calculate fired bullets for ballistics
351 self.bullets_fired[self.weapon] += 1;
352 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
355 if(cvar("g_antilag_bullets"))
356 if(pSpeed >= cvar("g_antilag_bullets"))
358 // NOTE: this may severely throw off weapon balance
359 lag = ANTILAG_LATENCY(self);
362 if(clienttype(self) != CLIENTTYPE_REAL)
364 if(cvar("g_antilag") == 0)
365 lag = 0; // only do hitscan, but no antilag
369 antilag_takeback(pl, time - lag);
374 savetime = frametime;
379 // DP tracetoss is stupid and always traces in 0.05s
380 // ticks. This makes it trace in 0.05*0.125s ticks
386 self.velocity = self.velocity * 0.125;
387 self.gravity *= 0.125 * 0.125;
389 tracetoss(self, oldself);
395 self.bullets_fired[self.weapon] += 1;
396 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
399 if(vlen(trace_endpos - self.origin) > 32)
400 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
401 if(trace_fraction == 1)
403 // won't hit anything anytime soon (DP's
404 // tracetoss does 200 tics of, here,
405 // 0.05*0.125s, that is, 1.25 seconds
408 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
409 setorigin(self, trace_endpos);
410 self.velocity_z -= sv_gravity * dt;
412 if(!SUB_OwnerCheck())
414 if(SUB_NoImpactCheck())
418 W_BallisticBullet_Hit ();
422 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
425 W_BallisticBullet_LeaveSolid_think();
427 frametime = savetime;
439 if(tracereffects & EF_RED)
440 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
442 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
446 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
451 dir = normalize(dir + randomvec() * spread);
452 end = start + dir * MAX_SHOT_DISTANCE;
453 if(self.antilag_debug)
454 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
456 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
460 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
462 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
463 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
464 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
465 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
466 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
472 void W_PrepareExplosionByDamage(entity attacker, void() explode)
474 self.takedamage = DAMAGE_NO;
475 self.event_damage = SUB_Null;
476 self.owner = attacker;
478 // do not explode NOW but in the NEXT FRAME!
479 // because recursive calls to RadiusDamage are not allowed
480 self.nextthink = time;
481 self.think = explode;