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;
27 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
29 local vector hitloc, force, endpoint, dir;
30 local entity ent, endent;
31 local float endq3surfaceflags;
37 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
52 // trace multiple times until we hit a wall, each obstacle will be made
53 // non-solid so we can hit the next, while doing this we spawn effects and
54 // note down which entities were hit so we can damage them later
57 if(self.antilag_debug)
58 WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
60 WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
62 // if it is world we can't hurt it so stop now
63 if (trace_ent == world || trace_fraction == 1)
66 // make the entity non-solid so we can hit the next one
67 trace_ent.railgunhit = TRUE;
68 trace_ent.railgunhitloc = end;
69 trace_ent.railgunhitsolidbackup = trace_ent.solid;
70 trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
71 trace_ent.railgunforce = WarpZone_TransformVelocity(WarpZone_trace_transform, force);
73 // stop if this is a wall
74 if (trace_ent.solid == SOLID_BSP)
77 // make the entity non-solid
78 trace_ent.solid = SOLID_NOT;
81 endpoint = trace_endpos;
83 endq3surfaceflags = trace_dphitq3surfaceflags;
85 // find all the entities the railgun hit and restore their solid state
86 ent = findfloat(world, railgunhit, TRUE);
89 // restore their solid type
90 ent.solid = ent.railgunhitsolidbackup;
91 ent = findfloat(ent, railgunhit, TRUE);
94 // spawn a temporary explosion entity for RadiusDamage calls
95 //explosion = spawn();
97 // Find all non-hit players the beam passed close by
98 if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
100 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
102 // nearest point on the beam
103 beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
105 f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
109 snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
111 if(!pseudoprojectile)
112 pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
113 soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
117 remove(pseudoprojectile);
120 // find all the entities the railgun hit and hurt them
121 ent = findfloat(world, railgunhit, TRUE);
124 // get the details we need to call the damage function
125 hitloc = ent.railgunhitloc;
127 f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
128 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
130 if(accuracy_isgooddamage(self.owner, ent))
131 totaldmg += bdamage * f;
135 Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs);
137 // create a small explosion to throw gibs around (if applicable)
138 //setorigin (explosion, hitloc);
139 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
141 ent.railgunhitloc = '0 0 0';
142 ent.railgunhitsolidbackup = SOLID_NOT;
143 ent.railgunhit = FALSE;
144 ent.railgundistance = 0;
146 // advance to the next entity
147 ent = findfloat(ent, railgunhit, TRUE);
150 // calculate hits and fired shots for hitscan
151 accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
153 trace_endpos = endpoint;
155 trace_dphitq3surfaceflags = endq3surfaceflags;
162 void W_BallisticBullet_Hit (void)
166 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
167 q = 1 + self.dmg_edge / self.dmg;
169 if(other.solid == SOLID_BSP)
170 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
172 if(other && other != self.enemy)
174 endzcurveparticles();
178 damage_headshotbonus = self.dmg_edge * f;
179 railgun_start = self.origin - 2 * frametime * self.velocity;
180 railgun_end = self.origin + 2 * frametime * self.velocity;
181 g = accuracy_isgooddamage(self.owner, other);
182 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
183 damage_headshotbonus = 0;
187 if(DEATH_WEAPONOF(self.projectiledeathtype) == WEP_SNIPERRIFLE)
190 AnnounceTo(self.owner, "headshot");
192 AnnounceTo(self.owner, "awesome");
195 // calculate hits for ballistic weapons
198 // do not exceed 100%
199 q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total;
200 self.dmg_total += f * self.dmg;
201 accuracy_add(self.owner, self.owner.weapon, 0, q);
205 self.enemy = other; // don't hit the same player twice with the same bullet
208 .void(void) W_BallisticBullet_LeaveSolid_think_save;
209 .float W_BallisticBullet_LeaveSolid_nextthink_save;
210 .vector W_BallisticBullet_LeaveSolid_origin;
211 .vector W_BallisticBullet_LeaveSolid_velocity;
213 void W_BallisticBullet_LeaveSolid_think()
215 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
216 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
218 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
219 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
220 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
222 self.flags &~= FL_ONGROUND;
224 if(self.enemy.solid == SOLID_BSP)
227 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
228 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
231 UpdateCSQCProjectile(self);
234 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
236 // move the entity along its velocity until it's out of solid, then let it resume
238 float dt, dst, velfactor, v0, vs;
242 // outside the world? forget it
243 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)
246 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
249 E0_m = 0.5 * v0 * v0;
250 maxdist = E0_m / constant;
251 // maxdist = 0.5 * v0 * v0 / constant
252 // dprint("max dist = ", ftos(maxdist), "\n");
254 if(maxdist <= autocvar_g_ballistics_mindistance)
257 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
259 if(trace_fraction == 1) // 1: we never got out of solid
262 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
264 dst = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - self.origin));
265 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
266 Es_m = E0_m - constant * dst;
269 // roundoff errors got us
275 dt = dst / (0.5 * (v0 + vs));
276 // this is not correct, but the differential equations have no analytic
277 // solution - and these times are very small anyway
278 //print("dt = ", ftos(dt), "\n");
280 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
281 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
282 self.think = W_BallisticBullet_LeaveSolid_think;
283 self.nextthink = time + dt;
285 vel = vel * velfactor;
287 self.velocity = '0 0 0';
288 self.flags |= FL_ONGROUND; // prevent moving
289 self.W_BallisticBullet_LeaveSolid_velocity = vel;
294 void W_BallisticBullet_Touch (void)
298 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
302 W_BallisticBullet_Hit ();
304 // if we hit "weapclip", bail out
306 // rationale of this check:
308 // any shader that is solid, nodraw AND trans is meant to clip weapon
309 // shots and players, but has no other effect!
311 // if it is not trans, it is caulk and should not have this side effect
314 // common/weapclip (intended)
315 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
316 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
317 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
318 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
324 density = other.ballistics_density;
329 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
335 self.projectiledeathtype |= HITTYPE_BOUNCE;
338 void endFireBallisticBullet()
340 endzcurveparticles();
343 entity fireBallisticBullet_trace_callback_ent;
344 float fireBallisticBullet_trace_callback_eff;
345 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
347 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
348 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
351 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)
353 float lag, dt, savetime, density;
357 antilagging = (autocvar_g_antilag_bullets && (pSpeed >= autocvar_g_antilag_bullets));
361 proj.classname = "bullet";
363 PROJECTILE_MAKETRIGGER(proj);
364 if(gravityfactor > 0)
366 proj.movetype = MOVETYPE_TOSS;
367 proj.gravity = gravityfactor;
370 proj.movetype = MOVETYPE_FLY;
371 proj.think = SUB_Remove;
372 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
373 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
374 proj.angles = vectoangles(proj.velocity);
375 proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant;
376 // so: bulletconstant = bullet mass / area of bullet circle
377 setorigin(proj, start);
378 proj.flags = FL_PROJECTILE;
380 proj.touch = W_BallisticBullet_Touch;
382 proj.dmg_edge = headshotbonus;
383 proj.dmg_force = force;
384 proj.projectiledeathtype = dtype;
386 proj.oldvelocity = proj.velocity;
388 other = proj; MUTATOR_CALLHOOK(EditProjectile);
394 if(tracereffects & EF_RED)
395 eff = particleeffectnum("tr_rifle");
396 else if(tracereffects & EF_BLUE)
397 eff = particleeffectnum("tr_rifle_weak");
399 eff = particleeffectnum("tr_bullet");
401 // NOTE: this may severely throw off weapon balance
402 lag = ANTILAG_LATENCY(self);
405 if(clienttype(self) != CLIENTTYPE_REAL)
407 if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag)
408 lag = 0; // only do hitscan, but no antilag
412 antilag_takeback(pl, time - lag);
417 savetime = frametime;
422 // DP tracetoss is stupid and always traces in 0.05s
423 // ticks. This makes it trace in 0.05*0.125s ticks
429 self.velocity = self.velocity * 0.125;
430 self.gravity *= 0.125 * 0.125;
432 fireBallisticBullet_trace_callback_ent = self;
433 fireBallisticBullet_trace_callback_eff = eff;
434 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
438 if(trace_fraction == 1)
440 // won't hit anything anytime soon (DP's
441 // tracetoss does 200 tics of, here,
442 // 0.05*0.125s, that is, 1.25 seconds
445 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
446 setorigin(self, trace_endpos);
447 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
449 if(!SUB_OwnerCheck())
451 if(SUB_NoImpactCheck())
455 W_BallisticBullet_Hit();
458 // if we hit "weapclip", bail out
460 // rationale of this check:
462 // any shader that is solid, nodraw AND trans is meant to clip weapon
463 // shots and players, but has no other effect!
465 // if it is not trans, it is caulk and should not have this side effect
468 // common/weapclip (intended)
469 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
470 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
471 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
472 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
475 density = other.ballistics_density;
480 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
483 W_BallisticBullet_LeaveSolid_think();
485 frametime = savetime;
497 if(tracereffects & EF_RED)
498 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
499 else if(tracereffects & EF_BLUE)
500 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
502 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
505 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
509 dir = normalize(dir + randomvec() * spread);
510 end = start + dir * MAX_SHOT_DISTANCE;
511 if(self.antilag_debug)
512 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
514 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
518 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
520 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
521 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
522 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
523 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
524 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
529 void W_PrepareExplosionByDamage(entity attacker, void() explode)
531 self.takedamage = DAMAGE_NO;
532 self.event_damage = SUB_Null;
533 self.owner = attacker;
534 self.realowner = attacker;
536 // do not explode NOW but in the NEXT FRAME!
537 // because recursive calls to RadiusDamage are not allowed
538 self.nextthink = time;
539 self.think = explode;