1 void W_GiveWeapon (entity e, float wep, string name)
8 e.weapons = e.weapons | W_WeaponBit(wep);
14 if (other.classname == "player")
16 sprint (other, "You got the ^2");
24 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
26 local vector hitloc, force, endpoint, dir;
27 local entity ent, endent;
28 local float endq3surfaceflags;
33 entity pseudoprojectile;
38 railgun_start = start;
41 dir = normalize(end - start);
42 length = vlen(end - start);
45 // go a little bit into the wall because we need to hit this wall later
48 // trace multiple times until we hit a wall, each obstacle will be made
49 // non-solid so we can hit the next, while doing this we spawn effects and
50 // note down which entities were hit so we can damage them later
53 if(self.antilag_debug)
54 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
56 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
58 // if it is world we can't hurt it so stop now
59 if (trace_ent == world || trace_fraction == 1)
62 // make the entity non-solid so we can hit the next one
63 trace_ent.railgunhit = TRUE;
64 trace_ent.railgunhitloc = end;
65 trace_ent.railgunhitsolidbackup = trace_ent.solid;
67 // stop if this is a wall
68 if (trace_ent.solid == SOLID_BSP)
71 // make the entity non-solid
72 trace_ent.solid = SOLID_NOT;
75 endpoint = trace_endpos;
77 endq3surfaceflags = trace_dphitq3surfaceflags;
79 // find all the entities the railgun hit and restore their solid state
80 ent = findfloat(world, railgunhit, TRUE);
83 // restore their solid type
84 ent.solid = ent.railgunhitsolidbackup;
85 ent = findfloat(ent, railgunhit, TRUE);
88 // spawn a temporary explosion entity for RadiusDamage calls
89 //explosion = spawn();
91 // Find all non-hit players the beam passed close by
92 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
94 FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) if not(msg_entity.classname == "spectator" && msg_entity.enemy == self) // we use realclient, so spectators can hear the whoosh too
96 // nearest point on the beam
97 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
99 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
103 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
105 if(!pseudoprojectile)
106 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
107 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
111 remove(pseudoprojectile);
114 // find all the entities the railgun hit and hurt them
115 ent = findfloat(world, railgunhit, TRUE);
118 // get the details we need to call the damage function
119 hitloc = ent.railgunhitloc;
120 ent.railgunhitloc = '0 0 0';
121 ent.railgunhitsolidbackup = SOLID_NOT;
122 ent.railgunhit = FALSE;
124 //for stats so that team hit will count as a miss
125 if(ent.flags & FL_CLIENT)
126 if(ent.deadflag == DEAD_NO)
130 if(ent.team == self.team)
133 f = ExponentialFalloff(mindist, maxdist, halflifedist, (ent.origin - start) * dir);
134 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, (ent.origin - start) * dir);
138 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
140 // create a small explosion to throw gibs around (if applicable)
141 //setorigin (explosion, hitloc);
142 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
144 // advance to the next entity
145 ent = findfloat(ent, railgunhit, TRUE);
148 // calculate hits and fired shots for hitscan
149 if not(inWarmupStage)
151 self.stats_fired[self.weapon - 1] += 1;
152 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
155 self.stats_hit[self.weapon - 1] += 1;
156 self.stat_hit = self.weapon + 64 * floor(self.stats_hit[self.weapon - 1]);
160 trace_endpos = endpoint;
162 trace_dphitq3surfaceflags = endq3surfaceflags;
168 void W_BallisticBullet_Hit (void)
172 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
174 if(other.solid == SOLID_BSP)
175 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
177 if(other && other != self.enemy)
179 endzcurveparticles();
183 damage_headshotbonus = self.dmg_edge;
184 railgun_start = self.origin - 2 * frametime * self.velocity;
185 railgun_end = self.origin + 2 * frametime * self.velocity;
187 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
188 damage_headshotbonus = 0;
190 if(self.dmg_edge != 0)
193 announce(self.owner, "announcer/male/headshot.wav");
195 announce(self.owner, "announcer/male/awesome.wav");
198 // calculate hits for ballistic weapons
199 if (other.flags & FL_CLIENT) // is the player a client
200 if (other.deadflag == DEAD_NO) // is the victim a corpse
201 if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
202 if not(inWarmupStage) // not in warm up stage
204 self.owner.stats_hit[self.owner.weapon - 1] += 1;
205 self.owner.stat_hit = self.owner.weapon + 64 * floor(self.owner.stats_hit[self.owner.weapon - 1]);
209 self.enemy = other; // don't hit the same player twice with the same bullet
212 .void(void) W_BallisticBullet_LeaveSolid_think_save;
213 .float W_BallisticBullet_LeaveSolid_nextthink_save;
214 .vector W_BallisticBullet_LeaveSolid_origin;
215 .vector W_BallisticBullet_LeaveSolid_velocity;
217 void W_BallisticBullet_LeaveSolid_think()
219 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
220 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
222 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
223 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
224 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
226 self.flags &~= FL_ONGROUND;
228 if(self.enemy.solid == SOLID_BSP)
231 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
232 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
235 UpdateCSQCProjectile(self);
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");
258 if(maxdist <= cvar("g_ballistics_mindistance"))
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 endFireBallisticBullet()
318 endzcurveparticles();
321 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)
323 float lag, dt, savetime;
328 proj.classname = "bullet";
330 PROJECTILE_MAKETRIGGER(proj);
331 if(gravityfactor > 0)
333 proj.movetype = MOVETYPE_TOSS;
334 proj.gravity = gravityfactor;
337 proj.movetype = MOVETYPE_FLY;
338 proj.think = SUB_Remove;
339 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
340 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, spread);
341 proj.angles = vectoangles(proj.velocity);
342 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
343 // so: bulletconstant = bullet mass / area of bullet circle
344 setorigin(proj, start);
345 proj.flags = FL_PROJECTILE;
347 proj.touch = W_BallisticBullet_Touch;
349 proj.dmg_edge = headshotbonus;
350 proj.dmg_force = force;
351 proj.projectiledeathtype = dtype;
353 proj.oldvelocity = proj.velocity;
355 if(cvar("g_antilag_bullets"))
356 if(pSpeed >= cvar("g_antilag_bullets"))
360 if(tracereffects & EF_RED)
361 eff = particleeffectnum("tr_rifle");
363 eff = particleeffectnum("tr_bullet");
365 // NOTE: this may severely throw off weapon balance
366 lag = ANTILAG_LATENCY(self);
369 if(clienttype(self) != CLIENTTYPE_REAL)
371 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
372 lag = 0; // only do hitscan, but no antilag
376 antilag_takeback(pl, time - lag);
381 savetime = frametime;
384 // update the accuracy stats - increase shots fired by 1
385 if not(inWarmupStage)
387 oldself.stats_fired[oldself.weapon - 1] += 1;
388 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
393 // DP tracetoss is stupid and always traces in 0.05s
394 // ticks. This makes it trace in 0.05*0.125s ticks
400 self.velocity = self.velocity * 0.125;
401 self.gravity *= 0.125 * 0.125;
403 tracetoss(self, oldself);
407 if(vlen(trace_endpos - self.origin) > 16)
408 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
409 if(trace_fraction == 1)
411 // won't hit anything anytime soon (DP's
412 // tracetoss does 200 tics of, here,
413 // 0.05*0.125s, that is, 1.25 seconds
416 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
417 setorigin(self, trace_endpos);
418 self.velocity_z -= sv_gravity * dt;
420 if(!SUB_OwnerCheck())
422 if(SUB_NoImpactCheck())
426 W_BallisticBullet_Hit();
430 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
433 W_BallisticBullet_LeaveSolid_think();
435 frametime = savetime;
447 // update the accuracy stats
448 if not(inWarmupStage)
450 self.stats_fired[self.weapon - 1] += 1;
451 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
454 if(tracereffects & EF_RED)
455 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
456 else if(tracereffects & EF_BLUE)
457 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
459 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
462 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
466 dir = normalize(dir + randomvec() * spread);
467 end = start + dir * MAX_SHOT_DISTANCE;
468 if(self.antilag_debug)
469 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
471 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
475 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
477 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
478 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
479 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
480 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
481 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
486 void W_PrepareExplosionByDamage(entity attacker, void() explode)
488 self.takedamage = DAMAGE_NO;
489 self.event_damage = SUB_Null;
490 self.owner = attacker;
492 // do not explode NOW but in the NEXT FRAME!
493 // because recursive calls to RadiusDamage are not allowed
494 self.nextthink = time;
495 self.think = explode;