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_UnTransformOrigin(WarpZone_trace_transform, 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 entity fireBallisticBullet_trace_callback_ent;
326 float fireBallisticBullet_trace_callback_eff;
327 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
329 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
330 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
333 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)
335 float lag, dt, savetime;
340 proj.classname = "bullet";
342 PROJECTILE_MAKETRIGGER(proj);
343 if(gravityfactor > 0)
345 proj.movetype = MOVETYPE_TOSS;
346 proj.gravity = gravityfactor;
349 proj.movetype = MOVETYPE_FLY;
350 proj.think = SUB_Remove;
351 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
352 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread);
353 proj.angles = vectoangles(proj.velocity);
354 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
355 // so: bulletconstant = bullet mass / area of bullet circle
356 setorigin(proj, start);
357 proj.flags = FL_PROJECTILE;
359 proj.touch = W_BallisticBullet_Touch;
361 proj.dmg_edge = headshotbonus;
362 proj.dmg_force = force;
363 proj.projectiledeathtype = dtype;
365 proj.oldvelocity = proj.velocity;
367 if(cvar("g_antilag_bullets"))
368 if(pSpeed >= cvar("g_antilag_bullets"))
372 if(tracereffects & EF_RED)
373 eff = particleeffectnum("tr_rifle");
375 eff = particleeffectnum("tr_bullet");
377 // NOTE: this may severely throw off weapon balance
378 lag = ANTILAG_LATENCY(self);
381 if(clienttype(self) != CLIENTTYPE_REAL)
383 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
384 lag = 0; // only do hitscan, but no antilag
388 antilag_takeback(pl, time - lag);
393 savetime = frametime;
396 // update the accuracy stats - increase shots fired by 1
397 if not(inWarmupStage)
399 oldself.stats_fired[oldself.weapon - 1] += 1;
400 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
405 // DP tracetoss is stupid and always traces in 0.05s
406 // ticks. This makes it trace in 0.05*0.125s ticks
412 self.velocity = self.velocity * 0.125;
413 self.gravity *= 0.125 * 0.125;
415 fireBallisticBullet_trace_callback_ent = self;
416 fireBallisticBullet_trace_callback_eff = eff;
417 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
421 if(trace_fraction == 1)
423 // won't hit anything anytime soon (DP's
424 // tracetoss does 200 tics of, here,
425 // 0.05*0.125s, that is, 1.25 seconds
428 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
429 setorigin(self, trace_endpos);
430 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
432 if(!SUB_OwnerCheck())
434 if(SUB_NoImpactCheck())
438 W_BallisticBullet_Hit();
442 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
445 W_BallisticBullet_LeaveSolid_think();
447 frametime = savetime;
459 // update the accuracy stats
460 if not(inWarmupStage)
462 self.stats_fired[self.weapon - 1] += 1;
463 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
466 if(tracereffects & EF_RED)
467 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
468 else if(tracereffects & EF_BLUE)
469 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
471 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
474 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
478 dir = normalize(dir + randomvec() * spread);
479 end = start + dir * MAX_SHOT_DISTANCE;
480 if(self.antilag_debug)
481 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
483 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
487 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
489 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
490 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
491 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
492 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
493 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
498 void W_PrepareExplosionByDamage(entity attacker, void() explode)
500 self.takedamage = DAMAGE_NO;
501 self.event_damage = SUB_Null;
502 self.owner = attacker;
504 // do not explode NOW but in the NEXT FRAME!
505 // because recursive calls to RadiusDamage are not allowed
506 self.nextthink = time;
507 self.think = explode;