1 void W_GiveWeapon (entity e, float wep, string name)
\r
8 e.weapons = e.weapons | W_WeaponBit(wep);
\r
13 if (other.classname == "player")
\r
15 sprint (other, "You got the ^2");
\r
16 sprint (other, name);
\r
17 sprint (other, "\n");
\r
23 .float railgundistance;
\r
24 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
\r
26 local vector hitloc, force, endpoint, dir;
\r
27 local entity ent, endent;
\r
28 local float endq3surfaceflags;
\r
35 railgun_start = start;
\r
38 dir = normalize(end - start);
\r
39 length = vlen(end - start);
\r
40 force = dir * bforce;
\r
42 // go a little bit into the wall because we need to hit this wall later
\r
45 // trace multiple times until we hit a wall, each obstacle will be made
\r
46 // non-solid so we can hit the next, while doing this we spawn effects and
\r
47 // note down which entities were hit so we can damage them later
\r
50 if(self.antilag_debug)
\r
51 WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
\r
53 WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
\r
55 // if it is world we can't hurt it so stop now
\r
56 if (trace_ent == world || trace_fraction == 1)
\r
59 // make the entity non-solid so we can hit the next one
\r
60 trace_ent.railgunhit = TRUE;
\r
61 trace_ent.railgunhitloc = end;
\r
62 trace_ent.railgunhitsolidbackup = trace_ent.solid;
\r
63 trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
\r
65 // stop if this is a wall
\r
66 if (trace_ent.solid == SOLID_BSP)
\r
69 // make the entity non-solid
\r
70 trace_ent.solid = SOLID_NOT;
\r
73 endpoint = trace_endpos;
\r
75 endq3surfaceflags = trace_dphitq3surfaceflags;
\r
77 // find all the entities the railgun hit and restore their solid state
\r
78 ent = findfloat(world, railgunhit, TRUE);
\r
81 // restore their solid type
\r
82 ent.solid = ent.railgunhitsolidbackup;
\r
83 ent = findfloat(ent, railgunhit, TRUE);
\r
86 // spawn a temporary explosion entity for RadiusDamage calls
\r
87 //explosion = spawn();
\r
89 // find all the entities the railgun hit and hurt them
\r
90 ent = findfloat(world, railgunhit, TRUE);
\r
93 // get the details we need to call the damage function
\r
94 hitloc = ent.railgunhitloc;
\r
96 //for stats so that team hit will count as a miss
\r
97 if(ent.flags & FL_CLIENT)
\r
98 if(ent.deadflag == DEAD_NO)
\r
102 if(ent.team == self.team)
\r
105 f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
\r
106 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
\r
108 // apply the damage
\r
109 if (ent.takedamage)
\r
110 Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
\r
112 // create a small explosion to throw gibs around (if applicable)
\r
113 //setorigin (explosion, hitloc);
\r
114 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
\r
116 ent.railgunhitloc = '0 0 0';
\r
117 ent.railgunhitsolidbackup = SOLID_NOT;
\r
118 ent.railgunhit = FALSE;
\r
119 ent.railgundistance = 0;
\r
121 // advance to the next entity
\r
122 ent = findfloat(ent, railgunhit, TRUE);
\r
125 // calculate hits and fired shots for hitscan
\r
126 if not(inWarmupStage)
\r
128 self.stats_fired[self.weapon - 1] += 1;
\r
129 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
\r
132 self.stats_hit[self.weapon - 1] += 1;
\r
133 self.stat_hit = self.weapon + 64 * floor(self.stats_hit[self.weapon - 1]);
\r
137 trace_endpos = endpoint;
\r
138 trace_ent = endent;
\r
139 trace_dphitq3surfaceflags = endq3surfaceflags;
\r
145 void W_BallisticBullet_Hit (void)
\r
149 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
\r
151 if(other.solid == SOLID_BSP)
\r
152 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
\r
154 if(other && other != self.enemy)
\r
156 endzcurveparticles();
\r
160 damage_headshotbonus = self.dmg_edge;
\r
161 railgun_start = self.origin - 2 * frametime * self.velocity;
\r
162 railgun_end = self.origin + 2 * frametime * self.velocity;
\r
164 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
\r
165 damage_headshotbonus = 0;
\r
167 if(self.dmg_edge != 0)
\r
170 AnnounceTo(self.owner, "headshot");
\r
172 AnnounceTo(self.owner, "awesome");
\r
175 // calculate hits for ballistic weapons
\r
176 if (other.flags & FL_CLIENT) // is the player a client
\r
177 if (other.deadflag == DEAD_NO) // is the victim a corpse
\r
178 if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
\r
179 if not(inWarmupStage) // not in warm up stage
\r
181 self.owner.stats_hit[self.owner.weapon - 1] += 1;
\r
182 self.owner.stat_hit = self.owner.weapon + 64 * floor(self.owner.stats_hit[self.owner.weapon - 1]);
\r
186 self.enemy = other; // don't hit the same player twice with the same bullet
\r
189 .void(void) W_BallisticBullet_LeaveSolid_think_save;
\r
190 .float W_BallisticBullet_LeaveSolid_nextthink_save;
\r
191 .vector W_BallisticBullet_LeaveSolid_origin;
\r
192 .vector W_BallisticBullet_LeaveSolid_velocity;
\r
194 void W_BallisticBullet_LeaveSolid_think()
\r
196 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
\r
197 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
\r
199 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
\r
200 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
\r
201 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
\r
203 self.flags &~= FL_ONGROUND;
\r
205 if(self.enemy.solid == SOLID_BSP)
\r
208 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
\r
209 Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, self);
\r
212 UpdateCSQCProjectile(self);
\r
215 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
\r
217 // move the entity along its velocity until it's out of solid, then let it resume
\r
219 float dt, dst, velfactor, v0, vs;
\r
223 // outside the world? forget it
\r
224 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)
\r
227 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
\r
230 E0_m = 0.5 * v0 * v0;
\r
231 maxdist = E0_m / constant;
\r
232 // maxdist = 0.5 * v0 * v0 / constant
\r
233 // dprint("max dist = ", ftos(maxdist), "\n");
\r
235 if(maxdist <= cvar("g_ballistics_mindistance"))
\r
238 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
\r
240 if(trace_fraction == 1) // 1: we never got out of solid
\r
243 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
\r
245 dst = vlen(trace_endpos - self.origin);
\r
246 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
\r
247 Es_m = E0_m - constant * dst;
\r
250 // roundoff errors got us
\r
253 vs = sqrt(2 * Es_m);
\r
254 velfactor = vs / v0;
\r
256 dt = dst / (0.5 * (v0 + vs));
\r
257 // this is not correct, but the differential equations have no analytic
\r
258 // solution - and these times are very small anyway
\r
259 //print("dt = ", ftos(dt), "\n");
\r
261 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
\r
262 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
\r
263 self.think = W_BallisticBullet_LeaveSolid_think;
\r
264 self.nextthink = time + dt;
\r
266 vel = vel * velfactor;
\r
268 self.velocity = '0 0 0';
\r
269 self.flags |= FL_ONGROUND; // prevent moving
\r
270 self.W_BallisticBullet_LeaveSolid_velocity = vel;
\r
275 void W_BallisticBullet_Touch (void)
\r
277 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
\r
281 W_BallisticBullet_Hit ();
\r
283 // go through solid!
\r
284 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
\r
290 self.projectiledeathtype |= HITTYPE_BOUNCE;
\r
293 void endFireBallisticBullet()
\r
295 endzcurveparticles();
\r
298 entity fireBallisticBullet_trace_callback_ent;
\r
299 float fireBallisticBullet_trace_callback_eff;
\r
300 void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
\r
302 if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
\r
303 zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
\r
306 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)
\r
308 float lag, dt, savetime;
\r
309 entity pl, oldself;
\r
313 proj.classname = "bullet";
\r
315 PROJECTILE_MAKETRIGGER(proj);
\r
316 if(gravityfactor > 0)
\r
318 proj.movetype = MOVETYPE_TOSS;
\r
319 proj.gravity = gravityfactor;
\r
322 proj.movetype = MOVETYPE_FLY;
\r
323 proj.think = SUB_Remove;
\r
324 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
\r
325 W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread);
\r
326 proj.angles = vectoangles(proj.velocity);
\r
327 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
\r
328 // so: bulletconstant = bullet mass / area of bullet circle
\r
329 setorigin(proj, start);
\r
330 proj.flags = FL_PROJECTILE;
\r
332 proj.touch = W_BallisticBullet_Touch;
\r
334 proj.dmg_edge = headshotbonus;
\r
335 proj.dmg_force = force;
\r
336 proj.projectiledeathtype = dtype;
\r
338 proj.oldvelocity = proj.velocity;
\r
340 if(cvar("g_antilag_bullets"))
\r
341 if(pSpeed >= cvar("g_antilag_bullets"))
\r
345 if(tracereffects & EF_RED)
\r
346 eff = particleeffectnum("tr_rifle");
\r
348 eff = particleeffectnum("tr_bullet");
\r
350 // NOTE: this may severely throw off weapon balance
\r
351 lag = ANTILAG_LATENCY(self);
\r
354 if(clienttype(self) != CLIENTTYPE_REAL)
\r
356 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
\r
357 lag = 0; // only do hitscan, but no antilag
\r
360 FOR_EACH_PLAYER(pl)
\r
361 antilag_takeback(pl, time - lag);
\r
366 savetime = frametime;
\r
369 // update the accuracy stats - increase shots fired by 1
\r
370 if not(inWarmupStage)
\r
372 oldself.stats_fired[oldself.weapon - 1] += 1;
\r
373 oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
\r
378 // DP tracetoss is stupid and always traces in 0.05s
\r
379 // ticks. This makes it trace in 0.05*0.125s ticks
\r
383 v0 = self.velocity;
\r
385 self.velocity = self.velocity * 0.125;
\r
386 self.gravity *= 0.125 * 0.125;
\r
387 trace_fraction = 0;
\r
388 fireBallisticBullet_trace_callback_ent = self;
\r
389 fireBallisticBullet_trace_callback_eff = eff;
\r
390 WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
\r
391 self.velocity = v0;
\r
394 if(trace_fraction == 1)
\r
396 // won't hit anything anytime soon (DP's
\r
397 // tracetoss does 200 tics of, here,
\r
398 // 0.05*0.125s, that is, 1.25 seconds
\r
401 dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
\r
402 setorigin(self, trace_endpos);
\r
403 self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
\r
405 if(!SUB_OwnerCheck())
\r
407 if(SUB_NoImpactCheck())
\r
411 W_BallisticBullet_Hit();
\r
414 // go through solid!
\r
415 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
\r
418 W_BallisticBullet_LeaveSolid_think();
\r
420 frametime = savetime;
\r
424 FOR_EACH_PLAYER(pl)
\r
425 antilag_restore(pl);
\r
432 // update the accuracy stats
\r
433 if not(inWarmupStage)
\r
435 self.stats_fired[self.weapon - 1] += 1;
\r
436 self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
\r
439 if(tracereffects & EF_RED)
\r
440 CSQCProjectile(proj, TRUE, PROJECTILE_EXAMPLE, TRUE);
\r
441 else if(tracereffects & EF_BLUE)
\r
442 CSQCProjectile(proj, TRUE, PROJECTILE_EXAMPLE, TRUE);
\r
444 CSQCProjectile(proj, TRUE, PROJECTILE_EXAMPLE, TRUE);
\r
447 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
\r
451 dir = normalize(dir + randomvec() * spread);
\r
452 end = start + dir * MAX_SHOT_DISTANCE;
\r
453 if(self.antilag_debug)
\r
454 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
\r
456 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
\r
458 end = trace_endpos;
\r
460 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
\r
462 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
\r
463 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
\r
464 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
\r
465 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
\r
466 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
\r
468 trace_endpos = end;
\r
471 void W_PrepareExplosionByDamage(entity attacker, void() explode)
\r
473 self.takedamage = DAMAGE_NO;
\r
474 self.event_damage = SUB_Null;
\r
475 self.owner = attacker;
\r
477 // do not explode NOW but in the NEXT FRAME!
\r
478 // because recursive calls to RadiusDamage are not allowed
\r
479 self.nextthink = time;
\r
480 self.think = explode;
\r