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, TSPEED_LINEAR, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, TSPEED_LINEAR, 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()
187 if(self.platmovetype && self.platmovetype != "")
189 // setup values for overriding train movement
190 // if a second value does not exist, both start and end speeds are the single value specified
192 n = tokenize_console(self.platmovetype);
193 self.platmovetype_start = stof(argv(0));
194 self.platmovetype_end = stof(argv(0));
196 self.platmovetype_end = stof(argv(1));
199 void spawnfunc_func_plat()
201 if (self.sounds == 0)
204 if(self.spawnflags & 4)
207 if(self.dmg && (!self.message))
208 self.message = "was squished";
209 if(self.dmg && (!self.message2))
210 self.message2 = "was squished by";
212 if (self.sounds == 1)
214 precache_sound ("plats/plat1.wav");
215 precache_sound ("plats/plat2.wav");
216 self.noise = "plats/plat1.wav";
217 self.noise1 = "plats/plat2.wav";
220 if (self.sounds == 2)
222 precache_sound ("plats/medplat1.wav");
223 precache_sound ("plats/medplat2.wav");
224 self.noise = "plats/medplat1.wav";
225 self.noise1 = "plats/medplat2.wav";
230 precache_sound (self.sound1);
231 self.noise = self.sound1;
235 precache_sound (self.sound2);
236 self.noise1 = self.sound2;
239 self.mangle = self.angles;
240 self.angles = '0 0 0';
242 self.classname = "plat";
243 if not(InitMovingBrushTrigger())
245 self.effects |= EF_LOWPRECISION;
246 setsize (self, self.mins , self.maxs);
248 self.blocked = plat_crush;
255 self.height = self.size_z - self.lip;
257 self.pos1 = self.origin;
258 self.pos2 = self.origin;
259 self.pos2_z = self.origin_z - self.height;
261 self.reset = plat_reset;
264 plat_spawn_inside_trigger (); // the "start moving" trigger
267 .float train_wait_turning;
278 // if using bezier curves and turning is enabled, the train will turn toward the next point while waiting
279 if(!self.train_wait_turning)
280 if(self.spawnflags & 1 && self.bezier_turn && self.wait >= 0)
284 targ = find(world, targetname, self.target);
285 org = normalize(targ.origin);
286 SUB_CalcAngleMove(org, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
287 self.train_wait_turning = TRUE;
292 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
294 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
296 self.train_wait_turning = FALSE;
301 self.think = train_next;
302 self.nextthink = self.ltime + self.wait;
311 targ = find(world, targetname, self.target);
312 self.target = targ.target;
313 if (self.spawnflags & 1)
315 cp = find(world, target, targ.targetname); // get the previous corner first
316 cp = find(world, targetname, cp.curve); // now get its second target (the control point)
317 if(cp.targetname == "")
318 cp_org = targ.origin - self.mins; // no control point found, assume a straight line to the destination
320 cp_org = cp.origin - self.mins;
323 objerror("train_next: no next target");
324 self.wait = targ.wait;
328 if(targ.platmovetype_start || targ.platmovetype_end)
330 // override train movement type
331 self.platmovetype_start = targ.platmovetype_start;
332 self.platmovetype_end = targ.platmovetype_end;
337 if (self.spawnflags & 1)
338 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
340 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
344 if (self.spawnflags & 1)
345 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
347 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
351 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
354 void func_train_find()
357 targ = find(world, targetname, self.target);
358 self.target = targ.target;
360 objerror("func_train_find: no next target");
361 setorigin(self, targ.origin - self.mins);
362 self.nextthink = self.ltime + 1;
363 self.think = train_next;
366 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
367 Ridable platform, targets spawnfunc_path_corner path to follow.
368 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
369 target : targetname of first spawnfunc_path_corner (starts here)
371 void spawnfunc_func_train()
373 if (self.noise != "")
374 precache_sound(self.noise);
377 objerror("func_train without a target");
380 if (self.spawnflags & 2)
381 self.bezier_turn = TRUE;
383 if not(InitMovingBrushTrigger())
385 self.effects |= EF_LOWPRECISION;
387 // wait for targets to spawn
388 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
390 self.blocked = generic_plat_blocked;
391 if(self.dmg & (!self.message))
392 self.message = " was squished";
393 if(self.dmg && (!self.message2))
394 self.message2 = "was squished by";
395 if(self.dmg && (!self.dmgtime))
397 self.dmgtime2 = time;
399 // TODO make a reset function for this one
402 void func_rotating_setactive(float astate)
405 if (astate == ACTIVE_TOGGLE)
407 if(self.active == ACTIVE_ACTIVE)
408 self.active = ACTIVE_NOT;
410 self.active = ACTIVE_ACTIVE;
413 self.active = astate;
415 if(self.active == ACTIVE_NOT)
416 self.avelocity = '0 0 0';
418 self.avelocity = self.pos1;
421 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
422 Brush model that spins in place on one axis (default Z).
423 speed : speed to rotate (in degrees per second)
424 noise : path/name of looping .wav file to play.
425 dmg : Do this mutch dmg every .dmgtime intervall when blocked
429 void spawnfunc_func_rotating()
431 if (self.noise != "")
433 precache_sound(self.noise);
434 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
437 self.active = ACTIVE_ACTIVE;
438 self.setactive = func_rotating_setactive;
442 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
443 if (self.spawnflags & 4) // X (untested)
444 self.avelocity = '0 0 1' * self.speed;
445 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
446 else if (self.spawnflags & 8) // Y (untested)
447 self.avelocity = '1 0 0' * self.speed;
448 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
450 self.avelocity = '0 1 0' * self.speed;
452 self.pos1 = self.avelocity;
454 if(self.dmg & (!self.message))
455 self.message = " was squished";
456 if(self.dmg && (!self.message2))
457 self.message2 = "was squished by";
460 if(self.dmg && (!self.dmgtime))
463 self.dmgtime2 = time;
465 if not(InitMovingBrushTrigger())
467 // no EF_LOWPRECISION here, as rounding angles is bad
469 self.blocked = generic_plat_blocked;
471 // wait for targets to spawn
472 self.nextthink = self.ltime + 999999999;
473 self.think = SUB_Null;
475 // TODO make a reset function for this one
479 void func_bobbing_controller_think()
482 self.nextthink = time + 0.1;
484 if not (self.owner.active == ACTIVE_ACTIVE)
486 self.owner.velocity = '0 0 0';
490 // calculate sinewave using makevectors
491 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
492 v = self.owner.destvec + self.owner.movedir * v_forward_y;
493 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
494 // * 10 so it will arrive in 0.1 sec
495 self.owner.velocity = (v - self.owner.origin) * 10;
498 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
499 Brush model that moves back and forth on one axis (default Z).
500 speed : how long one cycle takes in seconds (default 4)
501 height : how far the cycle moves (default 32)
502 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
503 noise : path/name of looping .wav file to play.
504 dmg : Do this mutch dmg every .dmgtime intervall when blocked
507 void spawnfunc_func_bobbing()
510 if (self.noise != "")
512 precache_sound(self.noise);
513 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
519 // center of bobbing motion
520 self.destvec = self.origin;
521 // time scale to get degrees
522 self.cnt = 360 / self.speed;
524 self.active = ACTIVE_ACTIVE;
526 // damage when blocked
527 self.blocked = generic_plat_blocked;
528 if(self.dmg & (!self.message))
529 self.message = " was squished";
530 if(self.dmg && (!self.message2))
531 self.message2 = "was squished by";
532 if(self.dmg && (!self.dmgtime))
534 self.dmgtime2 = time;
537 if (self.spawnflags & 1) // X
538 self.movedir = '1 0 0' * self.height;
539 else if (self.spawnflags & 2) // Y
540 self.movedir = '0 1 0' * self.height;
542 self.movedir = '0 0 1' * self.height;
544 if not(InitMovingBrushTrigger())
547 // wait for targets to spawn
548 controller = spawn();
549 controller.classname = "func_bobbing_controller";
550 controller.owner = self;
551 controller.nextthink = time + 1;
552 controller.think = func_bobbing_controller_think;
553 self.nextthink = self.ltime + 999999999;
554 self.think = SUB_Null;
556 // Savage: Reduce bandwith, critical on e.g. nexdm02
557 self.effects |= EF_LOWPRECISION;
559 // TODO make a reset function for this one
563 void func_pendulum_controller_think()
566 self.nextthink = time + 0.1;
568 if not (self.owner.active == ACTIVE_ACTIVE)
570 self.owner.avelocity_x = 0;
574 // calculate sinewave using makevectors
575 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
576 v = self.owner.speed * v_forward_y + self.cnt;
577 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
579 // * 10 so it will arrive in 0.1 sec
580 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
584 void spawnfunc_func_pendulum()
587 if (self.noise != "")
589 precache_sound(self.noise);
590 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
593 self.active = ACTIVE_ACTIVE;
595 // keys: angle, speed, phase, noise, freq
599 // not initializing self.dmg to 2, to allow damageless pendulum
601 if(self.dmg & (!self.message))
602 self.message = " was squished";
603 if(self.dmg && (!self.message2))
604 self.message2 = "was squished by";
605 if(self.dmg && (!self.dmgtime))
607 self.dmgtime2 = time;
609 self.blocked = generic_plat_blocked;
611 self.avelocity_z = 0.0000001;
612 if not(InitMovingBrushTrigger())
617 // find pendulum length (same formula as Q3A)
618 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
621 // copy initial angle
622 self.cnt = self.angles_z;
624 // wait for targets to spawn
625 controller = spawn();
626 controller.classname = "func_pendulum_controller";
627 controller.owner = self;
628 controller.nextthink = time + 1;
629 controller.think = func_pendulum_controller_think;
630 self.nextthink = self.ltime + 999999999;
631 self.think = SUB_Null;
633 //self.effects |= EF_LOWPRECISION;
635 // TODO make a reset function for this one
638 // button and multiple button
641 void() button_return;
645 self.state = STATE_TOP;
646 self.nextthink = self.ltime + self.wait;
647 self.think = button_return;
648 activator = self.enemy;
650 self.frame = 1; // use alternate textures
655 self.state = STATE_BOTTOM;
660 self.state = STATE_DOWN;
661 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
662 self.frame = 0; // use normal textures
664 self.takedamage = DAMAGE_YES; // can be shot again
668 void button_blocked()
670 // do nothing, just don't come all the way back out
676 self.health = self.max_health;
677 self.takedamage = DAMAGE_NO; // will be reset upon return
679 if (self.state == STATE_UP || self.state == STATE_TOP)
682 if (self.noise != "")
683 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
685 self.state = STATE_UP;
686 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
691 self.health = self.max_health;
692 setorigin(self, self.pos1);
693 self.frame = 0; // use normal textures
694 self.state = STATE_BOTTOM;
696 self.takedamage = DAMAGE_YES; // can be shot again
701 // if (activator.classname != "player")
703 // dprint(activator.classname);
704 // dprint(" triggered a button\n");
707 if not (self.active == ACTIVE_ACTIVE)
710 self.enemy = activator;
716 // if (activator.classname != "player")
718 // dprint(activator.classname);
719 // dprint(" touched a button\n");
723 if not(other.iscreature)
725 if(other.velocity * self.movedir < 0)
729 self.enemy = other.owner;
733 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
735 if(self.spawnflags & DOOR_NOSPLASH)
736 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
738 self.health = self.health - damage;
739 if (self.health <= 0)
741 // if (activator.classname != "player")
743 // dprint(activator.classname);
744 // dprint(" killed a button\n");
746 self.enemy = damage_attacker;
752 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
753 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.
755 "angle" determines the opening direction
756 "target" all entities with a matching targetname will be used
757 "speed" override the default 40 speed
758 "wait" override the default 1 second wait (-1 = never return)
759 "lip" override the default 4 pixel lip remaining at end of move
760 "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
767 void spawnfunc_func_button()
771 if not(InitMovingBrushTrigger())
773 self.effects |= EF_LOWPRECISION;
775 self.blocked = button_blocked;
776 self.use = button_use;
778 // if (self.health == 0) // all buttons are now shootable
782 self.max_health = self.health;
783 self.event_damage = button_damage;
784 self.takedamage = DAMAGE_YES;
787 self.touch = button_touch;
797 precache_sound(self.noise);
799 self.active = ACTIVE_ACTIVE;
801 self.pos1 = self.origin;
802 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
803 self.flags |= FL_NOTARGET;
809 float DOOR_START_OPEN = 1;
810 float DOOR_DONT_LINK = 4;
811 float DOOR_TOGGLE = 32;
815 Doors are similar to buttons, but can spawn a fat trigger field around them
816 to open without a touch, and they link together to form simultanious
819 Door.owner is the master door. If there is only one door, it points to itself.
820 If multiple doors, all will point to a single one.
822 Door.enemy chains from the master door through all doors linked in the chain.
827 =============================================================================
831 =============================================================================
836 void() door_rotating_go_down;
837 void() door_rotating_go_up;
842 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
843 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
846 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
847 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
849 //Dont chamge direction for dead or dying stuff
850 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
853 if (self.state == STATE_DOWN)
854 if (self.classname == "door")
859 door_rotating_go_up ();
862 if (self.classname == "door")
867 door_rotating_go_down ();
871 //gib dying stuff just to make sure
872 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
873 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
877 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
878 // if a door has a negative wait, it would never come back if blocked,
879 // so let it just squash the object to death real fast
880 /* if (self.wait >= 0)
882 if (self.state == STATE_DOWN)
893 if (self.noise1 != "")
894 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
895 self.state = STATE_TOP;
896 if (self.spawnflags & DOOR_TOGGLE)
897 return; // don't come down automatically
898 if (self.classname == "door")
900 self.think = door_go_down;
903 self.think = door_rotating_go_down;
905 self.nextthink = self.ltime + self.wait;
908 void door_hit_bottom()
910 if (self.noise1 != "")
911 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
912 self.state = STATE_BOTTOM;
917 if (self.noise2 != "")
918 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
921 self.takedamage = DAMAGE_YES;
922 self.health = self.max_health;
925 self.state = STATE_DOWN;
926 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
931 if (self.state == STATE_UP)
932 return; // already going up
934 if (self.state == STATE_TOP)
935 { // reset top wait time
936 self.nextthink = self.ltime + self.wait;
940 if (self.noise2 != "")
941 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
942 self.state = STATE_UP;
943 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
946 oldmessage = self.message;
949 self.message = oldmessage;
955 =============================================================================
959 =============================================================================
962 float door_check_keys(void) {
972 if not(door.itemkeys)
975 // this door require a key
976 // only a player can have a key
977 if (other.classname != "player")
980 if (item_keys_usekey(door, other)) {
981 // some keys were used
982 if (other.key_door_messagetime <= time) {
983 play2(other, "misc/talk.wav");
984 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
985 other.key_door_messagetime = time + 2;
989 if (other.key_door_messagetime <= time) {
990 play2(other, "misc/talk.wav");
991 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
992 other.key_door_messagetime = time + 2;
997 // door is now unlocked
998 play2(other, "misc/talk.wav");
999 centerprint(other, "Door unlocked!");
1011 if (self.owner != self)
1012 objerror ("door_fire: self.owner != self");
1016 if (self.spawnflags & DOOR_TOGGLE)
1018 if (self.state == STATE_UP || self.state == STATE_TOP)
1023 if (self.classname == "door")
1029 door_rotating_go_down ();
1032 } while ( (self != starte) && (self != world) );
1038 // trigger all paired doors
1042 if (self.classname == "door")
1047 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1048 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1050 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1051 self.pos2 = '0 0 0' - self.pos2;
1053 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1054 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1055 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1057 door_rotating_go_up ();
1061 } while ( (self != starte) && (self != world) );
1070 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1082 void door_trigger_touch()
1084 if (other.health < 1)
1085 if not(other.iscreature && other.deadflag == DEAD_NO)
1088 if (time < self.attack_finished_single)
1091 // check if door is locked
1092 if (!door_check_keys())
1095 self.attack_finished_single = time + 1;
1104 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1107 if(self.spawnflags & DOOR_NOSPLASH)
1108 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1110 self.health = self.health - damage;
1112 if (self.itemkeys) {
1113 // don't allow opening doors through damage if keys are required
1117 if (self.health <= 0)
1121 self.health = self.max_health;
1122 self.takedamage = DAMAGE_NO; // wil be reset upon return
1138 if(other.classname != "player")
1140 if (self.owner.attack_finished_single > time)
1143 self.owner.attack_finished_single = time + 2;
1145 if (!(self.owner.dmg) && (self.owner.message != ""))
1147 if (other.flags & FL_CLIENT)
1148 centerprint (other, self.owner.message);
1149 play2(other, "misc/talk.wav");
1154 void door_generic_plat_blocked()
1157 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1158 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1161 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1162 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1164 //Dont chamge direction for dead or dying stuff
1165 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1168 if (self.state == STATE_DOWN)
1169 door_rotating_go_up ();
1171 door_rotating_go_down ();
1174 //gib dying stuff just to make sure
1175 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1176 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1180 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1181 // if a door has a negative wait, it would never come back if blocked,
1182 // so let it just squash the object to death real fast
1183 /* if (self.wait >= 0)
1185 if (self.state == STATE_DOWN)
1186 door_rotating_go_up ();
1188 door_rotating_go_down ();
1194 void door_rotating_hit_top()
1196 if (self.noise1 != "")
1197 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1198 self.state = STATE_TOP;
1199 if (self.spawnflags & DOOR_TOGGLE)
1200 return; // don't come down automatically
1201 self.think = door_rotating_go_down;
1202 self.nextthink = self.ltime + self.wait;
1205 void door_rotating_hit_bottom()
1207 if (self.noise1 != "")
1208 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1209 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1211 self.pos2 = '0 0 0' - self.pos2;
1214 self.state = STATE_BOTTOM;
1217 void door_rotating_go_down()
1219 if (self.noise2 != "")
1220 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1221 if (self.max_health)
1223 self.takedamage = DAMAGE_YES;
1224 self.health = self.max_health;
1227 self.state = STATE_DOWN;
1228 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1231 void door_rotating_go_up()
1233 if (self.state == STATE_UP)
1234 return; // already going up
1236 if (self.state == STATE_TOP)
1237 { // reset top wait time
1238 self.nextthink = self.ltime + self.wait;
1241 if (self.noise2 != "")
1242 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1243 self.state = STATE_UP;
1244 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1247 oldmessage = self.message;
1250 self.message = oldmessage;
1257 =============================================================================
1261 =============================================================================
1265 entity spawn_field(vector fmins, vector fmaxs)
1271 trigger.classname = "doortriggerfield";
1272 trigger.movetype = MOVETYPE_NONE;
1273 trigger.solid = SOLID_TRIGGER;
1274 trigger.owner = self;
1275 trigger.touch = door_trigger_touch;
1279 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1284 float EntitiesTouching(entity e1, entity e2)
1286 if (e1.absmin_x > e2.absmax_x)
1288 if (e1.absmin_y > e2.absmax_y)
1290 if (e1.absmin_z > e2.absmax_z)
1292 if (e1.absmax_x < e2.absmin_x)
1294 if (e1.absmax_y < e2.absmin_y)
1296 if (e1.absmax_z < e2.absmin_z)
1312 vector cmins, cmaxs;
1315 return; // already linked by another door
1316 if (self.spawnflags & 4)
1318 self.owner = self.enemy = self;
1326 self.trigger_field = spawn_field(self.absmin, self.absmax);
1328 return; // don't want to link this door
1331 cmins = self.absmin;
1332 cmaxs = self.absmax;
1339 self.owner = starte; // master door
1342 starte.health = self.health;
1344 starte.targetname = self.targetname;
1345 if (self.message != "")
1346 starte.message = self.message;
1348 t = find(t, classname, self.classname);
1351 self.enemy = starte; // make the chain a loop
1353 // shootable, or triggered doors just needed the owner/enemy links,
1354 // they don't spawn a field
1365 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1370 if (EntitiesTouching(self,t))
1373 objerror ("cross connected doors");
1378 if (t.absmin_x < cmins_x)
1379 cmins_x = t.absmin_x;
1380 if (t.absmin_y < cmins_y)
1381 cmins_y = t.absmin_y;
1382 if (t.absmin_z < cmins_z)
1383 cmins_z = t.absmin_z;
1384 if (t.absmax_x > cmaxs_x)
1385 cmaxs_x = t.absmax_x;
1386 if (t.absmax_y > cmaxs_y)
1387 cmaxs_y = t.absmax_y;
1388 if (t.absmax_z > cmaxs_z)
1389 cmaxs_z = t.absmax_z;
1396 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1397 if two doors touch, they are assumed to be connected and operate as a unit.
1399 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1401 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).
1403 GOLD_KEY causes the door to open only if the activator holds a gold key.
1405 SILVER_KEY causes the door to open only if the activator holds a silver key.
1407 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1408 "angle" determines the opening direction
1409 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1410 "health" if set, door must be shot open
1411 "speed" movement speed (100 default)
1412 "wait" wait before returning (3 default, -1 = never return)
1413 "lip" lip remaining at end of move (8 default)
1414 "dmg" damage to inflict when blocked (2 default)
1421 FIXME: only one sound set available at the time being
1425 void door_init_startopen()
1427 setorigin (self, self.pos2);
1428 self.pos2 = self.pos1;
1429 self.pos1 = self.origin;
1434 setorigin(self, self.pos1);
1435 self.velocity = '0 0 0';
1436 self.state = STATE_BOTTOM;
1437 self.think = SUB_Null;
1440 // spawnflags require key (for now only func_door)
1441 #define SPAWNFLAGS_GOLD_KEY 8
1442 #define SPAWNFLAGS_SILVER_KEY 16
1443 void spawnfunc_func_door()
1445 // Quake 1 keys compatibility
1446 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1447 self.itemkeys |= ITEM_KEY_BIT(0);
1448 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1449 self.itemkeys |= ITEM_KEY_BIT(1);
1451 //if (!self.deathtype) // map makers can override this
1452 // self.deathtype = " got in the way";
1455 self.max_health = self.health;
1456 if not(InitMovingBrushTrigger())
1458 self.effects |= EF_LOWPRECISION;
1459 self.classname = "door";
1461 self.blocked = door_blocked;
1462 self.use = door_use;
1464 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1465 // if(self.spawnflags & 8)
1466 // self.dmg = 10000;
1468 if(self.dmg && (!self.message))
1469 self.message = "was squished";
1470 if(self.dmg && (!self.message2))
1471 self.message2 = "was squished by";
1473 if (self.sounds > 0)
1475 precache_sound ("plats/medplat1.wav");
1476 precache_sound ("plats/medplat2.wav");
1477 self.noise2 = "plats/medplat1.wav";
1478 self.noise1 = "plats/medplat2.wav";
1488 self.pos1 = self.origin;
1489 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1491 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1492 // but spawn in the open position
1493 if (self.spawnflags & DOOR_START_OPEN)
1494 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1496 self.state = STATE_BOTTOM;
1500 self.takedamage = DAMAGE_YES;
1501 self.event_damage = door_damage;
1507 self.touch = door_touch;
1509 // LinkDoors can't be done until all of the doors have been spawned, so
1510 // the sizes can be detected properly.
1511 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1513 self.reset = door_reset;
1516 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1517 if two doors touch, they are assumed to be connected and operate as a unit.
1519 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1521 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1522 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1523 must have set trigger_reverse to 1.
1524 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1526 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).
1528 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1529 "angle" determines the destination angle for opening. negative values reverse the direction.
1530 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1531 "health" if set, door must be shot open
1532 "speed" movement speed (100 default)
1533 "wait" wait before returning (3 default, -1 = never return)
1534 "dmg" damage to inflict when blocked (2 default)
1541 FIXME: only one sound set available at the time being
1544 void door_rotating_reset()
1546 self.angles = self.pos1;
1547 self.avelocity = '0 0 0';
1548 self.state = STATE_BOTTOM;
1549 self.think = SUB_Null;
1552 void door_rotating_init_startopen()
1554 self.angles = self.movedir;
1555 self.pos2 = '0 0 0';
1556 self.pos1 = self.movedir;
1560 void spawnfunc_func_door_rotating()
1563 //if (!self.deathtype) // map makers can override this
1564 // self.deathtype = " got in the way";
1566 // I abuse "movedir" for denoting the axis for now
1567 if (self.spawnflags & 64) // X (untested)
1568 self.movedir = '0 0 1';
1569 else if (self.spawnflags & 128) // Y (untested)
1570 self.movedir = '1 0 0';
1572 self.movedir = '0 1 0';
1574 if (self.angles_y==0) self.angles_y = 90;
1576 self.movedir = self.movedir * self.angles_y;
1577 self.angles = '0 0 0';
1579 self.max_health = self.health;
1580 self.avelocity = self.movedir;
1581 if not(InitMovingBrushTrigger())
1583 self.velocity = '0 0 0';
1584 //self.effects |= EF_LOWPRECISION;
1585 self.classname = "door_rotating";
1587 self.blocked = door_blocked;
1588 self.use = door_use;
1590 if(self.spawnflags & 8)
1593 if(self.dmg && (!self.message))
1594 self.message = "was squished";
1595 if(self.dmg && (!self.message2))
1596 self.message2 = "was squished by";
1598 if (self.sounds > 0)
1600 precache_sound ("plats/medplat1.wav");
1601 precache_sound ("plats/medplat2.wav");
1602 self.noise2 = "plats/medplat1.wav";
1603 self.noise1 = "plats/medplat2.wav";
1610 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1612 self.pos1 = '0 0 0';
1613 self.pos2 = self.movedir;
1615 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1616 // but spawn in the open position
1617 if (self.spawnflags & DOOR_START_OPEN)
1618 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1620 self.state = STATE_BOTTOM;
1624 self.takedamage = DAMAGE_YES;
1625 self.event_damage = door_damage;
1631 self.touch = door_touch;
1633 // LinkDoors can't be done until all of the doors have been spawned, so
1634 // the sizes can be detected properly.
1635 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1637 self.reset = door_rotating_reset;
1641 =============================================================================
1645 =============================================================================
1648 void() fd_secret_move1;
1649 void() fd_secret_move2;
1650 void() fd_secret_move3;
1651 void() fd_secret_move4;
1652 void() fd_secret_move5;
1653 void() fd_secret_move6;
1654 void() fd_secret_done;
1656 float SECRET_OPEN_ONCE = 1; // stays open
1657 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1658 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1659 float SECRET_NO_SHOOT = 8; // only opened by trigger
1660 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1663 void fd_secret_use()
1666 string message_save;
1668 self.health = 10000;
1669 self.bot_attack = TRUE;
1671 // exit if still moving around...
1672 if (self.origin != self.oldorigin)
1675 message_save = self.message;
1676 self.message = ""; // no more message
1677 SUB_UseTargets(); // fire all targets / killtargets
1678 self.message = message_save;
1680 self.velocity = '0 0 0';
1682 // Make a sound, wait a little...
1684 if (self.noise1 != "")
1685 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1686 self.nextthink = self.ltime + 0.1;
1688 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1689 makevectors(self.mangle);
1693 if (self.spawnflags & SECRET_1ST_DOWN)
1694 self.t_width = fabs(v_up * self.size);
1696 self.t_width = fabs(v_right * self.size);
1700 self.t_length = fabs(v_forward * self.size);
1702 if (self.spawnflags & SECRET_1ST_DOWN)
1703 self.dest1 = self.origin - v_up * self.t_width;
1705 self.dest1 = self.origin + v_right * (self.t_width * temp);
1707 self.dest2 = self.dest1 + v_forward * self.t_length;
1708 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1709 if (self.noise2 != "")
1710 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1713 // Wait after first movement...
1714 void fd_secret_move1()
1716 self.nextthink = self.ltime + 1.0;
1717 self.think = fd_secret_move2;
1718 if (self.noise3 != "")
1719 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1722 // Start moving sideways w/sound...
1723 void fd_secret_move2()
1725 if (self.noise2 != "")
1726 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1727 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1730 // Wait here until time to go back...
1731 void fd_secret_move3()
1733 if (self.noise3 != "")
1734 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1735 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1737 self.nextthink = self.ltime + self.wait;
1738 self.think = fd_secret_move4;
1743 void fd_secret_move4()
1745 if (self.noise2 != "")
1746 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1747 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1751 void fd_secret_move5()
1753 self.nextthink = self.ltime + 1.0;
1754 self.think = fd_secret_move6;
1755 if (self.noise3 != "")
1756 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1759 void fd_secret_move6()
1761 if (self.noise2 != "")
1762 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1763 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1766 void fd_secret_done()
1768 if (self.spawnflags&SECRET_YES_SHOOT)
1770 self.health = 10000;
1771 self.takedamage = DAMAGE_YES;
1772 //self.th_pain = fd_secret_use;
1774 if (self.noise3 != "")
1775 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1778 void secret_blocked()
1780 if (time < self.attack_finished_single)
1782 self.attack_finished_single = time + 0.5;
1783 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1795 if not(other.iscreature)
1797 if (self.attack_finished_single > time)
1800 self.attack_finished_single = time + 2;
1804 if (other.flags & FL_CLIENT)
1805 centerprint (other, self.message);
1806 play2(other, "misc/talk.wav");
1812 if (self.spawnflags&SECRET_YES_SHOOT)
1814 self.health = 10000;
1815 self.takedamage = DAMAGE_YES;
1817 setorigin(self, self.oldorigin);
1818 self.think = SUB_Null;
1821 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1822 Basic secret door. Slides back, then to the side. Angle determines direction.
1823 wait = # of seconds before coming back
1824 1st_left = 1st move is left of arrow
1825 1st_down = 1st move is down from arrow
1826 always_shoot = even if targeted, keep shootable
1827 t_width = override WIDTH to move back (or height if going down)
1828 t_length = override LENGTH to move sideways
1829 "dmg" damage to inflict when blocked (2 default)
1831 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1838 void spawnfunc_func_door_secret()
1840 /*if (!self.deathtype) // map makers can override this
1841 self.deathtype = " got in the way";*/
1847 self.mangle = self.angles;
1848 self.angles = '0 0 0';
1849 self.classname = "door";
1850 if not(InitMovingBrushTrigger())
1852 self.effects |= EF_LOWPRECISION;
1854 self.touch = secret_touch;
1855 self.blocked = secret_blocked;
1857 self.use = fd_secret_use;
1862 self.spawnflags |= SECRET_YES_SHOOT;
1864 if(self.spawnflags&SECRET_YES_SHOOT)
1866 self.health = 10000;
1867 self.takedamage = DAMAGE_YES;
1868 self.event_damage = fd_secret_use;
1870 self.oldorigin = self.origin;
1872 self.wait = 5; // 5 seconds before closing
1874 self.reset = secret_reset;
1878 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1879 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1880 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
1881 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1882 height: amplitude modifier (default 32)
1883 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1884 noise: path/name of looping .wav file to play.
1885 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1889 void func_fourier_controller_think()
1894 self.nextthink = time + 0.1;
1895 if not (self.owner.active == ACTIVE_ACTIVE)
1897 self.owner.velocity = '0 0 0';
1902 n = floor((tokenize_console(self.owner.netname)) / 5);
1903 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1905 v = self.owner.destvec;
1907 for(i = 0; i < n; ++i)
1909 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1910 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;
1913 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1914 // * 10 so it will arrive in 0.1 sec
1915 self.owner.velocity = (v - self.owner.origin) * 10;
1918 void spawnfunc_func_fourier()
1921 if (self.noise != "")
1923 precache_sound(self.noise);
1924 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1931 self.destvec = self.origin;
1932 self.cnt = 360 / self.speed;
1934 self.blocked = generic_plat_blocked;
1935 if(self.dmg & (!self.message))
1936 self.message = " was squished";
1937 if(self.dmg && (!self.message2))
1938 self.message2 = "was squished by";
1939 if(self.dmg && (!self.dmgtime))
1940 self.dmgtime = 0.25;
1941 self.dmgtime2 = time;
1943 if(self.netname == "")
1944 self.netname = "1 0 0 0 1";
1946 if not(InitMovingBrushTrigger())
1949 self.active = ACTIVE_ACTIVE;
1951 // wait for targets to spawn
1952 controller = spawn();
1953 controller.classname = "func_fourier_controller";
1954 controller.owner = self;
1955 controller.nextthink = time + 1;
1956 controller.think = func_fourier_controller_think;
1957 self.nextthink = self.ltime + 999999999;
1958 self.think = SUB_Null;
1960 // Savage: Reduce bandwith, critical on e.g. nexdm02
1961 self.effects |= EF_LOWPRECISION;
1963 // TODO make a reset function for this one
1966 // reusing some fields havocbots declared
1967 .entity wp00, wp01, wp02, wp03;
1969 .float targetfactor, target2factor, target3factor, target4factor;
1970 .vector targetnormal, target2normal, target3normal, target4normal;
1972 vector func_vectormamamam_origin(entity o, float t)
1984 p = e.origin + t * e.velocity;
1986 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1988 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1994 p = e.origin + t * e.velocity;
1996 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1998 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
2004 p = e.origin + t * e.velocity;
2006 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
2008 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
2014 p = e.origin + t * e.velocity;
2016 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
2018 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2024 void func_vectormamamam_controller_think()
2026 self.nextthink = time + 0.1;
2028 if not (self.owner.active == ACTIVE_ACTIVE)
2030 self.owner.velocity = '0 0 0';
2034 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2035 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2038 void func_vectormamamam_findtarget()
2040 if(self.target != "")
2041 self.wp00 = find(world, targetname, self.target);
2043 if(self.target2 != "")
2044 self.wp01 = find(world, targetname, self.target2);
2046 if(self.target3 != "")
2047 self.wp02 = find(world, targetname, self.target3);
2049 if(self.target4 != "")
2050 self.wp03 = find(world, targetname, self.target4);
2052 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2053 objerror("No reference entity found, so there is nothing to move. Aborting.");
2055 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2058 controller = spawn();
2059 controller.classname = "func_vectormamamam_controller";
2060 controller.owner = self;
2061 controller.nextthink = time + 1;
2062 controller.think = func_vectormamamam_controller_think;
2065 void spawnfunc_func_vectormamamam()
2067 if (self.noise != "")
2069 precache_sound(self.noise);
2070 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2073 if(!self.targetfactor)
2074 self.targetfactor = 1;
2076 if(!self.target2factor)
2077 self.target2factor = 1;
2079 if(!self.target3factor)
2080 self.target3factor = 1;
2082 if(!self.target4factor)
2083 self.target4factor = 1;
2085 if(vlen(self.targetnormal))
2086 self.targetnormal = normalize(self.targetnormal);
2088 if(vlen(self.target2normal))
2089 self.target2normal = normalize(self.target2normal);
2091 if(vlen(self.target3normal))
2092 self.target3normal = normalize(self.target3normal);
2094 if(vlen(self.target4normal))
2095 self.target4normal = normalize(self.target4normal);
2097 self.blocked = generic_plat_blocked;
2098 if(self.dmg & (!self.message))
2099 self.message = " was squished";
2100 if(self.dmg && (!self.message2))
2101 self.message2 = "was squished by";
2102 if(self.dmg && (!self.dmgtime))
2103 self.dmgtime = 0.25;
2104 self.dmgtime2 = time;
2106 if(self.netname == "")
2107 self.netname = "1 0 0 0 1";
2109 if not(InitMovingBrushTrigger())
2112 // wait for targets to spawn
2113 self.nextthink = self.ltime + 999999999;
2114 self.think = SUB_Null;
2116 // Savage: Reduce bandwith, critical on e.g. nexdm02
2117 self.effects |= EF_LOWPRECISION;
2119 self.active = ACTIVE_ACTIVE;
2121 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2124 void conveyor_think()
2128 // set myself as current conveyor where possible
2129 for(e = world; (e = findentity(e, conveyor, self)); )
2134 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2135 if(!e.conveyor.state)
2138 vector emin = e.absmin;
2139 vector emax = e.absmax;
2140 if(self.solid == SOLID_BSP)
2145 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2146 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2150 for(e = world; (e = findentity(e, conveyor, self)); )
2152 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2153 continue; // done in SV_PlayerPhysics
2155 setorigin(e, e.origin + self.movedir * sys_frametime);
2156 move_out_of_solid(e);
2157 UpdateCSQCProjectile(e);
2159 // stupid conveyor code
2160 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2161 if(trace_fraction > 0)
2162 setorigin(e, trace_endpos);
2167 self.nextthink = time;
2172 self.state = !self.state;
2175 void conveyor_reset()
2177 self.state = (self.spawnflags & 1);
2180 void conveyor_init()
2184 self.movedir = self.movedir * self.speed;
2185 self.think = conveyor_think;
2186 self.nextthink = time;
2189 self.use = conveyor_use;
2190 self.reset = conveyor_reset;
2197 void spawnfunc_trigger_conveyor()
2204 void spawnfunc_func_conveyor()
2207 InitMovingBrushTrigger();
2208 self.movetype = MOVETYPE_NONE;