]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_subs.qc
bugfix target_spawn, and change docs to match it
[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 /*
16 ==================
17 SUB_Remove
18
19 Remove self
20 ==================
21 */
22 void SUB_Remove (void)
23 {
24         remove (self);
25 }
26
27 /*
28 ==================
29 SUB_VanishOrRemove
30
31 Makes client invisible or removes non-client
32 ==================
33 */
34 void SUB_VanishOrRemove (entity ent)
35 {
36         if (ent.flags & FL_CLIENT)
37         {
38                 // vanish
39                 ent.model = "";
40                 ent.effects = 0;
41                 ent.glow_size = 0;
42                 ent.pflags = 0;
43         }
44         else
45         {
46                 // remove
47                 remove (ent);
48         }
49 }
50
51 void SUB_SetFade_Think (void)
52 {
53         self.think = SUB_SetFade_Think;
54         self.nextthink = self.fade_time;
55         self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
56         if (self.alpha < 0.01)
57                 SUB_VanishOrRemove(self);
58         self.alpha = bound(0.01, self.alpha, 1);
59 }
60
61 /*
62 ==================
63 SUB_SetFade
64
65 Fade 'ent' out when time >= 'when'
66 ==================
67 */
68 void SUB_SetFade (entity ent, float when, float fadetime)
69 {
70         //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
71         //      return;
72         //ent.alpha = 1;
73         ent.fade_rate = 1/fadetime;
74         ent.fade_time = when;
75         ent.think = SUB_SetFade_Think;
76         ent.nextthink = when;
77 }
78
79 /*
80 =============
81 SUB_CalcMove
82
83 calculate self.velocity and self.nextthink to reach dest from
84 self.origin traveling at speed
85 ===============
86 */
87 void SUB_CalcMoveDone (void)
88 {
89         // After moving, set origin to exact final destination
90
91         setorigin (self, self.finaldest);
92         self.velocity = '0 0 0';
93         self.nextthink = -1;
94         if (self.think1)
95                 self.think1 ();
96 }
97
98 void SUB_CalcMove (vector tdest, float tspeed, void() func)
99 {
100         vector  delta;
101         float   traveltime;
102
103         if (!tspeed)
104                 objerror ("No speed is defined!");
105
106         self.think1 = func;
107         self.finaldest = tdest;
108         self.think = SUB_CalcMoveDone;
109
110         if (tdest == self.origin)
111         {
112                 self.velocity = '0 0 0';
113                 self.nextthink = self.ltime + 0.1;
114                 return;
115         }
116
117         delta = tdest - self.origin;
118         traveltime = vlen (delta) / tspeed;
119
120         if (traveltime < 0.1)
121         {
122                 self.velocity = '0 0 0';
123                 self.nextthink = self.ltime + 0.1;
124                 return;
125         }
126
127         self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
128
129         self.nextthink = self.ltime + traveltime;
130 }
131
132 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
133 {
134         entity  oldself;
135
136         oldself = self;
137         self = ent;
138
139         SUB_CalcMove (tdest, tspeed, func);
140
141         self = oldself;
142 }
143
144 /*
145 =============
146 SUB_CalcAngleMove
147
148 calculate self.avelocity and self.nextthink to reach destangle from
149 self.angles rotating
150
151 The calling function should make sure self.think is valid
152 ===============
153 */
154 void SUB_CalcAngleMoveDone (void)
155 {
156         // After rotating, set angle to exact final angle
157         self.angles = self.finalangle;
158         self.avelocity = '0 0 0';
159         self.nextthink = -1;
160         if (self.think1)
161                 self.think1 ();
162 }
163
164 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
165 {
166         vector  delta;
167         float   traveltime;
168
169         if (!tspeed)
170                 objerror ("No speed is defined!");
171
172         delta = destangle - self.angles;
173         traveltime = vlen (delta) / tspeed;
174
175         self.avelocity = delta * (1 / traveltime);
176
177         self.think1 = func;
178         self.finalangle = destangle;
179
180         self.think = SUB_CalcAngleMoveDone;
181         self.nextthink = self.ltime + traveltime;
182 }
183
184 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
185 {
186         entity  oldself;
187
188         oldself = self;
189         self = ent;
190
191         SUB_CalcAngleMove (destangle, tspeed, func);
192
193         self = oldself;
194 }
195
196 /*
197 ==================
198 main
199
200 unused but required by the engine
201 ==================
202 */
203 void main (void)
204 {
205
206 }
207
208 // Misc
209
210 /*
211 ==================
212 traceline_antilag
213
214 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
215 Additionally it moves players back into the past before the trace and restores them afterward.
216 ==================
217 */
218 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
219 {
220         local entity player;
221         local float oldsolid;
222
223         // check whether antilagged traces are enabled
224         if (lag < 0.001)
225                 lag = 0;
226         if (clienttype(forent) != CLIENTTYPE_REAL)
227                 lag = 0; // only antilag for clients
228
229         // change shooter to SOLID_BBOX so the shot can hit corpses
230         oldsolid = source.solid;
231         source.solid = SOLID_BBOX;
232
233         if (lag)
234         {
235                 // take players back into the past
236                 player = player_list;
237                 while (player)
238                 {
239                         antilag_takeback(player, time - lag);
240                         player = player.nextplayer;
241                 }
242         }
243
244         // do the trace
245         traceline (v1, v2, nomonst, forent);
246
247         // restore players to current positions
248         if (lag)
249         {
250                 player = player_list;
251                 while (player)
252                 {
253                         antilag_restore(player);
254                         player = player.nextplayer;
255                 }
256         }
257
258         // restore shooter solid type
259         source.solid = oldsolid;
260 }
261 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
262 {
263         if (cvar("g_antilag") != 2)
264                 lag = 0;
265         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
266 }
267
268 /*
269 ==================
270 findbetterlocation
271
272 Returns a point at least 12 units away from walls
273 (useful for explosion animations, although the blast is performed where it really happened)
274 Ripped from DPMod
275 ==================
276 */
277 vector findbetterlocation (vector org, float mindist)
278 {
279         vector  loc;
280         vector vec;
281         float c;
282
283         vec = mindist * '1 0 0';
284         c = 0;
285         while (c < 6)
286         {
287                 traceline (org, org + vec, TRUE, world);
288                 vec = vec * -1;
289                 if (trace_fraction < 1)
290                 {
291                         loc = trace_endpos;
292                         traceline (loc, loc + vec, TRUE, world);
293                         if (trace_fraction >= 1)
294                                 org = loc + vec;
295                 }
296                 if (c & 1)
297                 {
298                         vec_z = vec_y;
299                         vec_y = vec_x;
300                         vec_x = vec_z;
301                 }
302                 c = c + 1;
303         }
304
305         return org;
306 }
307
308 /*
309 ==================
310 crandom
311
312 Returns a random number between -1.0 and 1.0
313 ==================
314 */
315 float crandom (void)
316 {
317         return 2 * (random () - 0.5);
318 }
319
320 /*
321 ==================
322 Angc used for animations
323 ==================
324 */
325
326
327 float angc (float a1, float a2)
328 {
329         float   a;
330
331         while (a1 > 180)
332                 a1 = a1 - 360;
333         while (a1 < -179)
334                 a1 = a1 + 360;
335
336         while (a2 > 180)
337                 a2 = a2 - 360;
338         while (a2 < -179)
339                 a2 = a2 + 360;
340
341         a = a1 - a2;
342         while (a > 180)
343                 a = a - 360;
344         while (a < -179)
345                 a = a + 360;
346
347         return a;
348 }
349
350 .string lodtarget1;
351 .string lodtarget2;
352 .string lodmodel1;
353 .string lodmodel2;
354 .float lodmodelindex0;
355 .float lodmodelindex1;
356 .float lodmodelindex2;
357 .float loddistance1;
358 .float loddistance2;
359
360 float LOD_customize()
361 {
362         float d;
363
364         // TODO csqc network this so it only gets sent once
365         d = vlen(self.origin - other.origin);
366         if(d < self.loddistance1)
367                 self.modelindex = self.lodmodelindex0;
368         else if(!self.lodmodelindex2 || d < self.loddistance2)
369                 self.modelindex = self.lodmodelindex1;
370         else
371                 self.modelindex = self.lodmodelindex2;
372
373         return TRUE;
374 }
375
376 void LOD_uncustomize()
377 {
378         self.modelindex = self.lodmodelindex0;
379 }
380
381 void LODmodel_attach()
382 {
383         entity e;
384
385         if(!self.loddistance1)
386                 self.loddistance1 = 1000;
387         if(!self.loddistance2)
388                 self.loddistance2 = 2000;
389         self.lodmodelindex0 = self.modelindex;
390
391         if(self.lodtarget1 != "")
392         {
393                 e = find(world, targetname, self.lodtarget1);
394                 if(e)
395                 {
396                         self.lodmodel1 = e.model;
397                         remove(e);
398                 }
399         }
400         if(self.lodtarget2 != "")
401         {
402                 e = find(world, targetname, self.lodtarget2);
403                 if(e)
404                 {
405                         self.lodmodel2 = e.model;
406                         remove(e);
407                 }
408         }
409
410         if(self.lodmodel1 != "")
411         {
412                 vector mi, ma;
413                 mi = self.mins;
414                 ma = self.maxs;
415
416                 precache_model(self.lodmodel1);
417                 setmodel(self, self.lodmodel1);
418                 self.lodmodelindex1 = self.modelindex;
419
420                 if(self.lodmodel2 != "")
421                 {
422                         precache_model(self.lodmodel2);
423                         setmodel(self, self.lodmodel2);
424                         self.lodmodelindex2 = self.modelindex;
425                 }
426
427                 self.modelindex = self.lodmodelindex0;
428                 setsize(self, mi, ma);
429         }
430
431         if(self.lodmodelindex1)
432                 SetCustomizer(self, LOD_customize, LOD_uncustomize);
433 }
434
435 void SetBrushEntityModel()
436 {
437         if(self.model != "")
438         {
439                 precache_model(self.model);
440                 setmodel(self, self.model); // no precision needed
441                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
442         }
443         setorigin(self, self.origin);
444         setsize(self, self.mins, self.maxs);
445 }
446
447 /*
448 ================
449 InitTrigger
450 ================
451 */
452
453 void SetMovedir()
454 {
455         if (self.movedir != '0 0 0')
456                 self.movedir = normalize(self.movedir);
457         else
458         {
459                 if (self.angles == '0 -1 0')
460                         self.movedir = '0 0 1';
461                 else if (self.angles == '0 -2 0')
462                         self.movedir = '0 0 -1';
463                 else
464                 {
465                         makevectors (self.angles);
466                         self.movedir = v_forward;
467                 }
468         }
469
470         self.angles = '0 0 0';
471 };
472
473 void InitTrigger()
474 {
475 // trigger angles are used for one-way touches.  An angle of 0 is assumed
476 // to mean no restrictions, so use a yaw of 360 instead.
477         if (self.movedir == '0 0 0')
478         if (self.angles != '0 0 0')
479                 SetMovedir ();
480         self.solid = SOLID_TRIGGER;
481         SetBrushEntityModel();
482         self.movetype = MOVETYPE_NONE;
483         self.modelindex = 0;
484         self.model = "";
485 };
486
487 void InitSolidBSPTrigger()
488 {
489 // trigger angles are used for one-way touches.  An angle of 0 is assumed
490 // to mean no restrictions, so use a yaw of 360 instead.
491         if (self.movedir == '0 0 0')
492         if (self.angles != '0 0 0')
493                 SetMovedir ();
494         self.solid = SOLID_BSP;
495         SetBrushEntityModel();
496         self.movetype = MOVETYPE_PUSH;
497 //      self.modelindex = 0;
498         self.model = "";
499 };
500
501 void InitMovingBrushTrigger()
502 {
503 // trigger angles are used for one-way touches.  An angle of 0 is assumed
504 // to mean no restrictions, so use a yaw of 360 instead.
505         self.solid = SOLID_BSP;
506         SetBrushEntityModel();
507         self.movetype = MOVETYPE_PUSH;
508 };