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;
36 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
51 // trace multiple times until we hit a wall, each obstacle will be made
52 // non-solid so we can hit the next, while doing this we spawn effects and
53 // note down which entities were hit so we can damage them later
56 if(self.antilag_debug)
57 WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
59 WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
61 // if it is world we can't hurt it so stop now
62 if (trace_ent == world || trace_fraction == 1)
65 // make the entity non-solid so we can hit the next one
66 trace_ent.railgunhit = TRUE;
67 trace_ent.railgunhitloc = end;
68 trace_ent.railgunhitsolidbackup = trace_ent.solid;
69 trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
71 // stop if this is a wall
72 if (trace_ent.solid == SOLID_BSP)
75 // make the entity non-solid
76 trace_ent.solid = SOLID_NOT;
79 endpoint = trace_endpos;
81 endq3surfaceflags = trace_dphitq3surfaceflags;
83 // find all the entities the railgun hit and restore their solid state
84 ent = findfloat(world, railgunhit, TRUE);
87 // restore their solid type
88 ent.solid = ent.railgunhitsolidbackup;
89 ent = findfloat(ent, railgunhit, TRUE);
92 // spawn a temporary explosion entity for RadiusDamage calls
93 //explosion = spawn();
95 // Find all non-hit players the beam passed close by
96 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
98 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
100 // nearest point on the beam
101 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
103 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
107 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
109 if(!pseudoprojectile)
110 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
111 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
115 remove(pseudoprojectile);
118 // find all the entities the railgun hit and hurt them
119 ent = findfloat(world, railgunhit, TRUE);
122 // get the details we need to call the damage function
123 hitloc = ent.railgunhitloc;
125 f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
126 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
128 if(accuracy_isgooddamage(self.owner, ent))
129 totaldmg += bdamage * f;
133 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
135 // create a small explosion to throw gibs around (if applicable)
136 //setorigin (explosion, hitloc);
137 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
139 ent.railgunhitloc = '0 0 0';
140 ent.railgunhitsolidbackup = SOLID_NOT;
141 ent.railgunhit = FALSE;
142 ent.railgundistance = 0;
144 // advance to the next entity
145 ent = findfloat(ent, railgunhit, TRUE);
148 // calculate hits and fired shots for hitscan
149 accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
151 trace_endpos = endpoint;
153 trace_dphitq3surfaceflags = endq3surfaceflags;
160 void W_BallisticBullet_Hit (void)
164 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
165 q = 1 + self.dmg_edge / self.dmg;
167 if(other.solid == SOLID_BSP)
168 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
170 if(other && other != self.enemy)
172 endzcurveparticles();
176 damage_headshotbonus = self.dmg_edge * f;
177 railgun_start = self.origin - 2 * frametime * self.velocity;
178 railgun_end = self.origin + 2 * frametime * self.velocity;
179 g = accuracy_isgooddamage(self.owner, other);
180 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
181 damage_headshotbonus = 0;
185 if(DEATH_WEAPONOF(self.projectiledeathtype) == WEP_CAMPINGRIFLE)
188 AnnounceTo(self.owner, "headshot");
190 AnnounceTo(self.owner, "awesome");
193 // calculate hits for ballistic weapons
196 // do not exceed 100%
197 q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total;
198 self.dmg_total += f * self.dmg;
199 accuracy_add(self.owner, self.owner.weapon, 0, q);
203 self.enemy = other; // don't hit the same player twice with the same bullet
206 .void(void) W_BallisticBullet_LeaveSolid_think_save;
207 .float W_BallisticBullet_LeaveSolid_nextthink_save;
208 .vector W_BallisticBullet_LeaveSolid_origin;
209 .vector W_BallisticBullet_LeaveSolid_velocity;
211 void W_BallisticBullet_LeaveSolid_think()
213 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
214 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
216 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
217 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
218 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
220 self.flags &~= FL_ONGROUND;
222 if(self.enemy.solid == SOLID_BSP)
225 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
226 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
229 UpdateCSQCProjectile(self);
232 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
234 // move the entity along its velocity until it's out of solid, then let it resume
236 float dt, dst, velfactor, v0, vs;
240 // outside the world? forget it
241 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)
244 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
247 E0_m = 0.5 * v0 * v0;
248 maxdist = E0_m / constant;
249 // maxdist = 0.5 * v0 * v0 / constant
250 // dprint("max dist = ", ftos(maxdist), "\n");
252 if(maxdist <= autocvar_g_ballistics_mindistance)
255 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
257 if(trace_fraction == 1) // 1: we never got out of solid
260 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
262 dst = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - self.origin));
263 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
264 Es_m = E0_m - constant * dst;
267 // roundoff errors got us
273 dt = dst / (0.5 * (v0 + vs));
274 // this is not correct, but the differential equations have no analytic
275 // solution - and these times are very small anyway
276 //print("dt = ", ftos(dt), "\n");
278 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
279 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
280 self.think = W_BallisticBullet_LeaveSolid_think;
281 self.nextthink = time + dt;
283 vel = vel * velfactor;
285 self.velocity = '0 0 0';
286 self.flags |= FL_ONGROUND; // prevent moving
287 self.W_BallisticBullet_LeaveSolid_velocity = vel;
292 void W_BallisticBullet_Touch (void)
296 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
300 W_BallisticBullet_Hit ();
302 // if we hit "weapclip", bail out
304 // rationale of this check:
306 // any shader that is solid, nodraw AND trans is meant to clip weapon
307 // shots and players, but has no other effect!
309 // if it is not trans, it is caulk and should not have this side effect
312 // common/weapclip (intended)
313 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
314 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
315 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
316 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
322 density = other.ballistics_density;
327 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
333 self.projectiledeathtype |= HITTYPE_BOUNCE;
336 void endFireBallisticBullet()
338 endzcurveparticles();
341 entity fireBallisticBullet_trace_callback_ent;
342 float fireBallisticBullet_trace_callback_eff;
343 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
345 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
346 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
349 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)
351 float lag, dt, savetime, density;
355 antilagging = (autocvar_g_antilag_bullets && (pSpeed >= autocvar_g_antilag_bullets));
359 proj.classname = "bullet";
361 PROJECTILE_MAKETRIGGER(proj);
362 if(gravityfactor > 0)
364 proj.movetype = MOVETYPE_TOSS;
365 proj.gravity = gravityfactor;
368 proj.movetype = MOVETYPE_FLY;
369 proj.think = SUB_Remove;
370 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
371 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
372 proj.angles = vectoangles(proj.velocity);
373 proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant;
374 // so: bulletconstant = bullet mass / area of bullet circle
375 setorigin(proj, start);
376 proj.flags = FL_PROJECTILE;
378 proj.touch = W_BallisticBullet_Touch;
380 proj.dmg_edge = headshotbonus;
381 proj.dmg_force = force;
382 proj.projectiledeathtype = dtype;
384 proj.oldvelocity = proj.velocity;
386 other = proj; MUTATOR_CALLHOOK(EditProjectile);
392 if(tracereffects & EF_RED)
393 eff = particleeffectnum("tr_rifle");
395 eff = particleeffectnum("tr_bullet");
397 // NOTE: this may severely throw off weapon balance
398 lag = ANTILAG_LATENCY(self);
401 if(clienttype(self) != CLIENTTYPE_REAL)
403 if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag)
404 lag = 0; // only do hitscan, but no antilag
408 antilag_takeback(pl, time - lag);
413 savetime = frametime;
418 // DP tracetoss is stupid and always traces in 0.05s
419 // ticks. This makes it trace in 0.05*0.125s ticks
425 self.velocity = self.velocity * 0.125;
426 self.gravity *= 0.125 * 0.125;
428 fireBallisticBullet_trace_callback_ent = self;
429 fireBallisticBullet_trace_callback_eff = eff;
430 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
434 if(trace_fraction == 1)
436 // won't hit anything anytime soon (DP's
437 // tracetoss does 200 tics of, here,
438 // 0.05*0.125s, that is, 1.25 seconds
441 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
442 setorigin(self, trace_endpos);
443 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
445 if(!SUB_OwnerCheck())
447 if(SUB_NoImpactCheck())
451 W_BallisticBullet_Hit();
454 // if we hit "weapclip", bail out
456 // rationale of this check:
458 // any shader that is solid, nodraw AND trans is meant to clip weapon
459 // shots and players, but has no other effect!
461 // if it is not trans, it is caulk and should not have this side effect
464 // common/weapclip (intended)
465 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
466 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
467 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
468 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
471 density = other.ballistics_density;
476 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
479 W_BallisticBullet_LeaveSolid_think();
481 frametime = savetime;
493 if(tracereffects & EF_RED)
494 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
495 else if(tracereffects & EF_BLUE)
496 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
498 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
501 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
505 dir = normalize(dir + randomvec() * spread);
506 end = start + dir * MAX_SHOT_DISTANCE;
507 if(self.antilag_debug)
508 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
510 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
514 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
516 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
517 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
518 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
519 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
520 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
525 void W_PrepareExplosionByDamage(entity attacker, void() explode)
527 self.takedamage = DAMAGE_NO;
528 self.event_damage = SUB_Null;
529 self.owner = attacker;
530 self.realowner = attacker;
532 // do not explode NOW but in the NEXT FRAME!
533 // because recursive calls to RadiusDamage are not allowed
534 self.nextthink = time;
535 self.think = explode;