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 .float railgundistance;
25 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
27 local vector hitloc, force, endpoint, dir;
28 local entity ent, endent;
29 local float endq3surfaceflags;
34 entity pseudoprojectile;
39 railgun_start = start;
42 dir = normalize(end - start);
43 length = vlen(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 WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
57 WarpZone_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;
67 trace_ent.railgundistance = vlen(WarpZone_trace_endpos - start);
69 // stop if this is a wall
70 if (trace_ent.solid == SOLID_BSP)
73 // make the entity non-solid
74 trace_ent.solid = SOLID_NOT;
77 endpoint = trace_endpos;
79 endq3surfaceflags = trace_dphitq3surfaceflags;
81 // find all the entities the railgun hit and restore their solid state
82 ent = findfloat(world, railgunhit, TRUE);
85 // restore their solid type
86 ent.solid = ent.railgunhitsolidbackup;
87 ent = findfloat(ent, railgunhit, TRUE);
90 // spawn a temporary explosion entity for RadiusDamage calls
91 //explosion = spawn();
93 // Find all non-hit players the beam passed close by
94 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
96 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
98 // nearest point on the beam
99 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
101 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
105 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
107 if(!pseudoprojectile)
108 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
109 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
113 remove(pseudoprojectile);
116 // find all the entities the railgun hit and hurt them
117 ent = findfloat(world, railgunhit, TRUE);
120 // get the details we need to call the damage function
121 hitloc = ent.railgunhitloc;
123 //for stats so that team hit will count as a miss
124 if(ent.flags & FL_CLIENT)
125 if(ent.deadflag == DEAD_NO)
129 if(ent.team == self.team)
132 f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
133 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
137 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
139 // create a small explosion to throw gibs around (if applicable)
140 //setorigin (explosion, hitloc);
141 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
143 ent.railgunhitloc = '0 0 0';
144 ent.railgunhitsolidbackup = SOLID_NOT;
145 ent.railgunhit = FALSE;
146 ent.railgundistance = 0;
148 // advance to the next entity
149 ent = findfloat(ent, railgunhit, TRUE);
152 // calculate hits and fired shots for hitscan
153 if not(inWarmupStage)
155 self.stats_fired[self.weapon - 1] += 1;
156 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
159 self.stats_hit[self.weapon - 1] += 1;
160 self.stat_hit = self.weapon + 64 * floor(self.stats_hit[self.weapon - 1]);
164 trace_endpos = endpoint;
166 trace_dphitq3surfaceflags = endq3surfaceflags;
172 void W_BallisticBullet_Hit (void)
176 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
178 if(other.solid == SOLID_BSP)
179 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
181 if(other && other != self.enemy)
183 endzcurveparticles();
187 damage_headshotbonus = self.dmg_edge;
188 railgun_start = self.origin - 2 * frametime * self.velocity;
189 railgun_end = self.origin + 2 * frametime * self.velocity;
191 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
192 damage_headshotbonus = 0;
194 if(self.dmg_edge != 0)
197 AnnounceTo(self.owner, "headshot");
199 AnnounceTo(self.owner, "awesome");
202 // calculate hits for ballistic weapons
203 if (other.flags & FL_CLIENT) // is the player a client
204 if (other.deadflag == DEAD_NO) // is the victim a corpse
205 if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
206 if not(inWarmupStage) // not in warm up stage
208 self.owner.stats_hit[self.owner.weapon - 1] += 1;
209 self.owner.stat_hit = self.owner.weapon + 64 * floor(self.owner.stats_hit[self.owner.weapon - 1]);
213 self.enemy = other; // don't hit the same player twice with the same bullet
216 .void(void) W_BallisticBullet_LeaveSolid_think_save;
217 .float W_BallisticBullet_LeaveSolid_nextthink_save;
218 .vector W_BallisticBullet_LeaveSolid_origin;
219 .vector W_BallisticBullet_LeaveSolid_velocity;
221 void W_BallisticBullet_LeaveSolid_think()
223 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
224 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
226 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
227 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
228 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
230 self.flags &~= FL_ONGROUND;
232 if(self.enemy.solid == SOLID_BSP)
235 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
236 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
239 UpdateCSQCProjectile(self);
242 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
244 // move the entity along its velocity until it's out of solid, then let it resume
246 float dt, dst, velfactor, v0, vs;
250 // outside the world? forget it
251 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)
254 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
257 E0_m = 0.5 * v0 * v0;
258 maxdist = E0_m / constant;
259 // maxdist = 0.5 * v0 * v0 / constant
260 // dprint("max dist = ", ftos(maxdist), "\n");
262 if(maxdist <= cvar("g_ballistics_mindistance"))
265 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
267 if(trace_fraction == 1) // 1: we never got out of solid
270 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
272 dst = vlen(trace_endpos - self.origin);
273 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
274 Es_m = E0_m - constant * dst;
277 // roundoff errors got us
283 dt = dst / (0.5 * (v0 + vs));
284 // this is not correct, but the differential equations have no analytic
285 // solution - and these times are very small anyway
286 //print("dt = ", ftos(dt), "\n");
288 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
289 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
290 self.think = W_BallisticBullet_LeaveSolid_think;
291 self.nextthink = time + dt;
293 vel = vel * velfactor;
295 self.velocity = '0 0 0';
296 self.flags |= FL_ONGROUND; // prevent moving
297 self.W_BallisticBullet_LeaveSolid_velocity = vel;
302 void W_BallisticBullet_Touch (void)
304 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
308 W_BallisticBullet_Hit ();
311 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
317 self.projectiledeathtype |= HITTYPE_BOUNCE;
320 void endFireBallisticBullet()
322 endzcurveparticles();
325 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)
327 float lag, dt, savetime;
332 proj.classname = "bullet";
334 PROJECTILE_MAKETRIGGER(proj);
335 if(gravityfactor > 0)
337 proj.movetype = MOVETYPE_TOSS;
338 proj.gravity = gravityfactor;
341 proj.movetype = MOVETYPE_FLY;
342 proj.think = SUB_Remove;
343 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
344 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, spread);
345 proj.angles = vectoangles(proj.velocity);
346 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
347 // so: bulletconstant = bullet mass / area of bullet circle
348 setorigin(proj, start);
349 proj.flags = FL_PROJECTILE;
351 proj.touch = W_BallisticBullet_Touch;
353 proj.dmg_edge = headshotbonus;
354 proj.dmg_force = force;
355 proj.projectiledeathtype = dtype;
357 proj.oldvelocity = proj.velocity;
359 if(cvar("g_antilag_bullets"))
360 if(pSpeed >= cvar("g_antilag_bullets"))
364 if(tracereffects & EF_RED)
365 eff = particleeffectnum("tr_rifle");
367 eff = particleeffectnum("tr_bullet");
369 // NOTE: this may severely throw off weapon balance
370 lag = ANTILAG_LATENCY(self);
373 if(clienttype(self) != CLIENTTYPE_REAL)
375 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
376 lag = 0; // only do hitscan, but no antilag
380 antilag_takeback(pl, time - lag);
385 savetime = frametime;
388 // update the accuracy stats - increase shots fired by 1
389 if not(inWarmupStage)
391 oldself.stats_fired[oldself.weapon - 1] += 1;
392 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
397 // DP tracetoss is stupid and always traces in 0.05s
398 // ticks. This makes it trace in 0.05*0.125s ticks
404 self.velocity = self.velocity * 0.125;
405 self.gravity *= 0.125 * 0.125;
407 tracetoss(self, oldself);
411 if(vlen(trace_endpos - self.origin) > 16)
412 zcurveparticles_from_tracetoss(eff, self.origin, trace_endpos, self.velocity);
413 if(trace_fraction == 1)
415 // won't hit anything anytime soon (DP's
416 // tracetoss does 200 tics of, here,
417 // 0.05*0.125s, that is, 1.25 seconds
420 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
421 setorigin(self, trace_endpos);
422 self.velocity_z -= sv_gravity * dt;
424 if(!SUB_OwnerCheck())
426 if(SUB_NoImpactCheck())
430 W_BallisticBullet_Hit();
434 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
437 W_BallisticBullet_LeaveSolid_think();
439 frametime = savetime;
451 // update the accuracy stats
452 if not(inWarmupStage)
454 self.stats_fired[self.weapon - 1] += 1;
455 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
458 if(tracereffects & EF_RED)
459 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
460 else if(tracereffects & EF_BLUE)
461 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
463 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
466 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
470 dir = normalize(dir + randomvec() * spread);
471 end = start + dir * MAX_SHOT_DISTANCE;
472 if(self.antilag_debug)
473 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
475 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
479 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
481 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
482 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
483 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
484 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
485 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
490 void W_PrepareExplosionByDamage(entity attacker, void() explode)
492 self.takedamage = DAMAGE_NO;
493 self.event_damage = SUB_Null;
494 self.owner = attacker;
496 // do not explode NOW but in the NEXT FRAME!
497 // because recursive calls to RadiusDamage are not allowed
498 self.nextthink = time;
499 self.think = explode;