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