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