Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
jumppads.qc
Go to the documentation of this file.
1#include "jumppads.qh"
2// TODO: split target_push and put it in the target folder
3#ifdef SVQC
5
6void trigger_push_use(entity this, entity actor, entity trigger)
7{
8 if(teamplay)
9 {
10 this.team = actor.team;
12 }
13}
14#endif
15
16REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH)
17REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH_VELOCITY)
18REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH)
19
20/*
21 trigger_push_calculatevelocity
22
23 Arguments:
24 org - origin of the object which is to be pushed
25 tgt - target entity (can be either a point or a model entity; if it is
26 the latter, its midpoint is used)
27 ht - jump height, measured from the higher one of org and tgt's midpoint
28 pushed_entity - object that is to be pushed
29
30 Returns: velocity for the jump
31 */
33{
34 float grav, sdist, zdist, vs, vz, jumpheight;
35 vector sdir, torg;
36
37 torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
38
39 grav = PHYS_GRAVITY(NULL);
40 if(pushed_entity && pushed_entity.gravity)
41 grav *= pushed_entity.gravity;
42
43 // Q3 has frametime-dependent gravity, but its trigger_push velocity calculation doesn't account for that.
44 // This discrepancy can be simulated accurately which ensures that all entities will arrive
45 // where they would in Q3 with gravity 800 at 125fps, even if entity-specific gravity is applied.
46 // This can be hard-coded because we don't support the Q3 world.gravity field at this time.
47 // See physicsCPMA.cfg for maths and test results.
49 grav /= 750/800; // exact float, unlike 800/750
50
51 zdist = torg.z - org.z;
52 sdist = vlen(torg - org - zdist * '0 0 1');
53 sdir = normalize(torg - org - zdist * '0 0 1');
54
55 // how high do we need to push the player?
56 jumpheight = fabs(ht);
57 if(zdist > 0)
58 jumpheight = jumpheight + zdist;
59
60 /*
61 STOP.
62
63 You will not understand the following equations anyway...
64 But here is what I did to get them.
65
66 I used the functions
67
68 s(t) = t * vs
69 z(t) = t * vz - 1/2 grav t^2
70
71 and solved for:
72
73 s(ti) = sdist
74 z(ti) = zdist
75 max(z, ti) = jumpheight
76
77 From these three equations, you will find the three parameters vs, vz
78 and ti.
79 */
80
81 // push them so high...
82 vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)!
83
84 // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
85 if(ht < 0)
86 if(zdist < 0)
87 vz = -vz;
88
89 vector solution;
90 solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
91 // ALWAYS solvable because jumpheight >= zdist
92 if(!solution.z)
93 solution_y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
94 if(zdist == 0)
95 solution_x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
96
97 float flighttime;
98 if(zdist < 0)
99 {
100 // down-jump
101 if(ht < 0)
102 {
103 // almost straight line type
104 // jump apex is before the jump
105 // we must take the larger one
106 flighttime = solution.y;
107 }
108 else
109 {
110 // regular jump
111 // jump apex is during the jump
112 // we must take the larger one too
113 flighttime = solution.y;
114 }
115 }
116 else
117 {
118 // up-jump
119 if(ht < 0)
120 {
121 // almost straight line type
122 // jump apex is after the jump
123 // we must take the smaller one
124 flighttime = solution.x;
125 }
126 else
127 {
128 // regular jump
129 // jump apex is during the jump
130 // we must take the larger one
131 flighttime = solution.y;
132 }
133 }
134 vs = sdist / flighttime;
135
136 // finally calculate the velocity
137 return sdir * vs + '0 0 1' * vz;
138}
139
140vector trigger_push_velocity_calculatevelocity(entity this, vector org, entity tgt, float speed, float count, entity pushed_entity, bool already_pushed)
141{
142 bool is_playerdir_xy = boolean(this.spawnflags & PUSH_VELOCITY_PLAYERDIR_XY);
143 bool is_add_xy = boolean(this.spawnflags & PUSH_VELOCITY_ADD_XY);
144 bool is_playerdir_z = boolean(this.spawnflags & PUSH_VELOCITY_PLAYERDIR_Z);
145 bool is_add_z = boolean(this.spawnflags & PUSH_VELOCITY_ADD_Z);
146 bool is_bidirectional_xy = boolean(this.spawnflags & PUSH_VELOCITY_BIDIRECTIONAL_XY);
147 bool is_bidirectional_z = boolean(this.spawnflags & PUSH_VELOCITY_BIDIRECTIONAL_Z);
148 bool is_clamp_negative_adds = boolean(this.spawnflags & PUSH_VELOCITY_CLAMP_NEGATIVE_ADDS);
149
150 vector sdir = normalize(vec2(pushed_entity.velocity));
151 float zdir = pushed_entity.velocity.z;
152 if(zdir != 0) zdir = copysign(1, zdir);
153
154 vector vs_tgt = '0 0 0';
155 float vz_tgt = 0;
156 if (!is_playerdir_xy || !is_playerdir_z)
157 {
158 vector vel_tgt = trigger_push_calculatevelocity(org, tgt, 0, pushed_entity);
159 vs_tgt = vec2(vel_tgt);
160 vz_tgt = vel_tgt.z;
161
162 // bidirectional jump pads do not play nicely with xonotic's jump pad targets
163 if (is_bidirectional_xy)
164 {
165 if (normalize(vs_tgt) * sdir < 0)
166 {
167 vs_tgt *= -1;
168 }
169 }
170
171 if (is_bidirectional_z)
172 {
173 if (signbit(vz_tgt) != signbit(zdir))
174 {
175 vz_tgt *= -1;
176 }
177 }
178 }
179
180 vector vs;
181 if (is_playerdir_xy)
182 {
183 vs = sdir * speed;
184 }
185 else
186 {
187 vs = vs_tgt;
188 }
189
190 float vz;
191 if (is_playerdir_z)
192 {
193 vz = zdir * count;
194 }
195 else
196 {
197 vz = vz_tgt;
198 }
199
200 if (is_add_xy)
201 {
202 vector vs_add = vec2(pushed_entity.velocity);
203 if (already_pushed)
204 {
205 vs = vs_add;
206 }
207 else
208 {
209 vs += vs_add;
210
211 if (is_clamp_negative_adds)
212 {
213 if ((normalize(vs) * sdir) < 0)
214 {
215 vs = '0 0 0';
216 }
217 }
218 }
219 }
220
221 if (is_add_z)
222 {
223 float vz_add = pushed_entity.velocity.z;
224 if (already_pushed)
225 {
226 vz = vz_add;
227 }
228 else
229 {
230 vz += vz_add;
231
232 if (is_clamp_negative_adds)
233 {
234 if (signbit(vz) != signbit(zdir))
235 {
236 vz = 0;
237 }
238 }
239 }
240 }
241
242 return vs + '0 0 1' * vz;
243}
244
245#ifdef SVQC
247{
248 bool found = false;
249 IL_EACH(g_moveables, it.last_pushed == this,
250 {
251 if(!WarpZoneLib_ExactTrigger_Touch(this, it, false))
252 it.last_pushed = NULL;
253 else
254 found = true;
255 });
256
257 if(found)
258 this.nextthink = time;
259 else
260 setthink(this, func_null);
261}
262#endif
263
264bool jumppad_push(entity this, entity targ, bool is_velocity_pad)
265{
266 if (!isPushable(targ))
267 return false;
268
269 vector org = targ.origin;
270
272 org = (this.absmin + this.absmax) * 0.5;
273
274 bool already_pushed = false;
275 if(is_velocity_pad) // remember velocity jump pads
276 {
277 if(this == targ.last_pushed || (targ.last_pushed && !STAT(Q3COMPAT, targ))) // if q3compat is active overwrite last stored jump pad, otherwise ignore
278 {
279 already_pushed = true;
280 }
281 else
282 {
283 targ.last_pushed = this; // may be briefly out of sync between client and server if client prediction is toggled
284
285 #ifdef SVQC
287 this.nextthink = time;
288 #endif
289 }
290 }
291
292 if(this.enemy)
293 {
294 if(!is_velocity_pad)
295 {
296 targ.velocity = trigger_push_calculatevelocity(org, this.enemy, this.height, targ);
297 }
298 else
299 {
300 targ.velocity = trigger_push_velocity_calculatevelocity(this, org, this.enemy, this.speed, this.count, targ, already_pushed);
301 }
302 }
303 else if(this.target && this.target != "")
304 {
305 entity e;
307 for(e = NULL; (e = find(e, targetname, this.target)); )
308 {
309 if(e.cnt)
310 RandomSelection_AddEnt(e, e.cnt, 1);
311 else
312 RandomSelection_AddEnt(e, 1, 1);
313 }
314 if(!is_velocity_pad)
315 {
317 }
318 else
319 {
320 targ.velocity = trigger_push_velocity_calculatevelocity(this, org, RandomSelection_chosen_ent, this.speed, this.count, targ, already_pushed);
321 }
322 }
323 else
324 {
325 if(!is_velocity_pad)
326 {
327 targ.velocity = this.movedir;
328 }
329 else
330 {
331#ifdef SVQC
332 objerror (this, "Jumppad with no target");
333#endif
334 return false;
335 }
336 }
337
338 if(!is_velocity_pad) UNSET_ONGROUND(targ);
339
340#ifdef CSQC
341 if (targ.flags & FL_PROJECTILE)
342 {
343 targ.angles = vectoangles (targ.velocity);
344 switch(targ.move_movetype)
345 {
346 case MOVETYPE_FLY:
348 targ.gravity = 1;
349 break;
352 targ.gravity = 1;
353 break;
354 }
355 }
356#endif
357
358#ifdef SVQC
359 if (IS_PLAYER(targ))
360 {
361 // reset tracking of oldvelocity for impact damage (sudden velocity changes)
362 targ.oldvelocity = targ.velocity;
363
364 // prevent sound spam when a player hits the jumppad more than once
365 // or when a dead player gets stuck in the jumppad for some reason
366 if(!already_pushed && this.pushltime < time && !(IS_DEAD(targ) && targ.velocity == '0 0 0'))
367 {
368 if (Q3COMPAT_COMMON && this.classname == "target_push")
369 this.pushltime = time + 1.5;
370 else
371 {
372 // flash when activated
373 Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1);
374 this.pushltime = time + 0.2;
375 }
376 _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
377 }
378
379 if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ))
380 {
381 bool found = false;
382 for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
383 if(targ.(jumppadsused[i]) == this)
384 found = true;
385 if(!found)
386 {
387 targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this;
388 targ.jumppadcount = targ.jumppadcount + 1;
389 }
390
391 if(IS_REAL_CLIENT(targ))
392 {
393 if(this.message)
394 centerprint(targ, this.message);
395 }
396 else
397 {
398 targ.lastteleporttime = time;
399 targ.lastteleport_origin = targ.origin;
400 }
401
402 if (!IS_DEAD(targ))
404 }
405 else
406 targ.jumppadcount = 1;
407
408 // reset tracking of who pushed you into a hazard (for kill credit)
409 targ.pushltime = 0;
410 targ.istypefrag = 0;
411 }
412
413 if(this.enemy.target)
414 SUB_UseTargets(this.enemy, targ, this);
415
416 if (targ.flags & FL_PROJECTILE)
417 {
418 targ.angles = vectoangles (targ.velocity);
419 targ.com_phys_gravity_factor = 1;
420 switch(targ.move_movetype)
421 {
422 case MOVETYPE_FLY:
424 targ.gravity = 1;
425 break;
428 targ.gravity = 1;
429 break;
430 }
432 }
433#endif
434
435 return true;
436}
437
439{
440 if (this.active == ACTIVE_NOT)
441 return;
442
443 if(this.team)
444 if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, toucher)))
445 return;
446
448
449 noref bool success = jumppad_push(this, toucher, false);
450
451#ifdef SVQC
452 if (success && (this.spawnflags & PUSH_ONCE))
453 {
454 settouch(this, func_null);
455 setthink(this, SUB_Remove);
456 this.nextthink = time;
457 }
458#endif
459}
460
462{
463 if (this.active == ACTIVE_NOT)
464 return;
465
466 if(this.team && DIFF_TEAM(this, toucher))
467 return;
468
470
471 jumppad_push(this, toucher, true);
472}
473
474#ifdef SVQC
475void trigger_push_link(entity this);
477bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org)
478{
479 setorigin(tracetest_ent, org);
480 tracetoss(tracetest_ent, tracetest_ent);
482 return false;
483
484 if (!jp.height)
485 {
486 // since tracetoss starting from jumppad's origin often fails when target
487 // is very close to real destination, start it directly from target's
488 // origin instead
489 vector ofs = '0 0 0';
490 if (vdist(vec2(tracetest_ent.velocity), <, autocvar_sv_maxspeed))
491 ofs = stepheightvec;
492
493 tracetest_ent.velocity.z = 0;
494 setorigin(tracetest_ent, targ.origin + ofs);
495 tracetoss(tracetest_ent, tracetest_ent);
496 if (trace_startsolid && ofs.z)
497 {
498 setorigin(tracetest_ent, targ.origin + ofs * 0.5);
499 tracetoss(tracetest_ent, tracetest_ent);
500 if (trace_startsolid && ofs.z)
501 {
502 setorigin(tracetest_ent, targ.origin);
503 tracetoss(tracetest_ent, tracetest_ent);
505 return false;
506 }
507 }
508 }
509 tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent);
510 return true;
511}
512
514{
515 setorigin(tracetest_ent, org);
516 tracetoss(tracetest_ent, tracetest_ent);
517
519 return false;
520 if (trace_ent == item)
521 return true;
522
523 tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent);
524
525 if (trace_ent == item)
526 return true;
527
528 return false;
529}
530#endif
531
532#ifdef SVQC
534{
535 // calculate a typical start point for the jump
536 vector org = (this.absmin + this.absmax) * 0.5;
537 org.z = this.absmax.z - PL_MIN_CONST.z - 7;
538 return org;
539}
540
542{
544
545 float grav = PHYS_GRAVITY(NULL);
546
547 entity t = this.enemy;
548 if (t)
549 {
550 entity e = spawn();
551 setsize(e, PL_MIN_CONST, PL_MAX_CONST);
554 vector v2 = trigger_push_calculatevelocity(endpos, t, this.height, e);
555 delete(e);
556 return (v.z + v2.z) / grav;
557 }
558 else if (!(this.target && this.target != ""))
559 {
560 if (!this.team)
561 {
562 vector v = this.movedir;
563
564 float t = v.z / grav;
565 float jump_height = 1/2 * grav * (t ** 2);
566 float remaining_height = org.z + jump_height - endpos.z;
567 float v2_z = sqrt(2 * grav * remaining_height);
568
569 return (v.z + v2_z) / grav;
570 }
571 }
572 return 0;
573}
574#endif
575
576// if (item != NULL) returns true if the item can be reached by using this jumppad, false otherwise
577// if (item == NULL) tests jumppad's trajectory and eventually spawns waypoints for it (return value doesn't matter)
578// NOTE: for simplicity's sake it improperly tests jumppad bboxes instead of bmodels
580{
581#ifdef SVQC
583#endif
584
585 if (this.target)
586 {
587 int n = 0;
588#ifdef SVQC
589 vector vel = '0 0 0';
590#endif
591 for(entity t = NULL; (t = find(t, targetname, this.target)); )
592 {
593 ++n;
594#ifdef SVQC
595 if(t.move_movetype != MOVETYPE_NONE)
596 continue;
597
598 // bots can't tell teamed jumppads from normal ones
599 if (this.team)
600 continue;
601
602 entity e = spawn();
603 setsize(e, PL_MIN_CONST, PL_MAX_CONST);
605 e.velocity = trigger_push_calculatevelocity(org, t, this.height, e);
606
607 vel = e.velocity;
608 vector best_target = '0 0 0';
609 vector best_org = '0 0 0';
610 vector best_vel = '0 0 0';
611 bool valid_best_target = false;
612 if (item)
613 {
615 {
616 delete(e);
617 return false;
618 }
619 }
620 else
621 {
622 // optimization: if horizontal velocity is 0 then target is not good since the trajectory
623 // will definitely go back to the jumppad (horizontal velocity of best_vel can't be 0 anyway)
624 if ((e.velocity.x != 0 || e.velocity.y != 0)
625 && trigger_push_testorigin(e, t, this, org))
626 {
627 best_target = trace_endpos;
628 best_org = org;
629 best_vel = e.velocity;
630 valid_best_target = true;
631 }
632 }
633
634 vector new_org;
635 vector dist = t.origin - org;
636 if (dist.x || dist.y) // if not perfectly vertical
637 {
638 // test trajectory with different starting points, sometimes the trajectory
639 // starting from the jumppad origin can't reach the real destination
640 // and destination waypoint ends up near the jumppad itself
641 vector flatdir = normalize(dist - eZ * dist.z);
642 vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y));
643 new_org = org + ofs;
644
645 LABEL(new_test)
646 e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e);
647 if (item)
648 {
649 if (!trigger_push_testorigin_for_item(e, item, new_org))
650 {
651 delete(e);
652 return false;
653 }
654 }
655 else
656 {
657 vel = e.velocity;
658 if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed))
659 e.velocity = autocvar_sv_maxspeed * flatdir;
660 if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50))
661 {
662 best_target = trace_endpos;
663 best_org = new_org;
664 best_vel = vel;
665 valid_best_target = true;
666 }
667 }
668 if (ofs && new_org != org - ofs)
669 {
670 new_org = org - ofs;
671 goto new_test;
672 }
673 }
674
675 if (item)
676 {
677 delete(e);
678 return true;
679 }
680
681 if (valid_best_target)
682 {
683 if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, best_target + PL_MIN_CONST, best_target + PL_MAX_CONST)))
684 {
685 float velxy = vlen(vec2(best_vel));
686 float cost = vlen(vec2(t.origin - best_org)) / velxy;
687 if(velxy < autocvar_sv_maxspeed)
688 velxy = autocvar_sv_maxspeed;
689 cost += vlen(vec2(best_target - t.origin)) / velxy;
690 waypoint_spawnforteleporter(this, best_target, cost, e);
691 }
692 }
693 delete(e);
694#endif
695 }
696
697 if(item)
698 return false;
699
700 if(!n)
701 {
702 // no dest!
703#ifdef SVQC
704 objerror (this, "Jumppad with nonexistant target");
705#endif
706 return false;
707 }
708 else if(n == 1)
709 {
710 // exactly one dest - bots love that
711 if (!this.team)
712 this.enemy = find(NULL, targetname, this.target);
713 else // bots can't tell teamed jumppads from normal ones
714 this.enemy = NULL;
715 }
716 else
717 {
718 // have to use random selection every single time
719 this.enemy = NULL;
720 }
721
722 }
723#ifdef SVQC
724 else
725 {
726 if (!this.team)
727 {
728 entity e = spawn();
729 setsize(e, PL_MIN_CONST, PL_MAX_CONST);
731 setorigin(e, org);
732 e.velocity = this.movedir;
733 tracetoss(e, e);
734 if (item)
735 {
736 bool r = (trace_ent == item);
737 delete(e);
738 return r;
739 }
742 delete(e);
743 }
744 else if (item)
745 return false;
746 }
747
748 defer(this, 0.1, trigger_push_updatelink);
749#endif
750 return true;
751}
752
754{
755 trigger_push_test(this, NULL);
756}
757
758#ifdef SVQC
759float trigger_push_send(entity this, entity to, float sf)
760{
761 WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
762
764 WriteInt24_t(MSG_ENTITY, this.spawnflags);
767
768 trigger_common_write(this, true);
769
770 return true;
771}
772
773float trigger_push_velocity_send(entity this, entity to, float sf)
774{
775 WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH_VELOCITY);
776
778 WriteInt24_t(MSG_ENTITY, this.spawnflags);
782
783 trigger_common_write(this, true);
784
785 return true;
786}
787
789{
791}
792
797
802
803/*
804 * ENTITY PARAMETERS:
805 *
806 * target: target of jump
807 * height: the absolute value is the height of the highest point of the jump
808 * trajectory above the higher one of the player and the target.
809 * the sign indicates whether the highest point is INSIDE (positive)
810 * or OUTSIDE (negative) of the jump trajectory. General rule: use
811 * positive values for targets mounted on the floor, and use negative
812 * values to target a point on the ceiling.
813 * movedir: if target is not set, movedir * speed * 10 is the velocity to be reached.
814 */
815spawnfunc(trigger_push)
816{
817 SetMovedir(this);
818
821 this.active = ACTIVE_ACTIVE;
822 this.use = trigger_push_use;
824
825 // normal push setup
826 if (!this.speed)
827 this.speed = 1000;
828 this.movedir = this.movedir * this.speed * 10;
829
830 if (!this.noise)
831 this.noise = "misc/jumppad.wav";
832 precache_sound (this.noise);
833
834 trigger_push_link(this); // link it now
835
836 IL_PUSH(g_jumppads, this);
837
838 // this must be called to spawn the teleport waypoints for bots
840}
841
842/*
843 * ENTITY PARAMETERS:
844 *
845 * target: this points to the target_position to which the player will jump.
846 * speed: XY speed for player-directional velocity pads - either sets or adds to the player's horizontal velocity.
847 * count: Z speed for player-directional velocity pads - either sets or adds to the player's vertical velocity.
848 */
849spawnfunc(trigger_push_velocity)
850{
853 this.active = ACTIVE_ACTIVE;
854 this.use = trigger_push_use;
856
857 // normal push setup
858 if (!this.noise)
859 this.noise = "misc/jumppad.wav";
860 precache_sound (this.noise);
861
862 trigger_push_velocity_link(this); // link it now
863}
864
865
866bool target_push_send(entity this, entity to, float sf)
867{
868 WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
869
870 WriteByte(MSG_ENTITY, this.cnt);
872 WriteVector(MSG_ENTITY, this.origin);
873
874 WriteAngleVector(MSG_ENTITY, this.angles);
875
876 return true;
877}
878
879void target_push_use(entity this, entity actor, entity trigger)
880{
881 if(trigger.classname == "trigger_push" || trigger == this)
882 return; // WTF, why is this a thing
883
884 jumppad_push(this, actor, false);
885}
886
888{
890 Net_LinkEntity(this, false, 0, target_push_send);
891 //this.SendFlags |= 1; // update
892}
893
895{
896 this.mangle = this.angles;
897 setorigin(this, this.origin);
898 target_push_link(this);
899}
900
901spawnfunc(target_push)
902{
903 target_push_init(this); // normal push target behaviour can be combined with a legacy pusher?
904 this.use = target_push_use;
905
906 if(this.target && this.target != "") // Q3 or old style Nexuiz pusher
907 {
908 entity trigger_ent = findchain(target, this.targetname);
909 if (trigger_ent)
910 {
911 // apply size of its trigger entity so that it can be tested like a conventional
912 // trigger_push jumppad and spawn an usable box waypoyint
913 this.absmin = trigger_ent.absmin;
914 this.absmax = trigger_ent.absmax;
915 }
916 IL_PUSH(g_jumppads, this);
918 }
919 else // Q3 .angles and .speed pusher
920 {
921 if (!this.speed)
922 this.speed = 1000;
923 SetMovedir(this); // this clears .angles so it must be after target_push_init()
924 this.movedir *= this.speed;
925 }
926
927 if (!this.noise)
928 {
930 this.noise = "sound/misc/windfly.wav"; // Q3 mappers provide this, it's not in pak0
931 else
932 this.noise = "misc/jumppad.wav";
933 }
934 precache_sound (this.noise);
935}
936
937spawnfunc(info_notnull)
938{
939 target_push_init(this);
940}
941spawnfunc(target_position)
942{
943 target_push_init(this);
944}
945
946#elif defined(CSQC)
947
948NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew)
949{
950 int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
951 this.spawnflags = ReadInt24_t();
952 this.active = ReadByte();
953 this.height = ReadCoord();
954
955 trigger_common_read(this, true);
956
957 this.entremove = trigger_remove_generic;
958 this.solid = SOLID_TRIGGER;
960 this.move_time = time;
961 defer(this, 0.25, trigger_push_findtarget);
962
963 return true;
964}
965
966NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH_VELOCITY, bool isnew)
967{
968 int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
969 this.spawnflags = ReadInt24_t();
970 this.active = ReadByte();
971 this.speed = ReadCoord();
972 this.count = ReadCoord();
973
974 trigger_common_read(this, true);
975
976 this.entremove = trigger_remove_generic;
977 this.solid = SOLID_TRIGGER;
979 this.move_time = time;
980
981 return true;
982}
983
984void target_push_remove(entity this)
985{
986 // strfree(this.classname);
987 strfree(this.targetname);
988}
989
990NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew)
991{
992 this.cnt = ReadByte();
993 this.targetname = strzone(ReadString());
994 this.origin = ReadVector();
995
996 this.angles = ReadAngleVector();
997
998 return = true;
999
1000 setorigin(this, this.origin);
1001
1002 this.drawmask = MASK_NORMAL;
1003 this.entremove = target_push_remove;
1004}
1005#endif
void animdecide_setaction(entity e, float action, float restart)
const int ANIMACTION_JUMP
void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent)
float height
Definition bobbing.qc:3
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
float cnt
Definition powerups.qc:24
string message
Definition powerups.qc:19
float count
Definition powerups.qc:22
int team
Definition main.qh:188
int spawnflags
Definition ammo.qh:15
#define IS_DEAD(s)
Definition player.qh:245
float autocvar_sv_maxspeed
Definition player.qh:53
#define IS_PLAYER(s)
Definition player.qh:243
#define Q3COMPAT_COMMON
Definition stats.qh:368
#define LABEL(id)
Definition compiler.qh:34
const vector PL_MIN_CONST
Definition constants.qh:56
const int FL_PROJECTILE
Definition constants.qh:85
const int INITPRIO_FINDTARGET
Definition constants.qh:96
const vector PL_MAX_CONST
Definition constants.qh:55
string classname
float drawmask
entity trace_ent
float DPCONTENTS_BOTCLIP
const float SOLID_TRIGGER
float DPCONTENTS_SOLID
const float MASK_NORMAL
float DPCONTENTS_BODY
float effects
float DPCONTENTS_PLAYERCLIP
float time
vector trace_endpos
float trace_startsolid
float nextthink
vector absmax
const float EF_NODEPTHTEST
vector origin
vector absmin
#define spawn
#define use
void UpdateCSQCProjectile(entity e)
void defer(entity this, float fdelay, void(entity) func)
Execute func() after time + fdelay.
Definition defer.qh:29
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:13
const int ACTIVE_NOT
Definition defs.qh:36
const int INVERT_TEAMS
Definition defs.qh:10
const int SF_TRIGGER_INIT
Definition defs.qh:22
int active
Definition defs.qh:34
const int ACTIVE_ACTIVE
Definition defs.qh:37
const int SF_TRIGGER_UPDATE
Definition defs.qh:23
float speed
Definition dynlight.qc:9
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:124
ent angles
Definition ent_cs.qc:121
solid
Definition ent_cs.qc:165
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_EACH(this, cond, body)
float trigger_push_send(entity this, entity to, float sf)
Definition jumppads.qc:759
vector trigger_push_get_start_point(entity this)
Definition jumppads.qc:533
void target_push_link(entity this)
Definition jumppads.qc:887
bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org)
Definition jumppads.qc:477
bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org)
Definition jumppads.qc:513
void trigger_push_use(entity this, entity actor, entity trigger)
Definition jumppads.qc:6
void target_push_init(entity this)
Definition jumppads.qc:894
void trigger_push_findtarget(entity this)
Definition jumppads.qc:753
bool trigger_push_test(entity this, entity item)
Definition jumppads.qc:579
bool jumppad_push(entity this, entity targ, bool is_velocity_pad)
Definition jumppads.qc:264
float trigger_push_get_push_time(entity this, vector endpos)
Definition jumppads.qc:541
vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity)
Definition jumppads.qc:32
bool target_push_send(entity this, entity to, float sf)
Definition jumppads.qc:866
void trigger_push_link(entity this)
Definition jumppads.qc:793
void trigger_push_velocity_think(entity this)
Definition jumppads.qc:246
float trigger_push_velocity_send(entity this, entity to, float sf)
Definition jumppads.qc:773
void trigger_push_updatelink(entity this)
Definition jumppads.qc:788
void trigger_push_velocity_link(entity this)
Definition jumppads.qc:798
vector trigger_push_velocity_calculatevelocity(entity this, vector org, entity tgt, float speed, float count, entity pushed_entity, bool already_pushed)
Definition jumppads.qc:140
void trigger_push_velocity_touch(entity this, entity toucher)
Definition jumppads.qc:461
void target_push_use(entity this, entity actor, entity trigger)
Definition jumppads.qc:879
void trigger_push_touch(entity this, entity toucher)
Definition jumppads.qc:438
#define PUSH_VELOCITY_PLAYERDIR_Z
Definition jumppads.qh:10
const int PUSH_ONCE
Definition jumppads.qh:4
#define PUSH_VELOCITY_ADD_Z
Definition jumppads.qh:11
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
#define PUSH_VELOCITY_PLAYERDIR_XY
Definition jumppads.qh:8
#define PUSH_VELOCITY_CLAMP_NEGATIVE_ADDS
Definition jumppads.qh:14
const int NUM_JUMPPADSUSED
Definition jumppads.qh:27
#define Q3_TARGET_PUSH_JUMPPAD
Definition jumppads.qh:16
IntrusiveList g_jumppads
Definition jumppads.qh:18
#define PUSH_VELOCITY_BIDIRECTIONAL_XY
Definition jumppads.qh:12
#define PUSH_VELOCITY_BIDIRECTIONAL_Z
Definition jumppads.qh:13
#define PUSH_VELOCITY_ADD_XY
Definition jumppads.qh:9
float pushltime
Definition jumppads.qh:21
const int PUSH_STATIC
Definition jumppads.qh:6
int SendFlags
Definition net.qh:118
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
int ReadInt24_t()
Definition net.qh:355
#define ReadAngleVector()
Definition net.qh:369
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
#define STAT(...)
Definition stats.qh:82
#define EXACTTRIGGER_TOUCH(e, t)
Definition common.qh:115
#define BITSET_ASSIGN(a, b)
Definition common.qh:104
vector movedir
Definition viewloc.qh:18
ERASEABLE vector solve_quadratic(float a, float b, float c)
ax^2 + bx + c = 0
Definition math.qh:304
bool signbit(float e)
Definition mathlib.qc:43
float copysign(float e, float f)
Definition mathlib.qc:225
void WriteString(string data, float dest, float desto)
entity find(entity start,.string field, string match)
float vlen(vector v)
vector vectoangles(vector v)
float sqrt(float f)
string precache_sound(string sample)
void WriteCoord(float data, float dest, float desto)
void centerprint(string text,...)
float min(float f,...)
vector normalize(vector v)
void WriteByte(float data, float dest, float desto)
float fabs(float f)
string strzone(string s)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_NONE
Definition movetypes.qh:129
#define PHYS_GRAVITY(s)
Definition movetypes.qh:53
const int MOVETYPE_BOUNCEMISSILE
Definition movetypes.qh:140
float move_time
Definition movetypes.qh:77
#define UNSET_ONGROUND(s)
Definition movetypes.qh:18
const int MOVETYPE_FLY
Definition movetypes.qh:134
const int MOVETYPE_TOSS
Definition movetypes.qh:135
const int MOVETYPE_BOUNCE
Definition movetypes.qh:139
vector stepheightvec
Definition navigation.qh:11
var void func_null()
#define NULL
Definition post.qh:14
#define objerror
Definition pre.qh:8
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
#define RandomSelection_AddEnt(e, weight, priority)
Definition random.qh:14
entity RandomSelection_chosen_ent
Definition random.qh:5
#define setthink(e, f)
vector
Definition self.qh:92
vector org
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
const int CH_TRIGGER
Definition sound.qh:12
const float VOL_BASE
Definition sound.qh:36
#define _sound(e, c, s, v, a)
Definition sound.qh:43
const float ATTEN_NORM
Definition sound.qh:30
#define spawnfunc(id)
Definition spawnfunc.qh:96
#define strfree(this)
Definition string.qh:59
void SetMovedir(entity this)
Definition subs.qc:540
string noise
Definition subs.qh:83
vector mangle
Definition subs.qh:51
entity enemy
Definition sv_ctf.qh:153
vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity)
Definition jumppads.qc:32
bool teamplay
Definition teams.qh:59
#define DIFF_TEAM(a, b)
Definition teams.qh:242
void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
Definition triggers.qc:104
bool isPushable(entity e)
Definition triggers.qc:3
void trigger_common_write(entity this, bool withtarget)
Definition triggers.qc:110
void trigger_remove_generic(entity this)
string targetname
Definition triggers.qh:56
void trigger_common_read(entity this, bool withtarget)
string target
Definition triggers.qh:55
void WarpZoneLib_ExactTrigger_Init(entity this, bool unsetmodel)
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
const vector eZ
Definition vector.qh:46
ERASEABLE float boxesoverlap(vector m1, vector m2, vector m3, vector m4)
requires that m2>m1 in all coordinates, and that m4>m3
Definition vector.qh:73
#define vec2(...)
Definition vector.qh:90
void InitializeEntity(entity e, void(entity this) func, int order)
Definition world.qc:2209
IntrusiveList g_moveables
Definition world.qh:157