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;
284 targ = find(world, targetname, self.target);
285 self.target = targ.target;
286 if (self.spawnflags & 1)
289 prev = find(world, target, targ.targetname); // get the previous corner first
290 cp = find(world, targetname, prev.target2); // now get its second target (the control point)
291 if(cp.targetname == "") // none found
293 // when using bezier curves, you must have a control point for each corner in the path
294 if(autocvar_developer)
295 dprint(strcat("Warning: func_train using beizer curves reached the path_corner '", prev.targetname, "' which does not have a control point. Please add a target2 for each path_corner used by this train!\n"));
296 cp_org = targ.origin - self.mins; // assume a straight line to the destination as fallback
299 cp_org = cp.origin - self.mins;
302 objerror("train_next: no next target");
303 self.wait = targ.wait;
309 if (self.spawnflags & 1)
310 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, targ.speed, train_wait);
312 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
316 if (self.spawnflags & 1)
317 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, self.speed, train_wait);
319 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
323 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
326 void func_train_find()
329 targ = find(world, targetname, self.target);
330 self.target = targ.target;
332 objerror("func_train_find: no next target");
333 setorigin(self, targ.origin - self.mins);
334 self.nextthink = self.ltime + 1;
335 self.think = train_next;
338 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
339 Ridable platform, targets spawnfunc_path_corner path to follow.
340 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
341 target : targetname of first spawnfunc_path_corner (starts here)
343 void spawnfunc_func_train()
345 if (self.noise != "")
346 precache_sound(self.noise);
349 objerror("func_train without a target");
353 if not(InitMovingBrushTrigger())
355 self.effects |= EF_LOWPRECISION;
357 // wait for targets to spawn
358 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
360 self.blocked = generic_plat_blocked;
361 if(self.dmg & (!self.message))
362 self.message = " was squished";
363 if(self.dmg && (!self.message2))
364 self.message2 = "was squished by";
365 if(self.dmg && (!self.dmgtime))
367 self.dmgtime2 = time;
369 // TODO make a reset function for this one
372 void func_rotating_setactive(float astate)
375 if (astate == ACTIVE_TOGGLE)
377 if(self.active == ACTIVE_ACTIVE)
378 self.active = ACTIVE_NOT;
380 self.active = ACTIVE_ACTIVE;
383 self.active = astate;
385 if(self.active == ACTIVE_NOT)
386 self.avelocity = '0 0 0';
388 self.avelocity = self.pos1;
391 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
392 Brush model that spins in place on one axis (default Z).
393 speed : speed to rotate (in degrees per second)
394 noise : path/name of looping .wav file to play.
395 dmg : Do this mutch dmg every .dmgtime intervall when blocked
399 void spawnfunc_func_rotating()
401 if (self.noise != "")
403 precache_sound(self.noise);
404 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
407 self.active = ACTIVE_ACTIVE;
408 self.setactive = func_rotating_setactive;
412 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
413 if (self.spawnflags & 4) // X (untested)
414 self.avelocity = '0 0 1' * self.speed;
415 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
416 else if (self.spawnflags & 8) // Y (untested)
417 self.avelocity = '1 0 0' * self.speed;
418 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
420 self.avelocity = '0 1 0' * self.speed;
422 self.pos1 = self.avelocity;
424 if(self.dmg & (!self.message))
425 self.message = " was squished";
426 if(self.dmg && (!self.message2))
427 self.message2 = "was squished by";
430 if(self.dmg && (!self.dmgtime))
433 self.dmgtime2 = time;
435 if not(InitMovingBrushTrigger())
437 // no EF_LOWPRECISION here, as rounding angles is bad
439 self.blocked = generic_plat_blocked;
441 // wait for targets to spawn
442 self.nextthink = self.ltime + 999999999;
443 self.think = SUB_Null;
445 // TODO make a reset function for this one
449 void func_bobbing_controller_think()
452 self.nextthink = time + 0.1;
454 if not (self.owner.active == ACTIVE_ACTIVE)
456 self.owner.velocity = '0 0 0';
460 // calculate sinewave using makevectors
461 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
462 v = self.owner.destvec + self.owner.movedir * v_forward_y;
463 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
464 // * 10 so it will arrive in 0.1 sec
465 self.owner.velocity = (v - self.owner.origin) * 10;
468 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
469 Brush model that moves back and forth on one axis (default Z).
470 speed : how long one cycle takes in seconds (default 4)
471 height : how far the cycle moves (default 32)
472 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
473 noise : path/name of looping .wav file to play.
474 dmg : Do this mutch dmg every .dmgtime intervall when blocked
477 void spawnfunc_func_bobbing()
480 if (self.noise != "")
482 precache_sound(self.noise);
483 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
489 // center of bobbing motion
490 self.destvec = self.origin;
491 // time scale to get degrees
492 self.cnt = 360 / self.speed;
494 self.active = ACTIVE_ACTIVE;
496 // damage when blocked
497 self.blocked = generic_plat_blocked;
498 if(self.dmg & (!self.message))
499 self.message = " was squished";
500 if(self.dmg && (!self.message2))
501 self.message2 = "was squished by";
502 if(self.dmg && (!self.dmgtime))
504 self.dmgtime2 = time;
507 if (self.spawnflags & 1) // X
508 self.movedir = '1 0 0' * self.height;
509 else if (self.spawnflags & 2) // Y
510 self.movedir = '0 1 0' * self.height;
512 self.movedir = '0 0 1' * self.height;
514 if not(InitMovingBrushTrigger())
517 // wait for targets to spawn
518 controller = spawn();
519 controller.classname = "func_bobbing_controller";
520 controller.owner = self;
521 controller.nextthink = time + 1;
522 controller.think = func_bobbing_controller_think;
523 self.nextthink = self.ltime + 999999999;
524 self.think = SUB_Null;
526 // Savage: Reduce bandwith, critical on e.g. nexdm02
527 self.effects |= EF_LOWPRECISION;
529 // TODO make a reset function for this one
533 void func_pendulum_controller_think()
536 self.nextthink = time + 0.1;
538 if not (self.owner.active == ACTIVE_ACTIVE)
540 self.owner.avelocity_x = 0;
544 // calculate sinewave using makevectors
545 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
546 v = self.owner.speed * v_forward_y + self.cnt;
547 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
549 // * 10 so it will arrive in 0.1 sec
550 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
554 void spawnfunc_func_pendulum()
557 if (self.noise != "")
559 precache_sound(self.noise);
560 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
563 self.active = ACTIVE_ACTIVE;
565 // keys: angle, speed, phase, noise, freq
569 // not initializing self.dmg to 2, to allow damageless pendulum
571 if(self.dmg & (!self.message))
572 self.message = " was squished";
573 if(self.dmg && (!self.message2))
574 self.message2 = "was squished by";
575 if(self.dmg && (!self.dmgtime))
577 self.dmgtime2 = time;
579 self.blocked = generic_plat_blocked;
581 self.avelocity_z = 0.0000001;
582 if not(InitMovingBrushTrigger())
587 // find pendulum length (same formula as Q3A)
588 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
591 // copy initial angle
592 self.cnt = self.angles_z;
594 // wait for targets to spawn
595 controller = spawn();
596 controller.classname = "func_pendulum_controller";
597 controller.owner = self;
598 controller.nextthink = time + 1;
599 controller.think = func_pendulum_controller_think;
600 self.nextthink = self.ltime + 999999999;
601 self.think = SUB_Null;
603 //self.effects |= EF_LOWPRECISION;
605 // TODO make a reset function for this one
608 // button and multiple button
611 void() button_return;
615 self.state = STATE_TOP;
616 self.nextthink = self.ltime + self.wait;
617 self.think = button_return;
618 activator = self.enemy;
620 self.frame = 1; // use alternate textures
625 self.state = STATE_BOTTOM;
630 self.state = STATE_DOWN;
631 SUB_CalcMove (self.pos1, self.speed, button_done);
632 self.frame = 0; // use normal textures
634 self.takedamage = DAMAGE_YES; // can be shot again
638 void button_blocked()
640 // do nothing, just don't come all the way back out
646 self.health = self.max_health;
647 self.takedamage = DAMAGE_NO; // will be reset upon return
649 if (self.state == STATE_UP || self.state == STATE_TOP)
652 if (self.noise != "")
653 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
655 self.state = STATE_UP;
656 SUB_CalcMove (self.pos2, self.speed, button_wait);
661 self.health = self.max_health;
662 setorigin(self, self.pos1);
663 self.frame = 0; // use normal textures
664 self.state = STATE_BOTTOM;
666 self.takedamage = DAMAGE_YES; // can be shot again
671 // if (activator.classname != "player")
673 // dprint(activator.classname);
674 // dprint(" triggered a button\n");
677 if not (self.active == ACTIVE_ACTIVE)
680 self.enemy = activator;
686 // if (activator.classname != "player")
688 // dprint(activator.classname);
689 // dprint(" touched a button\n");
693 if not(other.iscreature)
695 if(other.velocity * self.movedir < 0)
699 self.enemy = other.owner;
703 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
705 if(self.spawnflags & DOOR_NOSPLASH)
706 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
708 self.health = self.health - damage;
709 if (self.health <= 0)
711 // if (activator.classname != "player")
713 // dprint(activator.classname);
714 // dprint(" killed a button\n");
716 self.enemy = damage_attacker;
722 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
723 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.
725 "angle" determines the opening direction
726 "target" all entities with a matching targetname will be used
727 "speed" override the default 40 speed
728 "wait" override the default 1 second wait (-1 = never return)
729 "lip" override the default 4 pixel lip remaining at end of move
730 "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
737 void spawnfunc_func_button()
741 if not(InitMovingBrushTrigger())
743 self.effects |= EF_LOWPRECISION;
745 self.blocked = button_blocked;
746 self.use = button_use;
748 // if (self.health == 0) // all buttons are now shootable
752 self.max_health = self.health;
753 self.event_damage = button_damage;
754 self.takedamage = DAMAGE_YES;
757 self.touch = button_touch;
767 precache_sound(self.noise);
769 self.active = ACTIVE_ACTIVE;
771 self.pos1 = self.origin;
772 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
773 self.flags |= FL_NOTARGET;
779 float DOOR_START_OPEN = 1;
780 float DOOR_DONT_LINK = 4;
781 float DOOR_TOGGLE = 32;
785 Doors are similar to buttons, but can spawn a fat trigger field around them
786 to open without a touch, and they link together to form simultanious
789 Door.owner is the master door. If there is only one door, it points to itself.
790 If multiple doors, all will point to a single one.
792 Door.enemy chains from the master door through all doors linked in the chain.
797 =============================================================================
801 =============================================================================
806 void() door_rotating_go_down;
807 void() door_rotating_go_up;
812 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
813 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
816 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
817 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
819 //Dont chamge direction for dead or dying stuff
820 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
823 if (self.state == STATE_DOWN)
824 if (self.classname == "door")
829 door_rotating_go_up ();
832 if (self.classname == "door")
837 door_rotating_go_down ();
841 //gib dying stuff just to make sure
842 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
843 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
847 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
848 // if a door has a negative wait, it would never come back if blocked,
849 // so let it just squash the object to death real fast
850 /* if (self.wait >= 0)
852 if (self.state == STATE_DOWN)
863 if (self.noise1 != "")
864 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
865 self.state = STATE_TOP;
866 if (self.spawnflags & DOOR_TOGGLE)
867 return; // don't come down automatically
868 if (self.classname == "door")
870 self.think = door_go_down;
873 self.think = door_rotating_go_down;
875 self.nextthink = self.ltime + self.wait;
878 void door_hit_bottom()
880 if (self.noise1 != "")
881 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
882 self.state = STATE_BOTTOM;
887 if (self.noise2 != "")
888 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
891 self.takedamage = DAMAGE_YES;
892 self.health = self.max_health;
895 self.state = STATE_DOWN;
896 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
901 if (self.state == STATE_UP)
902 return; // already going up
904 if (self.state == STATE_TOP)
905 { // reset top wait time
906 self.nextthink = self.ltime + self.wait;
910 if (self.noise2 != "")
911 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
912 self.state = STATE_UP;
913 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
916 oldmessage = self.message;
919 self.message = oldmessage;
925 =============================================================================
929 =============================================================================
932 float door_check_keys(void) {
942 if not(door.itemkeys)
945 // this door require a key
946 // only a player can have a key
947 if (other.classname != "player")
950 if (item_keys_usekey(door, other)) {
951 // some keys were used
952 if (other.key_door_messagetime <= time) {
953 play2(other, "misc/talk.wav");
954 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
955 other.key_door_messagetime = time + 2;
959 if (other.key_door_messagetime <= time) {
960 play2(other, "misc/talk.wav");
961 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
962 other.key_door_messagetime = time + 2;
967 // door is now unlocked
968 play2(other, "misc/talk.wav");
969 centerprint(other, "Door unlocked!");
981 if (self.owner != self)
982 objerror ("door_fire: self.owner != self");
986 if (self.spawnflags & DOOR_TOGGLE)
988 if (self.state == STATE_UP || self.state == STATE_TOP)
993 if (self.classname == "door")
999 door_rotating_go_down ();
1002 } while ( (self != starte) && (self != world) );
1008 // trigger all paired doors
1012 if (self.classname == "door")
1017 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1018 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1020 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1021 self.pos2 = '0 0 0' - self.pos2;
1023 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1024 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1025 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1027 door_rotating_go_up ();
1031 } while ( (self != starte) && (self != world) );
1040 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1052 void door_trigger_touch()
1054 if (other.health < 1)
1055 if not(other.iscreature && other.deadflag == DEAD_NO)
1058 if (time < self.attack_finished_single)
1061 // check if door is locked
1062 if (!door_check_keys())
1065 self.attack_finished_single = time + 1;
1074 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1077 if(self.spawnflags & DOOR_NOSPLASH)
1078 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1080 self.health = self.health - damage;
1082 if (self.itemkeys) {
1083 // don't allow opening doors through damage if keys are required
1087 if (self.health <= 0)
1091 self.health = self.max_health;
1092 self.takedamage = DAMAGE_NO; // wil be reset upon return
1108 if(other.classname != "player")
1110 if (self.owner.attack_finished_single > time)
1113 self.owner.attack_finished_single = time + 2;
1115 if (!(self.owner.dmg) && (self.owner.message != ""))
1117 if (other.flags & FL_CLIENT)
1118 centerprint (other, self.owner.message);
1119 play2(other, "misc/talk.wav");
1124 void door_generic_plat_blocked()
1127 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1128 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1131 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1132 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1134 //Dont chamge direction for dead or dying stuff
1135 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1138 if (self.state == STATE_DOWN)
1139 door_rotating_go_up ();
1141 door_rotating_go_down ();
1144 //gib dying stuff just to make sure
1145 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1146 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1150 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1151 // if a door has a negative wait, it would never come back if blocked,
1152 // so let it just squash the object to death real fast
1153 /* if (self.wait >= 0)
1155 if (self.state == STATE_DOWN)
1156 door_rotating_go_up ();
1158 door_rotating_go_down ();
1164 void door_rotating_hit_top()
1166 if (self.noise1 != "")
1167 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1168 self.state = STATE_TOP;
1169 if (self.spawnflags & DOOR_TOGGLE)
1170 return; // don't come down automatically
1171 self.think = door_rotating_go_down;
1172 self.nextthink = self.ltime + self.wait;
1175 void door_rotating_hit_bottom()
1177 if (self.noise1 != "")
1178 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1179 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1181 self.pos2 = '0 0 0' - self.pos2;
1184 self.state = STATE_BOTTOM;
1187 void door_rotating_go_down()
1189 if (self.noise2 != "")
1190 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1191 if (self.max_health)
1193 self.takedamage = DAMAGE_YES;
1194 self.health = self.max_health;
1197 self.state = STATE_DOWN;
1198 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1201 void door_rotating_go_up()
1203 if (self.state == STATE_UP)
1204 return; // already going up
1206 if (self.state == STATE_TOP)
1207 { // reset top wait time
1208 self.nextthink = self.ltime + self.wait;
1211 if (self.noise2 != "")
1212 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1213 self.state = STATE_UP;
1214 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1217 oldmessage = self.message;
1220 self.message = oldmessage;
1227 =============================================================================
1231 =============================================================================
1235 entity spawn_field(vector fmins, vector fmaxs)
1241 trigger.classname = "doortriggerfield";
1242 trigger.movetype = MOVETYPE_NONE;
1243 trigger.solid = SOLID_TRIGGER;
1244 trigger.owner = self;
1245 trigger.touch = door_trigger_touch;
1249 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1254 float EntitiesTouching(entity e1, entity e2)
1256 if (e1.absmin_x > e2.absmax_x)
1258 if (e1.absmin_y > e2.absmax_y)
1260 if (e1.absmin_z > e2.absmax_z)
1262 if (e1.absmax_x < e2.absmin_x)
1264 if (e1.absmax_y < e2.absmin_y)
1266 if (e1.absmax_z < e2.absmin_z)
1282 vector cmins, cmaxs;
1285 return; // already linked by another door
1286 if (self.spawnflags & 4)
1288 self.owner = self.enemy = self;
1296 self.trigger_field = spawn_field(self.absmin, self.absmax);
1298 return; // don't want to link this door
1301 cmins = self.absmin;
1302 cmaxs = self.absmax;
1309 self.owner = starte; // master door
1312 starte.health = self.health;
1314 starte.targetname = self.targetname;
1315 if (self.message != "")
1316 starte.message = self.message;
1318 t = find(t, classname, self.classname);
1321 self.enemy = starte; // make the chain a loop
1323 // shootable, or triggered doors just needed the owner/enemy links,
1324 // they don't spawn a field
1335 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1340 if (EntitiesTouching(self,t))
1343 objerror ("cross connected doors");
1348 if (t.absmin_x < cmins_x)
1349 cmins_x = t.absmin_x;
1350 if (t.absmin_y < cmins_y)
1351 cmins_y = t.absmin_y;
1352 if (t.absmin_z < cmins_z)
1353 cmins_z = t.absmin_z;
1354 if (t.absmax_x > cmaxs_x)
1355 cmaxs_x = t.absmax_x;
1356 if (t.absmax_y > cmaxs_y)
1357 cmaxs_y = t.absmax_y;
1358 if (t.absmax_z > cmaxs_z)
1359 cmaxs_z = t.absmax_z;
1366 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1367 if two doors touch, they are assumed to be connected and operate as a unit.
1369 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1371 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).
1373 GOLD_KEY causes the door to open only if the activator holds a gold key.
1375 SILVER_KEY causes the door to open only if the activator holds a silver key.
1377 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1378 "angle" determines the opening direction
1379 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1380 "health" if set, door must be shot open
1381 "speed" movement speed (100 default)
1382 "wait" wait before returning (3 default, -1 = never return)
1383 "lip" lip remaining at end of move (8 default)
1384 "dmg" damage to inflict when blocked (2 default)
1391 FIXME: only one sound set available at the time being
1395 void door_init_startopen()
1397 setorigin (self, self.pos2);
1398 self.pos2 = self.pos1;
1399 self.pos1 = self.origin;
1404 setorigin(self, self.pos1);
1405 self.velocity = '0 0 0';
1406 self.state = STATE_BOTTOM;
1407 self.think = SUB_Null;
1410 // spawnflags require key (for now only func_door)
1411 #define SPAWNFLAGS_GOLD_KEY 8
1412 #define SPAWNFLAGS_SILVER_KEY 16
1413 void spawnfunc_func_door()
1415 // Quake 1 keys compatibility
1416 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1417 self.itemkeys |= ITEM_KEY_BIT(0);
1418 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1419 self.itemkeys |= ITEM_KEY_BIT(1);
1421 //if (!self.deathtype) // map makers can override this
1422 // self.deathtype = " got in the way";
1425 self.max_health = self.health;
1426 if not(InitMovingBrushTrigger())
1428 self.effects |= EF_LOWPRECISION;
1429 self.classname = "door";
1431 self.blocked = door_blocked;
1432 self.use = door_use;
1434 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1435 // if(self.spawnflags & 8)
1436 // self.dmg = 10000;
1438 if(self.dmg && (!self.message))
1439 self.message = "was squished";
1440 if(self.dmg && (!self.message2))
1441 self.message2 = "was squished by";
1443 if (self.sounds > 0)
1445 precache_sound ("plats/medplat1.wav");
1446 precache_sound ("plats/medplat2.wav");
1447 self.noise2 = "plats/medplat1.wav";
1448 self.noise1 = "plats/medplat2.wav";
1458 self.pos1 = self.origin;
1459 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1461 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1462 // but spawn in the open position
1463 if (self.spawnflags & DOOR_START_OPEN)
1464 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1466 self.state = STATE_BOTTOM;
1470 self.takedamage = DAMAGE_YES;
1471 self.event_damage = door_damage;
1477 self.touch = door_touch;
1479 // LinkDoors can't be done until all of the doors have been spawned, so
1480 // the sizes can be detected properly.
1481 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1483 self.reset = door_reset;
1486 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1487 if two doors touch, they are assumed to be connected and operate as a unit.
1489 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1491 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1492 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1493 must have set trigger_reverse to 1.
1494 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1496 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).
1498 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1499 "angle" determines the destination angle for opening. negative values reverse the direction.
1500 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1501 "health" if set, door must be shot open
1502 "speed" movement speed (100 default)
1503 "wait" wait before returning (3 default, -1 = never return)
1504 "dmg" damage to inflict when blocked (2 default)
1511 FIXME: only one sound set available at the time being
1514 void door_rotating_reset()
1516 self.angles = self.pos1;
1517 self.avelocity = '0 0 0';
1518 self.state = STATE_BOTTOM;
1519 self.think = SUB_Null;
1522 void door_rotating_init_startopen()
1524 self.angles = self.movedir;
1525 self.pos2 = '0 0 0';
1526 self.pos1 = self.movedir;
1530 void spawnfunc_func_door_rotating()
1533 //if (!self.deathtype) // map makers can override this
1534 // self.deathtype = " got in the way";
1536 // I abuse "movedir" for denoting the axis for now
1537 if (self.spawnflags & 64) // X (untested)
1538 self.movedir = '0 0 1';
1539 else if (self.spawnflags & 128) // Y (untested)
1540 self.movedir = '1 0 0';
1542 self.movedir = '0 1 0';
1544 if (self.angles_y==0) self.angles_y = 90;
1546 self.movedir = self.movedir * self.angles_y;
1547 self.angles = '0 0 0';
1549 self.max_health = self.health;
1550 self.avelocity = self.movedir;
1551 if not(InitMovingBrushTrigger())
1553 self.velocity = '0 0 0';
1554 //self.effects |= EF_LOWPRECISION;
1555 self.classname = "door_rotating";
1557 self.blocked = door_blocked;
1558 self.use = door_use;
1560 if(self.spawnflags & 8)
1563 if(self.dmg && (!self.message))
1564 self.message = "was squished";
1565 if(self.dmg && (!self.message2))
1566 self.message2 = "was squished by";
1568 if (self.sounds > 0)
1570 precache_sound ("plats/medplat1.wav");
1571 precache_sound ("plats/medplat2.wav");
1572 self.noise2 = "plats/medplat1.wav";
1573 self.noise1 = "plats/medplat2.wav";
1580 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1582 self.pos1 = '0 0 0';
1583 self.pos2 = self.movedir;
1585 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1586 // but spawn in the open position
1587 if (self.spawnflags & DOOR_START_OPEN)
1588 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1590 self.state = STATE_BOTTOM;
1594 self.takedamage = DAMAGE_YES;
1595 self.event_damage = door_damage;
1601 self.touch = door_touch;
1603 // LinkDoors can't be done until all of the doors have been spawned, so
1604 // the sizes can be detected properly.
1605 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1607 self.reset = door_rotating_reset;
1611 =============================================================================
1615 =============================================================================
1618 void() fd_secret_move1;
1619 void() fd_secret_move2;
1620 void() fd_secret_move3;
1621 void() fd_secret_move4;
1622 void() fd_secret_move5;
1623 void() fd_secret_move6;
1624 void() fd_secret_done;
1626 float SECRET_OPEN_ONCE = 1; // stays open
1627 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1628 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1629 float SECRET_NO_SHOOT = 8; // only opened by trigger
1630 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1633 void fd_secret_use()
1636 string message_save;
1638 self.health = 10000;
1639 self.bot_attack = TRUE;
1641 // exit if still moving around...
1642 if (self.origin != self.oldorigin)
1645 message_save = self.message;
1646 self.message = ""; // no more message
1647 SUB_UseTargets(); // fire all targets / killtargets
1648 self.message = message_save;
1650 self.velocity = '0 0 0';
1652 // Make a sound, wait a little...
1654 if (self.noise1 != "")
1655 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1656 self.nextthink = self.ltime + 0.1;
1658 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1659 makevectors(self.mangle);
1663 if (self.spawnflags & SECRET_1ST_DOWN)
1664 self.t_width = fabs(v_up * self.size);
1666 self.t_width = fabs(v_right * self.size);
1670 self.t_length = fabs(v_forward * self.size);
1672 if (self.spawnflags & SECRET_1ST_DOWN)
1673 self.dest1 = self.origin - v_up * self.t_width;
1675 self.dest1 = self.origin + v_right * (self.t_width * temp);
1677 self.dest2 = self.dest1 + v_forward * self.t_length;
1678 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1679 if (self.noise2 != "")
1680 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1683 // Wait after first movement...
1684 void fd_secret_move1()
1686 self.nextthink = self.ltime + 1.0;
1687 self.think = fd_secret_move2;
1688 if (self.noise3 != "")
1689 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1692 // Start moving sideways w/sound...
1693 void fd_secret_move2()
1695 if (self.noise2 != "")
1696 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1697 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1700 // Wait here until time to go back...
1701 void fd_secret_move3()
1703 if (self.noise3 != "")
1704 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1705 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1707 self.nextthink = self.ltime + self.wait;
1708 self.think = fd_secret_move4;
1713 void fd_secret_move4()
1715 if (self.noise2 != "")
1716 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1717 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1721 void fd_secret_move5()
1723 self.nextthink = self.ltime + 1.0;
1724 self.think = fd_secret_move6;
1725 if (self.noise3 != "")
1726 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1729 void fd_secret_move6()
1731 if (self.noise2 != "")
1732 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1733 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1736 void fd_secret_done()
1738 if (self.spawnflags&SECRET_YES_SHOOT)
1740 self.health = 10000;
1741 self.takedamage = DAMAGE_YES;
1742 //self.th_pain = fd_secret_use;
1744 if (self.noise3 != "")
1745 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1748 void secret_blocked()
1750 if (time < self.attack_finished_single)
1752 self.attack_finished_single = time + 0.5;
1753 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1765 if not(other.iscreature)
1767 if (self.attack_finished_single > time)
1770 self.attack_finished_single = time + 2;
1774 if (other.flags & FL_CLIENT)
1775 centerprint (other, self.message);
1776 play2(other, "misc/talk.wav");
1782 if (self.spawnflags&SECRET_YES_SHOOT)
1784 self.health = 10000;
1785 self.takedamage = DAMAGE_YES;
1787 setorigin(self, self.oldorigin);
1788 self.think = SUB_Null;
1791 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1792 Basic secret door. Slides back, then to the side. Angle determines direction.
1793 wait = # of seconds before coming back
1794 1st_left = 1st move is left of arrow
1795 1st_down = 1st move is down from arrow
1796 always_shoot = even if targeted, keep shootable
1797 t_width = override WIDTH to move back (or height if going down)
1798 t_length = override LENGTH to move sideways
1799 "dmg" damage to inflict when blocked (2 default)
1801 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1808 void spawnfunc_func_door_secret()
1810 /*if (!self.deathtype) // map makers can override this
1811 self.deathtype = " got in the way";*/
1817 self.mangle = self.angles;
1818 self.angles = '0 0 0';
1819 self.classname = "door";
1820 if not(InitMovingBrushTrigger())
1822 self.effects |= EF_LOWPRECISION;
1824 self.touch = secret_touch;
1825 self.blocked = secret_blocked;
1827 self.use = fd_secret_use;
1832 self.spawnflags |= SECRET_YES_SHOOT;
1834 if(self.spawnflags&SECRET_YES_SHOOT)
1836 self.health = 10000;
1837 self.takedamage = DAMAGE_YES;
1838 self.event_damage = fd_secret_use;
1840 self.oldorigin = self.origin;
1842 self.wait = 5; // 5 seconds before closing
1844 self.reset = secret_reset;
1848 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1849 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1850 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
1851 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1852 height: amplitude modifier (default 32)
1853 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1854 noise: path/name of looping .wav file to play.
1855 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1859 void func_fourier_controller_think()
1864 self.nextthink = time + 0.1;
1865 if not (self.owner.active == ACTIVE_ACTIVE)
1867 self.owner.velocity = '0 0 0';
1872 n = floor((tokenize_console(self.owner.netname)) / 5);
1873 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1875 v = self.owner.destvec;
1877 for(i = 0; i < n; ++i)
1879 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1880 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;
1883 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1884 // * 10 so it will arrive in 0.1 sec
1885 self.owner.velocity = (v - self.owner.origin) * 10;
1888 void spawnfunc_func_fourier()
1891 if (self.noise != "")
1893 precache_sound(self.noise);
1894 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1901 self.destvec = self.origin;
1902 self.cnt = 360 / self.speed;
1904 self.blocked = generic_plat_blocked;
1905 if(self.dmg & (!self.message))
1906 self.message = " was squished";
1907 if(self.dmg && (!self.message2))
1908 self.message2 = "was squished by";
1909 if(self.dmg && (!self.dmgtime))
1910 self.dmgtime = 0.25;
1911 self.dmgtime2 = time;
1913 if(self.netname == "")
1914 self.netname = "1 0 0 0 1";
1916 if not(InitMovingBrushTrigger())
1919 self.active = ACTIVE_ACTIVE;
1921 // wait for targets to spawn
1922 controller = spawn();
1923 controller.classname = "func_fourier_controller";
1924 controller.owner = self;
1925 controller.nextthink = time + 1;
1926 controller.think = func_fourier_controller_think;
1927 self.nextthink = self.ltime + 999999999;
1928 self.think = SUB_Null;
1930 // Savage: Reduce bandwith, critical on e.g. nexdm02
1931 self.effects |= EF_LOWPRECISION;
1933 // TODO make a reset function for this one
1936 // reusing some fields havocbots declared
1937 .entity wp00, wp01, wp02, wp03;
1939 .float targetfactor, target2factor, target3factor, target4factor;
1940 .vector targetnormal, target2normal, target3normal, target4normal;
1942 vector func_vectormamamam_origin(entity o, float t)
1954 p = e.origin + t * e.velocity;
1956 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1958 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1964 p = e.origin + t * e.velocity;
1966 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1968 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1974 p = e.origin + t * e.velocity;
1976 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1978 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1984 p = e.origin + t * e.velocity;
1986 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1988 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1994 void func_vectormamamam_controller_think()
1996 self.nextthink = time + 0.1;
1998 if not (self.owner.active == ACTIVE_ACTIVE)
2000 self.owner.velocity = '0 0 0';
2004 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2005 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2008 void func_vectormamamam_findtarget()
2010 if(self.target != "")
2011 self.wp00 = find(world, targetname, self.target);
2013 if(self.target2 != "")
2014 self.wp01 = find(world, targetname, self.target2);
2016 if(self.target3 != "")
2017 self.wp02 = find(world, targetname, self.target3);
2019 if(self.target4 != "")
2020 self.wp03 = find(world, targetname, self.target4);
2022 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2023 objerror("No reference entity found, so there is nothing to move. Aborting.");
2025 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2028 controller = spawn();
2029 controller.classname = "func_vectormamamam_controller";
2030 controller.owner = self;
2031 controller.nextthink = time + 1;
2032 controller.think = func_vectormamamam_controller_think;
2035 void spawnfunc_func_vectormamamam()
2037 if (self.noise != "")
2039 precache_sound(self.noise);
2040 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2043 if(!self.targetfactor)
2044 self.targetfactor = 1;
2046 if(!self.target2factor)
2047 self.target2factor = 1;
2049 if(!self.target3factor)
2050 self.target3factor = 1;
2052 if(!self.target4factor)
2053 self.target4factor = 1;
2055 if(vlen(self.targetnormal))
2056 self.targetnormal = normalize(self.targetnormal);
2058 if(vlen(self.target2normal))
2059 self.target2normal = normalize(self.target2normal);
2061 if(vlen(self.target3normal))
2062 self.target3normal = normalize(self.target3normal);
2064 if(vlen(self.target4normal))
2065 self.target4normal = normalize(self.target4normal);
2067 self.blocked = generic_plat_blocked;
2068 if(self.dmg & (!self.message))
2069 self.message = " was squished";
2070 if(self.dmg && (!self.message2))
2071 self.message2 = "was squished by";
2072 if(self.dmg && (!self.dmgtime))
2073 self.dmgtime = 0.25;
2074 self.dmgtime2 = time;
2076 if(self.netname == "")
2077 self.netname = "1 0 0 0 1";
2079 if not(InitMovingBrushTrigger())
2082 // wait for targets to spawn
2083 self.nextthink = self.ltime + 999999999;
2084 self.think = SUB_Null;
2086 // Savage: Reduce bandwith, critical on e.g. nexdm02
2087 self.effects |= EF_LOWPRECISION;
2089 self.active = ACTIVE_ACTIVE;
2091 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2094 void conveyor_think()
2098 // set myself as current conveyor where possible
2099 for(e = world; (e = findentity(e, conveyor, self)); )
2104 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2105 if(!e.conveyor.state)
2108 vector emin = e.absmin;
2109 vector emax = e.absmax;
2110 if(self.solid == SOLID_BSP)
2115 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2116 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2120 for(e = world; (e = findentity(e, conveyor, self)); )
2122 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2123 continue; // done in SV_PlayerPhysics
2125 setorigin(e, e.origin + self.movedir * sys_frametime);
2126 move_out_of_solid(e);
2127 UpdateCSQCProjectile(e);
2129 // stupid conveyor code
2130 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2131 if(trace_fraction > 0)
2132 setorigin(e, trace_endpos);
2137 self.nextthink = time;
2142 self.state = !self.state;
2145 void conveyor_reset()
2147 self.state = (self.spawnflags & 1);
2150 void conveyor_init()
2154 self.movedir = self.movedir * self.speed;
2155 self.think = conveyor_think;
2156 self.nextthink = time;
2159 self.use = conveyor_use;
2160 self.reset = conveyor_reset;
2167 void spawnfunc_trigger_conveyor()
2174 void spawnfunc_func_conveyor()
2177 InitMovingBrushTrigger();
2178 self.movetype = MOVETYPE_NONE;