]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/w_common.qc
Initial checkout of Vore Tournament 0.1.alpha.
[voretournament/voretournament.git] / data / qcsrc / server / w_common.qc
1 void W_GiveWeapon (entity e, float wep, string name)\r
2 {\r
3         entity oldself;\r
4 \r
5         if (!wep)\r
6                 return;\r
7 \r
8         e.weapons = e.weapons | W_WeaponBit(wep);\r
9 \r
10         oldself = self;\r
11         self = e;\r
12 \r
13         if (other.classname == "player")\r
14         {\r
15                 sprint (other, "You got the ^2");\r
16                 sprint (other, name);\r
17                 sprint (other, "\n");\r
18         }\r
19 \r
20         self = oldself;\r
21 }\r
22 \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
25 {\r
26         local vector hitloc, force, endpoint, dir;\r
27         local entity ent, endent;\r
28         local float endq3surfaceflags;\r
29 \r
30         float length;\r
31         float f, ffs;\r
32 \r
33         float hit;\r
34 \r
35         railgun_start = start;\r
36         railgun_end = end;\r
37 \r
38         dir = normalize(end - start);\r
39         length = vlen(end - start);\r
40         force = dir * bforce;\r
41 \r
42         // go a little bit into the wall because we need to hit this wall later\r
43         end = end + dir;\r
44 \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
48         while (1)\r
49         {\r
50                 if(self.antilag_debug)\r
51                         WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);\r
52                 else\r
53                         WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));\r
54 \r
55                 // if it is world we can't hurt it so stop now\r
56                 if (trace_ent == world || trace_fraction == 1)\r
57                         break;\r
58 \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
64 \r
65                 // stop if this is a wall\r
66                 if (trace_ent.solid == SOLID_BSP)\r
67                         break;\r
68 \r
69                 // make the entity non-solid\r
70                 trace_ent.solid = SOLID_NOT;\r
71         }\r
72 \r
73         endpoint = trace_endpos;\r
74         endent = trace_ent;\r
75         endq3surfaceflags = trace_dphitq3surfaceflags;\r
76 \r
77         // find all the entities the railgun hit and restore their solid state\r
78         ent = findfloat(world, railgunhit, TRUE);\r
79         while (ent)\r
80         {\r
81                 // restore their solid type\r
82                 ent.solid = ent.railgunhitsolidbackup;\r
83                 ent = findfloat(ent, railgunhit, TRUE);\r
84         }\r
85 \r
86         // spawn a temporary explosion entity for RadiusDamage calls\r
87         //explosion = spawn();\r
88 \r
89         // find all the entities the railgun hit and hurt them\r
90         ent = findfloat(world, railgunhit, TRUE);\r
91         while (ent)\r
92         {\r
93                 // get the details we need to call the damage function\r
94                 hitloc = ent.railgunhitloc;\r
95 \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
99                         hit = 1;\r
100 \r
101                 if(teams_matter)\r
102                 if(ent.team == self.team)\r
103                         hit = 0;\r
104 \r
105                 f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);\r
106                 ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);\r
107 \r
108                 // apply the damage\r
109                 if (ent.takedamage)\r
110                         Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);\r
111 \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
115 \r
116                 ent.railgunhitloc = '0 0 0';\r
117                 ent.railgunhitsolidbackup = SOLID_NOT;\r
118                 ent.railgunhit = FALSE;\r
119                 ent.railgundistance = 0;\r
120 \r
121                 // advance to the next entity\r
122                 ent = findfloat(ent, railgunhit, TRUE);\r
123         }\r
124 \r
125         // calculate hits and fired shots for hitscan\r
126         if not(inWarmupStage)\r
127         {\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
130 \r
131                 if(hit) {\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
134                 }\r
135         }\r
136 \r
137         trace_endpos = endpoint;\r
138         trace_ent = endent;\r
139         trace_dphitq3surfaceflags = endq3surfaceflags;\r
140 }\r
141 \r
142 .float dmg_edge;\r
143 .float dmg_force;\r
144 .float dmg_radius;\r
145 void W_BallisticBullet_Hit (void)\r
146 {\r
147         float f;\r
148 \r
149         f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier\r
150 \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
153 \r
154         if(other && other != self.enemy)\r
155         {\r
156                 endzcurveparticles();\r
157 \r
158                 headshot = 0;\r
159                 yoda = 0;\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
163 \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
166 \r
167                 if(self.dmg_edge != 0)\r
168                 {\r
169                         if(headshot)\r
170                                 AnnounceTo(self.owner, "headshot");\r
171                         if(yoda)\r
172                                 AnnounceTo(self.owner, "awesome");\r
173                 }\r
174 \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
180                 {\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
183                 }\r
184         }\r
185 \r
186         self.enemy = other; // don't hit the same player twice with the same bullet\r
187 }\r
188 \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
193 \r
194 void W_BallisticBullet_LeaveSolid_think()\r
195 {\r
196         setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);\r
197         self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;\r
198 \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
202 \r
203         self.flags &~= FL_ONGROUND;\r
204 \r
205         if(self.enemy.solid == SOLID_BSP)\r
206         {\r
207                 float f;\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
210         }\r
211 \r
212         UpdateCSQCProjectile(self);\r
213 }\r
214 \r
215 float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant)\r
216 {\r
217         // move the entity along its velocity until it's out of solid, then let it resume\r
218 \r
219         float dt, dst, velfactor, v0, vs;\r
220         float maxdist;\r
221         float E0_m, Es_m;\r
222 \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
225                 return 0;\r
226 \r
227         // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass\r
228         v0 = vlen(vel);\r
229 \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
234 \r
235         if(maxdist <= cvar("g_ballistics_mindistance"))\r
236                 return 0;\r
237 \r
238         traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);\r
239 \r
240         if(trace_fraction == 1) // 1: we never got out of solid\r
241                 return 0;\r
242 \r
243         self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;\r
244 \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
248         if(Es_m <= 0)\r
249         {\r
250                 // roundoff errors got us\r
251                 return 0;\r
252         }\r
253         vs = sqrt(2 * Es_m);\r
254         velfactor = vs / v0;\r
255 \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
260 \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
265 \r
266         vel = vel * velfactor;\r
267 \r
268         self.velocity = '0 0 0';\r
269         self.flags |= FL_ONGROUND; // prevent moving\r
270         self.W_BallisticBullet_LeaveSolid_velocity = vel;\r
271 \r
272         return 1;\r
273 }\r
274 \r
275 void W_BallisticBullet_Touch (void)\r
276 {\r
277         if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!\r
278                 return;\r
279 \r
280         PROJECTILE_TOUCH;\r
281         W_BallisticBullet_Hit ();\r
282 \r
283         // go through solid!\r
284         if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))\r
285         {\r
286                 remove(self);\r
287                 return;\r
288         }\r
289 \r
290         self.projectiledeathtype |= HITTYPE_BOUNCE;\r
291 }\r
292 \r
293 void endFireBallisticBullet()\r
294 {\r
295         endzcurveparticles();\r
296 }\r
297 \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
301 {\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
304 }\r
305 \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
307 {\r
308         float lag, dt, savetime;\r
309         entity pl, oldself;\r
310 \r
311         entity proj;\r
312         proj = spawn();\r
313         proj.classname = "bullet";\r
314         proj.owner = self;\r
315         PROJECTILE_MAKETRIGGER(proj);\r
316         if(gravityfactor > 0)\r
317         {\r
318                 proj.movetype = MOVETYPE_TOSS;\r
319                 proj.gravity = gravityfactor;\r
320         }\r
321         else\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
331 \r
332         proj.touch = W_BallisticBullet_Touch;\r
333         proj.dmg = damage;\r
334         proj.dmg_edge = headshotbonus;\r
335         proj.dmg_force = force;\r
336         proj.projectiledeathtype = dtype;\r
337 \r
338         proj.oldvelocity = proj.velocity;\r
339 \r
340         if(cvar("g_antilag_bullets"))\r
341         if(pSpeed >= cvar("g_antilag_bullets"))\r
342         {\r
343                 float eff;\r
344 \r
345                 if(tracereffects & EF_RED)\r
346                         eff = particleeffectnum("tr_rifle");\r
347                 else\r
348                         eff = particleeffectnum("tr_bullet");\r
349 \r
350                 // NOTE: this may severely throw off weapon balance\r
351                 lag = ANTILAG_LATENCY(self);\r
352                 if(lag < 0.001)\r
353                         lag = 0;\r
354                 if(clienttype(self) != CLIENTTYPE_REAL)\r
355                         lag = 0;\r
356                 if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)\r
357                         lag = 0; // only do hitscan, but no antilag\r
358 \r
359                 if(lag)\r
360                         FOR_EACH_PLAYER(pl)\r
361                                 antilag_takeback(pl, time - lag);\r
362 \r
363                 oldself = self;\r
364                 self = proj;\r
365 \r
366                 savetime = frametime;\r
367                 frametime = 0.05;\r
368 \r
369                 // update the accuracy stats - increase shots fired by 1\r
370                 if not(inWarmupStage)\r
371                 {\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
374                 }\r
375 \r
376                 for(;;)\r
377                 {\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
380                         // instead.\r
381                         vector v0;\r
382                         float g0;\r
383                         v0 = self.velocity;\r
384                         g0 = self.gravity;\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
392                         self.gravity = g0;\r
393 \r
394                         if(trace_fraction == 1)\r
395                                 break;\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
399 \r
400                         other = trace_ent;\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
404 \r
405                         if(!SUB_OwnerCheck())\r
406                         {\r
407                                 if(SUB_NoImpactCheck())\r
408                                         break;\r
409 \r
410                                 // hit the player\r
411                                 W_BallisticBullet_Hit();\r
412                         }\r
413 \r
414                         // go through solid!\r
415                         if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius))\r
416                                 break;\r
417 \r
418                         W_BallisticBullet_LeaveSolid_think();\r
419                 }\r
420                 frametime = savetime;\r
421                 self = oldself;\r
422 \r
423                 if(lag)\r
424                         FOR_EACH_PLAYER(pl)\r
425                                 antilag_restore(pl);\r
426 \r
427                 remove(proj);\r
428 \r
429                 return;\r
430         }\r
431 \r
432         // update the accuracy stats\r
433         if not(inWarmupStage)\r
434         {\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
437         }\r
438 \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
443         else\r
444                 CSQCProjectile(proj, TRUE, PROJECTILE_EXAMPLE, TRUE);\r
445 }\r
446 \r
447 void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)\r
448 {\r
449         vector  end;\r
450 \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
455         else\r
456                 traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));\r
457 \r
458         end = trace_endpos;\r
459 \r
460         if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))\r
461         {\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
467         }\r
468         trace_endpos = end;\r
469 }\r
470 \r
471 void W_PrepareExplosionByDamage(entity attacker, void() explode)\r
472 {\r
473         self.takedamage = DAMAGE_NO;\r
474         self.event_damage = SUB_Null;\r
475         self.owner = attacker;\r
476 \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
481 }\r