]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_subs.qc
added dpm weapon support
[divverent/nexuiz.git] / data / qcsrc / server / g_subs.qc
1 void SUB_Null() {};
2
3 void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
4 void()  SUB_CalcMoveDone;
5 void() SUB_CalcAngleMoveDone;
6 //void() SUB_UseTargets;
7 void() SUB_Remove;
8
9 void spawnfunc_info_null (void)
10 {
11         remove(self);
12         // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
13 }
14
15 void setanim(entity e, vector anim, float looping, float override, float restart)
16 {
17         if (!restart)
18         if (anim_x == e.animstate_startframe)
19         if (anim_y == e.animstate_numframes)
20         if (anim_z == e.animstate_framerate)
21                 return;
22         e.animstate_startframe = anim_x;
23         e.animstate_numframes = anim_y;
24         e.animstate_framerate = anim_z;
25         e.animstate_starttime = time;
26         e.animstate_endtime = time + e.animstate_numframes / e.animstate_framerate;
27         e.animstate_looping = looping;
28         e.animstate_override = override;
29         e.frame = e.animstate_startframe;
30 };
31
32 void updateanim(entity e)
33 {
34         if (time >= e.animstate_endtime)
35         {
36                 if (e.animstate_looping)
37                 {
38                         e.animstate_starttime = e.animstate_endtime;
39                         e.animstate_endtime = e.animstate_endtime + e.animstate_numframes / e.animstate_framerate;
40                 }
41                 e.animstate_override = FALSE;
42         }
43         e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
44 };
45
46 float animparseerror;
47 vector animparseline(float animfile)
48 {
49         local string line;
50         local float c;
51         local vector anim;
52         if (animfile < 0)
53                 return '0 1 2';
54         line = fgets(animfile);
55         c = tokenize_sane(line);
56         if (c != 3)
57         {
58                 animparseerror = TRUE;
59                 return '0 1 2';
60         }
61         anim_x = stof(argv(0));
62         anim_y = stof(argv(1));
63         anim_z = stof(argv(2));
64         // don't allow completely bogus values
65         if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
66                 anim = '0 1 2';
67         return anim;
68 };
69
70 /*
71 ==================
72 SUB_Remove
73
74 Remove self
75 ==================
76 */
77 void SUB_Remove (void)
78 {
79         remove (self);
80 }
81
82 /*
83 ==================
84 SUB_Friction
85
86 Applies some friction to self
87 ==================
88 */
89 .float friction;
90 void SUB_Friction (void)
91 {
92         self.nextthink = time;
93         if(self.flags & FL_ONGROUND)
94                 self.velocity = self.velocity * (1 - frametime * self.friction);
95 }
96
97 /*
98 ==================
99 SUB_VanishOrRemove
100
101 Makes client invisible or removes non-client
102 ==================
103 */
104 void SUB_VanishOrRemove (entity ent)
105 {
106         if (ent.flags & FL_CLIENT)
107         {
108                 // vanish
109                 ent.model = "";
110                 ent.effects = 0;
111                 ent.glow_size = 0;
112                 ent.pflags = 0;
113         }
114         else
115         {
116                 // remove
117                 remove (ent);
118         }
119 }
120
121 void SUB_SetFade_Think (void)
122 {
123         self.think = SUB_SetFade_Think;
124         self.nextthink = self.fade_time;
125         self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
126         if (self.alpha < 0.01)
127                 SUB_VanishOrRemove(self);
128         self.alpha = bound(0.01, self.alpha, 1);
129 }
130
131 /*
132 ==================
133 SUB_SetFade
134
135 Fade 'ent' out when time >= 'when'
136 ==================
137 */
138 void SUB_SetFade (entity ent, float when, float fadetime)
139 {
140         //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
141         //      return;
142         //ent.alpha = 1;
143         ent.fade_rate = 1/fadetime;
144         ent.fade_time = when;
145         ent.think = SUB_SetFade_Think;
146         ent.nextthink = when;
147 }
148
149 /*
150 =============
151 SUB_CalcMove
152
153 calculate self.velocity and self.nextthink to reach dest from
154 self.origin traveling at speed
155 ===============
156 */
157 void SUB_CalcMoveDone (void)
158 {
159         // After moving, set origin to exact final destination
160
161         setorigin (self, self.finaldest);
162         self.velocity = '0 0 0';
163         self.nextthink = -1;
164         if (self.think1)
165                 self.think1 ();
166 }
167
168 void SUB_CalcMove (vector tdest, float tspeed, void() func)
169 {
170         vector  delta;
171         float   traveltime;
172
173         if (!tspeed)
174                 objerror ("No speed is defined!");
175
176         self.think1 = func;
177         self.finaldest = tdest;
178         self.think = SUB_CalcMoveDone;
179
180         if (tdest == self.origin)
181         {
182                 self.velocity = '0 0 0';
183                 self.nextthink = self.ltime + 0.1;
184                 return;
185         }
186
187         delta = tdest - self.origin;
188         traveltime = vlen (delta) / tspeed;
189
190         if (traveltime < 0.1)
191         {
192                 self.velocity = '0 0 0';
193                 self.nextthink = self.ltime + 0.1;
194                 return;
195         }
196
197         self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
198
199         self.nextthink = self.ltime + traveltime;
200 }
201
202 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
203 {
204         entity  oldself;
205
206         oldself = self;
207         self = ent;
208
209         SUB_CalcMove (tdest, tspeed, func);
210
211         self = oldself;
212 }
213
214 /*
215 =============
216 SUB_CalcAngleMove
217
218 calculate self.avelocity and self.nextthink to reach destangle from
219 self.angles rotating
220
221 The calling function should make sure self.think is valid
222 ===============
223 */
224 void SUB_CalcAngleMoveDone (void)
225 {
226         // After rotating, set angle to exact final angle
227         self.angles = self.finalangle;
228         self.avelocity = '0 0 0';
229         self.nextthink = -1;
230         if (self.think1)
231                 self.think1 ();
232 }
233
234 // FIXME: I fixed this function only for rotation around the main axes
235 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
236 {
237         vector  delta;
238         float   traveltime;
239
240         if (!tspeed)
241                 objerror ("No speed is defined!");
242         
243         // take the shortest distance for the angles
244         self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
245         self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
246         self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
247         delta = destangle - self.angles;
248         traveltime = vlen (delta) / tspeed;
249   
250         self.think1 = func;
251         self.finalangle = destangle;
252         self.think = SUB_CalcAngleMoveDone;
253
254         if (traveltime < 0.1)
255         {
256                 self.avelocity = '0 0 0';
257                 self.nextthink = self.ltime + 0.1;
258                 return;
259         }
260
261         self.avelocity = delta * (1 / traveltime);
262         self.nextthink = self.ltime + traveltime;
263 }
264
265 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
266 {
267         entity  oldself;
268
269         oldself = self;
270         self = ent;
271
272         SUB_CalcAngleMove (destangle, tspeed, func);
273
274         self = oldself;
275 }
276
277 /*
278 ==================
279 main
280
281 unused but required by the engine
282 ==================
283 */
284 void main (void)
285 {
286
287 }
288
289 // Misc
290
291 /*
292 ==================
293 traceline_antilag
294
295 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
296 Additionally it moves players back into the past before the trace and restores them afterward.
297 ==================
298 */
299 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
300 {
301         local entity player;
302         local float oldsolid;
303
304         // check whether antilagged traces are enabled
305         if (lag < 0.001)
306                 lag = 0;
307         if (clienttype(forent) != CLIENTTYPE_REAL)
308                 lag = 0; // only antilag for clients
309
310         // change shooter to SOLID_BBOX so the shot can hit corpses
311         oldsolid = source.dphitcontentsmask;
312         source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
313
314         if (lag)
315         {
316                 // take players back into the past
317                 player = player_list;
318                 while (player)
319                 {
320                         antilag_takeback(player, time - lag);
321                         player = player.nextplayer;
322                 }
323         }
324
325         // do the trace
326         traceline (v1, v2, nomonst, forent);
327
328         // restore players to current positions
329         if (lag)
330         {
331                 player = player_list;
332                 while (player)
333                 {
334                         antilag_restore(player);
335                         player = player.nextplayer;
336                 }
337         }
338
339         // restore shooter solid type
340         source.dphitcontentsmask = oldsolid;
341 }
342 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
343 {
344         if (cvar("g_antilag") != 2)
345                 lag = 0;
346         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
347 }
348
349 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
350 {
351         vector pos, dir, t;
352         float nudge;
353
354         //nudge = 2 * cvar("collision_impactnudge"); // why not?
355         nudge = 0.5;
356
357         dir = normalize(v2 - v1);
358
359         pos = v1 + dir * nudge;
360
361         float c;
362         c = 0;
363
364         for(;;)
365         {
366                 if((pos - v1) * dir >= (v2 - v1) * dir)
367                 {
368                         // went too far
369                         trace_fraction = 1;
370                         trace_endpos = v2;
371                         return c;
372                 }
373
374                 tracebox(pos, mi, ma, v2, nomonsters, forent);
375                 ++c;
376
377                 if(c == 50)
378                 {
379                         dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
380                         dprint("  Nudging gets us nowhere at ", vtos(pos), "\n");
381                         dprint("  trace_endpos is ", vtos(trace_endpos), "\n");
382                         dprint("  trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
383                 }
384
385                 if(trace_startsolid)
386                 {
387                         // we started inside solid.
388                         // then trace from endpos to pos
389                         t = trace_endpos;
390                         tracebox(t, mi, ma, pos, nomonsters, forent);
391                         ++c;
392                         if(trace_startsolid)
393                         {
394                                 // t is still inside solid? bad
395                                 // force advance, then, and retry
396                                 pos = t + dir * nudge;
397                         }
398                         else
399                         {
400                                 // we actually LEFT solid!
401                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
402                                 return c;
403                         }
404                 }
405                 else
406                 {
407                         // pos is outside solid?!? but why?!? never mind, just return it.
408                         trace_endpos = pos;
409                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
410                         return c;
411                 }
412         }
413 }
414
415 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
416 {
417 #if 0
418         vector pos, dir, t;
419         float nudge;
420
421         //nudge = 2 * cvar("collision_impactnudge"); // why not?
422         nudge = 0.5;
423
424         dir = normalize(v2 - v1);
425
426         pos = v1 + dir * nudge;
427
428         for(;;)
429         {
430                 if((pos - v1) * dir >= (v2 - v1) * dir)
431                 {
432                         // went too far
433                         trace_fraction = 1;
434                         return;
435                 }
436
437                 traceline(pos, v2, nomonsters, forent);
438
439                 if(trace_startsolid)
440                 {
441                         // we started inside solid.
442                         // then trace from endpos to pos
443                         t = trace_endpos;
444                         traceline(t, pos, nomonsters, forent);
445                         if(trace_startsolid)
446                         {
447                                 // t is inside solid? bad
448                                 // force advance, then
449                                 pos = pos + dir * nudge;
450                         }
451                         else
452                         {
453                                 // we actually LEFT solid!
454                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
455                                 return;
456                         }
457                 }
458                 else
459                 {
460                         // pos is outside solid?!? but why?!? never mind, just return it.
461                         trace_endpos = pos;
462                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
463                         return;
464                 }
465         }
466 #else
467         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
468 }
469
470 /*
471 ==================
472 findbetterlocation
473
474 Returns a point at least 12 units away from walls
475 (useful for explosion animations, although the blast is performed where it really happened)
476 Ripped from DPMod
477 ==================
478 */
479 vector findbetterlocation (vector org, float mindist)
480 {
481         vector  loc;
482         vector vec;
483         float c, h;
484
485         vec = mindist * '1 0 0';
486         c = 0;
487         while (c < 6)
488         {
489                 traceline (org, org + vec, TRUE, world);
490                 vec = vec * -1;
491                 if (trace_fraction < 1)
492                 {
493                         loc = trace_endpos;
494                         traceline (loc, loc + vec, TRUE, world);
495                         if (trace_fraction >= 1)
496                                 org = loc + vec;
497                 }
498                 if (c & 1)
499                 {
500                         h = vec_y;
501                         vec_y = vec_x;
502                         vec_x = vec_z;
503                         vec_z = h;
504                 }
505                 c = c + 1;
506         }
507
508         return org;
509 }
510
511 /*
512 ==================
513 crandom
514
515 Returns a random number between -1.0 and 1.0
516 ==================
517 */
518 float crandom (void)
519 {
520         return 2 * (random () - 0.5);
521 }
522
523 /*
524 ==================
525 Angc used for animations
526 ==================
527 */
528
529
530 float angc (float a1, float a2)
531 {
532         float   a;
533
534         while (a1 > 180)
535                 a1 = a1 - 360;
536         while (a1 < -179)
537                 a1 = a1 + 360;
538
539         while (a2 > 180)
540                 a2 = a2 - 360;
541         while (a2 < -179)
542                 a2 = a2 + 360;
543
544         a = a1 - a2;
545         while (a > 180)
546                 a = a - 360;
547         while (a < -179)
548                 a = a + 360;
549
550         return a;
551 }
552
553 .string lodtarget1;
554 .string lodtarget2;
555 .string lodmodel1;
556 .string lodmodel2;
557 .float lodmodelindex0;
558 .float lodmodelindex1;
559 .float lodmodelindex2;
560 .float loddistance1;
561 .float loddistance2;
562
563 vector NearestPointOnBox(entity box, vector org);
564 float LOD_customize()
565 {
566         float d;
567
568         if(cvar("loddebug"))
569         {
570                 d = cvar("loddebug");
571                 if(d == 1)
572                         self.modelindex = self.lodmodelindex0;
573                 else if(d == 2)
574                         self.modelindex = self.lodmodelindex1;
575                 else // if(d == 3)
576                         self.modelindex = self.lodmodelindex2;
577                 return TRUE;
578         }
579
580         // TODO csqc network this so it only gets sent once
581         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
582         if(d < self.loddistance1)
583                 self.modelindex = self.lodmodelindex0;
584         else if(!self.lodmodelindex2 || d < self.loddistance2)
585                 self.modelindex = self.lodmodelindex1;
586         else
587                 self.modelindex = self.lodmodelindex2;
588
589         return TRUE;
590 }
591
592 void LOD_uncustomize()
593 {
594         self.modelindex = self.lodmodelindex0;
595 }
596
597 void LODmodel_attach()
598 {
599         entity e;
600
601         if(!self.loddistance1)
602                 self.loddistance1 = 1000;
603         if(!self.loddistance2)
604                 self.loddistance2 = 2000;
605         self.lodmodelindex0 = self.modelindex;
606
607         if(self.lodtarget1 != "")
608         {
609                 e = find(world, targetname, self.lodtarget1);
610                 if(e)
611                 {
612                         self.lodmodel1 = e.model;
613                         remove(e);
614                 }
615         }
616         if(self.lodtarget2 != "")
617         {
618                 e = find(world, targetname, self.lodtarget2);
619                 if(e)
620                 {
621                         self.lodmodel2 = e.model;
622                         remove(e);
623                 }
624         }
625
626         if(cvar("loddebug") < 0)
627         {
628                 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
629         }
630
631         if(self.lodmodel1 != "")
632         {
633                 vector mi, ma;
634                 mi = self.mins;
635                 ma = self.maxs;
636
637                 precache_model(self.lodmodel1);
638                 setmodel(self, self.lodmodel1);
639                 self.lodmodelindex1 = self.modelindex;
640
641                 if(self.lodmodel2 != "")
642                 {
643                         precache_model(self.lodmodel2);
644                         setmodel(self, self.lodmodel2);
645                         self.lodmodelindex2 = self.modelindex;
646                 }
647
648                 self.modelindex = self.lodmodelindex0;
649                 setsize(self, mi, ma);
650         }
651
652         if(self.lodmodelindex1)
653                 SetCustomizer(self, LOD_customize, LOD_uncustomize);
654 }
655
656 void SetBrushEntityModel()
657 {
658         if(self.model != "")
659         {
660                 precache_model(self.model);
661                 setmodel(self, self.model); // no precision needed
662                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
663         }
664         setorigin(self, self.origin);
665         if(self.scale)
666                 setsize(self, self.mins * self.scale, self.maxs * self.scale);
667         else
668                 setsize(self, self.mins, self.maxs);
669 }
670
671 /*
672 ================
673 InitTrigger
674 ================
675 */
676
677 void SetMovedir()
678 {
679         if (self.movedir != '0 0 0')
680                 self.movedir = normalize(self.movedir);
681         else
682         {
683                 if (self.angles == '0 -1 0')
684                         self.movedir = '0 0 1';
685                 else if (self.angles == '0 -2 0')
686                         self.movedir = '0 0 -1';
687                 else
688                 {
689                         makevectors (self.angles);
690                         self.movedir = v_forward;
691                 }
692         }
693
694         self.angles = '0 0 0';
695 };
696
697 void InitTrigger()
698 {
699 // trigger angles are used for one-way touches.  An angle of 0 is assumed
700 // to mean no restrictions, so use a yaw of 360 instead.
701         if (self.movedir == '0 0 0')
702         if (self.angles != '0 0 0')
703                 SetMovedir ();
704         self.solid = SOLID_TRIGGER;
705         SetBrushEntityModel();
706         self.movetype = MOVETYPE_NONE;
707         self.modelindex = 0;
708         self.model = "";
709 };
710
711 void InitSolidBSPTrigger()
712 {
713 // trigger angles are used for one-way touches.  An angle of 0 is assumed
714 // to mean no restrictions, so use a yaw of 360 instead.
715         if (self.movedir == '0 0 0')
716         if (self.angles != '0 0 0')
717                 SetMovedir ();
718         self.solid = SOLID_BSP;
719         SetBrushEntityModel();
720         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
721 //      self.modelindex = 0;
722         self.model = "";
723 };
724
725 float InitMovingBrushTrigger()
726 {
727 // trigger angles are used for one-way touches.  An angle of 0 is assumed
728 // to mean no restrictions, so use a yaw of 360 instead.
729         self.solid = SOLID_BSP;
730         SetBrushEntityModel();
731         self.movetype = MOVETYPE_PUSH;
732         if(self.modelindex == 0)
733         {
734                 objerror("InitMovingBrushTrigger: no brushes found!");
735                 return 0;
736         }
737         return 1;
738 };