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)
306 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
310 W_BallisticBullet_Hit ();
312 density = other.ballistics_density;
317 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
323 self.projectiledeathtype |= HITTYPE_BOUNCE;
326 void endFireBallisticBullet()
328 endzcurveparticles();
331 entity fireBallisticBullet_trace_callback_ent;
332 float fireBallisticBullet_trace_callback_eff;
333 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
335 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
336 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
339 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)
341 float lag, dt, savetime, density;
346 proj.classname = "bullet";
348 PROJECTILE_MAKETRIGGER(proj);
349 if(gravityfactor > 0)
351 proj.movetype = MOVETYPE_TOSS;
352 proj.gravity = gravityfactor;
355 proj.movetype = MOVETYPE_FLY;
356 proj.think = SUB_Remove;
357 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
358 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread);
359 proj.angles = vectoangles(proj.velocity);
360 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
361 // so: bulletconstant = bullet mass / area of bullet circle
362 setorigin(proj, start);
363 proj.flags = FL_PROJECTILE;
365 proj.touch = W_BallisticBullet_Touch;
367 proj.dmg_edge = headshotbonus;
368 proj.dmg_force = force;
369 proj.projectiledeathtype = dtype;
371 proj.oldvelocity = proj.velocity;
373 if(cvar("g_antilag_bullets"))
374 if(pSpeed >= cvar("g_antilag_bullets"))
378 if(tracereffects & EF_RED)
379 eff = particleeffectnum("tr_rifle");
381 eff = particleeffectnum("tr_bullet");
383 // NOTE: this may severely throw off weapon balance
384 lag = ANTILAG_LATENCY(self);
387 if(clienttype(self) != CLIENTTYPE_REAL)
389 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
390 lag = 0; // only do hitscan, but no antilag
394 antilag_takeback(pl, time - lag);
399 savetime = frametime;
402 // update the accuracy stats - increase shots fired by 1
403 if not(inWarmupStage)
405 oldself.stats_fired[oldself.weapon - 1] += 1;
406 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
411 // DP tracetoss is stupid and always traces in 0.05s
412 // ticks. This makes it trace in 0.05*0.125s ticks
418 self.velocity = self.velocity * 0.125;
419 self.gravity *= 0.125 * 0.125;
421 fireBallisticBullet_trace_callback_ent = self;
422 fireBallisticBullet_trace_callback_eff = eff;
423 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
427 if(trace_fraction == 1)
429 // won't hit anything anytime soon (DP's
430 // tracetoss does 200 tics of, here,
431 // 0.05*0.125s, that is, 1.25 seconds
434 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
435 setorigin(self, trace_endpos);
436 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
438 if(!SUB_OwnerCheck())
440 if(SUB_NoImpactCheck())
444 W_BallisticBullet_Hit();
447 density = other.ballistics_density;
452 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
455 W_BallisticBullet_LeaveSolid_think();
457 frametime = savetime;
469 // update the accuracy stats
470 if not(inWarmupStage)
472 self.stats_fired[self.weapon - 1] += 1;
473 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
476 if(tracereffects & EF_RED)
477 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
478 else if(tracereffects & EF_BLUE)
479 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
481 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
484 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
488 dir = normalize(dir + randomvec() * spread);
489 end = start + dir * MAX_SHOT_DISTANCE;
490 if(self.antilag_debug)
491 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
493 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
497 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
499 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
500 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
501 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
502 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
503 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
508 void W_PrepareExplosionByDamage(entity attacker, void() explode)
510 self.takedamage = DAMAGE_NO;
511 self.event_damage = SUB_Null;
512 self.owner = attacker;
514 // do not explode NOW but in the NEXT FRAME!
515 // because recursive calls to RadiusDamage are not allowed
516 self.nextthink = time;
517 self.think = explode;