]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/warpzonelib/common.qc
Rename defs to qh
[xonotic/xonotic-data.pk3dir.git] / qcsrc / warpzonelib / common.qc
1 #if defined(CSQC)
2         #include "../dpdefs/csprogsdefs.qh"
3     #include "common.qh"
4     #include "../server/t_items.qh"
5 #elif defined(MENUQC)
6 #elif defined(SVQC)
7         #include "../dpdefs/progsdefs.qh"
8     #include "../dpdefs/dpextensions.qh"
9     #include "../server/sys-post.qh"
10     #include "common.qh"
11     #include "../common/weapons/weapons.qh"
12 #endif
13
14 void WarpZone_Accumulator_Clear(entity acc)
15 {
16         acc.warpzone_transform = '0 0 0';
17         acc.warpzone_shift = '0 0 0';
18 }
19 void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s)
20 {
21         vector tr, st;
22         tr = AnglesTransform_Multiply(t, acc.warpzone_transform);
23         st = AnglesTransform_Multiply_GetPostShift(t, s, acc.warpzone_transform, acc.warpzone_shift);
24         acc.warpzone_transform = tr;
25         acc.warpzone_shift = st;
26 }
27 void WarpZone_Accumulator_Add(entity acc, entity wz)
28 {
29         WarpZone_Accumulator_AddTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
30 }
31 void WarpZone_Accumulator_AddInverseTransform(entity acc, vector t, vector s)
32 {
33         vector tt, ss;
34         tt = AnglesTransform_Invert(t);
35         ss = AnglesTransform_PrePostShift_GetPostShift(s, tt, '0 0 0');
36         WarpZone_Accumulator_AddTransform(acc, tt, ss);
37         // yes, this probably can be done simpler... but this way is "obvious" :)
38 }
39 void WarpZone_Accumulator_AddInverse(entity acc, entity wz)
40 {
41         WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
42 }
43
44 .vector(vector, vector) camera_transform;
45 float autocvar_cl_warpzone_usetrace = 1;
46 vector WarpZone_camera_transform(vector org, vector ang)
47 {
48         vector vf, vr, vu;
49         if(self.warpzone_fadestart)
50                 if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
51                         return org;
52                         // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
53                         // unneeded on client, on server this helps a lot
54         vf = v_forward;
55         vr = v_right;
56         vu = v_up;
57         org = WarpZone_TransformOrigin(self, org);
58         vf = WarpZone_TransformVelocity(self, vf);
59         vr = WarpZone_TransformVelocity(self, vr);
60         vu = WarpZone_TransformVelocity(self, vu);
61         if(autocvar_cl_warpzone_usetrace)
62                 traceline(self.warpzone_targetorigin, org, MOVE_NOMONSTERS, world);
63         else
64                 trace_endpos = self.warpzone_targetorigin;
65         v_forward = vf;
66         v_right = vr;
67         v_up = vu;
68         return org;
69 }
70
71 void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang)
72 {
73         e.warpzone_transform = AnglesTransform_RightDivide(other_ang, AnglesTransform_TurnDirectionFR(my_ang));
74         e.warpzone_shift = AnglesTransform_PrePostShift_GetPostShift(my_org, e.warpzone_transform, other_org);
75         e.warpzone_origin = my_org;
76         e.warpzone_targetorigin = other_org;
77         e.warpzone_angles = my_ang;
78         e.warpzone_targetangles = other_ang;
79         fixedmakevectors(my_ang); e.warpzone_forward = v_forward;
80         fixedmakevectors(other_ang); e.warpzone_targetforward = v_forward;
81         e.camera_transform = WarpZone_camera_transform;
82 }
83
84 vector WarpZone_Camera_camera_transform(vector org, vector ang)
85 {
86         // a fixed camera view
87         if(self.warpzone_fadestart)
88                 if(vlen(org - self.origin - 0.5 * (self.mins + self.maxs)) > self.warpzone_fadeend + 400)
89                         return org;
90                         // don't transform if zone faded out (plus 400qu safety margin for typical speeds and latencies)
91                         // unneeded on client, on server this helps a lot
92         trace_endpos = self.warpzone_origin;
93         makevectors(self.warpzone_angles);
94         return self.warpzone_origin;
95 }
96
97 void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang) // we assume that e.oldorigin and e.avelocity point to view origin and direction
98 {
99         e.warpzone_origin = my_org;
100         e.warpzone_angles = my_ang;
101         e.camera_transform = WarpZone_Camera_camera_transform;
102 }
103
104 .entity enemy;
105
106 vector WarpZoneLib_BoxTouchesBrush_mins;
107 vector WarpZoneLib_BoxTouchesBrush_maxs;
108 entity WarpZoneLib_BoxTouchesBrush_ent;
109 entity WarpZoneLib_BoxTouchesBrush_ignore;
110 float WarpZoneLib_BoxTouchesBrush_Recurse()
111 {
112         float s;
113         entity se;
114         float f;
115
116         tracebox('0 0 0', WarpZoneLib_BoxTouchesBrush_mins, WarpZoneLib_BoxTouchesBrush_maxs, '0 0 0', MOVE_NOMONSTERS, WarpZoneLib_BoxTouchesBrush_ignore);
117 #ifdef CSQC
118         if (trace_networkentity)
119         {
120                 dprint("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n");
121                 // we cannot continue, as a player blocks us...
122                 // so, abort
123                 return 0;
124         }
125 #endif
126         if (!trace_ent)
127                 return 0;
128         if (trace_ent == WarpZoneLib_BoxTouchesBrush_ent)
129                 return 1;
130
131         se = trace_ent;
132         s = se.solid;
133         se.solid = SOLID_NOT;
134         f = WarpZoneLib_BoxTouchesBrush_Recurse();
135         se.solid = s;
136
137         return f;
138 }
139
140 float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
141 {
142     float f, s;
143
144     if(!e.modelindex || e.warpzone_isboxy)
145         return 1;
146
147     s = e.solid;
148     e.solid = SOLID_BSP;
149     WarpZoneLib_BoxTouchesBrush_mins = mi;
150     WarpZoneLib_BoxTouchesBrush_maxs = ma;
151     WarpZoneLib_BoxTouchesBrush_ent = e;
152     WarpZoneLib_BoxTouchesBrush_ignore = ig;
153     f = WarpZoneLib_BoxTouchesBrush_Recurse();
154     e.solid = s;
155
156     return f;
157 }
158
159 entity WarpZone_Find(vector mi, vector ma)
160 {
161         // if we are near any warpzone planes - MOVE AWAY (work around nearclip)
162         entity e;
163         if(!warpzone_warpzones_exist)
164                 return world;
165         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
166                 if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
167                         return e;
168         return world;
169 }
170
171 void WarpZone_MakeAllSolid()
172 {
173         entity e;
174         if(!warpzone_warpzones_exist)
175                 return;
176         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
177                 e.solid = SOLID_BSP;
178 }
179
180 void WarpZone_MakeAllOther()
181 {
182         entity e;
183         if(!warpzone_warpzones_exist)
184                 return;
185         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
186                 e.solid = SOLID_TRIGGER;
187 }
188
189 void WarpZone_Trace_InitTransform()
190 {
191         if(!WarpZone_trace_transform)
192         {
193                 WarpZone_trace_transform = spawn();
194                 WarpZone_trace_transform.classname = "warpzone_trace_transform";
195         }
196         WarpZone_Accumulator_Clear(WarpZone_trace_transform);
197 }
198 void WarpZone_Trace_AddTransform(entity wz)
199 {
200         WarpZone_Accumulator_Add(WarpZone_trace_transform, wz);
201 }
202
203 void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb)
204 {
205         float nomonsters_adjusted;
206         float frac, sol, i;
207         float contentshack;
208         vector o0, e0;
209         entity wz;
210         vector vf, vr, vu;
211
212         WarpZone_trace_forent = forent;
213         WarpZone_trace_firstzone = world;
214         WarpZone_trace_lastzone = world;
215         WarpZone_Trace_InitTransform();
216         if(!warpzone_warpzones_exist)
217         {
218                 if(nomonsters == MOVE_NOTHING)
219                 {
220                         trace_endpos = end;
221                         trace_fraction = 1;
222                         if(cb)
223                                 cb(org, trace_endpos, end);
224                         return;
225                 }
226                 else
227                 {
228                         tracebox(org, mi, ma, end, nomonsters, WarpZone_trace_forent);
229                         if(cb)
230                                 cb(org, trace_endpos, end);
231                         return;
232                 }
233         }
234
235         vf = v_forward;
236         vr = v_right;
237         vu = v_up;
238         o0 = org;
239         e0 = end;
240
241         switch(nomonsters)
242         {
243                 case MOVE_WORLDONLY:
244                 case MOVE_NOTHING:
245                         nomonsters_adjusted = MOVE_NOMONSTERS;
246                         break;
247                 default:
248                         nomonsters_adjusted = nomonsters;
249                         break;
250         }
251         if((contentshack = (WarpZone_trace_forent.dphitcontentsmask && !(WarpZone_trace_forent.dphitcontentsmask & DPCONTENTS_SOLID))))
252                 BITSET_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
253
254         // if starting in warpzone, first transform
255         wz = WarpZone_Find(org + mi, org + ma);
256         if(wz)
257         {
258                 WarpZone_trace_firstzone = wz;
259                 WarpZone_trace_lastzone = wz;
260                 if(zone && wz != zone)
261                 {
262                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
263                         sol = 1;
264                         trace_fraction = 0;
265                         trace_endpos = org;
266                         goto fail;
267                 }
268                 WarpZone_Trace_AddTransform(wz);
269                 org = WarpZone_TransformOrigin(wz, org);
270                 end = WarpZone_TransformOrigin(wz, end);
271         }
272         WarpZone_MakeAllSolid();
273         sol = -1;
274         frac = 0;
275         i = 16;
276         for(0;;)
277         {
278                 if(--i < 1)
279                 {
280                         dprint("Too many warpzones in sequence, aborting trace.\n");
281                         trace_ent = world;
282                         break;
283                 }
284                 tracebox(org, mi, ma, end, nomonsters_adjusted, WarpZone_trace_forent);
285                 if(cb)
286                         cb(org, trace_endpos, end);
287                 if(sol < 0)
288                         sol = trace_startsolid;
289
290                 frac = trace_fraction = frac + (1 - frac) * trace_fraction;
291                 if(trace_fraction >= 1)
292                         break;
293                 if(trace_ent.classname != "trigger_warpzone")
294                 {
295                         if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent) || (contentshack && (trace_dphitcontents & WarpZone_trace_forent.dphitcontentsmask) == DPCONTENTS_SOLID))
296                         {
297                                 // continue the trace, ignoring this hit (we only care for warpzones)
298                                 org = trace_endpos + normalize(end - org);
299                                 continue;
300                                 // we cannot do an inverted trace here, as we do care for further warpzones inside that "solid" to be found
301                                 // otherwise, players could block entrances that way
302                         }
303                         break;
304                 }
305                 if(trace_ent == wz)
306                 {
307                         // FIXME can this check be removed? Do we really need it?
308                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
309                         trace_ent = world;
310                         break;
311                 }
312                 wz = trace_ent;
313                 if(!WarpZone_trace_firstzone)
314                         WarpZone_trace_firstzone = wz;
315                 WarpZone_trace_lastzone = wz;
316                 if(zone && wz != zone)
317                         break;
318                 WarpZone_Trace_AddTransform(wz);
319                 // we hit a warpzone... so, let's perform the trace after the warp again
320                 org = WarpZone_TransformOrigin(wz, trace_endpos);
321                 end = WarpZone_TransformOrigin(wz, end);
322
323                 // we got warped, so let's step back a bit
324                 tracebox(org, mi, ma, org + normalize(org - end) * 32, nomonsters_adjusted, WarpZone_trace_forent);
325                 org = trace_endpos;
326         }
327         WarpZone_MakeAllOther();
328 :fail
329         if(contentshack)
330                 BITCLR_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
331         trace_startsolid = sol;
332         v_forward = vf;
333         v_right = vr;
334         v_up = vu;
335 }
336
337 void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
338 {
339         WarpZone_TraceBox_ThroughZone(org, mi, ma, end, nomonsters, forent, world, WarpZone_trace_callback_t_null);
340 }
341
342 void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
343 {
344         WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
345 }
346
347 void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb)
348 {
349         float g, dt, i;
350         vector vf, vr, vu, v0, o0;
351         entity wz;
352
353         o0 = e.origin;
354         v0 = e.velocity;
355         g = cvar("sv_gravity") * e.gravity;
356
357         WarpZone_trace_forent = forent;
358         WarpZone_trace_firstzone = world;
359         WarpZone_trace_lastzone = world;
360         WarpZone_Trace_InitTransform();
361         WarpZone_tracetoss_time = 0;
362         if(!warpzone_warpzones_exist)
363         {
364                 tracetoss(e, WarpZone_trace_forent);
365                 if(cb)
366                         cb(e.origin, trace_endpos, trace_endpos);
367                 dt = vlen(e.origin - o0) / vlen(e.velocity);
368                 WarpZone_tracetoss_time += dt;
369                 e.velocity_z -= dt * g;
370                 WarpZone_tracetoss_velocity = e.velocity;
371                 e.velocity = v0;
372                 return;
373         }
374
375         vf = v_forward;
376         vr = v_right;
377         vu = v_up;
378
379         // if starting in warpzone, first transform
380         wz = WarpZone_Find(e.origin + e.mins, e.origin + e.maxs);
381         if(wz)
382         {
383                 WarpZone_trace_firstzone = wz;
384                 WarpZone_trace_lastzone = wz;
385                 if(zone && wz != zone)
386                 {
387                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
388
389                         WarpZone_tracetoss_time = 0;
390                         trace_endpos = o0;
391                         goto fail;
392                 }
393                 WarpZone_Trace_AddTransform(wz);
394                 setorigin(e, WarpZone_TransformOrigin(wz, e.origin));
395                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
396         }
397         WarpZone_MakeAllSolid();
398         i = 16;
399         for(0;;)
400         {
401                 if(--i < 1)
402                 {
403                         dprint("Too many warpzones in sequence, aborting trace.\n");
404                         trace_ent = world;
405                         break;
406                 }
407                 tracetoss(e, WarpZone_trace_forent);
408                 if(cb)
409                         cb(e.origin, trace_endpos, trace_endpos);
410                 dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
411                 WarpZone_tracetoss_time += dt;
412                 e.origin = trace_endpos;
413                 e.velocity_z -= dt * g;
414                 if(trace_fraction >= 1)
415                         break;
416                 if(trace_ent.classname != "trigger_warpzone")
417                         break;
418                 if(trace_ent == wz)
419                 {
420                         // FIXME can this check be removed? Do we really need it?
421                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
422                         trace_ent = world;
423                         break;
424                 }
425                 wz = trace_ent;
426                 if(!WarpZone_trace_firstzone)
427                         WarpZone_trace_firstzone = wz;
428                 WarpZone_trace_lastzone = wz;
429                 if(zone && wz != zone)
430                         break;
431                 WarpZone_Trace_AddTransform(wz);
432                 // we hit a warpzone... so, let's perform the trace after the warp again
433                 e.origin = WarpZone_TransformOrigin(wz, e.origin);
434                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
435
436                 // we got warped, so let's step back a bit
437                 e.velocity = -e.velocity;
438                 tracetoss(e, WarpZone_trace_forent);
439                 dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
440                 WarpZone_tracetoss_time -= dt;
441                 e.origin = trace_endpos;
442                 e.velocity = -e.velocity;
443         }
444         WarpZone_MakeAllOther();
445 :fail
446         WarpZone_tracetoss_velocity = e.velocity;
447         v_forward = vf;
448         v_right = vr;
449         v_up = vu;
450         // restore old entity data (caller just uses trace_endpos, WarpZone_tracetoss_velocity and the transform)
451         e.velocity = v0;
452         e.origin = o0;
453 }
454
455 void WarpZone_TraceToss(entity e, entity forent)
456 {
457         WarpZone_TraceToss_ThroughZone(e, forent, world, WarpZone_trace_callback_t_null);
458 }
459
460 entity WarpZone_TrailParticles_trace_callback_own;
461 float WarpZone_TrailParticles_trace_callback_eff;
462 void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
463 {
464         trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
465 }
466
467 void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
468 {
469         WarpZone_TrailParticles_trace_callback_own = own;
470         WarpZone_TrailParticles_trace_callback_eff = eff;
471         WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_trace_callback);
472 }
473
474 #ifdef CSQC
475 float WarpZone_TrailParticles_trace_callback_f;
476 float WarpZone_TrailParticles_trace_callback_flags;
477 void WarpZone_TrailParticles_WithMultiplier_trace_callback(vector from, vector endpos, vector to)
478 {
479         boxparticles(WarpZone_TrailParticles_trace_callback_eff, WarpZone_TrailParticles_trace_callback_own, from, endpos, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_own.velocity, WarpZone_TrailParticles_trace_callback_f, WarpZone_TrailParticles_trace_callback_flags);
480 }
481
482 void WarpZone_TrailParticles_WithMultiplier(entity own, float eff, vector org, vector end, float f, int boxflags)
483 {
484         WarpZone_TrailParticles_trace_callback_own = own;
485         WarpZone_TrailParticles_trace_callback_eff = eff;
486         WarpZone_TrailParticles_trace_callback_f = f;
487         WarpZone_TrailParticles_trace_callback_flags = boxflags | PARTICLES_DRAWASTRAIL;
488         WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_WithMultiplier_trace_callback);
489 }
490 #endif
491
492 float WarpZone_PlaneDist(entity wz, vector v)
493 {
494         return (v - wz.warpzone_origin) * wz.warpzone_forward;
495 }
496
497 float WarpZone_TargetPlaneDist(entity wz, vector v)
498 {
499         return (v - wz.warpzone_targetorigin) * wz.warpzone_targetforward;
500 }
501
502 vector WarpZone_TransformOrigin(entity wz, vector v)
503 {
504         return wz.warpzone_shift + AnglesTransform_Apply(wz.warpzone_transform, v);
505 }
506
507 vector WarpZone_TransformVelocity(entity wz, vector v)
508 {
509         return AnglesTransform_Apply(wz.warpzone_transform, v);
510 }
511
512 vector WarpZone_TransformAngles(entity wz, vector v)
513 {
514         return AnglesTransform_ApplyToAngles(wz.warpzone_transform, v);
515 }
516
517 vector WarpZone_TransformVAngles(entity wz, vector ang)
518 {
519 #ifdef KEEP_ROLL
520         float roll;
521         roll = ang.z;
522         ang_z = 0;
523 #endif
524
525         ang = AnglesTransform_ApplyToVAngles(wz.warpzone_transform, ang);
526
527 #ifdef KEEP_ROLL
528         ang = AnglesTransform_Normalize(ang, true);
529         ang = AnglesTransform_CancelRoll(ang);
530         ang_z = roll;
531 #else
532         ang = AnglesTransform_Normalize(ang, false);
533 #endif
534
535         return ang;
536 }
537
538 vector WarpZone_UnTransformOrigin(entity wz, vector v)
539 {
540         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v - wz.warpzone_shift);
541 }
542
543 vector WarpZone_UnTransformVelocity(entity wz, vector v)
544 {
545         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v);
546 }
547
548 vector WarpZone_UnTransformAngles(entity wz, vector v)
549 {
550         return AnglesTransform_ApplyToAngles(AnglesTransform_Invert(wz.warpzone_transform), v);
551 }
552
553 vector WarpZone_UnTransformVAngles(entity wz, vector ang)
554 {
555         float roll;
556
557         roll = ang.z;
558         ang_z = 0;
559
560         ang = AnglesTransform_ApplyToVAngles(AnglesTransform_Invert(wz.warpzone_transform), ang);
561         ang = AnglesTransform_Normalize(ang, true);
562         ang = AnglesTransform_CancelRoll(ang);
563
564         ang_z = roll;
565         return ang;
566 }
567
568 vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
569 {
570         vector nearest;
571         nearest_x = bound(mi.x, org.x, ma.x);
572         nearest_y = bound(mi.y, org.y, ma.y);
573         nearest_z = bound(mi.z, org.z, ma.z);
574         return nearest;
575 }
576
577 .float WarpZone_findradius_hit;
578 .entity WarpZone_findradius_next;
579 void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,               vector transform, vector shift, float needlineofsight)
580 //                               blast origin of current search   original blast origin   how to untransform (victim to blast system)
581 {
582         vector org_new;
583         vector org0_new;
584         vector shift_new, transform_new;
585         vector p;
586         entity e, e0;
587         entity wz;
588         if(rad <= 0)
589                 return;
590         e0 = findradius(org, rad);
591         wz = world;
592
593         for(e = e0; e; e = e.chain)
594         {
595                 p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
596                 if(needlineofsight)
597                 {
598                         traceline(org, p, MOVE_NOMONSTERS, e);
599                         if(trace_fraction < 1)
600                                 continue;
601                 }
602                 if(!e.WarpZone_findradius_hit || vlen(e.WarpZone_findradius_dist) > vlen(org0 - p))
603                 {
604                         e.WarpZone_findradius_nearest = p;
605                         e.WarpZone_findradius_dist = org0 - p;
606                         e.WarpZone_findradius_findorigin = org;
607                         e.WarpZone_findradius_findradius = rad;
608                         if(e.classname == "warpzone_refsys")
609                         {
610                                 // ignore, especially: do not overwrite the refsys parameters
611                         }
612                         else if(e.classname == "trigger_warpzone")
613                         {
614                                 e.WarpZone_findradius_next = wz;
615                                 wz = e;
616                                 e.WarpZone_findradius_hit = 1;
617                                 e.enemy.WarpZone_findradius_dist = '0 0 0'; // we don't want to go through this zone ever again
618                                 e.enemy.WarpZone_findradius_hit = 1;
619                         }
620                         else
621                         {
622                                 e.warpzone_transform = transform;
623                                 e.warpzone_shift = shift;
624                                 e.WarpZone_findradius_hit = 1;
625                         }
626                 }
627         }
628         for(e = wz; e; e = e.WarpZone_findradius_next)
629         {
630                 org0_new = WarpZone_TransformOrigin(e, org);
631                 traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
632                 org_new = trace_endpos;
633
634                 transform_new = AnglesTransform_Multiply(e.warpzone_transform, transform);
635                 shift_new = AnglesTransform_Multiply_GetPostShift(e.warpzone_transform, e.warpzone_shift, transform, shift);
636                 WarpZone_FindRadius_Recurse(
637                         org_new,
638                         bound(0, rad - vlen(org_new - org0_new), rad - 8),
639                         org0_new,
640                         transform_new, shift_new,
641                         needlineofsight);
642                 e.WarpZone_findradius_hit = 0;
643                 e.enemy.WarpZone_findradius_hit = 0;
644         }
645 }
646 entity WarpZone_FindRadius(vector org, float rad, float needlineofsight)
647 {
648         entity e0, e;
649         WarpZone_FindRadius_Recurse(org, rad, org, '0 0 0', '0 0 0', needlineofsight);
650         e0 = findchainfloat(WarpZone_findradius_hit, 1);
651         for(e = e0; e; e = e.chain)
652                 e.WarpZone_findradius_hit = 0;
653         return e0;
654 }
655
656 .entity WarpZone_refsys;
657 void WarpZone_RefSys_GC()
658 {
659         // garbage collect unused reference systems
660         self.nextthink = time + 1;
661         if(self.owner.WarpZone_refsys != self)
662                 remove(self);
663 }
664 void WarpZone_RefSys_CheckCreate(entity me)
665 {
666         if(me.WarpZone_refsys.owner != me)
667         {
668                 me.WarpZone_refsys = spawn();
669                 me.WarpZone_refsys.classname = "warpzone_refsys";
670                 me.WarpZone_refsys.owner = me;
671                 me.WarpZone_refsys.think = WarpZone_RefSys_GC;
672                 me.WarpZone_refsys.nextthink = time + 1;
673                 WarpZone_Accumulator_Clear(me.WarpZone_refsys);
674         }
675 }
676 void WarpZone_RefSys_Clear(entity me)
677 {
678         if(me.WarpZone_refsys)
679         {
680                 remove(me.WarpZone_refsys);
681                 me.WarpZone_refsys = world;
682         }
683 }
684 void WarpZone_RefSys_AddTransform(entity me, vector t, vector s)
685 {
686         if(t != '0 0 0' || s != '0 0 0')
687         {
688                 WarpZone_RefSys_CheckCreate(me);
689                 WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
690         }
691 }
692 void WarpZone_RefSys_Add(entity me, entity wz)
693 {
694         WarpZone_RefSys_AddTransform(me, wz.warpzone_transform, wz.warpzone_shift);
695 }
696 void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s)
697 {
698         if(t != '0 0 0' || s != '0 0 0')
699         {
700                 WarpZone_RefSys_CheckCreate(me);
701                 WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, t, s);
702         }
703 }
704 void WarpZone_RefSys_AddInverse(entity me, entity wz)
705 {
706         WarpZone_RefSys_AddInverseTransform(me, wz.warpzone_transform, wz.warpzone_shift);
707 }
708 .vector WarpZone_refsys_incremental_shift;
709 .vector WarpZone_refsys_incremental_transform;
710 void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
711 {
712         //vector t, s;
713         if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
714         if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
715                 return;
716         WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, me.WarpZone_refsys_incremental_transform, me.WarpZone_refsys_incremental_shift);
717         WarpZone_Accumulator_Add(me.WarpZone_refsys, ref.WarpZone_refsys);
718         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
719         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
720 }
721 void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
722 {
723         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
724         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
725 }
726 vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
727 {
728         if(from.WarpZone_refsys)
729                 org = WarpZone_UnTransformOrigin(from.WarpZone_refsys, org);
730         if(to.WarpZone_refsys)
731                 org = WarpZone_TransformOrigin(to.WarpZone_refsys, org);
732         return org;
733 }
734 vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
735 {
736         if(from.WarpZone_refsys)
737                 vel = WarpZone_UnTransformVelocity(from.WarpZone_refsys, vel);
738         if(to.WarpZone_refsys)
739                 vel = WarpZone_TransformVelocity(to.WarpZone_refsys, vel);
740         return vel;
741 }
742 vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang)
743 {
744         if(from.WarpZone_refsys)
745                 ang = WarpZone_UnTransformAngles(from.WarpZone_refsys, ang);
746         if(to.WarpZone_refsys)
747                 ang = WarpZone_TransformAngles(to.WarpZone_refsys, ang);
748         return ang;
749 }
750 vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
751 {
752         if(from.WarpZone_refsys)
753                 ang = WarpZone_UnTransformVAngles(from.WarpZone_refsys, ang);
754         if(to.WarpZone_refsys)
755                 ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
756         return ang;
757 }
758 void WarpZone_RefSys_Copy(entity me, entity from)
759 {
760         if(from.WarpZone_refsys)
761         {
762                 WarpZone_RefSys_CheckCreate(me);
763                 me.WarpZone_refsys.warpzone_shift = from.WarpZone_refsys.warpzone_shift;
764                 me.WarpZone_refsys.warpzone_transform = from.WarpZone_refsys.warpzone_transform;
765         }
766         else
767                 WarpZone_RefSys_Clear(me);
768 }
769 entity WarpZone_RefSys_SpawnSameRefSys(entity me)
770 {
771         entity e;
772         e = spawn();
773         WarpZone_RefSys_Copy(e, me);
774         return e;
775 }