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