2 void W_GiveWeapon (entity e, float wep, string name)
9 e.weapons = e.weapons | W_WeaponBit(wep);
15 if (other.classname == "player")
17 sprint (other, "You got the ^2");
25 .float railgundistance;
26 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
28 local vector hitloc, force, endpoint, dir;
29 local entity ent, endent;
30 local float endq3surfaceflags;
35 entity pseudoprojectile;
40 railgun_start = start;
43 dir = normalize(end - start);
44 length = vlen(end - start);
47 // go a little bit into the wall because we need to hit this wall later
50 // trace multiple times until we hit a wall, each obstacle will be made
51 // non-solid so we can hit the next, while doing this we spawn effects and
52 // note down which entities were hit so we can damage them later
55 if(self.antilag_debug)
56 WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
58 WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
60 // if it is world we can't hurt it so stop now
61 if (trace_ent == world || trace_fraction == 1)
64 // make the entity non-solid so we can hit the next one
65 trace_ent.railgunhit = TRUE;
66 trace_ent.railgunhitloc = end;
67 trace_ent.railgunhitsolidbackup = trace_ent.solid;
68 trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
70 // stop if this is a wall
71 if (trace_ent.solid == SOLID_BSP)
74 // make the entity non-solid
75 trace_ent.solid = SOLID_NOT;
78 endpoint = trace_endpos;
80 endq3surfaceflags = trace_dphitq3surfaceflags;
82 // find all the entities the railgun hit and restore their solid state
83 ent = findfloat(world, railgunhit, TRUE);
86 // restore their solid type
87 ent.solid = ent.railgunhitsolidbackup;
88 ent = findfloat(ent, railgunhit, TRUE);
91 // spawn a temporary explosion entity for RadiusDamage calls
92 //explosion = spawn();
94 // Find all non-hit players the beam passed close by
95 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
97 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
99 // nearest point on the beam
100 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
102 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
106 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
108 if(!pseudoprojectile)
109 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
110 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
114 remove(pseudoprojectile);
117 // find all the entities the railgun hit and hurt them
118 ent = findfloat(world, railgunhit, TRUE);
121 // get the details we need to call the damage function
122 hitloc = ent.railgunhitloc;
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.railgundistance);
134 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
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 ent.railgunhitloc = '0 0 0';
145 ent.railgunhitsolidbackup = SOLID_NOT;
146 ent.railgunhit = FALSE;
147 ent.railgundistance = 0;
149 // advance to the next entity
150 ent = findfloat(ent, railgunhit, TRUE);
153 // calculate hits and fired shots for hitscan
154 if not(inWarmupStage)
156 self.stats_fired[self.weapon - 1] += 1;
157 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
160 self.stats_hit[self.weapon - 1] += 1;
161 self.stat_hit = self.weapon + 64 * floor(self.stats_hit[self.weapon - 1]);
165 trace_endpos = endpoint;
167 trace_dphitq3surfaceflags = endq3surfaceflags;
173 void W_BallisticBullet_Hit (void)
177 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
179 if(other.solid == SOLID_BSP)
180 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
182 if(other && other != self.enemy)
184 endzcurveparticles();
188 damage_headshotbonus = self.dmg_edge;
189 railgun_start = self.origin - 2 * frametime * self.velocity;
190 railgun_end = self.origin + 2 * frametime * self.velocity;
192 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
193 damage_headshotbonus = 0;
195 if(self.dmg_edge != 0)
198 AnnounceTo(self.owner, "headshot");
200 AnnounceTo(self.owner, "awesome");
203 // calculate hits for ballistic weapons
204 if (other.flags & FL_CLIENT) // is the player a client
205 if (other.deadflag == DEAD_NO) // is the victim a corpse
206 if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
207 if not(inWarmupStage) // not in warm up stage
209 self.owner.stats_hit[self.owner.weapon - 1] += 1;
210 self.owner.stat_hit = self.owner.weapon + 64 * floor(self.owner.stats_hit[self.owner.weapon - 1]);
214 self.enemy = other; // don't hit the same player twice with the same bullet
217 .void(void) W_BallisticBullet_LeaveSolid_think_save;
218 .float W_BallisticBullet_LeaveSolid_nextthink_save;
219 .vector W_BallisticBullet_LeaveSolid_origin;
220 .vector W_BallisticBullet_LeaveSolid_velocity;
222 void W_BallisticBullet_LeaveSolid_think()
224 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
225 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
227 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
228 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
229 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
231 self.flags &~= FL_ONGROUND;
233 if(self.enemy.solid == SOLID_BSP)
236 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
237 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
240 UpdateCSQCProjectile(self);
243 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
245 // move the entity along its velocity until it's out of solid, then let it resume
247 float dt, dst, velfactor, v0, vs;
251 // outside the world? forget it
252 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)
255 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
258 E0_m = 0.5 * v0 * v0;
259 maxdist = E0_m / constant;
260 // maxdist = 0.5 * v0 * v0 / constant
261 // dprint("max dist = ", ftos(maxdist), "\n");
263 if(maxdist <= cvar("g_ballistics_mindistance"))
266 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
268 if(trace_fraction == 1) // 1: we never got out of solid
271 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
273 dst = max(cvar("g_ballistics_mindistance"), vlen(trace_endpos - self.origin));
274 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
275 Es_m = E0_m - constant * dst;
278 // roundoff errors got us
284 dt = dst / (0.5 * (v0 + vs));
285 // this is not correct, but the differential equations have no analytic
286 // solution - and these times are very small anyway
287 //print("dt = ", ftos(dt), "\n");
289 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
290 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
291 self.think = W_BallisticBullet_LeaveSolid_think;
292 self.nextthink = time + dt;
294 vel = vel * velfactor;
296 self.velocity = '0 0 0';
297 self.flags |= FL_ONGROUND; // prevent moving
298 self.W_BallisticBullet_LeaveSolid_velocity = vel;
303 void W_BallisticBullet_Touch (void)
307 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
311 W_BallisticBullet_Hit ();
313 density = other.ballistics_density;
318 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
324 self.projectiledeathtype |= HITTYPE_BOUNCE;
327 void endFireBallisticBullet()
329 endzcurveparticles();
332 entity fireBallisticBullet_trace_callback_ent;
333 float fireBallisticBullet_trace_callback_eff;
334 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
336 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
337 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
340 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)
342 float lag, dt, savetime, density;
346 antilagging = (cvar("g_antilag_bullets") && (pSpeed >= cvar("g_antilag_bullets")));
350 proj.classname = "bullet";
352 PROJECTILE_MAKETRIGGER(proj);
353 if(gravityfactor > 0)
355 proj.movetype = MOVETYPE_TOSS;
356 proj.gravity = gravityfactor;
359 proj.movetype = MOVETYPE_FLY;
360 proj.think = SUB_Remove;
361 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
362 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
363 proj.angles = vectoangles(proj.velocity);
364 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
365 // so: bulletconstant = bullet mass / area of bullet circle
366 setorigin(proj, start);
367 proj.flags = FL_PROJECTILE;
369 proj.touch = W_BallisticBullet_Touch;
371 proj.dmg_edge = headshotbonus;
372 proj.dmg_force = force;
373 proj.projectiledeathtype = dtype;
375 proj.oldvelocity = proj.velocity;
377 other = proj; MUTATOR_CALLHOOK(EditProjectile);
383 if(tracereffects & EF_RED)
384 eff = particleeffectnum("tr_rifle");
386 eff = particleeffectnum("tr_bullet");
388 // NOTE: this may severely throw off weapon balance
389 lag = ANTILAG_LATENCY(self);
392 if(clienttype(self) != CLIENTTYPE_REAL)
394 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
395 lag = 0; // only do hitscan, but no antilag
399 antilag_takeback(pl, time - lag);
404 savetime = frametime;
407 // update the accuracy stats - increase shots fired by 1
408 if not(inWarmupStage)
410 oldself.stats_fired[oldself.weapon - 1] += 1;
411 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
416 // DP tracetoss is stupid and always traces in 0.05s
417 // ticks. This makes it trace in 0.05*0.125s ticks
423 self.velocity = self.velocity * 0.125;
424 self.gravity *= 0.125 * 0.125;
426 fireBallisticBullet_trace_callback_ent = self;
427 fireBallisticBullet_trace_callback_eff = eff;
428 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
432 if(trace_fraction == 1)
434 // won't hit anything anytime soon (DP's
435 // tracetoss does 200 tics of, here,
436 // 0.05*0.125s, that is, 1.25 seconds
439 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
440 setorigin(self, trace_endpos);
441 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
443 if(!SUB_OwnerCheck())
445 if(SUB_NoImpactCheck())
449 W_BallisticBullet_Hit();
452 density = other.ballistics_density;
457 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
460 W_BallisticBullet_LeaveSolid_think();
462 frametime = savetime;
474 // update the accuracy stats
475 if not(inWarmupStage)
477 self.stats_fired[self.weapon - 1] += 1;
478 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
481 if(tracereffects & EF_RED)
482 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
483 else if(tracereffects & EF_BLUE)
484 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
486 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
489 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
493 dir = normalize(dir + randomvec() * spread);
494 end = start + dir * MAX_SHOT_DISTANCE;
495 if(self.antilag_debug)
496 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
498 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
502 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
504 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
505 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
506 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
507 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
508 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
513 void W_PrepareExplosionByDamage(entity attacker, void() explode)
515 self.takedamage = DAMAGE_NO;
516 self.event_damage = SUB_Null;
517 self.owner = attacker;
518 self.realowner = attacker;
520 // do not explode NOW but in the NEXT FRAME!
521 // because recursive calls to RadiusDamage are not allowed
522 self.nextthink = time;
523 self.think = explode;