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;
347 proj.classname = "bullet";
349 PROJECTILE_MAKETRIGGER(proj);
350 if(gravityfactor > 0)
352 proj.movetype = MOVETYPE_TOSS;
353 proj.gravity = gravityfactor;
356 proj.movetype = MOVETYPE_FLY;
357 proj.think = SUB_Remove;
358 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
359 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread);
360 proj.angles = vectoangles(proj.velocity);
361 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
362 // so: bulletconstant = bullet mass / area of bullet circle
363 setorigin(proj, start);
364 proj.flags = FL_PROJECTILE;
366 proj.touch = W_BallisticBullet_Touch;
368 proj.dmg_edge = headshotbonus;
369 proj.dmg_force = force;
370 proj.projectiledeathtype = dtype;
372 proj.oldvelocity = proj.velocity;
374 if(cvar("g_antilag_bullets"))
375 if(pSpeed >= cvar("g_antilag_bullets"))
379 if(tracereffects & EF_RED)
380 eff = particleeffectnum("tr_rifle");
382 eff = particleeffectnum("tr_bullet");
384 // NOTE: this may severely throw off weapon balance
385 lag = ANTILAG_LATENCY(self);
388 if(clienttype(self) != CLIENTTYPE_REAL)
390 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
391 lag = 0; // only do hitscan, but no antilag
395 antilag_takeback(pl, time - lag);
400 savetime = frametime;
403 // update the accuracy stats - increase shots fired by 1
404 if not(inWarmupStage)
406 oldself.stats_fired[oldself.weapon - 1] += 1;
407 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
412 // DP tracetoss is stupid and always traces in 0.05s
413 // ticks. This makes it trace in 0.05*0.125s ticks
419 self.velocity = self.velocity * 0.125;
420 self.gravity *= 0.125 * 0.125;
422 fireBallisticBullet_trace_callback_ent = self;
423 fireBallisticBullet_trace_callback_eff = eff;
424 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
428 if(trace_fraction == 1)
430 // won't hit anything anytime soon (DP's
431 // tracetoss does 200 tics of, here,
432 // 0.05*0.125s, that is, 1.25 seconds
435 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
436 setorigin(self, trace_endpos);
437 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
439 if(!SUB_OwnerCheck())
441 if(SUB_NoImpactCheck())
445 W_BallisticBullet_Hit();
448 density = other.ballistics_density;
453 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
456 W_BallisticBullet_LeaveSolid_think();
458 frametime = savetime;
470 // update the accuracy stats
471 if not(inWarmupStage)
473 self.stats_fired[self.weapon - 1] += 1;
474 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
477 if(tracereffects & EF_RED)
478 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
479 else if(tracereffects & EF_BLUE)
480 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
482 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
485 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
489 dir = normalize(dir + randomvec() * spread);
490 end = start + dir * MAX_SHOT_DISTANCE;
491 if(self.antilag_debug)
492 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
494 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
498 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
500 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
501 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
502 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
503 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
504 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
509 void W_PrepareExplosionByDamage(entity attacker, void() explode)
511 self.takedamage = DAMAGE_NO;
512 self.event_damage = SUB_Null;
513 self.owner = attacker;
515 // do not explode NOW but in the NEXT FRAME!
516 // because recursive calls to RadiusDamage are not allowed
517 self.nextthink = time;
518 self.think = explode;