]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/w_common.qc
Fix constant flyby sound for nex when spectating nex/minsta user.
[divverent/nexuiz.git] / data / qcsrc / server / w_common.qc
1 .float bullets_hit[WEP_COUNT];          //for hitscan bullets hit
2 .float bullets_fired[WEP_COUNT];        //for hitscan bullets fired
3
4 FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bullets_hit);
5 FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bullets_fired);
6
7 void W_GiveWeapon (entity e, float wep, string name)
8 {
9         entity oldself;
10
11         if (!wep)
12                 return;
13
14         e.weapons = e.weapons | W_WeaponBit(wep);
15
16         oldself = self;
17         self = e;
18
19         if (other.classname == "player")
20         {
21                 sprint (other, "You got the ^2");
22                 sprint (other, name);
23                 sprint (other, "\n");
24         }
25
26
27         self = oldself;
28 }
29
30 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype)
31 {
32         local vector hitloc, force, endpoint, dir;
33         local entity ent, endent;
34         local float endq3surfaceflags;
35         //local entity explosion;
36         float did_hit;
37         float length,step;
38         string snd;
39
40         did_hit = 0;
41
42         railgun_start = start;
43         railgun_end = end;
44
45         dir = normalize(end - start);
46         force = dir * bforce;
47
48         // go a little bit into the wall because we need to hit this wall later
49         end = end + dir;
50
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
54         while (1)
55         {
56                 if(self.antilag_debug)
57                         traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
58                 else
59                         traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
60
61                 // if it is world we can't hurt it so stop now
62                 if (trace_ent == world || trace_fraction == 1)
63                         break;
64
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
70                 // stop if this is a wall
71                 if (trace_ent.solid == SOLID_BSP)
72                         break;
73
74                 // make the entity non-solid
75                 trace_ent.solid = SOLID_NOT;
76         }
77
78         endpoint = trace_endpos;
79         endent = trace_ent;
80         endq3surfaceflags = trace_dphitq3surfaceflags;
81
82         // Find all players the beam passed close by
83         length = vlen(start - endpoint);
84         for(step = 0; step < length; step += 16)
85         {
86         ent = findradius(start + dir * step,128);
87         while(ent)
88         {
89             if(clienttype(ent) == CLIENTTYPE_REAL)
90
91             if(ent.health != -666)
92                 if(ent != self)
93                     ent.ticrate = 1;
94
95             ent = ent.chain;
96         }
97     }
98
99     // And play a flyby sound to them
100     ent = findchainfloat(ticrate,1);
101     while(ent)
102     {
103         snd = "weapons/nexwhoosh";
104         snd = strcat(snd,ftos(rint(random() * 2) +1));
105         snd = strcat(snd,".ogg");
106         play2(ent, snd);
107         ent.ticrate = 0;
108         ent = ent.chain;
109     }
110
111         // find all the entities the railgun hit and restore their solid state
112         ent = findfloat(world, railgunhit, TRUE);
113         while (ent)
114         {
115                 // restore their solid type
116                 ent.solid = ent.railgunhitsolidbackup;
117                 ent = findfloat(ent, railgunhit, TRUE);
118         }
119
120         // spawn a temporary explosion entity for RadiusDamage calls
121         //explosion = spawn();
122
123         // find all the entities the railgun hit and hurt them
124         ent = findfloat(world, railgunhit, TRUE);
125         while (ent)
126         {
127                 // get the details we need to call the damage function
128                 hitloc = ent.railgunhitloc;
129                 ent.railgunhitloc = '0 0 0';
130                 ent.railgunhitsolidbackup = SOLID_NOT;
131                 ent.railgunhit = FALSE;
132
133                 //for stats so that team hit will count as a miss
134                 if(ent.flags & FL_CLIENT)
135                 if(ent.deadflag == DEAD_NO)
136                         did_hit = 1;
137
138                 if(teams_matter)
139                 if(ent.team == self.team)
140                         did_hit = 0;
141
142                 // apply the damage
143                 if (ent.takedamage || ent.classname == "case")
144                         Damage (ent, self, self, bdamage, deathtype, hitloc, force);
145
146                 // create a small explosion to throw gibs around (if applicable)
147                 //setorigin (explosion, hitloc);
148                 //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
149
150                 // advance to the next entity
151                 ent = findfloat(ent, railgunhit, TRUE);
152         }
153
154         //calculate hits and fired shots for hitscan
155         if not(inWarmupStage)
156         if not(self.isbot)
157         {
158                 self.bullets_fired[self.weapon] += 1;
159
160                 if(did_hit)
161                         self.bullets_hit[self.weapon] += 1;
162
163                 // update the client and store in addstat() in g_world
164                 self.damage_hits = self.weapon + 64 * rint(self.bullets_hit[self.weapon]);
165                 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
166         }
167
168         // we're done with the explosion entity, remove it
169         //remove(explosion);
170
171         trace_endpos = endpoint;
172         trace_ent = endent;
173         trace_dphitq3surfaceflags = endq3surfaceflags;
174 }
175
176 .float dmg_edge;
177 .float dmg_force;
178 .float dmg_radius;
179 void W_BallisticBullet_Hit (void)
180 {
181         float f;
182
183         float hit;
184         hit = 0;
185
186         f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
187
188         if(other.solid == SOLID_BSP)
189                 Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, self.dmg_force * normalize(self.velocity) * f, self.projectiledeathtype, self);
190
191         if(other && other != self.enemy)
192         {
193                 headshot = 0;
194                 yoda = 0;
195                 damage_headshotbonus = self.dmg_edge;
196                 railgun_start = self.origin - 2 * frametime * self.velocity;
197                 railgun_end = self.origin + 2 * frametime * self.velocity;
198
199                 if(other.flags & FL_CLIENT)
200                 if(other.deadflag == DEAD_NO)
201                         hit = 1;
202
203                 if(teamplay)
204                 if(other.team == self.owner.team)
205                         hit = 0;
206
207                 Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
208                 damage_headshotbonus = 0;
209
210                 if(self.dmg_edge != 0)
211                 {
212                         if(headshot)
213                                 announce(self.owner, "announcer/male/headshot.wav");
214                         if(yoda)
215                                 announce(self.owner, "announcer/male/awesome.wav");
216                 }
217
218                 //calculate hits for ballistic weapons
219                 if not(self.owner.isbot)
220                 {
221                         if(hit)
222                                 self.owner.bullets_hit[self.owner.weapon] += 1;
223                         // update the client
224                         self.owner.damage_hits = self.owner.weapon + 64 * rint(self.owner.bullets_hit[self.owner.weapon]);
225                 }
226
227                 //sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
228         }
229
230         self.enemy = other; // don't hit the same player twice with the same bullet
231 }
232
233 .void(void) W_BallisticBullet_LeaveSolid_think_save;
234 .float W_BallisticBullet_LeaveSolid_nextthink_save;
235 .vector W_BallisticBullet_LeaveSolid_origin;
236 .vector W_BallisticBullet_LeaveSolid_velocity;
237
238 void W_BallisticBullet_LeaveSolid_think()
239 {
240         setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
241         self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
242
243         self.think = self.W_BallisticBullet_LeaveSolid_think_save;
244         self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
245         self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null;
246
247         self.flags &~= FL_ONGROUND;
248
249         if(self.enemy.solid == SOLID_BSP)
250         {
251                 float f;
252                 f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
253                 Damage_DamageInfo(self.origin, 0, 0, 0, self.dmg_force * normalize(self.velocity) * -f, self.projectiledeathtype, self);
254         }
255
256         UpdateCSQCProjectile(self);
257 }
258
259 // a fake logarithm function
260 float log(float x)
261 {
262         if(x < 0.0001)
263                 return 0;
264         if(x > 0.9 && x < 1.1)
265                 return x - 1;
266         return 2 * log(sqrt(x));
267 }
268
269 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)
270 {
271         // move the entity along its velocity until it's out of solid, then let it resume
272
273         float dt, dst, velfactor, v0, vs;
274         float maxdist;
275         float E0_m, Es_m;
276
277         // outside the world? forget it
278         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)
279                 return 0;
280
281         // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
282         v0 = vlen(vel);
283
284         E0_m = 0.5 * v0 * v0;
285         maxdist = E0_m / constant;
286         // maxdist = 0.5 * v0 * v0 / constant
287         // dprint("max dist = ", ftos(maxdist), "\n");
288
289         if(maxdist <= 0.5)
290                 return 0;
291
292         traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
293
294         if(trace_fraction == 1) // 1: we never got out of solid
295                 return 0;
296
297         self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
298
299         dst = vlen(trace_endpos - self.origin);
300         // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
301         Es_m = E0_m - constant * dst;
302         if(Es_m <= 0)
303         {
304                 // roundoff errors got us
305                 return 0;
306         }
307         vs = sqrt(2 * Es_m);
308         velfactor = vs / v0;
309
310         dt = dst / (0.5 * (v0 + vs));
311         // this is not correct, but the differential equations have no analytic
312         // solution - and these times are very small anyway
313         //print("dt = ", ftos(dt), "\n");
314
315         self.W_BallisticBullet_LeaveSolid_think_save = self.think;
316         self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
317         self.think = W_BallisticBullet_LeaveSolid_think;
318         self.nextthink = time + dt;
319
320         vel = vel * velfactor;
321
322         self.velocity = '0 0 0';
323         self.flags |= FL_ONGROUND; // prevent moving
324         self.W_BallisticBullet_LeaveSolid_velocity = vel;
325
326         return 1;
327 }
328
329 void W_BallisticBullet_Touch (void)
330 {
331         if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
332                 return;
333
334         PROJECTILE_TOUCH;
335         W_BallisticBullet_Hit ();
336
337         // go through solid!
338         if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
339         {
340                 remove(self);
341                 return;
342         }
343
344         self.projectiledeathtype |= HITTYPE_BOUNCE;
345 }
346
347 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)
348 {
349         float lag, dt, savetime;
350         entity pl, oldself;
351
352         entity proj;
353         proj = spawn();
354         proj.classname = "bullet";
355         proj.owner = self;
356         PROJECTILE_MAKETRIGGER(proj);
357         if(gravityfactor > 0)
358         {
359                 proj.movetype = MOVETYPE_TOSS;
360                 proj.gravity = gravityfactor;
361         }
362         else
363                 proj.movetype = MOVETYPE_FLY;
364         proj.think = SUB_Remove;
365         proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
366         proj.velocity = (dir + randomvec() * spread) * pSpeed;
367         W_SetupProjectileVelocity(proj);
368         proj.angles = vectoangles(proj.velocity);
369         proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
370         // so: bulletconstant = bullet mass / area of bullet circle
371         setorigin(proj, start);
372         proj.flags = FL_PROJECTILE;
373
374         proj.touch = W_BallisticBullet_Touch;
375         proj.dmg = damage;
376         proj.dmg_edge = headshotbonus;
377         proj.dmg_force = force;
378         proj.projectiledeathtype = dtype;
379
380         proj.oldvelocity = proj.velocity;
381
382         //calculate fired bullets for ballistics
383         if not(self.isbot)
384         {
385                 self.bullets_fired[self.weapon] += 1;
386                 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
387         }
388
389         if(cvar("g_antilag_bullets"))
390         if(pSpeed >= cvar("g_antilag_bullets"))
391         {
392                 // NOTE: this may severely throw off weapon balance
393                 lag = ANTILAG_LATENCY(self);
394                 if(lag < 0.001)
395                         lag = 0;
396                 if(clienttype(self) != CLIENTTYPE_REAL)
397                         lag = 0;
398                 if(cvar("g_antilag") == 0)
399                         lag = 0; // only do hitscan, but no antilag
400
401                 if(lag)
402                         FOR_EACH_PLAYER(pl)
403                                 antilag_takeback(pl, time - lag);
404
405                 oldself = self;
406                 self = proj;
407
408                 savetime = frametime;
409                 frametime = 0.05;
410
411                 for(;;)
412                 {
413                         // DP tracetoss is stupid and always traces in 0.05s
414                         // ticks. This makes it trace in 0.05*0.125s ticks
415                         // instead.
416                         vector v0;
417                         float g0;
418                         v0 = self.velocity;
419                         g0 = self.gravity;
420                         self.velocity = self.velocity * 0.125;
421                         self.gravity *= 0.125 * 0.125;
422                         trace_fraction = 0;
423                         tracetoss(self, oldself);
424                         self.velocity = v0;
425                         self.gravity = g0;
426
427                         if not(self.isbot)
428                         {
429                                 self.bullets_fired[self.weapon] += 1;
430                                 self.maxdamage_fired = self.weapon + 64 * rint(self.bullets_fired[self.weapon]);
431                         }
432
433                         if(vlen(trace_endpos - self.origin) > 32)
434                                 zcurveparticles_from_tracetoss(particleeffectnum("tr_bullet"), self.origin, trace_endpos, self.velocity);
435                         if(trace_fraction == 1)
436                                 break;
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
440
441                         other = trace_ent;
442                         dt = vlen(trace_endpos - self.origin) / vlen(self.velocity); // this is only approximate!
443                         setorigin(self, trace_endpos);
444                         self.velocity_z -= sv_gravity * dt;
445
446                         if(!SUB_OwnerCheck())
447                         {
448                                 if(SUB_NoImpactCheck())
449                                         break;
450
451                                 // hit the player
452                                 W_BallisticBullet_Hit ();
453                         }
454
455                         // go through solid!
456                         if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))
457                                 break;
458
459                         W_BallisticBullet_LeaveSolid_think();
460                 }
461                 frametime = savetime;
462                 self = oldself;
463
464                 if(lag)
465                         FOR_EACH_PLAYER(pl)
466                                 antilag_restore(pl);
467
468                 remove(proj);
469
470                 return;
471         }
472
473         if(tracereffects & EF_RED)
474                 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
475         else
476                 CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
477 }
478
479
480 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
481 {
482         vector  end;
483         //local entity e;
484
485         dir = normalize(dir + randomvec() * spread);
486         end = start + dir * MAX_SHOT_DISTANCE;
487         if(self.antilag_debug)
488                 traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
489         else
490                 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
491
492     end = trace_endpos;
493
494         if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
495         {
496                 pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
497                 if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
498                         Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * force, dtype, self);
499                 Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
500                 //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
501         }
502         trace_endpos = end;
503 }
504
505
506 void W_PrepareExplosionByDamage(entity attacker, void() explode)
507 {
508         self.takedamage = DAMAGE_NO;
509         self.event_damage = SUB_Null;
510         self.owner = attacker;
511
512         // do not explode NOW but in the NEXT FRAME!
513         // because recursive calls to RadiusDamage are not allowed
514         self.nextthink = time;
515         self.think = explode;
516 }