]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/warpzonelib/common.qc
e4acfb1693a2d86586cd3fb53cda548547b83f7b
[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 frac, sol, i;
169         vector o0, e0;
170         entity wz;
171         vector vf, vr, vu;
172         vf = v_forward;
173         vr = v_right;
174         vu = v_up;
175         o0 = org;
176         e0 = end;
177         WarpZone_Trace_InitTransform();
178         // if starting in warpzone, first transform
179         wz = WarpZone_Find(org + mi, org + ma);
180         if(wz)
181         {
182                 if(zone && wz != zone)
183                 {
184                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
185                         sol = 1;
186                         trace_fraction = 0;
187                         trace_endpos = org;
188                         goto fail;
189                 }
190                 WarpZone_Trace_AddTransform(wz);
191                 org = WarpZone_TransformOrigin(wz, trace_endpos);
192                 end = WarpZone_TransformOrigin(wz, end);
193         }
194         WarpZone_MakeAllSolid();
195         sol = -1;
196         frac = 0;
197         i = 16;
198         for(;;)
199         {
200                 if(--i < 1)
201                 {
202                         dprint("Too many warpzones in sequence, aborting trace.\n");
203                         break;
204                 }
205                 tracebox(org, mi, ma, end, nomonsters, forent);
206                 if(cb)
207                         cb(org, trace_endpos, end);
208                 if(sol < 0)
209                         sol = trace_startsolid;
210
211                 frac = trace_fraction = frac + (1 - frac) * trace_fraction;
212                 if(trace_fraction >= 1)
213                         break;
214                 if(trace_ent.classname != "trigger_warpzone")
215                         break;
216                 if(trace_ent == wz)
217                 {
218                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
219                         break;
220                 }
221                 wz = trace_ent;
222                 if(zone && wz != zone)
223                         break;
224                 WarpZone_Trace_AddTransform(wz);
225                 // we hit a warpzone... so, let's perform the trace after the warp again
226                 org = WarpZone_TransformOrigin(wz, trace_endpos);
227                 end = WarpZone_TransformOrigin(wz, end);
228         }
229         WarpZone_MakeAllOther();
230 :fail
231         trace_startsolid = sol;
232         v_forward = vf;
233         v_right = vr;
234         v_up = vu;
235 }
236
237 void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
238 {
239         WarpZone_TraceBox_ThroughZone(org, mi, ma, end, nomonsters, forent, world, WarpZone_trace_callback_t_null);
240 }
241
242 void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
243 {
244         WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
245 }
246
247 void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZone_trace_callback_t cb)
248 {
249         float g, dt, i;
250         vector vf, vr, vu, v0, o0;
251         entity wz;
252
253         vf = v_forward;
254         vr = v_right;
255         vu = v_up;
256         o0 = e.origin;
257         v0 = e.velocity;
258         WarpZone_Trace_InitTransform();
259         // if starting in warpzone, first transform
260         wz = WarpZone_Find(e.origin + e.mins, e.origin + e.maxs);
261         if(wz)
262         {
263                 if(zone && wz != zone)
264                 {
265                         // we are in ANOTHER warpzone. This is bad. Make a zero length trace and return.
266
267                         WarpZone_tracetoss_time = 0;
268                         trace_endpos = o0;
269                         goto fail;
270                 }
271                 WarpZone_Trace_AddTransform(wz);
272                 setorigin(e, WarpZone_TransformOrigin(wz, e.origin));
273                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
274         }
275         WarpZone_MakeAllSolid();
276         g = cvar("sv_gravity") * e.gravity;
277         WarpZone_tracetoss_time = 0;
278         i = 16;
279         for(;;)
280         {
281                 if(--i < 1)
282                 {
283                         dprint("Too many warpzones in sequence, aborting trace.\n");
284                         break;
285                 }
286                 tracetoss(e, forent);
287                 if(cb)
288                         cb(e.origin, trace_endpos, trace_endpos);
289                 e.origin = trace_endpos;
290                 e.velocity_z -= WarpZone_tracetoss_time * g;
291                 dt = vlen(e.origin - o0) / vlen(e.velocity);
292                 WarpZone_tracetoss_time += dt;
293                 if(trace_fraction >= 1)
294                         break;
295                 if(trace_ent.classname != "trigger_warpzone")
296                         break;
297                 if(trace_ent == wz)
298                 {
299                         dprint("I transformed into the same zone again, wtf, aborting the trace\n");
300                         break;
301                 }
302                 wz = trace_ent;
303                 if(zone && wz != zone)
304                         break;
305                 WarpZone_Trace_AddTransform(wz);
306                 // we hit a warpzone... so, let's perform the trace after the warp again
307                 e.origin = WarpZone_TransformOrigin(wz, e.origin);
308                 e.velocity = WarpZone_TransformVelocity(wz, e.velocity);
309         }
310         WarpZone_MakeAllOther();
311 :fail
312         WarpZone_tracetoss_velocity = e.velocity;
313         v_forward = vf;
314         v_right = vr;
315         v_up = vu;
316         // restore old entity data (caller just uses trace_endpos, WarpZone_tracetoss_velocity and the transform)
317         e.velocity = v0;
318         e.origin = o0;
319 }
320
321 void WarpZone_TraceToss(entity e, entity forent)
322 {
323         WarpZone_TraceToss_ThroughZone(e, forent, world, WarpZone_trace_callback_t_null);
324 }
325
326 entity WarpZone_TrailParticles_trace_callback_own;
327 float WarpZone_TrailParticles_trace_callback_eff;
328 void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
329 {
330         trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
331 }
332
333 void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
334 {
335         WarpZone_TrailParticles_trace_callback_own = own;
336         WarpZone_TrailParticles_trace_callback_eff = eff;
337         WarpZone_TraceBox_ThroughZone(org, '0 0 0', '0 0 0', end, MOVE_NOMONSTERS, world, world, WarpZone_TrailParticles_trace_callback);
338 }
339
340 float WarpZone_PlaneDist(entity wz, vector v)
341 {
342         return (v - wz.warpzone_origin) * wz.warpzone_forward;
343 }
344
345 float WarpZone_TargetPlaneDist(entity wz, vector v)
346 {
347         return (v - wz.warpzone_targetorigin) * wz.warpzone_targetforward;
348 }
349
350 vector WarpZone_TransformOrigin(entity wz, vector v)
351 {
352         return wz.warpzone_shift + AnglesTransform_Apply(wz.warpzone_transform, v);
353 }
354
355 vector WarpZone_TransformVelocity(entity wz, vector v)
356 {
357         return AnglesTransform_Apply(wz.warpzone_transform, v);
358 }
359
360 vector WarpZone_TransformAngles(entity wz, vector v)
361 {
362         return AnglesTransform_ApplyToAngles(wz.warpzone_transform, v);
363 }
364
365 vector WarpZone_TransformVAngles(entity wz, vector ang)
366 {
367 #ifdef KEEP_ROLL
368         float roll;
369         roll = ang_z;
370         ang_z = 0;
371 #endif
372
373         ang = AnglesTransform_ApplyToVAngles(wz.warpzone_transform, ang);
374
375 #ifdef KEEP_ROLL
376         ang = AnglesTransform_Normalize(ang, TRUE);
377         ang = AnglesTransform_CancelRoll(ang);
378         ang_z = roll;
379 #else
380         ang = AnglesTransform_Normalize(ang, FALSE);
381 #endif
382
383         return ang;
384 }
385
386 vector WarpZone_UnTransformOrigin(entity wz, vector v)
387 {
388         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v - wz.warpzone_shift);
389 }
390
391 vector WarpZone_UnTransformVelocity(entity wz, vector v)
392 {
393         return AnglesTransform_Apply(AnglesTransform_Invert(wz.warpzone_transform), v);
394 }
395
396 vector WarpZone_UnTransformAngles(entity wz, vector v)
397 {
398         return AnglesTransform_ApplyToAngles(AnglesTransform_Invert(wz.warpzone_transform), v);
399 }
400
401 vector WarpZone_UnTransformVAngles(entity wz, vector ang)
402 {
403         float roll;
404
405         roll = ang_z;
406         ang_z = 0;
407
408         ang = AnglesTransform_ApplyToVAngles(AnglesTransform_Invert(wz.warpzone_transform), ang);
409         ang = AnglesTransform_Normalize(ang, TRUE);
410         ang = AnglesTransform_CancelRoll(ang);
411
412         ang_z = roll;
413         return ang;
414 }
415
416 vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
417 {
418         vector nearest;
419         nearest_x = bound(mi_x, org_x, ma_x);
420         nearest_y = bound(mi_y, org_y, ma_y);
421         nearest_z = bound(mi_z, org_z, ma_z);
422         return nearest;
423 }
424
425 .float WarpZone_findradius_hit;
426 .entity WarpZone_findradius_next;
427 void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,               vector transform, vector shift, float needlineofsight)
428 //                               blast origin of current search   original blast origin   how to untransform (victim to blast system)
429 {
430         vector org_new;
431         vector org0_new;
432         vector shift_new, transform_new;
433         vector p;
434         entity e, e0;
435         entity wz;
436         if(rad <= 0)
437                 return;
438         e0 = findradius(org, rad);
439         wz = world;
440
441         for(e = e0; e; e = e.chain)
442         {
443                 p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
444                 if(needlineofsight)
445                 {
446                         traceline(org, p, MOVE_NOMONSTERS, e);
447                         if(trace_fraction < 1)
448                                 continue;
449                 }
450                 if(!e.WarpZone_findradius_hit || vlen(e.WarpZone_findradius_dist) > vlen(org0 - p))
451                 {
452                         e.WarpZone_findradius_nearest = p;
453                         e.WarpZone_findradius_dist = org0 - p;
454                         e.WarpZone_findradius_findorigin = org;
455                         e.WarpZone_findradius_findradius = rad;
456                         if(e.classname == "warpzone_refsys")
457                         {
458                                 // ignore, especially: do not overwrite the refsys parameters
459                         }
460                         else if(e.classname == "trigger_warpzone")
461                         {
462                                 e.WarpZone_findradius_next = wz;
463                                 wz = e;
464                                 e.WarpZone_findradius_hit = 1;
465                                 e.enemy.WarpZone_findradius_dist = '0 0 0'; // we don't want to go through this zone ever again
466                                 e.enemy.WarpZone_findradius_hit = 1;
467                         }
468                         else
469                         {
470                                 e.warpzone_transform = transform;
471                                 e.warpzone_shift = shift;
472                                 e.WarpZone_findradius_hit = 1;
473                         }
474                 }
475         }
476         for(e = wz; e; e = e.WarpZone_findradius_next)
477         {
478                 org0_new = WarpZone_TransformOrigin(e, org);
479                 traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
480                 org_new = trace_endpos;
481
482                 transform_new = AnglesTransform_Multiply(e.warpzone_transform, transform);
483                 shift_new = AnglesTransform_Multiply_GetPostShift(e.warpzone_transform, e.warpzone_shift, transform, shift);
484                 WarpZone_FindRadius_Recurse(
485                         org_new,
486                         bound(0, rad - vlen(org_new - org0_new), rad - 8),
487                         org0_new,
488                         transform_new, shift_new,
489                         needlineofsight);
490                 e.WarpZone_findradius_hit = 0;
491                 e.enemy.WarpZone_findradius_hit = 0;
492         }
493 }
494 entity WarpZone_FindRadius(vector org, float rad, float needlineofsight)
495 {
496         entity e0, e;
497         WarpZone_FindRadius_Recurse(org, rad, org, '0 0 0', '0 0 0', needlineofsight);
498         e0 = findchainfloat(WarpZone_findradius_hit, 1);
499         for(e = e0; e; e = e.chain)
500                 e.WarpZone_findradius_hit = 0;
501         return e0;
502 }
503
504 .entity WarpZone_refsys;
505 void WarpZone_RefSys_GC()
506 {
507         // garbage collect unused reference systems
508         self.nextthink = time + 1;
509         if(self.owner.WarpZone_refsys != self)
510                 remove(self);
511 }
512 void WarpZone_RefSys_Add(entity me, entity wz)
513 {
514         if(me.WarpZone_refsys.owner != me)
515         {
516                 me.WarpZone_refsys = spawn();
517                 me.WarpZone_refsys.classname = "warpzone_refsys";
518                 me.WarpZone_refsys.owner = me;
519                 me.WarpZone_refsys.think = WarpZone_RefSys_GC;
520                 me.WarpZone_refsys.nextthink = time + 1;
521                 WarpZone_Accumulator_Clear(me.WarpZone_refsys);
522         }
523         if(wz)
524                 WarpZone_Accumulator_Add(me.WarpZone_refsys, wz);
525 }
526 .vector WarpZone_refsys_incremental_shift;
527 .vector WarpZone_refsys_incremental_transform;
528 void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
529 {
530         vector t, s;
531         if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
532         if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
533                 return;
534         if(me.WarpZone_refsys.owner != me)
535         {
536                 me.WarpZone_refsys = spawn();
537                 me.WarpZone_refsys.classname = "warpzone_refsys";
538                 me.WarpZone_refsys.owner = me;
539                 me.WarpZone_refsys.think = WarpZone_RefSys_GC;
540                 me.WarpZone_refsys.nextthink = time + 1;
541                 WarpZone_Accumulator_Clear(me.WarpZone_refsys);
542         }
543         t = AnglesTransform_Invert(me.WarpZone_refsys_incremental_transform);
544         s = AnglesTransform_PrePostShift_GetPostShift(me.WarpZone_refsys_incremental_shift, t, '0 0 0');
545         WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
546         WarpZone_Accumulator_Add(me.WarpZone_refsys, ref);
547         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
548         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
549 }
550 void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref)
551 {
552         me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
553         me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
554 }
555 vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org)
556 {
557         if(from.WarpZone_refsys)
558                 org = WarpZone_UnTransformOrigin(from.WarpZone_refsys, org);
559         if(to.WarpZone_refsys)
560                 org = WarpZone_TransformOrigin(to.WarpZone_refsys, org);
561         return org;
562 }
563 vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel)
564 {
565         if(from.WarpZone_refsys)
566                 vel = WarpZone_UnTransformVelocity(from.WarpZone_refsys, vel);
567         if(to.WarpZone_refsys)
568                 vel = WarpZone_TransformVelocity(to.WarpZone_refsys, vel);
569         return vel;
570 }
571 vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang)
572 {
573         if(from.WarpZone_refsys)
574                 ang = WarpZone_UnTransformAngles(from.WarpZone_refsys, ang);
575         if(to.WarpZone_refsys)
576                 ang = WarpZone_TransformAngles(to.WarpZone_refsys, ang);
577         return ang;
578 }
579 vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
580 {
581         if(from.WarpZone_refsys)
582                 ang = WarpZone_UnTransformVAngles(from.WarpZone_refsys, ang);
583         if(to.WarpZone_refsys)
584                 ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
585         return ang;
586 }
587 entity WarpZone_RefSys_SpawnSameRefSys(entity me)
588 {
589         entity e;
590         e = spawn();
591         if(me.WarpZone_refsys)
592         {
593                 e.WarpZone_refsys = spawn();
594                 e.WarpZone_refsys.classname = "warpzone_refsys";
595                 e.WarpZone_refsys.owner = e;
596                 e.WarpZone_refsys.think = WarpZone_RefSys_GC;
597                 e.WarpZone_refsys.nextthink = time + 1;
598                 e.WarpZone_refsys.warpzone_shift = me.WarpZone_refsys.warpzone_shift;
599                 e.WarpZone_refsys.warpzone_transform = me.WarpZone_refsys.warpzone_transform;
600         }
601         return e;
602 }