properly draw the hook through warpzones again too; fix interpolation issues
[xonotic/xonotic-data.pk3dir.git] / qcsrc / warpzonelib / common.qc
1 void WarpZone_Accumulator_Clear(entity acc)
2 {
3         acc.warpzone_transform = '0 0 0';
4         acc.warpzone_shift = '0 0 0';
5 }
6 void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s)
7 {
8         vector tr, st;
9         tr = AnglesTransform_Multiply(t, acc.warpzone_transform);
10         st = AnglesTransform_Multiply_GetPostShift(t, s, acc.warpzone_transform, acc.warpzone_shift);
11         acc.warpzone_transform = tr;
12         acc.warpzone_shift = st;
13 }
14 void WarpZone_Accumulator_Add(entity acc, entity wz)
15 {
16         vector t, st;
17         t = AnglesTransform_Multiply(wz.warpzone_transform, acc.warpzone_transform);
18         st = AnglesTransform_Multiply_GetPostShift(wz.warpzone_transform, wz.warpzone_shift, acc.warpzone_transform, acc.warpzone_shift);
19         acc.warpzone_transform = t;
20         acc.warpzone_shift = st;
21 }
22
23 .vector(vector, vector) camera_transform;
24 var float autocvar_cl_warpzone_usetrace = 1;
25 vector WarpZone_camera_transform(vector org, vector ang)
26 {
27         vector vf, vr, vu;
28         vf = v_forward;
29         vr = v_right;
30         vu = v_up;
31         org = WarpZone_TransformOrigin(self, org);
32         vf = WarpZone_TransformVelocity(self, vf);
33         vr = WarpZone_TransformVelocity(self, vr);
34         vu = WarpZone_TransformVelocity(self, vu);
35         if(autocvar_cl_warpzone_usetrace)
36                 traceline(self.warpzone_targetorigin, org, MOVE_NOMONSTERS, world);
37         else
38                 trace_endpos = self.warpzone_targetorigin;
39         v_forward = vf;
40         v_right = vr;
41         v_up = vu;
42         return org;
43 }
44
45 void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, vector other_ang)
46 {
47         e.warpzone_transform = AnglesTransform_Divide(other_ang, AnglesTransform_TurnDirectionFR(my_ang));
48         e.warpzone_shift = AnglesTransform_PrePostShift_GetPostShift(my_org, e.warpzone_transform, other_org);
49         e.warpzone_origin = my_org;
50         e.warpzone_targetorigin = other_org;
51         e.warpzone_angles = my_ang;
52         e.warpzone_targetangles = other_ang;
53         fixedmakevectors(my_ang); e.warpzone_forward = v_forward;
54         fixedmakevectors(other_ang); e.warpzone_targetforward = v_forward;
55         e.camera_transform = WarpZone_camera_transform;
56 }
57
58 vector WarpZone_Camera_camera_transform(vector org, vector ang)
59 {
60         // a fixed camera view
61         trace_endpos = self.warpzone_origin;
62         makevectors(self.warpzone_angles);
63         return self.warpzone_origin;
64 }
65
66 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
67 {
68         e.warpzone_origin = my_org;
69         e.warpzone_angles = my_ang;
70         e.camera_transform = WarpZone_Camera_camera_transform;
71 }
72
73 .entity enemy;
74
75 vector WarpZoneLib_BoxTouchesBrush_mins;
76 vector WarpZoneLib_BoxTouchesBrush_maxs;
77 entity WarpZoneLib_BoxTouchesBrush_ent;
78 entity WarpZoneLib_BoxTouchesBrush_ignore;
79 float WarpZoneLib_BoxTouchesBrush_Recurse()
80 {
81         float s;
82         entity se;
83         float f;
84
85         tracebox('0 0 0', WarpZoneLib_BoxTouchesBrush_mins, WarpZoneLib_BoxTouchesBrush_maxs, '0 0 0', MOVE_NOMONSTERS, WarpZoneLib_BoxTouchesBrush_ignore);
86 #ifdef CSQC
87         if (trace_networkentity)
88         {
89                 dprint("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n");
90                 // we cannot continue, as a player blocks us...
91                 // so, abort
92                 return 0;
93         }
94 #endif
95         if not(trace_ent)
96                 return 0;
97         if (trace_ent == WarpZoneLib_BoxTouchesBrush_ent)
98                 return 1;
99
100         se = trace_ent;
101         s = se.solid;
102         se.solid = SOLID_NOT;
103         f = WarpZoneLib_BoxTouchesBrush_Recurse();
104         se.solid = s;
105
106         return f;
107 }
108
109 float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig)
110 {
111     float f, s;
112
113     if not(e.modelindex)
114         return 1;
115
116     s = e.solid;
117     e.solid = SOLID_BSP;
118     WarpZoneLib_BoxTouchesBrush_mins = mi;
119     WarpZoneLib_BoxTouchesBrush_maxs = ma;
120     WarpZoneLib_BoxTouchesBrush_ent = e;
121     WarpZoneLib_BoxTouchesBrush_ignore = ig;
122     f = WarpZoneLib_BoxTouchesBrush_Recurse();
123     e.solid = s;
124
125     return f;
126 }
127
128 entity WarpZone_Find(vector mi, vector ma)
129 {
130         // if we are near any warpzone planes - MOVE AWAY (work around nearclip)
131         entity e;
132         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
133                 if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world))
134                         return e;
135         return world;
136 }
137
138 void WarpZone_MakeAllSolid()
139 {
140         entity e;
141         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
142                 e.solid = SOLID_BSP;
143 }
144
145 void WarpZone_MakeAllOther()
146 {
147         entity e;
148         for(e = world; (e = find(e, classname, "trigger_warpzone")); )
149                 e.solid = SOLID_TRIGGER;
150 }
151
152 void WarpZone_Trace_InitTransform()
153 {
154         if(!WarpZone_trace_transform)
155         {
156                 WarpZone_trace_transform = spawn();
157                 WarpZone_trace_transform.classname = "warpzone_trace_transform";
158         }
159         WarpZone_Accumulator_Clear(WarpZone_trace_transform);
160 }
161 void WarpZone_Trace_AddTransform(entity wz)
162 {
163         WarpZone_Accumulator_Add(WarpZone_trace_transform, wz);
164 }
165
166 void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent, entity zone, WarpZone_trace_callback_t cb)
167 {
168         float nomonsters_adjusted;
169         float frac, sol, i;
170         vector o0, e0;
171         entity wz;
172         vector vf, vr, vu;
173         vf = v_forward;
174         vr = v_right;
175         vu = v_up;
176         o0 = org;
177         e0 = end;
178
179         switch(nomonsters)
180         {
181                 case MOVE_WORLDONLY:
182                 case MOVE_NOTHING:
183                         nomonsters_adjusted = MOVE_NOMONSTERS;
184                         break;
185                 default:
186                         nomonsters_adjusted = nomonsters;
187                         break;
188         }
189
190         WarpZone_Trace_InitTransform();
191         // if starting in warpzone, first transform
192         wz = WarpZone_Find(org + mi, org + ma);
193         if(wz)
194         {
195                 if(zone && wz != zone)
196                 {
197                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
198                         sol = 1;
199                         trace_fraction = 0;
200                         trace_endpos = org;
201                         goto fail;
202                 }
203                 WarpZone_Trace_AddTransform(wz);
204                 org = WarpZone_TransformOrigin(wz, trace_endpos);
205                 end = WarpZone_TransformOrigin(wz, end);
206         }
207         WarpZone_MakeAllSolid();
208         sol = -1;
209         frac = 0;
210         i = 16;
211         for(;;)
212         {
213                 if(--i < 1)
214                 {
215                         dprint("Too many warpzones in sequence, aborting trace.\n");
216                         trace_ent = world;
217                         break;
218                 }
219                 tracebox(org, mi, ma, end, nomonsters_adjusted, forent);
220                 if(cb)
221                         cb(org, trace_endpos, end);
222                 if(sol < 0)
223                         sol = trace_startsolid;
224
225                 frac = trace_fraction = frac + (1 - frac) * trace_fraction;
226                 if(trace_fraction >= 1)
227                         break;
228                 if(trace_ent.classname != "trigger_warpzone")
229                 {
230                         if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent))
231                         {
232                                 // continue the trace, ignoring this hit (we only care for warpzones)
233                                 org = trace_endpos + normalize(end - org);
234                                 continue;
235                                 // we cannot do an inverted trace here, as we do care for further warpzones inside that "solid" to be found
236                                 // otherwise, players could block entrances that way
237                         }
238                         break;
239                 }
240                 if(trace_ent == wz)
241                 {
242                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
243                         trace_ent = world;
244                         break;
245                 }
246                 wz = trace_ent;
247                 if(zone && wz != zone)
248                         break;
249                 WarpZone_Trace_AddTransform(wz);
250                 // we hit a warpzone... so, let's perform the trace after the warp again
251                 org = WarpZone_TransformOrigin(wz, trace_endpos);
252                 end = WarpZone_TransformOrigin(wz, end);
253         }
254         WarpZone_MakeAllOther();
255 :fail
256         trace_startsolid = sol;
257         v_forward = vf;
258         v_right = vr;
259         v_up = vu;
260 }
261
262 void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
263 {
264         WarpZone_TraceBox_ThroughZone(org, mi, ma, end, nomonsters, forent, world, WarpZone_trace_callback_t_null);
265 }
266
267 void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
268 {
269         WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
270 }
271
272 void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb)
273 {
274         float g, dt, i;
275         vector vf, vr, vu, v0, o0;
276         entity wz;
277
278         vf = v_forward;
279         vr = v_right;
280         vu = v_up;
281         o0 = e.origin;
282         v0 = e.velocity;
283
284         WarpZone_Trace_InitTransform();
285         // if starting in warpzone, first transform
286         wz = WarpZone_Find(e.origin + e.mins, e.origin + e.maxs);
287         if(wz)
288         {
289                 if(zone && wz != zone)
290                 {
291                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
292
293                         WarpZone_tracetoss_time = 0;
294                         trace_endpos = o0;
295                         goto fail;
296                 }
297                 WarpZone_Trace_AddTransform(wz);
298                 setorigin(e, WarpZone_TransformOrigin(wz, e.origin));
299                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
300         }
301         WarpZone_MakeAllSolid();
302         g = cvar("sv_gravity") * e.gravity;
303         WarpZone_tracetoss_time = 0;
304         i = 16;
305         for(;;)
306         {
307                 if(--i < 1)
308                 {
309                         dprint("Too many warpzones in sequence, aborting trace.\n");
310                         trace_ent = world;
311                         break;
312                 }
313                 tracetoss(e, forent);
314                 if(cb)
315                         cb(e.origin, trace_endpos, trace_endpos);
316                 e.origin = trace_endpos;
317                 e.velocity_z -= WarpZone_tracetoss_time * g;
318                 dt = vlen(e.origin - o0) / vlen(e.velocity);
319                 WarpZone_tracetoss_time += dt;
320                 if(trace_fraction >= 1)
321                         break;
322                 if(trace_ent.classname != "trigger_warpzone")
323                         break;
324                 if(trace_ent == wz)
325                 {
326                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
327                         trace_ent = world;
328                         break;
329                 }
330                 wz = trace_ent;
331                 if(zone && wz != zone)
332                         break;
333                 WarpZone_Trace_AddTransform(wz);
334                 // we hit a warpzone... so, let's perform the trace after the warp again
335                 e.origin = WarpZone_TransformOrigin(wz, e.origin);
336                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
337         }
338         WarpZone_MakeAllOther();
339 :fail
340         WarpZone_tracetoss_velocity = e.velocity;
341         v_forward = vf;
342         v_right = vr;
343         v_up = vu;
344         // restore old entity data (caller just uses trace_endpos, WarpZone_tracetoss_velocity and the transform)
345         e.velocity = v0;
346         e.origin = o0;
347 }
348
349 void WarpZone_TraceToss(entity e, entity forent)
350 {
351         WarpZone_TraceToss_ThroughZone(e, forent, world, WarpZone_trace_callback_t_null);
352 }
353
354 entity WarpZone_TrailParticles_trace_callback_own;
355 float WarpZone_TrailParticles_trace_callback_eff;
356 void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
357 {
358         trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
359 }
360
361 void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
362 {
363         WarpZone_TrailParticles_trace_callback_own = own;
364         WarpZone_TrailParticles_trace_callback_eff = eff;
365         WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_trace_callback);
366 }
367
368 float WarpZone_PlaneDist(entity wz, vector v)
369 {
370         return (v - wz.warpzone_origin) * wz.warpzone_forward;
371 }
372
373 float WarpZone_TargetPlaneDist(entity wz, vector v)
374 {
375         return (v - wz.warpzone_targetorigin) * wz.warpzone_targetforward;
376 }
377
378 vector WarpZone_TransformOrigin(entity wz, vector v)
379 {
380         return wz.warpzone_shift + AnglesTransform_Apply(wz.warpzone_transform, v);
381 }
382
383 vector WarpZone_TransformVelocity(entity wz, vector v)
384 {
385         return AnglesTransform_Apply(wz.warpzone_transform, v);
386 }
387
388 vector WarpZone_TransformAngles(entity wz, vector v)
389 {
390         return AnglesTransform_ApplyToAngles(wz.warpzone_transform, v);
391 }
392
393 vector WarpZone_TransformVAngles(entity wz, vector ang)
394 {
395 #ifdef KEEP_ROLL
396         float roll;
397         roll = ang_z;
398         ang_z = 0;
399 #endif
400
401         ang = AnglesTransform_ApplyToVAngles(wz.warpzone_transform, ang);
402
403 #ifdef KEEP_ROLL
404         ang = AnglesTransform_Normalize(ang, TRUE);
405         ang = AnglesTransform_CancelRoll(ang);
406         ang_z = roll;
407 #else
408         ang = AnglesTransform_Normalize(ang, FALSE);
409 #endif
410
411         return ang;
412 }
413
414 vector WarpZone_UnTransformOrigin(entity wz, vector v)
415 {
416         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v - wz.warpzone_shift);
417 }
418
419 vector WarpZone_UnTransformVelocity(entity wz, vector v)
420 {
421         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v);
422 }
423
424 vector WarpZone_UnTransformAngles(entity wz, vector v)
425 {
426         return AnglesTransform_ApplyToAngles(AnglesTransform_Invert(wz.warpzone_transform), v);
427 }
428
429 vector WarpZone_UnTransformVAngles(entity wz, vector ang)
430 {
431         float roll;
432
433         roll = ang_z;
434         ang_z = 0;
435
436         ang = AnglesTransform_ApplyToVAngles(AnglesTransform_Invert(wz.warpzone_transform), ang);
437         ang = AnglesTransform_Normalize(ang, TRUE);
438         ang = AnglesTransform_CancelRoll(ang);
439
440         ang_z = roll;
441         return ang;
442 }
443
444 vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
445 {
446         vector nearest;
447         nearest_x = bound(mi_x, org_x, ma_x);
448         nearest_y = bound(mi_y, org_y, ma_y);
449         nearest_z = bound(mi_z, org_z, ma_z);
450         return nearest;
451 }
452
453 .float WarpZone_findradius_hit;
454 .entity WarpZone_findradius_next;
455 void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,               vector transform, vector shift, float needlineofsight)
456 //                               blast origin of current search   original blast origin   how to untransform (victim to blast system)
457 {
458         vector org_new;
459         vector org0_new;
460         vector shift_new, transform_new;
461         vector p;
462         entity e, e0;
463         entity wz;
464         if(rad <= 0)
465                 return;
466         e0 = findradius(org, rad);
467         wz = world;
468
469         for(e = e0; e; e = e.chain)
470         {
471                 p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
472                 if(needlineofsight)
473                 {
474                         traceline(org, p, MOVE_NOMONSTERS, e);
475                         if(trace_fraction < 1)
476                                 continue;
477                 }
478                 if(!e.WarpZone_findradius_hit || vlen(e.WarpZone_findradius_dist) > vlen(org0 - p))
479                 {
480                         e.WarpZone_findradius_nearest = p;
481                         e.WarpZone_findradius_dist = org0 - p;
482                         e.WarpZone_findradius_findorigin = org;
483                         e.WarpZone_findradius_findradius = rad;
484                         if(e.classname == "warpzone_refsys")
485                         {
486                                 // ignore, especially: do not overwrite the refsys parameters
487                         }
488                         else if(e.classname == "trigger_warpzone")
489                         {
490                                 e.WarpZone_findradius_next = wz;
491                                 wz = e;
492                                 e.WarpZone_findradius_hit = 1;
493                                 e.enemy.WarpZone_findradius_dist = '0 0 0'; // we don't want to go through this zone ever again
494                                 e.enemy.WarpZone_findradius_hit = 1;
495                         }
496                         else
497                         {
498                                 e.warpzone_transform = transform;
499                                 e.warpzone_shift = shift;
500                                 e.WarpZone_findradius_hit = 1;
501                         }
502                 }
503         }
504         for(e = wz; e; e = e.WarpZone_findradius_next)
505         {
506                 org0_new = WarpZone_TransformOrigin(e, org);
507                 traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
508                 org_new = trace_endpos;
509
510                 transform_new = AnglesTransform_Multiply(e.warpzone_transform, transform);
511                 shift_new = AnglesTransform_Multiply_GetPostShift(e.warpzone_transform, e.warpzone_shift, transform, shift);
512                 WarpZone_FindRadius_Recurse(
513                         org_new,
514                         bound(0, rad - vlen(org_new - org0_new), rad - 8),
515                         org0_new,
516                         transform_new, shift_new,
517                         needlineofsight);
518                 e.WarpZone_findradius_hit = 0;
519                 e.enemy.WarpZone_findradius_hit = 0;
520         }
521 }
522 entity WarpZone_FindRadius(vector org, float rad, float needlineofsight)
523 {
524         entity e0, e;
525         WarpZone_FindRadius_Recurse(org, rad, org, '0 0 0', '0 0 0', needlineofsight);
526         e0 = findchainfloat(WarpZone_findradius_hit, 1);
527         for(e = e0; e; e = e.chain)
528                 e.WarpZone_findradius_hit = 0;
529         return e0;
530 }
531
532 .entity WarpZone_refsys;
533 void WarpZone_RefSys_GC()
534 {
535         // garbage collect unused reference systems
536         self.nextthink = time + 1;
537         if(self.owner.WarpZone_refsys != self)
538                 remove(self);
539 }
540 void WarpZone_RefSys_Add(entity me, entity wz)
541 {
542         if(me.WarpZone_refsys.owner != me)
543         {
544                 me.WarpZone_refsys = spawn();
545                 me.WarpZone_refsys.classname = "warpzone_refsys";
546                 me.WarpZone_refsys.owner = me;
547                 me.WarpZone_refsys.think = WarpZone_RefSys_GC;
548                 me.WarpZone_refsys.nextthink = time + 1;
549                 WarpZone_Accumulator_Clear(me.WarpZone_refsys);
550         }
551         if(wz)
552                 WarpZone_Accumulator_Add(me.WarpZone_refsys, wz);
553 }
554 .vector WarpZone_refsys_incremental_shift;
555 .vector WarpZone_refsys_incremental_transform;
556 void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
557 {
558         vector t, s;
559         if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
560         if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
561                 return;
562         if(me.WarpZone_refsys.owner != me)
563         {
564                 me.WarpZone_refsys = spawn();
565                 me.WarpZone_refsys.classname = "warpzone_refsys";
566                 me.WarpZone_refsys.owner = me;
567                 me.WarpZone_refsys.think = WarpZone_RefSys_GC;
568                 me.WarpZone_refsys.nextthink = time + 1;
569                 WarpZone_Accumulator_Clear(me.WarpZone_refsys);
570         }
571         t = AnglesTransform_Invert(me.WarpZone_refsys_incremental_transform);
572         s = AnglesTransform_PrePostShift_GetPostShift(me.WarpZone_refsys_incremental_shift, t, '0 0 0');
573         WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
574         WarpZone_Accumulator_Add(me.WarpZone_refsys, ref);
575         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
576         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
577 }
578 void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
579 {
580         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
581         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
582 }
583 vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
584 {
585         if(from.WarpZone_refsys)
586                 org = WarpZone_UnTransformOrigin(from.WarpZone_refsys, org);
587         if(to.WarpZone_refsys)
588                 org = WarpZone_TransformOrigin(to.WarpZone_refsys, org);
589         return org;
590 }
591 vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
592 {
593         if(from.WarpZone_refsys)
594                 vel = WarpZone_UnTransformVelocity(from.WarpZone_refsys, vel);
595         if(to.WarpZone_refsys)
596                 vel = WarpZone_TransformVelocity(to.WarpZone_refsys, vel);
597         return vel;
598 }
599 vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang)
600 {
601         if(from.WarpZone_refsys)
602                 ang = WarpZone_UnTransformAngles(from.WarpZone_refsys, ang);
603         if(to.WarpZone_refsys)
604                 ang = WarpZone_TransformAngles(to.WarpZone_refsys, ang);
605         return ang;
606 }
607 vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
608 {
609         if(from.WarpZone_refsys)
610                 ang = WarpZone_UnTransformVAngles(from.WarpZone_refsys, ang);
611         if(to.WarpZone_refsys)
612                 ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
613         return ang;
614 }
615 entity WarpZone_RefSys_SpawnSameRefSys(entity me)
616 {
617         entity e;
618         e = spawn();
619         if(me.WarpZone_refsys)
620         {
621                 e.WarpZone_refsys = spawn();
622                 e.WarpZone_refsys.classname = "warpzone_refsys";
623                 e.WarpZone_refsys.owner = e;
624                 e.WarpZone_refsys.think = WarpZone_RefSys_GC;
625                 e.WarpZone_refsys.nextthink = time + 1;
626                 e.WarpZone_refsys.warpzone_shift = me.WarpZone_refsys.warpzone_shift;
627                 e.WarpZone_refsys.warpzone_transform = me.WarpZone_refsys.warpzone_transform;
628         }
629         return e;
630 }