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