2 void generic_plat_blocked()
4 if(self.dmg && other.takedamage != DAMAGE_NO) {
5 if(self.dmgtime2 < time) {
6 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
7 self.dmgtime2 = time + self.dmgtime;
10 // Gib dead/dying stuff
11 if(other.deadflag != DEAD_NO)
12 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
18 float STATE_BOTTOM = 1;
22 .entity trigger_field;
24 void() plat_center_touch;
25 void() plat_outside_touch;
26 void() plat_trigger_use;
30 float PLAT_LOW_TRIGGER = 1;
32 void plat_spawn_inside_trigger()
38 trigger.touch = plat_center_touch;
39 trigger.movetype = MOVETYPE_NONE;
40 trigger.solid = SOLID_TRIGGER;
43 tmin = self.absmin + '25 25 0';
44 tmax = self.absmax - '25 25 -8';
45 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
46 if (self.spawnflags & PLAT_LOW_TRIGGER)
49 if (self.size_x <= 50)
51 tmin_x = (self.mins_x + self.maxs_x) / 2;
54 if (self.size_y <= 50)
56 tmin_y = (self.mins_y + self.maxs_y) / 2;
64 setsize (trigger, tmin, tmax);
68 // otherwise, something is fishy...
70 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
75 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
77 self.think = plat_go_down;
78 self.nextthink = self.ltime + 3;
81 void plat_hit_bottom()
83 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
89 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
91 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
101 void plat_center_touch()
103 if not(other.iscreature)
106 if (other.health <= 0)
112 else if (self.state == 1)
113 self.nextthink = self.ltime + 1; // delay going down
116 void plat_outside_touch()
118 if not(other.iscreature)
121 if (other.health <= 0)
129 void plat_trigger_use()
132 return; // already activated
139 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
140 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
142 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
143 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
144 // Gib dead/dying stuff
145 if(other.deadflag != DEAD_NO)
146 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
151 else if (self.state == 3)
153 // when in other states, then the plat_crush event came delayed after
154 // plat state already had changed
155 // this isn't a bug per se!
163 objerror ("plat_use: not in up state");
167 .string sound1, sound2;
173 setorigin (self, self.pos1);
179 setorigin (self, self.pos2);
181 self.use = plat_trigger_use;
185 void spawnfunc_path_corner() { }
186 void spawnfunc_func_plat()
188 if (self.sounds == 0)
191 if(self.spawnflags & 4)
194 if(self.dmg && (!self.message))
195 self.message = "was squished";
196 if(self.dmg && (!self.message2))
197 self.message2 = "was squished by";
199 if (self.sounds == 1)
201 precache_sound ("plats/plat1.wav");
202 precache_sound ("plats/plat2.wav");
203 self.noise = "plats/plat1.wav";
204 self.noise1 = "plats/plat2.wav";
207 if (self.sounds == 2)
209 precache_sound ("plats/medplat1.wav");
210 precache_sound ("plats/medplat2.wav");
211 self.noise = "plats/medplat1.wav";
212 self.noise1 = "plats/medplat2.wav";
217 precache_sound (self.sound1);
218 self.noise = self.sound1;
222 precache_sound (self.sound2);
223 self.noise1 = self.sound2;
226 self.mangle = self.angles;
227 self.angles = '0 0 0';
229 self.classname = "plat";
230 if not(InitMovingBrushTrigger())
232 self.effects |= EF_LOWPRECISION;
233 setsize (self, self.mins , self.maxs);
235 self.blocked = plat_crush;
242 self.height = self.size_z - self.lip;
244 self.pos1 = self.origin;
245 self.pos2 = self.origin;
246 self.pos2_z = self.origin_z - self.height;
248 self.reset = plat_reset;
251 plat_spawn_inside_trigger (); // the "start moving" trigger
259 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
267 self.think = train_next;
268 self.nextthink = self.ltime + self.wait;
282 targ = find(world, targetname, self.target);
283 self.target = targ.target;
284 if (self.spawnflags & 1)
286 cp = find(world, target, targ.targetname); // get the previous corner first
287 cp = find(world, targetname, cp.target2); // now get its second target
288 if(cp.targetname == "") // none found
290 // when using bezier curves, you must have a control point for each corner in the train's path
291 if(autocvar_developer)
292 dprint("Warning: func_train using beizer curves reached a path_corner without a control point. Please add a target2 for each path_corner used by this train!\n");
293 cp = targ; // assume a straight line to the destination as fallback
297 objerror("train_next: no next target");
298 self.wait = targ.wait;
304 if (self.spawnflags & 1)
305 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, targ.speed, train_wait);
307 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
311 if (self.spawnflags & 1)
312 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, self.speed, train_wait);
314 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
318 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
321 void func_train_find()
324 targ = find(world, targetname, self.target);
325 self.target = targ.target;
327 objerror("func_train_find: no next target");
328 setorigin(self, targ.origin - self.mins);
329 self.nextthink = self.ltime + 1;
330 self.think = train_next;
333 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
334 Ridable platform, targets spawnfunc_path_corner path to follow.
335 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
336 target : targetname of first spawnfunc_path_corner (starts here)
338 void spawnfunc_func_train()
340 if (self.noise != "")
341 precache_sound(self.noise);
344 objerror("func_train without a target");
348 if not(InitMovingBrushTrigger())
350 self.effects |= EF_LOWPRECISION;
352 // wait for targets to spawn
353 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
355 self.blocked = generic_plat_blocked;
356 if(self.dmg & (!self.message))
357 self.message = " was squished";
358 if(self.dmg && (!self.message2))
359 self.message2 = "was squished by";
360 if(self.dmg && (!self.dmgtime))
362 self.dmgtime2 = time;
364 // TODO make a reset function for this one
367 void func_rotating_setactive(float astate)
370 if (astate == ACTIVE_TOGGLE)
372 if(self.active == ACTIVE_ACTIVE)
373 self.active = ACTIVE_NOT;
375 self.active = ACTIVE_ACTIVE;
378 self.active = astate;
380 if(self.active == ACTIVE_NOT)
381 self.avelocity = '0 0 0';
383 self.avelocity = self.pos1;
386 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
387 Brush model that spins in place on one axis (default Z).
388 speed : speed to rotate (in degrees per second)
389 noise : path/name of looping .wav file to play.
390 dmg : Do this mutch dmg every .dmgtime intervall when blocked
394 void spawnfunc_func_rotating()
396 if (self.noise != "")
398 precache_sound(self.noise);
399 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
402 self.active = ACTIVE_ACTIVE;
403 self.setactive = func_rotating_setactive;
407 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
408 if (self.spawnflags & 4) // X (untested)
409 self.avelocity = '0 0 1' * self.speed;
410 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
411 else if (self.spawnflags & 8) // Y (untested)
412 self.avelocity = '1 0 0' * self.speed;
413 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
415 self.avelocity = '0 1 0' * self.speed;
417 self.pos1 = self.avelocity;
419 if(self.dmg & (!self.message))
420 self.message = " was squished";
421 if(self.dmg && (!self.message2))
422 self.message2 = "was squished by";
425 if(self.dmg && (!self.dmgtime))
428 self.dmgtime2 = time;
430 if not(InitMovingBrushTrigger())
432 // no EF_LOWPRECISION here, as rounding angles is bad
434 self.blocked = generic_plat_blocked;
436 // wait for targets to spawn
437 self.nextthink = self.ltime + 999999999;
438 self.think = SUB_Null;
440 // TODO make a reset function for this one
444 void func_bobbing_controller_think()
447 self.nextthink = time + 0.1;
449 if not (self.owner.active == ACTIVE_ACTIVE)
451 self.owner.velocity = '0 0 0';
455 // calculate sinewave using makevectors
456 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
457 v = self.owner.destvec + self.owner.movedir * v_forward_y;
458 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
459 // * 10 so it will arrive in 0.1 sec
460 self.owner.velocity = (v - self.owner.origin) * 10;
463 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
464 Brush model that moves back and forth on one axis (default Z).
465 speed : how long one cycle takes in seconds (default 4)
466 height : how far the cycle moves (default 32)
467 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
468 noise : path/name of looping .wav file to play.
469 dmg : Do this mutch dmg every .dmgtime intervall when blocked
472 void spawnfunc_func_bobbing()
475 if (self.noise != "")
477 precache_sound(self.noise);
478 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
484 // center of bobbing motion
485 self.destvec = self.origin;
486 // time scale to get degrees
487 self.cnt = 360 / self.speed;
489 self.active = ACTIVE_ACTIVE;
491 // damage when blocked
492 self.blocked = generic_plat_blocked;
493 if(self.dmg & (!self.message))
494 self.message = " was squished";
495 if(self.dmg && (!self.message2))
496 self.message2 = "was squished by";
497 if(self.dmg && (!self.dmgtime))
499 self.dmgtime2 = time;
502 if (self.spawnflags & 1) // X
503 self.movedir = '1 0 0' * self.height;
504 else if (self.spawnflags & 2) // Y
505 self.movedir = '0 1 0' * self.height;
507 self.movedir = '0 0 1' * self.height;
509 if not(InitMovingBrushTrigger())
512 // wait for targets to spawn
513 controller = spawn();
514 controller.classname = "func_bobbing_controller";
515 controller.owner = self;
516 controller.nextthink = time + 1;
517 controller.think = func_bobbing_controller_think;
518 self.nextthink = self.ltime + 999999999;
519 self.think = SUB_Null;
521 // Savage: Reduce bandwith, critical on e.g. nexdm02
522 self.effects |= EF_LOWPRECISION;
524 // TODO make a reset function for this one
528 void func_pendulum_controller_think()
531 self.nextthink = time + 0.1;
533 if not (self.owner.active == ACTIVE_ACTIVE)
535 self.owner.avelocity_x = 0;
539 // calculate sinewave using makevectors
540 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
541 v = self.owner.speed * v_forward_y + self.cnt;
542 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
544 // * 10 so it will arrive in 0.1 sec
545 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
549 void spawnfunc_func_pendulum()
552 if (self.noise != "")
554 precache_sound(self.noise);
555 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
558 self.active = ACTIVE_ACTIVE;
560 // keys: angle, speed, phase, noise, freq
564 // not initializing self.dmg to 2, to allow damageless pendulum
566 if(self.dmg & (!self.message))
567 self.message = " was squished";
568 if(self.dmg && (!self.message2))
569 self.message2 = "was squished by";
570 if(self.dmg && (!self.dmgtime))
572 self.dmgtime2 = time;
574 self.blocked = generic_plat_blocked;
576 self.avelocity_z = 0.0000001;
577 if not(InitMovingBrushTrigger())
582 // find pendulum length (same formula as Q3A)
583 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
586 // copy initial angle
587 self.cnt = self.angles_z;
589 // wait for targets to spawn
590 controller = spawn();
591 controller.classname = "func_pendulum_controller";
592 controller.owner = self;
593 controller.nextthink = time + 1;
594 controller.think = func_pendulum_controller_think;
595 self.nextthink = self.ltime + 999999999;
596 self.think = SUB_Null;
598 //self.effects |= EF_LOWPRECISION;
600 // TODO make a reset function for this one
603 // button and multiple button
606 void() button_return;
610 self.state = STATE_TOP;
611 self.nextthink = self.ltime + self.wait;
612 self.think = button_return;
613 activator = self.enemy;
615 self.frame = 1; // use alternate textures
620 self.state = STATE_BOTTOM;
625 self.state = STATE_DOWN;
626 SUB_CalcMove (self.pos1, self.speed, button_done);
627 self.frame = 0; // use normal textures
629 self.takedamage = DAMAGE_YES; // can be shot again
633 void button_blocked()
635 // do nothing, just don't come all the way back out
641 self.health = self.max_health;
642 self.takedamage = DAMAGE_NO; // will be reset upon return
644 if (self.state == STATE_UP || self.state == STATE_TOP)
647 if (self.noise != "")
648 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
650 self.state = STATE_UP;
651 SUB_CalcMove (self.pos2, self.speed, button_wait);
656 self.health = self.max_health;
657 setorigin(self, self.pos1);
658 self.frame = 0; // use normal textures
659 self.state = STATE_BOTTOM;
661 self.takedamage = DAMAGE_YES; // can be shot again
666 // if (activator.classname != "player")
668 // dprint(activator.classname);
669 // dprint(" triggered a button\n");
672 if not (self.active == ACTIVE_ACTIVE)
675 self.enemy = activator;
681 // if (activator.classname != "player")
683 // dprint(activator.classname);
684 // dprint(" touched a button\n");
688 if not(other.iscreature)
690 if(other.velocity * self.movedir < 0)
694 self.enemy = other.owner;
698 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
700 if(self.spawnflags & DOOR_NOSPLASH)
701 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
703 self.health = self.health - damage;
704 if (self.health <= 0)
706 // if (activator.classname != "player")
708 // dprint(activator.classname);
709 // dprint(" killed a button\n");
711 self.enemy = damage_attacker;
717 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
718 When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
720 "angle" determines the opening direction
721 "target" all entities with a matching targetname will be used
722 "speed" override the default 40 speed
723 "wait" override the default 1 second wait (-1 = never return)
724 "lip" override the default 4 pixel lip remaining at end of move
725 "health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the MinstaGib laser
732 void spawnfunc_func_button()
736 if not(InitMovingBrushTrigger())
738 self.effects |= EF_LOWPRECISION;
740 self.blocked = button_blocked;
741 self.use = button_use;
743 // if (self.health == 0) // all buttons are now shootable
747 self.max_health = self.health;
748 self.event_damage = button_damage;
749 self.takedamage = DAMAGE_YES;
752 self.touch = button_touch;
762 precache_sound(self.noise);
764 self.active = ACTIVE_ACTIVE;
766 self.pos1 = self.origin;
767 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
768 self.flags |= FL_NOTARGET;
774 float DOOR_START_OPEN = 1;
775 float DOOR_DONT_LINK = 4;
776 float DOOR_TOGGLE = 32;
780 Doors are similar to buttons, but can spawn a fat trigger field around them
781 to open without a touch, and they link together to form simultanious
784 Door.owner is the master door. If there is only one door, it points to itself.
785 If multiple doors, all will point to a single one.
787 Door.enemy chains from the master door through all doors linked in the chain.
792 =============================================================================
796 =============================================================================
801 void() door_rotating_go_down;
802 void() door_rotating_go_up;
807 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
808 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
811 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
812 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
814 //Dont chamge direction for dead or dying stuff
815 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
818 if (self.state == STATE_DOWN)
819 if (self.classname == "door")
824 door_rotating_go_up ();
827 if (self.classname == "door")
832 door_rotating_go_down ();
836 //gib dying stuff just to make sure
837 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
838 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
842 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
843 // if a door has a negative wait, it would never come back if blocked,
844 // so let it just squash the object to death real fast
845 /* if (self.wait >= 0)
847 if (self.state == STATE_DOWN)
858 if (self.noise1 != "")
859 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
860 self.state = STATE_TOP;
861 if (self.spawnflags & DOOR_TOGGLE)
862 return; // don't come down automatically
863 if (self.classname == "door")
865 self.think = door_go_down;
868 self.think = door_rotating_go_down;
870 self.nextthink = self.ltime + self.wait;
873 void door_hit_bottom()
875 if (self.noise1 != "")
876 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
877 self.state = STATE_BOTTOM;
882 if (self.noise2 != "")
883 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
886 self.takedamage = DAMAGE_YES;
887 self.health = self.max_health;
890 self.state = STATE_DOWN;
891 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
896 if (self.state == STATE_UP)
897 return; // already going up
899 if (self.state == STATE_TOP)
900 { // reset top wait time
901 self.nextthink = self.ltime + self.wait;
905 if (self.noise2 != "")
906 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
907 self.state = STATE_UP;
908 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
911 oldmessage = self.message;
914 self.message = oldmessage;
920 =============================================================================
924 =============================================================================
927 float door_check_keys(void) {
937 if not(door.itemkeys)
940 // this door require a key
941 // only a player can have a key
942 if (other.classname != "player")
945 if (item_keys_usekey(door, other)) {
946 // some keys were used
947 if (other.key_door_messagetime <= time) {
948 play2(other, "misc/talk.wav");
949 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
950 other.key_door_messagetime = time + 2;
954 if (other.key_door_messagetime <= time) {
955 play2(other, "misc/talk.wav");
956 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
957 other.key_door_messagetime = time + 2;
962 // door is now unlocked
963 play2(other, "misc/talk.wav");
964 centerprint(other, "Door unlocked!");
976 if (self.owner != self)
977 objerror ("door_fire: self.owner != self");
981 if (self.spawnflags & DOOR_TOGGLE)
983 if (self.state == STATE_UP || self.state == STATE_TOP)
988 if (self.classname == "door")
994 door_rotating_go_down ();
997 } while ( (self != starte) && (self != world) );
1003 // trigger all paired doors
1007 if (self.classname == "door")
1012 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1013 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1015 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1016 self.pos2 = '0 0 0' - self.pos2;
1018 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1019 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1020 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1022 door_rotating_go_up ();
1026 } while ( (self != starte) && (self != world) );
1035 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1047 void door_trigger_touch()
1049 if (other.health < 1)
1050 if not(other.iscreature && other.deadflag == DEAD_NO)
1053 if (time < self.attack_finished_single)
1056 // check if door is locked
1057 if (!door_check_keys())
1060 self.attack_finished_single = time + 1;
1069 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1072 if(self.spawnflags & DOOR_NOSPLASH)
1073 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1075 self.health = self.health - damage;
1077 if (self.itemkeys) {
1078 // don't allow opening doors through damage if keys are required
1082 if (self.health <= 0)
1086 self.health = self.max_health;
1087 self.takedamage = DAMAGE_NO; // wil be reset upon return
1103 if(other.classname != "player")
1105 if (self.owner.attack_finished_single > time)
1108 self.owner.attack_finished_single = time + 2;
1110 if (!(self.owner.dmg) && (self.owner.message != ""))
1112 if (other.flags & FL_CLIENT)
1113 centerprint (other, self.owner.message);
1114 play2(other, "misc/talk.wav");
1119 void door_generic_plat_blocked()
1122 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1123 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1126 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1127 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1129 //Dont chamge direction for dead or dying stuff
1130 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1133 if (self.state == STATE_DOWN)
1134 door_rotating_go_up ();
1136 door_rotating_go_down ();
1139 //gib dying stuff just to make sure
1140 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1141 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1145 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1146 // if a door has a negative wait, it would never come back if blocked,
1147 // so let it just squash the object to death real fast
1148 /* if (self.wait >= 0)
1150 if (self.state == STATE_DOWN)
1151 door_rotating_go_up ();
1153 door_rotating_go_down ();
1159 void door_rotating_hit_top()
1161 if (self.noise1 != "")
1162 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1163 self.state = STATE_TOP;
1164 if (self.spawnflags & DOOR_TOGGLE)
1165 return; // don't come down automatically
1166 self.think = door_rotating_go_down;
1167 self.nextthink = self.ltime + self.wait;
1170 void door_rotating_hit_bottom()
1172 if (self.noise1 != "")
1173 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1174 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1176 self.pos2 = '0 0 0' - self.pos2;
1179 self.state = STATE_BOTTOM;
1182 void door_rotating_go_down()
1184 if (self.noise2 != "")
1185 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1186 if (self.max_health)
1188 self.takedamage = DAMAGE_YES;
1189 self.health = self.max_health;
1192 self.state = STATE_DOWN;
1193 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1196 void door_rotating_go_up()
1198 if (self.state == STATE_UP)
1199 return; // already going up
1201 if (self.state == STATE_TOP)
1202 { // reset top wait time
1203 self.nextthink = self.ltime + self.wait;
1206 if (self.noise2 != "")
1207 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1208 self.state = STATE_UP;
1209 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1212 oldmessage = self.message;
1215 self.message = oldmessage;
1222 =============================================================================
1226 =============================================================================
1230 entity spawn_field(vector fmins, vector fmaxs)
1236 trigger.classname = "doortriggerfield";
1237 trigger.movetype = MOVETYPE_NONE;
1238 trigger.solid = SOLID_TRIGGER;
1239 trigger.owner = self;
1240 trigger.touch = door_trigger_touch;
1244 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1249 float EntitiesTouching(entity e1, entity e2)
1251 if (e1.absmin_x > e2.absmax_x)
1253 if (e1.absmin_y > e2.absmax_y)
1255 if (e1.absmin_z > e2.absmax_z)
1257 if (e1.absmax_x < e2.absmin_x)
1259 if (e1.absmax_y < e2.absmin_y)
1261 if (e1.absmax_z < e2.absmin_z)
1277 vector cmins, cmaxs;
1280 return; // already linked by another door
1281 if (self.spawnflags & 4)
1283 self.owner = self.enemy = self;
1291 self.trigger_field = spawn_field(self.absmin, self.absmax);
1293 return; // don't want to link this door
1296 cmins = self.absmin;
1297 cmaxs = self.absmax;
1304 self.owner = starte; // master door
1307 starte.health = self.health;
1309 starte.targetname = self.targetname;
1310 if (self.message != "")
1311 starte.message = self.message;
1313 t = find(t, classname, self.classname);
1316 self.enemy = starte; // make the chain a loop
1318 // shootable, or triggered doors just needed the owner/enemy links,
1319 // they don't spawn a field
1330 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1335 if (EntitiesTouching(self,t))
1338 objerror ("cross connected doors");
1343 if (t.absmin_x < cmins_x)
1344 cmins_x = t.absmin_x;
1345 if (t.absmin_y < cmins_y)
1346 cmins_y = t.absmin_y;
1347 if (t.absmin_z < cmins_z)
1348 cmins_z = t.absmin_z;
1349 if (t.absmax_x > cmaxs_x)
1350 cmaxs_x = t.absmax_x;
1351 if (t.absmax_y > cmaxs_y)
1352 cmaxs_y = t.absmax_y;
1353 if (t.absmax_z > cmaxs_z)
1354 cmaxs_z = t.absmax_z;
1361 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1362 if two doors touch, they are assumed to be connected and operate as a unit.
1364 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1366 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
1368 GOLD_KEY causes the door to open only if the activator holds a gold key.
1370 SILVER_KEY causes the door to open only if the activator holds a silver key.
1372 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1373 "angle" determines the opening direction
1374 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1375 "health" if set, door must be shot open
1376 "speed" movement speed (100 default)
1377 "wait" wait before returning (3 default, -1 = never return)
1378 "lip" lip remaining at end of move (8 default)
1379 "dmg" damage to inflict when blocked (2 default)
1386 FIXME: only one sound set available at the time being
1390 void door_init_startopen()
1392 setorigin (self, self.pos2);
1393 self.pos2 = self.pos1;
1394 self.pos1 = self.origin;
1399 setorigin(self, self.pos1);
1400 self.velocity = '0 0 0';
1401 self.state = STATE_BOTTOM;
1402 self.think = SUB_Null;
1405 // spawnflags require key (for now only func_door)
1406 #define SPAWNFLAGS_GOLD_KEY 8
1407 #define SPAWNFLAGS_SILVER_KEY 16
1408 void spawnfunc_func_door()
1410 // Quake 1 keys compatibility
1411 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1412 self.itemkeys |= ITEM_KEY_BIT(0);
1413 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1414 self.itemkeys |= ITEM_KEY_BIT(1);
1416 //if (!self.deathtype) // map makers can override this
1417 // self.deathtype = " got in the way";
1420 self.max_health = self.health;
1421 if not(InitMovingBrushTrigger())
1423 self.effects |= EF_LOWPRECISION;
1424 self.classname = "door";
1426 self.blocked = door_blocked;
1427 self.use = door_use;
1429 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1430 // if(self.spawnflags & 8)
1431 // self.dmg = 10000;
1433 if(self.dmg && (!self.message))
1434 self.message = "was squished";
1435 if(self.dmg && (!self.message2))
1436 self.message2 = "was squished by";
1438 if (self.sounds > 0)
1440 precache_sound ("plats/medplat1.wav");
1441 precache_sound ("plats/medplat2.wav");
1442 self.noise2 = "plats/medplat1.wav";
1443 self.noise1 = "plats/medplat2.wav";
1453 self.pos1 = self.origin;
1454 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1456 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1457 // but spawn in the open position
1458 if (self.spawnflags & DOOR_START_OPEN)
1459 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1461 self.state = STATE_BOTTOM;
1465 self.takedamage = DAMAGE_YES;
1466 self.event_damage = door_damage;
1472 self.touch = door_touch;
1474 // LinkDoors can't be done until all of the doors have been spawned, so
1475 // the sizes can be detected properly.
1476 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1478 self.reset = door_reset;
1481 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1482 if two doors touch, they are assumed to be connected and operate as a unit.
1484 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1486 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1487 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1488 must have set trigger_reverse to 1.
1489 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1491 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
1493 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1494 "angle" determines the destination angle for opening. negative values reverse the direction.
1495 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1496 "health" if set, door must be shot open
1497 "speed" movement speed (100 default)
1498 "wait" wait before returning (3 default, -1 = never return)
1499 "dmg" damage to inflict when blocked (2 default)
1506 FIXME: only one sound set available at the time being
1509 void door_rotating_reset()
1511 self.angles = self.pos1;
1512 self.avelocity = '0 0 0';
1513 self.state = STATE_BOTTOM;
1514 self.think = SUB_Null;
1517 void door_rotating_init_startopen()
1519 self.angles = self.movedir;
1520 self.pos2 = '0 0 0';
1521 self.pos1 = self.movedir;
1525 void spawnfunc_func_door_rotating()
1528 //if (!self.deathtype) // map makers can override this
1529 // self.deathtype = " got in the way";
1531 // I abuse "movedir" for denoting the axis for now
1532 if (self.spawnflags & 64) // X (untested)
1533 self.movedir = '0 0 1';
1534 else if (self.spawnflags & 128) // Y (untested)
1535 self.movedir = '1 0 0';
1537 self.movedir = '0 1 0';
1539 if (self.angles_y==0) self.angles_y = 90;
1541 self.movedir = self.movedir * self.angles_y;
1542 self.angles = '0 0 0';
1544 self.max_health = self.health;
1545 self.avelocity = self.movedir;
1546 if not(InitMovingBrushTrigger())
1548 self.velocity = '0 0 0';
1549 //self.effects |= EF_LOWPRECISION;
1550 self.classname = "door_rotating";
1552 self.blocked = door_blocked;
1553 self.use = door_use;
1555 if(self.spawnflags & 8)
1558 if(self.dmg && (!self.message))
1559 self.message = "was squished";
1560 if(self.dmg && (!self.message2))
1561 self.message2 = "was squished by";
1563 if (self.sounds > 0)
1565 precache_sound ("plats/medplat1.wav");
1566 precache_sound ("plats/medplat2.wav");
1567 self.noise2 = "plats/medplat1.wav";
1568 self.noise1 = "plats/medplat2.wav";
1575 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1577 self.pos1 = '0 0 0';
1578 self.pos2 = self.movedir;
1580 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1581 // but spawn in the open position
1582 if (self.spawnflags & DOOR_START_OPEN)
1583 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1585 self.state = STATE_BOTTOM;
1589 self.takedamage = DAMAGE_YES;
1590 self.event_damage = door_damage;
1596 self.touch = door_touch;
1598 // LinkDoors can't be done until all of the doors have been spawned, so
1599 // the sizes can be detected properly.
1600 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1602 self.reset = door_rotating_reset;
1606 =============================================================================
1610 =============================================================================
1613 void() fd_secret_move1;
1614 void() fd_secret_move2;
1615 void() fd_secret_move3;
1616 void() fd_secret_move4;
1617 void() fd_secret_move5;
1618 void() fd_secret_move6;
1619 void() fd_secret_done;
1621 float SECRET_OPEN_ONCE = 1; // stays open
1622 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1623 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1624 float SECRET_NO_SHOOT = 8; // only opened by trigger
1625 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1628 void fd_secret_use()
1631 string message_save;
1633 self.health = 10000;
1634 self.bot_attack = TRUE;
1636 // exit if still moving around...
1637 if (self.origin != self.oldorigin)
1640 message_save = self.message;
1641 self.message = ""; // no more message
1642 SUB_UseTargets(); // fire all targets / killtargets
1643 self.message = message_save;
1645 self.velocity = '0 0 0';
1647 // Make a sound, wait a little...
1649 if (self.noise1 != "")
1650 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1651 self.nextthink = self.ltime + 0.1;
1653 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1654 makevectors(self.mangle);
1658 if (self.spawnflags & SECRET_1ST_DOWN)
1659 self.t_width = fabs(v_up * self.size);
1661 self.t_width = fabs(v_right * self.size);
1665 self.t_length = fabs(v_forward * self.size);
1667 if (self.spawnflags & SECRET_1ST_DOWN)
1668 self.dest1 = self.origin - v_up * self.t_width;
1670 self.dest1 = self.origin + v_right * (self.t_width * temp);
1672 self.dest2 = self.dest1 + v_forward * self.t_length;
1673 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1674 if (self.noise2 != "")
1675 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1678 // Wait after first movement...
1679 void fd_secret_move1()
1681 self.nextthink = self.ltime + 1.0;
1682 self.think = fd_secret_move2;
1683 if (self.noise3 != "")
1684 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1687 // Start moving sideways w/sound...
1688 void fd_secret_move2()
1690 if (self.noise2 != "")
1691 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1692 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1695 // Wait here until time to go back...
1696 void fd_secret_move3()
1698 if (self.noise3 != "")
1699 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1700 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1702 self.nextthink = self.ltime + self.wait;
1703 self.think = fd_secret_move4;
1708 void fd_secret_move4()
1710 if (self.noise2 != "")
1711 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1712 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1716 void fd_secret_move5()
1718 self.nextthink = self.ltime + 1.0;
1719 self.think = fd_secret_move6;
1720 if (self.noise3 != "")
1721 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1724 void fd_secret_move6()
1726 if (self.noise2 != "")
1727 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1728 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1731 void fd_secret_done()
1733 if (self.spawnflags&SECRET_YES_SHOOT)
1735 self.health = 10000;
1736 self.takedamage = DAMAGE_YES;
1737 //self.th_pain = fd_secret_use;
1739 if (self.noise3 != "")
1740 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1743 void secret_blocked()
1745 if (time < self.attack_finished_single)
1747 self.attack_finished_single = time + 0.5;
1748 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1760 if not(other.iscreature)
1762 if (self.attack_finished_single > time)
1765 self.attack_finished_single = time + 2;
1769 if (other.flags & FL_CLIENT)
1770 centerprint (other, self.message);
1771 play2(other, "misc/talk.wav");
1777 if (self.spawnflags&SECRET_YES_SHOOT)
1779 self.health = 10000;
1780 self.takedamage = DAMAGE_YES;
1782 setorigin(self, self.oldorigin);
1783 self.think = SUB_Null;
1786 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1787 Basic secret door. Slides back, then to the side. Angle determines direction.
1788 wait = # of seconds before coming back
1789 1st_left = 1st move is left of arrow
1790 1st_down = 1st move is down from arrow
1791 always_shoot = even if targeted, keep shootable
1792 t_width = override WIDTH to move back (or height if going down)
1793 t_length = override LENGTH to move sideways
1794 "dmg" damage to inflict when blocked (2 default)
1796 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1803 void spawnfunc_func_door_secret()
1805 /*if (!self.deathtype) // map makers can override this
1806 self.deathtype = " got in the way";*/
1812 self.mangle = self.angles;
1813 self.angles = '0 0 0';
1814 self.classname = "door";
1815 if not(InitMovingBrushTrigger())
1817 self.effects |= EF_LOWPRECISION;
1819 self.touch = secret_touch;
1820 self.blocked = secret_blocked;
1822 self.use = fd_secret_use;
1827 self.spawnflags |= SECRET_YES_SHOOT;
1829 if(self.spawnflags&SECRET_YES_SHOOT)
1831 self.health = 10000;
1832 self.takedamage = DAMAGE_YES;
1833 self.event_damage = fd_secret_use;
1835 self.oldorigin = self.origin;
1837 self.wait = 5; // 5 seconds before closing
1839 self.reset = secret_reset;
1843 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1844 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1845 netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
1846 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1847 height: amplitude modifier (default 32)
1848 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1849 noise: path/name of looping .wav file to play.
1850 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1854 void func_fourier_controller_think()
1859 self.nextthink = time + 0.1;
1860 if not (self.owner.active == ACTIVE_ACTIVE)
1862 self.owner.velocity = '0 0 0';
1867 n = floor((tokenize_console(self.owner.netname)) / 5);
1868 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1870 v = self.owner.destvec;
1872 for(i = 0; i < n; ++i)
1874 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1875 v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward_y;
1878 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1879 // * 10 so it will arrive in 0.1 sec
1880 self.owner.velocity = (v - self.owner.origin) * 10;
1883 void spawnfunc_func_fourier()
1886 if (self.noise != "")
1888 precache_sound(self.noise);
1889 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1896 self.destvec = self.origin;
1897 self.cnt = 360 / self.speed;
1899 self.blocked = generic_plat_blocked;
1900 if(self.dmg & (!self.message))
1901 self.message = " was squished";
1902 if(self.dmg && (!self.message2))
1903 self.message2 = "was squished by";
1904 if(self.dmg && (!self.dmgtime))
1905 self.dmgtime = 0.25;
1906 self.dmgtime2 = time;
1908 if(self.netname == "")
1909 self.netname = "1 0 0 0 1";
1911 if not(InitMovingBrushTrigger())
1914 self.active = ACTIVE_ACTIVE;
1916 // wait for targets to spawn
1917 controller = spawn();
1918 controller.classname = "func_fourier_controller";
1919 controller.owner = self;
1920 controller.nextthink = time + 1;
1921 controller.think = func_fourier_controller_think;
1922 self.nextthink = self.ltime + 999999999;
1923 self.think = SUB_Null;
1925 // Savage: Reduce bandwith, critical on e.g. nexdm02
1926 self.effects |= EF_LOWPRECISION;
1928 // TODO make a reset function for this one
1931 // reusing some fields havocbots declared
1932 .entity wp00, wp01, wp02, wp03;
1934 .float targetfactor, target2factor, target3factor, target4factor;
1935 .vector targetnormal, target2normal, target3normal, target4normal;
1937 vector func_vectormamamam_origin(entity o, float t)
1949 p = e.origin + t * e.velocity;
1951 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1953 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1959 p = e.origin + t * e.velocity;
1961 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1963 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1969 p = e.origin + t * e.velocity;
1971 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1973 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1979 p = e.origin + t * e.velocity;
1981 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1983 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1989 void func_vectormamamam_controller_think()
1991 self.nextthink = time + 0.1;
1993 if not (self.owner.active == ACTIVE_ACTIVE)
1995 self.owner.velocity = '0 0 0';
1999 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2000 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2003 void func_vectormamamam_findtarget()
2005 if(self.target != "")
2006 self.wp00 = find(world, targetname, self.target);
2008 if(self.target2 != "")
2009 self.wp01 = find(world, targetname, self.target2);
2011 if(self.target3 != "")
2012 self.wp02 = find(world, targetname, self.target3);
2014 if(self.target4 != "")
2015 self.wp03 = find(world, targetname, self.target4);
2017 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2018 objerror("No reference entity found, so there is nothing to move. Aborting.");
2020 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2023 controller = spawn();
2024 controller.classname = "func_vectormamamam_controller";
2025 controller.owner = self;
2026 controller.nextthink = time + 1;
2027 controller.think = func_vectormamamam_controller_think;
2030 void spawnfunc_func_vectormamamam()
2032 if (self.noise != "")
2034 precache_sound(self.noise);
2035 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2038 if(!self.targetfactor)
2039 self.targetfactor = 1;
2041 if(!self.target2factor)
2042 self.target2factor = 1;
2044 if(!self.target3factor)
2045 self.target3factor = 1;
2047 if(!self.target4factor)
2048 self.target4factor = 1;
2050 if(vlen(self.targetnormal))
2051 self.targetnormal = normalize(self.targetnormal);
2053 if(vlen(self.target2normal))
2054 self.target2normal = normalize(self.target2normal);
2056 if(vlen(self.target3normal))
2057 self.target3normal = normalize(self.target3normal);
2059 if(vlen(self.target4normal))
2060 self.target4normal = normalize(self.target4normal);
2062 self.blocked = generic_plat_blocked;
2063 if(self.dmg & (!self.message))
2064 self.message = " was squished";
2065 if(self.dmg && (!self.message2))
2066 self.message2 = "was squished by";
2067 if(self.dmg && (!self.dmgtime))
2068 self.dmgtime = 0.25;
2069 self.dmgtime2 = time;
2071 if(self.netname == "")
2072 self.netname = "1 0 0 0 1";
2074 if not(InitMovingBrushTrigger())
2077 // wait for targets to spawn
2078 self.nextthink = self.ltime + 999999999;
2079 self.think = SUB_Null;
2081 // Savage: Reduce bandwith, critical on e.g. nexdm02
2082 self.effects |= EF_LOWPRECISION;
2084 self.active = ACTIVE_ACTIVE;
2086 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2089 void conveyor_think()
2093 // set myself as current conveyor where possible
2094 for(e = world; (e = findentity(e, conveyor, self)); )
2099 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2100 if(!e.conveyor.state)
2103 vector emin = e.absmin;
2104 vector emax = e.absmax;
2105 if(self.solid == SOLID_BSP)
2110 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2111 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2115 for(e = world; (e = findentity(e, conveyor, self)); )
2117 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2118 continue; // done in SV_PlayerPhysics
2120 setorigin(e, e.origin + self.movedir * sys_frametime);
2121 move_out_of_solid(e);
2122 UpdateCSQCProjectile(e);
2124 // stupid conveyor code
2125 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2126 if(trace_fraction > 0)
2127 setorigin(e, trace_endpos);
2132 self.nextthink = time;
2137 self.state = !self.state;
2140 void conveyor_reset()
2142 self.state = (self.spawnflags & 1);
2145 void conveyor_init()
2149 self.movedir = self.movedir * self.speed;
2150 self.think = conveyor_think;
2151 self.nextthink = time;
2154 self.use = conveyor_use;
2155 self.reset = conveyor_reset;
2162 void spawnfunc_trigger_conveyor()
2169 void spawnfunc_func_conveyor()
2172 InitMovingBrushTrigger();
2173 self.movetype = MOVETYPE_NONE;