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)
287 prev = find(world, target, targ.targetname); // get the previous corner first
288 cp = find(world, targetname, prev.target2); // now get its second target
289 if(cp.targetname == "") // none found
291 // when using bezier curves, you must have a control point for each corner in the train's path
292 if(autocvar_developer)
293 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"));
295 setorigin(cp, targ.origin - self.mins); // assume a straight line to the destination as fallback
299 objerror("train_next: no next target");
300 self.wait = targ.wait;
306 if (self.spawnflags & 1)
307 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, targ.speed, train_wait);
309 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
313 if (self.spawnflags & 1)
314 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, self.speed, train_wait);
316 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
320 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
323 void func_train_find()
326 targ = find(world, targetname, self.target);
327 self.target = targ.target;
329 objerror("func_train_find: no next target");
330 setorigin(self, targ.origin - self.mins);
331 self.nextthink = self.ltime + 1;
332 self.think = train_next;
335 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
336 Ridable platform, targets spawnfunc_path_corner path to follow.
337 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
338 target : targetname of first spawnfunc_path_corner (starts here)
340 void spawnfunc_func_train()
342 if (self.noise != "")
343 precache_sound(self.noise);
346 objerror("func_train without a target");
350 if not(InitMovingBrushTrigger())
352 self.effects |= EF_LOWPRECISION;
354 // wait for targets to spawn
355 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
357 self.blocked = generic_plat_blocked;
358 if(self.dmg & (!self.message))
359 self.message = " was squished";
360 if(self.dmg && (!self.message2))
361 self.message2 = "was squished by";
362 if(self.dmg && (!self.dmgtime))
364 self.dmgtime2 = time;
366 // TODO make a reset function for this one
369 void func_rotating_setactive(float astate)
372 if (astate == ACTIVE_TOGGLE)
374 if(self.active == ACTIVE_ACTIVE)
375 self.active = ACTIVE_NOT;
377 self.active = ACTIVE_ACTIVE;
380 self.active = astate;
382 if(self.active == ACTIVE_NOT)
383 self.avelocity = '0 0 0';
385 self.avelocity = self.pos1;
388 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
389 Brush model that spins in place on one axis (default Z).
390 speed : speed to rotate (in degrees per second)
391 noise : path/name of looping .wav file to play.
392 dmg : Do this mutch dmg every .dmgtime intervall when blocked
396 void spawnfunc_func_rotating()
398 if (self.noise != "")
400 precache_sound(self.noise);
401 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
404 self.active = ACTIVE_ACTIVE;
405 self.setactive = func_rotating_setactive;
409 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
410 if (self.spawnflags & 4) // X (untested)
411 self.avelocity = '0 0 1' * self.speed;
412 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
413 else if (self.spawnflags & 8) // Y (untested)
414 self.avelocity = '1 0 0' * self.speed;
415 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
417 self.avelocity = '0 1 0' * self.speed;
419 self.pos1 = self.avelocity;
421 if(self.dmg & (!self.message))
422 self.message = " was squished";
423 if(self.dmg && (!self.message2))
424 self.message2 = "was squished by";
427 if(self.dmg && (!self.dmgtime))
430 self.dmgtime2 = time;
432 if not(InitMovingBrushTrigger())
434 // no EF_LOWPRECISION here, as rounding angles is bad
436 self.blocked = generic_plat_blocked;
438 // wait for targets to spawn
439 self.nextthink = self.ltime + 999999999;
440 self.think = SUB_Null;
442 // TODO make a reset function for this one
446 void func_bobbing_controller_think()
449 self.nextthink = time + 0.1;
451 if not (self.owner.active == ACTIVE_ACTIVE)
453 self.owner.velocity = '0 0 0';
457 // calculate sinewave using makevectors
458 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
459 v = self.owner.destvec + self.owner.movedir * v_forward_y;
460 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
461 // * 10 so it will arrive in 0.1 sec
462 self.owner.velocity = (v - self.owner.origin) * 10;
465 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
466 Brush model that moves back and forth on one axis (default Z).
467 speed : how long one cycle takes in seconds (default 4)
468 height : how far the cycle moves (default 32)
469 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
470 noise : path/name of looping .wav file to play.
471 dmg : Do this mutch dmg every .dmgtime intervall when blocked
474 void spawnfunc_func_bobbing()
477 if (self.noise != "")
479 precache_sound(self.noise);
480 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
486 // center of bobbing motion
487 self.destvec = self.origin;
488 // time scale to get degrees
489 self.cnt = 360 / self.speed;
491 self.active = ACTIVE_ACTIVE;
493 // damage when blocked
494 self.blocked = generic_plat_blocked;
495 if(self.dmg & (!self.message))
496 self.message = " was squished";
497 if(self.dmg && (!self.message2))
498 self.message2 = "was squished by";
499 if(self.dmg && (!self.dmgtime))
501 self.dmgtime2 = time;
504 if (self.spawnflags & 1) // X
505 self.movedir = '1 0 0' * self.height;
506 else if (self.spawnflags & 2) // Y
507 self.movedir = '0 1 0' * self.height;
509 self.movedir = '0 0 1' * self.height;
511 if not(InitMovingBrushTrigger())
514 // wait for targets to spawn
515 controller = spawn();
516 controller.classname = "func_bobbing_controller";
517 controller.owner = self;
518 controller.nextthink = time + 1;
519 controller.think = func_bobbing_controller_think;
520 self.nextthink = self.ltime + 999999999;
521 self.think = SUB_Null;
523 // Savage: Reduce bandwith, critical on e.g. nexdm02
524 self.effects |= EF_LOWPRECISION;
526 // TODO make a reset function for this one
530 void func_pendulum_controller_think()
533 self.nextthink = time + 0.1;
535 if not (self.owner.active == ACTIVE_ACTIVE)
537 self.owner.avelocity_x = 0;
541 // calculate sinewave using makevectors
542 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
543 v = self.owner.speed * v_forward_y + self.cnt;
544 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
546 // * 10 so it will arrive in 0.1 sec
547 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
551 void spawnfunc_func_pendulum()
554 if (self.noise != "")
556 precache_sound(self.noise);
557 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
560 self.active = ACTIVE_ACTIVE;
562 // keys: angle, speed, phase, noise, freq
566 // not initializing self.dmg to 2, to allow damageless pendulum
568 if(self.dmg & (!self.message))
569 self.message = " was squished";
570 if(self.dmg && (!self.message2))
571 self.message2 = "was squished by";
572 if(self.dmg && (!self.dmgtime))
574 self.dmgtime2 = time;
576 self.blocked = generic_plat_blocked;
578 self.avelocity_z = 0.0000001;
579 if not(InitMovingBrushTrigger())
584 // find pendulum length (same formula as Q3A)
585 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
588 // copy initial angle
589 self.cnt = self.angles_z;
591 // wait for targets to spawn
592 controller = spawn();
593 controller.classname = "func_pendulum_controller";
594 controller.owner = self;
595 controller.nextthink = time + 1;
596 controller.think = func_pendulum_controller_think;
597 self.nextthink = self.ltime + 999999999;
598 self.think = SUB_Null;
600 //self.effects |= EF_LOWPRECISION;
602 // TODO make a reset function for this one
605 // button and multiple button
608 void() button_return;
612 self.state = STATE_TOP;
613 self.nextthink = self.ltime + self.wait;
614 self.think = button_return;
615 activator = self.enemy;
617 self.frame = 1; // use alternate textures
622 self.state = STATE_BOTTOM;
627 self.state = STATE_DOWN;
628 SUB_CalcMove (self.pos1, self.speed, button_done);
629 self.frame = 0; // use normal textures
631 self.takedamage = DAMAGE_YES; // can be shot again
635 void button_blocked()
637 // do nothing, just don't come all the way back out
643 self.health = self.max_health;
644 self.takedamage = DAMAGE_NO; // will be reset upon return
646 if (self.state == STATE_UP || self.state == STATE_TOP)
649 if (self.noise != "")
650 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
652 self.state = STATE_UP;
653 SUB_CalcMove (self.pos2, self.speed, button_wait);
658 self.health = self.max_health;
659 setorigin(self, self.pos1);
660 self.frame = 0; // use normal textures
661 self.state = STATE_BOTTOM;
663 self.takedamage = DAMAGE_YES; // can be shot again
668 // if (activator.classname != "player")
670 // dprint(activator.classname);
671 // dprint(" triggered a button\n");
674 if not (self.active == ACTIVE_ACTIVE)
677 self.enemy = activator;
683 // if (activator.classname != "player")
685 // dprint(activator.classname);
686 // dprint(" touched a button\n");
690 if not(other.iscreature)
692 if(other.velocity * self.movedir < 0)
696 self.enemy = other.owner;
700 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
702 if(self.spawnflags & DOOR_NOSPLASH)
703 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
705 self.health = self.health - damage;
706 if (self.health <= 0)
708 // if (activator.classname != "player")
710 // dprint(activator.classname);
711 // dprint(" killed a button\n");
713 self.enemy = damage_attacker;
719 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
720 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.
722 "angle" determines the opening direction
723 "target" all entities with a matching targetname will be used
724 "speed" override the default 40 speed
725 "wait" override the default 1 second wait (-1 = never return)
726 "lip" override the default 4 pixel lip remaining at end of move
727 "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
734 void spawnfunc_func_button()
738 if not(InitMovingBrushTrigger())
740 self.effects |= EF_LOWPRECISION;
742 self.blocked = button_blocked;
743 self.use = button_use;
745 // if (self.health == 0) // all buttons are now shootable
749 self.max_health = self.health;
750 self.event_damage = button_damage;
751 self.takedamage = DAMAGE_YES;
754 self.touch = button_touch;
764 precache_sound(self.noise);
766 self.active = ACTIVE_ACTIVE;
768 self.pos1 = self.origin;
769 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
770 self.flags |= FL_NOTARGET;
776 float DOOR_START_OPEN = 1;
777 float DOOR_DONT_LINK = 4;
778 float DOOR_TOGGLE = 32;
782 Doors are similar to buttons, but can spawn a fat trigger field around them
783 to open without a touch, and they link together to form simultanious
786 Door.owner is the master door. If there is only one door, it points to itself.
787 If multiple doors, all will point to a single one.
789 Door.enemy chains from the master door through all doors linked in the chain.
794 =============================================================================
798 =============================================================================
803 void() door_rotating_go_down;
804 void() door_rotating_go_up;
809 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
810 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
813 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
814 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
816 //Dont chamge direction for dead or dying stuff
817 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
820 if (self.state == STATE_DOWN)
821 if (self.classname == "door")
826 door_rotating_go_up ();
829 if (self.classname == "door")
834 door_rotating_go_down ();
838 //gib dying stuff just to make sure
839 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
840 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
844 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
845 // if a door has a negative wait, it would never come back if blocked,
846 // so let it just squash the object to death real fast
847 /* if (self.wait >= 0)
849 if (self.state == STATE_DOWN)
860 if (self.noise1 != "")
861 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
862 self.state = STATE_TOP;
863 if (self.spawnflags & DOOR_TOGGLE)
864 return; // don't come down automatically
865 if (self.classname == "door")
867 self.think = door_go_down;
870 self.think = door_rotating_go_down;
872 self.nextthink = self.ltime + self.wait;
875 void door_hit_bottom()
877 if (self.noise1 != "")
878 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
879 self.state = STATE_BOTTOM;
884 if (self.noise2 != "")
885 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
888 self.takedamage = DAMAGE_YES;
889 self.health = self.max_health;
892 self.state = STATE_DOWN;
893 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
898 if (self.state == STATE_UP)
899 return; // already going up
901 if (self.state == STATE_TOP)
902 { // reset top wait time
903 self.nextthink = self.ltime + self.wait;
907 if (self.noise2 != "")
908 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
909 self.state = STATE_UP;
910 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
913 oldmessage = self.message;
916 self.message = oldmessage;
922 =============================================================================
926 =============================================================================
929 float door_check_keys(void) {
939 if not(door.itemkeys)
942 // this door require a key
943 // only a player can have a key
944 if (other.classname != "player")
947 if (item_keys_usekey(door, other)) {
948 // some keys were used
949 if (other.key_door_messagetime <= time) {
950 play2(other, "misc/talk.wav");
951 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
952 other.key_door_messagetime = time + 2;
956 if (other.key_door_messagetime <= time) {
957 play2(other, "misc/talk.wav");
958 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
959 other.key_door_messagetime = time + 2;
964 // door is now unlocked
965 play2(other, "misc/talk.wav");
966 centerprint(other, "Door unlocked!");
978 if (self.owner != self)
979 objerror ("door_fire: self.owner != self");
983 if (self.spawnflags & DOOR_TOGGLE)
985 if (self.state == STATE_UP || self.state == STATE_TOP)
990 if (self.classname == "door")
996 door_rotating_go_down ();
999 } while ( (self != starte) && (self != world) );
1005 // trigger all paired doors
1009 if (self.classname == "door")
1014 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1015 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1017 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1018 self.pos2 = '0 0 0' - self.pos2;
1020 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1021 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1022 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1024 door_rotating_go_up ();
1028 } while ( (self != starte) && (self != world) );
1037 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1049 void door_trigger_touch()
1051 if (other.health < 1)
1052 if not(other.iscreature && other.deadflag == DEAD_NO)
1055 if (time < self.attack_finished_single)
1058 // check if door is locked
1059 if (!door_check_keys())
1062 self.attack_finished_single = time + 1;
1071 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1074 if(self.spawnflags & DOOR_NOSPLASH)
1075 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1077 self.health = self.health - damage;
1079 if (self.itemkeys) {
1080 // don't allow opening doors through damage if keys are required
1084 if (self.health <= 0)
1088 self.health = self.max_health;
1089 self.takedamage = DAMAGE_NO; // wil be reset upon return
1105 if(other.classname != "player")
1107 if (self.owner.attack_finished_single > time)
1110 self.owner.attack_finished_single = time + 2;
1112 if (!(self.owner.dmg) && (self.owner.message != ""))
1114 if (other.flags & FL_CLIENT)
1115 centerprint (other, self.owner.message);
1116 play2(other, "misc/talk.wav");
1121 void door_generic_plat_blocked()
1124 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1125 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1128 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1129 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1131 //Dont chamge direction for dead or dying stuff
1132 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1135 if (self.state == STATE_DOWN)
1136 door_rotating_go_up ();
1138 door_rotating_go_down ();
1141 //gib dying stuff just to make sure
1142 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1143 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1147 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1148 // if a door has a negative wait, it would never come back if blocked,
1149 // so let it just squash the object to death real fast
1150 /* if (self.wait >= 0)
1152 if (self.state == STATE_DOWN)
1153 door_rotating_go_up ();
1155 door_rotating_go_down ();
1161 void door_rotating_hit_top()
1163 if (self.noise1 != "")
1164 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1165 self.state = STATE_TOP;
1166 if (self.spawnflags & DOOR_TOGGLE)
1167 return; // don't come down automatically
1168 self.think = door_rotating_go_down;
1169 self.nextthink = self.ltime + self.wait;
1172 void door_rotating_hit_bottom()
1174 if (self.noise1 != "")
1175 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1176 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1178 self.pos2 = '0 0 0' - self.pos2;
1181 self.state = STATE_BOTTOM;
1184 void door_rotating_go_down()
1186 if (self.noise2 != "")
1187 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1188 if (self.max_health)
1190 self.takedamage = DAMAGE_YES;
1191 self.health = self.max_health;
1194 self.state = STATE_DOWN;
1195 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1198 void door_rotating_go_up()
1200 if (self.state == STATE_UP)
1201 return; // already going up
1203 if (self.state == STATE_TOP)
1204 { // reset top wait time
1205 self.nextthink = self.ltime + self.wait;
1208 if (self.noise2 != "")
1209 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1210 self.state = STATE_UP;
1211 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1214 oldmessage = self.message;
1217 self.message = oldmessage;
1224 =============================================================================
1228 =============================================================================
1232 entity spawn_field(vector fmins, vector fmaxs)
1238 trigger.classname = "doortriggerfield";
1239 trigger.movetype = MOVETYPE_NONE;
1240 trigger.solid = SOLID_TRIGGER;
1241 trigger.owner = self;
1242 trigger.touch = door_trigger_touch;
1246 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1251 float EntitiesTouching(entity e1, entity e2)
1253 if (e1.absmin_x > e2.absmax_x)
1255 if (e1.absmin_y > e2.absmax_y)
1257 if (e1.absmin_z > e2.absmax_z)
1259 if (e1.absmax_x < e2.absmin_x)
1261 if (e1.absmax_y < e2.absmin_y)
1263 if (e1.absmax_z < e2.absmin_z)
1279 vector cmins, cmaxs;
1282 return; // already linked by another door
1283 if (self.spawnflags & 4)
1285 self.owner = self.enemy = self;
1293 self.trigger_field = spawn_field(self.absmin, self.absmax);
1295 return; // don't want to link this door
1298 cmins = self.absmin;
1299 cmaxs = self.absmax;
1306 self.owner = starte; // master door
1309 starte.health = self.health;
1311 starte.targetname = self.targetname;
1312 if (self.message != "")
1313 starte.message = self.message;
1315 t = find(t, classname, self.classname);
1318 self.enemy = starte; // make the chain a loop
1320 // shootable, or triggered doors just needed the owner/enemy links,
1321 // they don't spawn a field
1332 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1337 if (EntitiesTouching(self,t))
1340 objerror ("cross connected doors");
1345 if (t.absmin_x < cmins_x)
1346 cmins_x = t.absmin_x;
1347 if (t.absmin_y < cmins_y)
1348 cmins_y = t.absmin_y;
1349 if (t.absmin_z < cmins_z)
1350 cmins_z = t.absmin_z;
1351 if (t.absmax_x > cmaxs_x)
1352 cmaxs_x = t.absmax_x;
1353 if (t.absmax_y > cmaxs_y)
1354 cmaxs_y = t.absmax_y;
1355 if (t.absmax_z > cmaxs_z)
1356 cmaxs_z = t.absmax_z;
1363 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1364 if two doors touch, they are assumed to be connected and operate as a unit.
1366 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1368 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).
1370 GOLD_KEY causes the door to open only if the activator holds a gold key.
1372 SILVER_KEY causes the door to open only if the activator holds a silver key.
1374 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1375 "angle" determines the opening direction
1376 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1377 "health" if set, door must be shot open
1378 "speed" movement speed (100 default)
1379 "wait" wait before returning (3 default, -1 = never return)
1380 "lip" lip remaining at end of move (8 default)
1381 "dmg" damage to inflict when blocked (2 default)
1388 FIXME: only one sound set available at the time being
1392 void door_init_startopen()
1394 setorigin (self, self.pos2);
1395 self.pos2 = self.pos1;
1396 self.pos1 = self.origin;
1401 setorigin(self, self.pos1);
1402 self.velocity = '0 0 0';
1403 self.state = STATE_BOTTOM;
1404 self.think = SUB_Null;
1407 // spawnflags require key (for now only func_door)
1408 #define SPAWNFLAGS_GOLD_KEY 8
1409 #define SPAWNFLAGS_SILVER_KEY 16
1410 void spawnfunc_func_door()
1412 // Quake 1 keys compatibility
1413 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1414 self.itemkeys |= ITEM_KEY_BIT(0);
1415 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1416 self.itemkeys |= ITEM_KEY_BIT(1);
1418 //if (!self.deathtype) // map makers can override this
1419 // self.deathtype = " got in the way";
1422 self.max_health = self.health;
1423 if not(InitMovingBrushTrigger())
1425 self.effects |= EF_LOWPRECISION;
1426 self.classname = "door";
1428 self.blocked = door_blocked;
1429 self.use = door_use;
1431 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1432 // if(self.spawnflags & 8)
1433 // self.dmg = 10000;
1435 if(self.dmg && (!self.message))
1436 self.message = "was squished";
1437 if(self.dmg && (!self.message2))
1438 self.message2 = "was squished by";
1440 if (self.sounds > 0)
1442 precache_sound ("plats/medplat1.wav");
1443 precache_sound ("plats/medplat2.wav");
1444 self.noise2 = "plats/medplat1.wav";
1445 self.noise1 = "plats/medplat2.wav";
1455 self.pos1 = self.origin;
1456 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1458 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1459 // but spawn in the open position
1460 if (self.spawnflags & DOOR_START_OPEN)
1461 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1463 self.state = STATE_BOTTOM;
1467 self.takedamage = DAMAGE_YES;
1468 self.event_damage = door_damage;
1474 self.touch = door_touch;
1476 // LinkDoors can't be done until all of the doors have been spawned, so
1477 // the sizes can be detected properly.
1478 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1480 self.reset = door_reset;
1483 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1484 if two doors touch, they are assumed to be connected and operate as a unit.
1486 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1488 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1489 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1490 must have set trigger_reverse to 1.
1491 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1493 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).
1495 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1496 "angle" determines the destination angle for opening. negative values reverse the direction.
1497 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1498 "health" if set, door must be shot open
1499 "speed" movement speed (100 default)
1500 "wait" wait before returning (3 default, -1 = never return)
1501 "dmg" damage to inflict when blocked (2 default)
1508 FIXME: only one sound set available at the time being
1511 void door_rotating_reset()
1513 self.angles = self.pos1;
1514 self.avelocity = '0 0 0';
1515 self.state = STATE_BOTTOM;
1516 self.think = SUB_Null;
1519 void door_rotating_init_startopen()
1521 self.angles = self.movedir;
1522 self.pos2 = '0 0 0';
1523 self.pos1 = self.movedir;
1527 void spawnfunc_func_door_rotating()
1530 //if (!self.deathtype) // map makers can override this
1531 // self.deathtype = " got in the way";
1533 // I abuse "movedir" for denoting the axis for now
1534 if (self.spawnflags & 64) // X (untested)
1535 self.movedir = '0 0 1';
1536 else if (self.spawnflags & 128) // Y (untested)
1537 self.movedir = '1 0 0';
1539 self.movedir = '0 1 0';
1541 if (self.angles_y==0) self.angles_y = 90;
1543 self.movedir = self.movedir * self.angles_y;
1544 self.angles = '0 0 0';
1546 self.max_health = self.health;
1547 self.avelocity = self.movedir;
1548 if not(InitMovingBrushTrigger())
1550 self.velocity = '0 0 0';
1551 //self.effects |= EF_LOWPRECISION;
1552 self.classname = "door_rotating";
1554 self.blocked = door_blocked;
1555 self.use = door_use;
1557 if(self.spawnflags & 8)
1560 if(self.dmg && (!self.message))
1561 self.message = "was squished";
1562 if(self.dmg && (!self.message2))
1563 self.message2 = "was squished by";
1565 if (self.sounds > 0)
1567 precache_sound ("plats/medplat1.wav");
1568 precache_sound ("plats/medplat2.wav");
1569 self.noise2 = "plats/medplat1.wav";
1570 self.noise1 = "plats/medplat2.wav";
1577 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1579 self.pos1 = '0 0 0';
1580 self.pos2 = self.movedir;
1582 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1583 // but spawn in the open position
1584 if (self.spawnflags & DOOR_START_OPEN)
1585 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1587 self.state = STATE_BOTTOM;
1591 self.takedamage = DAMAGE_YES;
1592 self.event_damage = door_damage;
1598 self.touch = door_touch;
1600 // LinkDoors can't be done until all of the doors have been spawned, so
1601 // the sizes can be detected properly.
1602 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1604 self.reset = door_rotating_reset;
1608 =============================================================================
1612 =============================================================================
1615 void() fd_secret_move1;
1616 void() fd_secret_move2;
1617 void() fd_secret_move3;
1618 void() fd_secret_move4;
1619 void() fd_secret_move5;
1620 void() fd_secret_move6;
1621 void() fd_secret_done;
1623 float SECRET_OPEN_ONCE = 1; // stays open
1624 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1625 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1626 float SECRET_NO_SHOOT = 8; // only opened by trigger
1627 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1630 void fd_secret_use()
1633 string message_save;
1635 self.health = 10000;
1636 self.bot_attack = TRUE;
1638 // exit if still moving around...
1639 if (self.origin != self.oldorigin)
1642 message_save = self.message;
1643 self.message = ""; // no more message
1644 SUB_UseTargets(); // fire all targets / killtargets
1645 self.message = message_save;
1647 self.velocity = '0 0 0';
1649 // Make a sound, wait a little...
1651 if (self.noise1 != "")
1652 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1653 self.nextthink = self.ltime + 0.1;
1655 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1656 makevectors(self.mangle);
1660 if (self.spawnflags & SECRET_1ST_DOWN)
1661 self.t_width = fabs(v_up * self.size);
1663 self.t_width = fabs(v_right * self.size);
1667 self.t_length = fabs(v_forward * self.size);
1669 if (self.spawnflags & SECRET_1ST_DOWN)
1670 self.dest1 = self.origin - v_up * self.t_width;
1672 self.dest1 = self.origin + v_right * (self.t_width * temp);
1674 self.dest2 = self.dest1 + v_forward * self.t_length;
1675 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1676 if (self.noise2 != "")
1677 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1680 // Wait after first movement...
1681 void fd_secret_move1()
1683 self.nextthink = self.ltime + 1.0;
1684 self.think = fd_secret_move2;
1685 if (self.noise3 != "")
1686 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1689 // Start moving sideways w/sound...
1690 void fd_secret_move2()
1692 if (self.noise2 != "")
1693 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1694 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1697 // Wait here until time to go back...
1698 void fd_secret_move3()
1700 if (self.noise3 != "")
1701 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1702 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1704 self.nextthink = self.ltime + self.wait;
1705 self.think = fd_secret_move4;
1710 void fd_secret_move4()
1712 if (self.noise2 != "")
1713 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1714 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1718 void fd_secret_move5()
1720 self.nextthink = self.ltime + 1.0;
1721 self.think = fd_secret_move6;
1722 if (self.noise3 != "")
1723 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1726 void fd_secret_move6()
1728 if (self.noise2 != "")
1729 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1730 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1733 void fd_secret_done()
1735 if (self.spawnflags&SECRET_YES_SHOOT)
1737 self.health = 10000;
1738 self.takedamage = DAMAGE_YES;
1739 //self.th_pain = fd_secret_use;
1741 if (self.noise3 != "")
1742 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1745 void secret_blocked()
1747 if (time < self.attack_finished_single)
1749 self.attack_finished_single = time + 0.5;
1750 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1762 if not(other.iscreature)
1764 if (self.attack_finished_single > time)
1767 self.attack_finished_single = time + 2;
1771 if (other.flags & FL_CLIENT)
1772 centerprint (other, self.message);
1773 play2(other, "misc/talk.wav");
1779 if (self.spawnflags&SECRET_YES_SHOOT)
1781 self.health = 10000;
1782 self.takedamage = DAMAGE_YES;
1784 setorigin(self, self.oldorigin);
1785 self.think = SUB_Null;
1788 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1789 Basic secret door. Slides back, then to the side. Angle determines direction.
1790 wait = # of seconds before coming back
1791 1st_left = 1st move is left of arrow
1792 1st_down = 1st move is down from arrow
1793 always_shoot = even if targeted, keep shootable
1794 t_width = override WIDTH to move back (or height if going down)
1795 t_length = override LENGTH to move sideways
1796 "dmg" damage to inflict when blocked (2 default)
1798 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1805 void spawnfunc_func_door_secret()
1807 /*if (!self.deathtype) // map makers can override this
1808 self.deathtype = " got in the way";*/
1814 self.mangle = self.angles;
1815 self.angles = '0 0 0';
1816 self.classname = "door";
1817 if not(InitMovingBrushTrigger())
1819 self.effects |= EF_LOWPRECISION;
1821 self.touch = secret_touch;
1822 self.blocked = secret_blocked;
1824 self.use = fd_secret_use;
1829 self.spawnflags |= SECRET_YES_SHOOT;
1831 if(self.spawnflags&SECRET_YES_SHOOT)
1833 self.health = 10000;
1834 self.takedamage = DAMAGE_YES;
1835 self.event_damage = fd_secret_use;
1837 self.oldorigin = self.origin;
1839 self.wait = 5; // 5 seconds before closing
1841 self.reset = secret_reset;
1845 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1846 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1847 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
1848 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1849 height: amplitude modifier (default 32)
1850 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1851 noise: path/name of looping .wav file to play.
1852 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1856 void func_fourier_controller_think()
1861 self.nextthink = time + 0.1;
1862 if not (self.owner.active == ACTIVE_ACTIVE)
1864 self.owner.velocity = '0 0 0';
1869 n = floor((tokenize_console(self.owner.netname)) / 5);
1870 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1872 v = self.owner.destvec;
1874 for(i = 0; i < n; ++i)
1876 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1877 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;
1880 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1881 // * 10 so it will arrive in 0.1 sec
1882 self.owner.velocity = (v - self.owner.origin) * 10;
1885 void spawnfunc_func_fourier()
1888 if (self.noise != "")
1890 precache_sound(self.noise);
1891 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1898 self.destvec = self.origin;
1899 self.cnt = 360 / self.speed;
1901 self.blocked = generic_plat_blocked;
1902 if(self.dmg & (!self.message))
1903 self.message = " was squished";
1904 if(self.dmg && (!self.message2))
1905 self.message2 = "was squished by";
1906 if(self.dmg && (!self.dmgtime))
1907 self.dmgtime = 0.25;
1908 self.dmgtime2 = time;
1910 if(self.netname == "")
1911 self.netname = "1 0 0 0 1";
1913 if not(InitMovingBrushTrigger())
1916 self.active = ACTIVE_ACTIVE;
1918 // wait for targets to spawn
1919 controller = spawn();
1920 controller.classname = "func_fourier_controller";
1921 controller.owner = self;
1922 controller.nextthink = time + 1;
1923 controller.think = func_fourier_controller_think;
1924 self.nextthink = self.ltime + 999999999;
1925 self.think = SUB_Null;
1927 // Savage: Reduce bandwith, critical on e.g. nexdm02
1928 self.effects |= EF_LOWPRECISION;
1930 // TODO make a reset function for this one
1933 // reusing some fields havocbots declared
1934 .entity wp00, wp01, wp02, wp03;
1936 .float targetfactor, target2factor, target3factor, target4factor;
1937 .vector targetnormal, target2normal, target3normal, target4normal;
1939 vector func_vectormamamam_origin(entity o, float t)
1951 p = e.origin + t * e.velocity;
1953 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1955 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1961 p = e.origin + t * e.velocity;
1963 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1965 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1971 p = e.origin + t * e.velocity;
1973 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1975 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1981 p = e.origin + t * e.velocity;
1983 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1985 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1991 void func_vectormamamam_controller_think()
1993 self.nextthink = time + 0.1;
1995 if not (self.owner.active == ACTIVE_ACTIVE)
1997 self.owner.velocity = '0 0 0';
2001 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2002 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2005 void func_vectormamamam_findtarget()
2007 if(self.target != "")
2008 self.wp00 = find(world, targetname, self.target);
2010 if(self.target2 != "")
2011 self.wp01 = find(world, targetname, self.target2);
2013 if(self.target3 != "")
2014 self.wp02 = find(world, targetname, self.target3);
2016 if(self.target4 != "")
2017 self.wp03 = find(world, targetname, self.target4);
2019 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2020 objerror("No reference entity found, so there is nothing to move. Aborting.");
2022 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2025 controller = spawn();
2026 controller.classname = "func_vectormamamam_controller";
2027 controller.owner = self;
2028 controller.nextthink = time + 1;
2029 controller.think = func_vectormamamam_controller_think;
2032 void spawnfunc_func_vectormamamam()
2034 if (self.noise != "")
2036 precache_sound(self.noise);
2037 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2040 if(!self.targetfactor)
2041 self.targetfactor = 1;
2043 if(!self.target2factor)
2044 self.target2factor = 1;
2046 if(!self.target3factor)
2047 self.target3factor = 1;
2049 if(!self.target4factor)
2050 self.target4factor = 1;
2052 if(vlen(self.targetnormal))
2053 self.targetnormal = normalize(self.targetnormal);
2055 if(vlen(self.target2normal))
2056 self.target2normal = normalize(self.target2normal);
2058 if(vlen(self.target3normal))
2059 self.target3normal = normalize(self.target3normal);
2061 if(vlen(self.target4normal))
2062 self.target4normal = normalize(self.target4normal);
2064 self.blocked = generic_plat_blocked;
2065 if(self.dmg & (!self.message))
2066 self.message = " was squished";
2067 if(self.dmg && (!self.message2))
2068 self.message2 = "was squished by";
2069 if(self.dmg && (!self.dmgtime))
2070 self.dmgtime = 0.25;
2071 self.dmgtime2 = time;
2073 if(self.netname == "")
2074 self.netname = "1 0 0 0 1";
2076 if not(InitMovingBrushTrigger())
2079 // wait for targets to spawn
2080 self.nextthink = self.ltime + 999999999;
2081 self.think = SUB_Null;
2083 // Savage: Reduce bandwith, critical on e.g. nexdm02
2084 self.effects |= EF_LOWPRECISION;
2086 self.active = ACTIVE_ACTIVE;
2088 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2091 void conveyor_think()
2095 // set myself as current conveyor where possible
2096 for(e = world; (e = findentity(e, conveyor, self)); )
2101 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2102 if(!e.conveyor.state)
2105 vector emin = e.absmin;
2106 vector emax = e.absmax;
2107 if(self.solid == SOLID_BSP)
2112 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2113 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2117 for(e = world; (e = findentity(e, conveyor, self)); )
2119 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2120 continue; // done in SV_PlayerPhysics
2122 setorigin(e, e.origin + self.movedir * sys_frametime);
2123 move_out_of_solid(e);
2124 UpdateCSQCProjectile(e);
2126 // stupid conveyor code
2127 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2128 if(trace_fraction > 0)
2129 setorigin(e, trace_endpos);
2134 self.nextthink = time;
2139 self.state = !self.state;
2142 void conveyor_reset()
2144 self.state = (self.spawnflags & 1);
2147 void conveyor_init()
2151 self.movedir = self.movedir * self.speed;
2152 self.think = conveyor_think;
2153 self.nextthink = time;
2156 self.use = conveyor_use;
2157 self.reset = conveyor_reset;
2164 void spawnfunc_trigger_conveyor()
2171 void spawnfunc_func_conveyor()
2174 InitMovingBrushTrigger();
2175 self.movetype = MOVETYPE_NONE;