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;
177 railgun_start = self.origin - 2 * frametime * self.velocity;
178 railgun_end = self.origin + 2 * frametime * self.velocity;
180 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
181 damage_headshotbonus = 0;
183 if(self.dmg_edge != 0)
188 AnnounceTo(self.owner, "headshot");
192 AnnounceTo(self.owner, "awesome");
194 // calculate hits for ballistic weapons
195 if(accuracy_isgooddamage(self.owner, other))
197 // do not exceed 100%
198 q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total;
199 self.dmg_total += f * self.dmg;
200 accuracy_add(self.owner, self.owner.weapon, 0, q);
204 self.enemy = other; // don't hit the same player twice with the same bullet
207 .void(void) W_BallisticBullet_LeaveSolid_think_save;
208 .float W_BallisticBullet_LeaveSolid_nextthink_save;
209 .vector W_BallisticBullet_LeaveSolid_origin;
210 .vector W_BallisticBullet_LeaveSolid_velocity;
212 void W_BallisticBullet_LeaveSolid_think()
214 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
215 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
217 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
218 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
219 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
221 self.flags &~= FL_ONGROUND;
223 if(self.enemy.solid == SOLID_BSP)
226 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
227 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
230 UpdateCSQCProjectile(self);
233 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
235 // move the entity along its velocity until it's out of solid, then let it resume
237 float dt, dst, velfactor, v0, vs;
241 // outside the world? forget it
242 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)
245 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
248 E0_m = 0.5 * v0 * v0;
249 maxdist = E0_m / constant;
250 // maxdist = 0.5 * v0 * v0 / constant
251 // dprint("max dist = ", ftos(maxdist), "\n");
253 if(maxdist <= cvar("g_ballistics_mindistance"))
256 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
258 if(trace_fraction == 1) // 1: we never got out of solid
261 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
263 dst = max(cvar("g_ballistics_mindistance"), vlen(trace_endpos - self.origin));
264 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
265 Es_m = E0_m - constant * dst;
268 // roundoff errors got us
274 dt = dst / (0.5 * (v0 + vs));
275 // this is not correct, but the differential equations have no analytic
276 // solution - and these times are very small anyway
277 //print("dt = ", ftos(dt), "\n");
279 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
280 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
281 self.think = W_BallisticBullet_LeaveSolid_think;
282 self.nextthink = time + dt;
284 vel = vel * velfactor;
286 self.velocity = '0 0 0';
287 self.flags |= FL_ONGROUND; // prevent moving
288 self.W_BallisticBullet_LeaveSolid_velocity = vel;
293 void W_BallisticBullet_Touch (void)
297 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
301 W_BallisticBullet_Hit ();
303 // if we hit "weapclip", bail out
305 // rationale of this check:
307 // any shader that is solid, nodraw AND trans is meant to clip weapon
308 // shots and players, but has no other effect!
310 // if it is not trans, it is caulk and should not have this side effect
313 // common/weapclip (intended)
314 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
315 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
316 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
317 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
323 density = other.ballistics_density;
328 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
334 self.projectiledeathtype |= HITTYPE_BOUNCE;
337 void endFireBallisticBullet()
339 endzcurveparticles();
342 entity fireBallisticBullet_trace_callback_ent;
343 float fireBallisticBullet_trace_callback_eff;
344 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
346 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
347 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
350 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)
352 float lag, dt, savetime, density;
356 antilagging = (cvar("g_antilag_bullets") && (pSpeed >= cvar("g_antilag_bullets")));
360 proj.classname = "bullet";
362 PROJECTILE_MAKETRIGGER(proj);
363 if(gravityfactor > 0)
365 proj.movetype = MOVETYPE_TOSS;
366 proj.gravity = gravityfactor;
369 proj.movetype = MOVETYPE_FLY;
370 proj.think = SUB_Remove;
371 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
372 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
373 proj.angles = vectoangles(proj.velocity);
374 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
375 // so: bulletconstant = bullet mass / area of bullet circle
376 setorigin(proj, start);
377 proj.flags = FL_PROJECTILE;
379 proj.touch = W_BallisticBullet_Touch;
381 proj.dmg_edge = headshotbonus;
382 proj.dmg_force = force;
383 proj.projectiledeathtype = dtype;
385 proj.oldvelocity = proj.velocity;
387 other = proj; MUTATOR_CALLHOOK(EditProjectile);
393 if(tracereffects & EF_RED)
394 eff = particleeffectnum("tr_rifle");
396 eff = particleeffectnum("tr_bullet");
398 // NOTE: this may severely throw off weapon balance
399 lag = ANTILAG_LATENCY(self);
402 if(clienttype(self) != CLIENTTYPE_REAL)
404 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
405 lag = 0; // only do hitscan, but no antilag
409 antilag_takeback(pl, time - lag);
414 savetime = frametime;
419 // DP tracetoss is stupid and always traces in 0.05s
420 // ticks. This makes it trace in 0.05*0.125s ticks
426 self.velocity = self.velocity * 0.125;
427 self.gravity *= 0.125 * 0.125;
429 fireBallisticBullet_trace_callback_ent = self;
430 fireBallisticBullet_trace_callback_eff = eff;
431 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
435 if(trace_fraction == 1)
437 // won't hit anything anytime soon (DP's
438 // tracetoss does 200 tics of, here,
439 // 0.05*0.125s, that is, 1.25 seconds
442 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
443 setorigin(self, trace_endpos);
444 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
446 if(!SUB_OwnerCheck())
448 if(SUB_NoImpactCheck())
452 W_BallisticBullet_Hit();
455 density = other.ballistics_density;
460 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
463 W_BallisticBullet_LeaveSolid_think();
465 frametime = savetime;
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;
514 self.realowner = attacker;
516 // do not explode NOW but in the NEXT FRAME!
517 // because recursive calls to RadiusDamage are not allowed
518 self.nextthink = time;
519 self.think = explode;