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(ent.flags & FL_CLIENT)
129 if(ent.deadflag == DEAD_NO)
130 if(!teamplay || ent.team != self.team)
131 totaldmg += bdamage * f;
135 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * 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 if not(inWarmupStage)
152 accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
154 trace_endpos = endpoint;
156 trace_dphitq3surfaceflags = endq3surfaceflags;
163 void W_BallisticBullet_Hit (void)
167 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
168 q = 1 + self.dmg_edge / self.dmg;
170 if(other.solid == SOLID_BSP)
171 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
173 if(other && other != self.enemy)
175 endzcurveparticles();
179 damage_headshotbonus = self.dmg_edge;
180 railgun_start = self.origin - 2 * frametime * self.velocity;
181 railgun_end = self.origin + 2 * frametime * self.velocity;
183 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
184 damage_headshotbonus = 0;
186 if(self.dmg_edge != 0)
191 AnnounceTo(self.owner, "headshot");
195 AnnounceTo(self.owner, "awesome");
197 // calculate hits for ballistic weapons
198 if (other.flags & FL_CLIENT) // is the player a client
199 if (other.deadflag == DEAD_NO) // is the victim a corpse
200 if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
201 if not(inWarmupStage) // not in warm up stage
203 // do not exceed 100%
204 q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total;
205 self.dmg_total += f * self.dmg;
206 accuracy_add(self.owner, self.owner.weapon, 0, q);
210 self.enemy = other; // don't hit the same player twice with the same bullet
213 .void(void) W_BallisticBullet_LeaveSolid_think_save;
214 .float W_BallisticBullet_LeaveSolid_nextthink_save;
215 .vector W_BallisticBullet_LeaveSolid_origin;
216 .vector W_BallisticBullet_LeaveSolid_velocity;
218 void W_BallisticBullet_LeaveSolid_think()
220 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
221 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
223 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
224 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
225 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
227 self.flags &~= FL_ONGROUND;
229 if(self.enemy.solid == SOLID_BSP)
232 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
233 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
236 UpdateCSQCProjectile(self);
239 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
241 // move the entity along its velocity until it's out of solid, then let it resume
243 float dt, dst, velfactor, v0, vs;
247 // outside the world? forget it
248 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)
251 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
254 E0_m = 0.5 * v0 * v0;
255 maxdist = E0_m / constant;
256 // maxdist = 0.5 * v0 * v0 / constant
257 // dprint("max dist = ", ftos(maxdist), "\n");
259 if(maxdist <= cvar("g_ballistics_mindistance"))
262 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
264 if(trace_fraction == 1) // 1: we never got out of solid
267 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
269 dst = max(cvar("g_ballistics_mindistance"), vlen(trace_endpos - self.origin));
270 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
271 Es_m = E0_m - constant * dst;
274 // roundoff errors got us
280 dt = dst / (0.5 * (v0 + vs));
281 // this is not correct, but the differential equations have no analytic
282 // solution - and these times are very small anyway
283 //print("dt = ", ftos(dt), "\n");
285 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
286 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
287 self.think = W_BallisticBullet_LeaveSolid_think;
288 self.nextthink = time + dt;
290 vel = vel * velfactor;
292 self.velocity = '0 0 0';
293 self.flags |= FL_ONGROUND; // prevent moving
294 self.W_BallisticBullet_LeaveSolid_velocity = vel;
299 void W_BallisticBullet_Touch (void)
303 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
307 W_BallisticBullet_Hit ();
309 // if we hit "weapclip", bail out
311 // rationale of this check:
313 // any shader that is solid, nodraw AND trans is meant to clip weapon
314 // shots and players, but has no other effect!
316 // if it is not trans, it is caulk and should not have this side effect
319 // common/weapclip (intended)
320 // common/noimpact (is supposed to eat projectiles, but is erased farther above)
321 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
322 if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
323 if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
329 density = other.ballistics_density;
334 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
340 self.projectiledeathtype |= HITTYPE_BOUNCE;
343 void endFireBallisticBullet()
345 endzcurveparticles();
348 entity fireBallisticBullet_trace_callback_ent;
349 float fireBallisticBullet_trace_callback_eff;
350 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
352 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
353 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
356 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)
358 float lag, dt, savetime, density;
362 antilagging = (cvar("g_antilag_bullets") && (pSpeed >= cvar("g_antilag_bullets")));
366 proj.classname = "bullet";
368 PROJECTILE_MAKETRIGGER(proj);
369 if(gravityfactor > 0)
371 proj.movetype = MOVETYPE_TOSS;
372 proj.gravity = gravityfactor;
375 proj.movetype = MOVETYPE_FLY;
376 proj.think = SUB_Remove;
377 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
378 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
379 proj.angles = vectoangles(proj.velocity);
380 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
381 // so: bulletconstant = bullet mass / area of bullet circle
382 setorigin(proj, start);
383 proj.flags = FL_PROJECTILE;
385 proj.touch = W_BallisticBullet_Touch;
387 proj.dmg_edge = headshotbonus;
388 proj.dmg_force = force;
389 proj.projectiledeathtype = dtype;
391 proj.oldvelocity = proj.velocity;
393 other = proj; MUTATOR_CALLHOOK(EditProjectile);
399 if(tracereffects & EF_RED)
400 eff = particleeffectnum("tr_rifle");
402 eff = particleeffectnum("tr_bullet");
404 // NOTE: this may severely throw off weapon balance
405 lag = ANTILAG_LATENCY(self);
408 if(clienttype(self) != CLIENTTYPE_REAL)
410 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
411 lag = 0; // only do hitscan, but no antilag
415 antilag_takeback(pl, time - lag);
420 savetime = frametime;
425 // DP tracetoss is stupid and always traces in 0.05s
426 // ticks. This makes it trace in 0.05*0.125s ticks
432 self.velocity = self.velocity * 0.125;
433 self.gravity *= 0.125 * 0.125;
435 fireBallisticBullet_trace_callback_ent = self;
436 fireBallisticBullet_trace_callback_eff = eff;
437 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
441 if(trace_fraction == 1)
443 // won't hit anything anytime soon (DP's
444 // tracetoss does 200 tics of, here,
445 // 0.05*0.125s, that is, 1.25 seconds
448 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
449 setorigin(self, trace_endpos);
450 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
452 if(!SUB_OwnerCheck())
454 if(SUB_NoImpactCheck())
458 W_BallisticBullet_Hit();
461 density = other.ballistics_density;
466 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density))
469 W_BallisticBullet_LeaveSolid_think();
471 frametime = savetime;
483 if(tracereffects & EF_RED)
484 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
485 else if(tracereffects & EF_BLUE)
486 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
488 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
491 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
495 dir = normalize(dir + randomvec() * spread);
496 end = start + dir * MAX_SHOT_DISTANCE;
497 if(self.antilag_debug)
498 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
500 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
504 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
506 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
507 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
508 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
509 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
510 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
515 void W_PrepareExplosionByDamage(entity attacker, void() explode)
517 self.takedamage = DAMAGE_NO;
518 self.event_damage = SUB_Null;
519 self.owner = attacker;
520 self.realowner = attacker;
522 // do not explode NOW but in the NEXT FRAME!
523 // because recursive calls to RadiusDamage are not allowed
524 self.nextthink = time;
525 self.think = explode;