2 void W_GiveWeapon (entity e, float wep, string name)
9 e.weapons = e.weapons | W_WeaponBit(wep);
14 if (other.classname == "player")
16 sprint (other, "You got the ^2");
25 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype)
27 local vector hitloc, force, endpoint, dir;
28 local entity ent, endent;
29 local float endq3surfaceflags;
30 //local entity explosion;
32 railgun_start = start;
35 dir = normalize(end - start);
38 // go a little bit into the wall because we need to hit this wall later
41 // trace multiple times until we hit a wall, each obstacle will be made
42 // non-solid so we can hit the next, while doing this we spawn effects and
43 // note down which entities were hit so we can damage them later
46 if(self.antilag_debug)
47 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
49 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
51 // if it is world we can't hurt it so stop now
52 if (trace_ent == world || trace_fraction == 1)
55 // make the entity non-solid so we can hit the next one
56 trace_ent.railgunhit = TRUE;
57 trace_ent.railgunhitloc = end;
58 trace_ent.railgunhitsolidbackup = trace_ent.solid;
60 // stop if this is a wall
61 if (trace_ent.solid == SOLID_BSP)
64 // make the entity non-solid
65 trace_ent.solid = SOLID_NOT;
68 endpoint = trace_endpos;
70 endq3surfaceflags = trace_dphitq3surfaceflags;
72 // find all the entities the railgun hit and restore their solid state
73 ent = findfloat(world, railgunhit, TRUE);
76 // restore their solid type
77 ent.solid = ent.railgunhitsolidbackup;
78 ent = findfloat(ent, railgunhit, TRUE);
81 // spawn a temporary explosion entity for RadiusDamage calls
82 //explosion = spawn();
84 // find all the entities the railgun hit and hurt them
85 ent = findfloat(world, railgunhit, TRUE);
88 // get the details we need to call the damage function
89 hitloc = ent.railgunhitloc;
90 ent.railgunhitloc = '0 0 0';
91 ent.railgunhitsolidbackup = SOLID_NOT;
92 ent.railgunhit = FALSE;
95 if (ent.takedamage || ent.classname == "case")
96 Damage (ent, self, self, bdamage, deathtype, hitloc, force);
98 // create a small explosion to throw gibs around (if applicable)
99 //setorigin (explosion, hitloc);
100 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
102 // advance to the next entity
103 ent = findfloat(ent, railgunhit, TRUE);
106 // we're done with the explosion entity, remove it
109 trace_endpos = endpoint;
111 trace_dphitq3surfaceflags = endq3surfaceflags;
117 void W_BallisticBullet_Hit (void)
121 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
123 if(other.solid == SOLID_BSP)
124 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype);
126 if(other && other != self.enemy)
130 damage_headshotbonus = self.dmg_edge;
131 railgun_start = self.origin - 2 * frametime * self.velocity;
132 railgun_end = self.origin + 2 * frametime * self.velocity;
134 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
135 damage_headshotbonus = 0;
137 if(self.dmg_edge != 0)
140 announce(self.owner, "announcer/male/headshot.wav");
142 announce(self.owner, "announcer/male/yoda.wav");
145 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
148 self.enemy = other; // don't hit the same player twice with the same bullet
151 .void(void) W_BallisticBullet_LeaveSolid_think_save;
152 .float W_BallisticBullet_LeaveSolid_nextthink_save;
153 .vector W_BallisticBullet_LeaveSolid_origin;
154 .vector W_BallisticBullet_LeaveSolid_velocity;
156 void W_BallisticBullet_LeaveSolid_think()
158 setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
159 self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
161 self.think = self.W_BallisticBullet_LeaveSolid_think_save;
162 self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save) + 1;
163 self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
165 self.flags &~= FL_ONGROUND;
167 if(self.enemy.solid == SOLID_BSP)
170 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
171 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype);
174 UpdateCSQCProjectile(self);
177 // a fake logarithm function
182 if(x > 0.9 && x < 1.1)
184 return 2 * log(sqrt(x));
187 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
189 // move the entity along its velocity until it's out of solid, then let it resume
191 float dt, dst, velfactor, v0, vs;
195 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
198 E0_m = 0.5 * v0 * v0;
199 maxdist = E0_m / constant;
200 // maxdist = 0.5 * v0 * v0 / constant
201 // dprint("max dist = ", ftos(maxdist), "\n");
206 traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
208 if(trace_fraction == 1) // 1: we never got out of solid
211 self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
213 dst = vlen(trace_endpos - self.origin);
214 // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
215 Es_m = E0_m - constant * dst;
218 // roundoff errors got us
224 dt = dst / (0.5 * (v0 + vs));
225 // this is not correct, but the differential equations have no analytic
226 // solution - and these times are very small anyway
227 //print("dt = ", ftos(dt), "\n");
229 self.W_BallisticBullet_LeaveSolid_think_save = self.think;
230 self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
231 self.think = W_BallisticBullet_LeaveSolid_think;
232 self.nextthink = time + dt;
234 vel = vel * velfactor;
236 self.velocity = '0 0 0';
237 self.flags |= FL_ONGROUND; // prevent moving
238 self.W_BallisticBullet_LeaveSolid_velocity = vel;
243 void W_BallisticBullet_Touch (void)
245 if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
249 W_BallisticBullet_Hit ();
252 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
258 self.projectiledeathtype |= HITTYPE_BOUNCE;
261 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)
263 float lag, dt, savetime;
268 proj.classname = "bullet";
270 proj.solid = SOLID_BBOX;
271 if(gravityfactor > 0)
273 proj.movetype = MOVETYPE_TOSS;
274 proj.gravity = gravityfactor;
277 proj.movetype = MOVETYPE_FLY;
278 proj.think = SUB_Remove;
279 proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
280 proj.velocity = (dir + randomvec() * spread) * pSpeed;
281 W_SetupProjectileVelocity(proj);
282 proj.angles = vectoangles(proj.velocity);
283 proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
284 // so: bulletconstant = bullet mass / area of bullet circle
285 setorigin(proj, start);
286 proj.flags = FL_PROJECTILE;
288 proj.touch = W_BallisticBullet_Touch;
290 proj.dmg_edge = headshotbonus;
291 proj.dmg_force = force;
292 proj.projectiledeathtype = dtype;
294 proj.oldvelocity = proj.velocity;
296 if(cvar("g_antilag_bullets"))
297 if(pSpeed >= cvar("g_antilag_bullets"))
299 // NOTE: this may severely throw off weapon balance
300 lag = ANTILAG_LATENCY(self);
303 if(clienttype(self) != CLIENTTYPE_REAL)
308 antilag_takeback(pl, time - lag);
313 savetime = frametime;
318 tracetoss(self, oldself);
320 dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
321 setorigin(self, trace_endpos);
322 self.velocity_z -= sv_gravity * dt;
324 if(!SUB_OwnerCheck())
326 if(SUB_NoImpactCheck())
330 W_BallisticBullet_Hit ();
334 if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
337 W_BallisticBullet_LeaveSolid_think();
339 frametime = savetime;
351 if(tracereffects & EF_RED)
352 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
354 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
359 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
364 if(cvar("g_ballistics_force"))
366 if (DEATH_ISWEAPON(dtype, WEP_SHOTGUN))
367 fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_shotgun_speed"), 5, damage, 0, force, dtype, 0, 1, cvar("g_ballistics_force_shotgun_bulletconstant"));
369 fireBallisticBullet(start, dir, spread, cvar("g_ballistics_force_uzi_speed"), 5, damage, 0, force, dtype, 0, 1, cvar("g_ballistics_force_shotgun_bulletconstant"));
373 dir = dir + randomvec() * spread;
374 end = start + dir * MAX_SHOT_DISTANCE;
375 if(self.antilag_debug)
376 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
378 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
384 e.movetype = MOVETYPE_FLY;
386 e.think = SUB_Remove;
387 e.nextthink = time + vlen(trace_endpos - start) / 6000;
388 e.velocity = dir * 6000;
389 e.angles = vectoangles(e.velocity);
390 setorigin (e, start);
391 e.flags = FL_PROJECTILE;
393 CSQCProjectile(e, TRUE, PROJECTILE_BULLET, TRUE);
396 if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
398 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
399 Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype);
400 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
405 void W_PrepareExplosionByDamage(entity attacker, void() explode)
407 self.takedamage = DAMAGE_NO;
408 self.event_damage = SUB_Null;
409 self.owner = attacker;
411 // do not explode NOW but in the NEXT FRAME!
412 // because recursive calls to RadiusDamage are not allowed
413 self.nextthink = time;
414 self.think = explode;