2 void generic_plat_blocked()
4 if(self.dmg && other.takedamage != DAMAGE_NO) {
5 if(self.dmgtime2 < time) {
6 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
7 self.dmgtime2 = time + self.dmgtime;
10 // Gib dead/dying stuff
11 if(other.deadflag != DEAD_NO)
12 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
18 float STATE_BOTTOM = 1;
22 .entity trigger_field;
24 void() plat_center_touch;
25 void() plat_outside_touch;
26 void() plat_trigger_use;
30 float PLAT_LOW_TRIGGER = 1;
32 void plat_spawn_inside_trigger()
38 trigger.touch = plat_center_touch;
39 trigger.movetype = MOVETYPE_NONE;
40 trigger.solid = SOLID_TRIGGER;
43 tmin = self.absmin + '25 25 0';
44 tmax = self.absmax - '25 25 -8';
45 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
46 if (self.spawnflags & PLAT_LOW_TRIGGER)
49 if (self.size_x <= 50)
51 tmin_x = (self.mins_x + self.maxs_x) / 2;
54 if (self.size_y <= 50)
56 tmin_y = (self.mins_y + self.maxs_y) / 2;
64 setsize (trigger, tmin, tmax);
68 // otherwise, something is fishy...
70 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
75 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
77 self.think = plat_go_down;
78 self.nextthink = self.ltime + 3;
81 void plat_hit_bottom()
83 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
89 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
91 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
101 void plat_center_touch()
103 if not(other.iscreature)
106 if (other.health <= 0)
112 else if (self.state == 1)
113 self.nextthink = self.ltime + 1; // delay going down
116 void plat_outside_touch()
118 if not(other.iscreature)
121 if (other.health <= 0)
129 void plat_trigger_use()
132 return; // already activated
139 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
140 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
142 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
143 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
144 // Gib dead/dying stuff
145 if(other.deadflag != DEAD_NO)
146 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
151 else if (self.state == 3)
153 // when in other states, then the plat_crush event came delayed after
154 // plat state already had changed
155 // this isn't a bug per se!
163 objerror ("plat_use: not in up state");
167 .string sound1, sound2;
173 setorigin (self, self.pos1);
179 setorigin (self, self.pos2);
181 self.use = plat_trigger_use;
185 void spawnfunc_path_corner() { }
186 void spawnfunc_func_plat()
188 if (self.sounds == 0)
191 if(self.spawnflags & 4)
194 if(self.dmg && (!self.message))
195 self.message = "was squished";
196 if(self.dmg && (!self.message2))
197 self.message2 = "was squished by";
199 if (self.sounds == 1)
201 precache_sound ("plats/plat1.wav");
202 precache_sound ("plats/plat2.wav");
203 self.noise = "plats/plat1.wav";
204 self.noise1 = "plats/plat2.wav";
207 if (self.sounds == 2)
209 precache_sound ("plats/medplat1.wav");
210 precache_sound ("plats/medplat2.wav");
211 self.noise = "plats/medplat1.wav";
212 self.noise1 = "plats/medplat2.wav";
217 precache_sound (self.sound1);
218 self.noise = self.sound1;
222 precache_sound (self.sound2);
223 self.noise1 = self.sound2;
226 self.mangle = self.angles;
227 self.angles = '0 0 0';
229 self.classname = "plat";
230 if not(InitMovingBrushTrigger())
232 self.effects |= EF_LOWPRECISION;
233 setsize (self, self.mins , self.maxs);
235 self.blocked = plat_crush;
242 self.height = self.size_z - self.lip;
244 self.pos1 = self.origin;
245 self.pos2 = self.origin;
246 self.pos2_z = self.origin_z - self.height;
248 self.reset = plat_reset;
251 plat_spawn_inside_trigger (); // the "start moving" trigger
259 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
267 self.think = train_next;
268 self.nextthink = self.ltime + self.wait;
282 targ = find(world, targetname, self.target);
283 self.target = targ.target;
284 if (self.spawnflags & 1)
286 cp = find(world, target, targ.targetname); // get the previous corner first
287 cp = find(world, targetname, cp.target2); // now get its second target
288 if(cp.targetname == "") // none found
290 // when using bezier curves, you must have a control point for each corner in the train's path
291 if(autocvar_developer)
292 dprint(strcat("Warning: func_train using beizer curves heading to the path_corner '", targ.targetname, "' which does not have a control point. Please add a target2 for each path_corner used by this train!\n"));
294 setorigin(cp, targ.origin - self.mins); // assume a straight line to the destination as fallback
298 objerror("train_next: no next target");
299 self.wait = targ.wait;
305 if (self.spawnflags & 1)
306 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, targ.speed, train_wait);
308 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
312 if (self.spawnflags & 1)
313 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, self.speed, train_wait);
315 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
319 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
322 void func_train_find()
325 targ = find(world, targetname, self.target);
326 self.target = targ.target;
328 objerror("func_train_find: no next target");
329 setorigin(self, targ.origin - self.mins);
330 self.nextthink = self.ltime + 1;
331 self.think = train_next;
334 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
335 Ridable platform, targets spawnfunc_path_corner path to follow.
336 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
337 target : targetname of first spawnfunc_path_corner (starts here)
339 void spawnfunc_func_train()
341 if (self.noise != "")
342 precache_sound(self.noise);
345 objerror("func_train without a target");
349 if not(InitMovingBrushTrigger())
351 self.effects |= EF_LOWPRECISION;
353 // wait for targets to spawn
354 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
356 self.blocked = generic_plat_blocked;
357 if(self.dmg & (!self.message))
358 self.message = " was squished";
359 if(self.dmg && (!self.message2))
360 self.message2 = "was squished by";
361 if(self.dmg && (!self.dmgtime))
363 self.dmgtime2 = time;
365 // TODO make a reset function for this one
368 void func_rotating_setactive(float astate)
371 if (astate == ACTIVE_TOGGLE)
373 if(self.active == ACTIVE_ACTIVE)
374 self.active = ACTIVE_NOT;
376 self.active = ACTIVE_ACTIVE;
379 self.active = astate;
381 if(self.active == ACTIVE_NOT)
382 self.avelocity = '0 0 0';
384 self.avelocity = self.pos1;
387 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
388 Brush model that spins in place on one axis (default Z).
389 speed : speed to rotate (in degrees per second)
390 noise : path/name of looping .wav file to play.
391 dmg : Do this mutch dmg every .dmgtime intervall when blocked
395 void spawnfunc_func_rotating()
397 if (self.noise != "")
399 precache_sound(self.noise);
400 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
403 self.active = ACTIVE_ACTIVE;
404 self.setactive = func_rotating_setactive;
408 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
409 if (self.spawnflags & 4) // X (untested)
410 self.avelocity = '0 0 1' * self.speed;
411 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
412 else if (self.spawnflags & 8) // Y (untested)
413 self.avelocity = '1 0 0' * self.speed;
414 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
416 self.avelocity = '0 1 0' * self.speed;
418 self.pos1 = self.avelocity;
420 if(self.dmg & (!self.message))
421 self.message = " was squished";
422 if(self.dmg && (!self.message2))
423 self.message2 = "was squished by";
426 if(self.dmg && (!self.dmgtime))
429 self.dmgtime2 = time;
431 if not(InitMovingBrushTrigger())
433 // no EF_LOWPRECISION here, as rounding angles is bad
435 self.blocked = generic_plat_blocked;
437 // wait for targets to spawn
438 self.nextthink = self.ltime + 999999999;
439 self.think = SUB_Null;
441 // TODO make a reset function for this one
445 void func_bobbing_controller_think()
448 self.nextthink = time + 0.1;
450 if not (self.owner.active == ACTIVE_ACTIVE)
452 self.owner.velocity = '0 0 0';
456 // calculate sinewave using makevectors
457 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
458 v = self.owner.destvec + self.owner.movedir * v_forward_y;
459 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
460 // * 10 so it will arrive in 0.1 sec
461 self.owner.velocity = (v - self.owner.origin) * 10;
464 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
465 Brush model that moves back and forth on one axis (default Z).
466 speed : how long one cycle takes in seconds (default 4)
467 height : how far the cycle moves (default 32)
468 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
469 noise : path/name of looping .wav file to play.
470 dmg : Do this mutch dmg every .dmgtime intervall when blocked
473 void spawnfunc_func_bobbing()
476 if (self.noise != "")
478 precache_sound(self.noise);
479 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
485 // center of bobbing motion
486 self.destvec = self.origin;
487 // time scale to get degrees
488 self.cnt = 360 / self.speed;
490 self.active = ACTIVE_ACTIVE;
492 // damage when blocked
493 self.blocked = generic_plat_blocked;
494 if(self.dmg & (!self.message))
495 self.message = " was squished";
496 if(self.dmg && (!self.message2))
497 self.message2 = "was squished by";
498 if(self.dmg && (!self.dmgtime))
500 self.dmgtime2 = time;
503 if (self.spawnflags & 1) // X
504 self.movedir = '1 0 0' * self.height;
505 else if (self.spawnflags & 2) // Y
506 self.movedir = '0 1 0' * self.height;
508 self.movedir = '0 0 1' * self.height;
510 if not(InitMovingBrushTrigger())
513 // wait for targets to spawn
514 controller = spawn();
515 controller.classname = "func_bobbing_controller";
516 controller.owner = self;
517 controller.nextthink = time + 1;
518 controller.think = func_bobbing_controller_think;
519 self.nextthink = self.ltime + 999999999;
520 self.think = SUB_Null;
522 // Savage: Reduce bandwith, critical on e.g. nexdm02
523 self.effects |= EF_LOWPRECISION;
525 // TODO make a reset function for this one
529 void func_pendulum_controller_think()
532 self.nextthink = time + 0.1;
534 if not (self.owner.active == ACTIVE_ACTIVE)
536 self.owner.avelocity_x = 0;
540 // calculate sinewave using makevectors
541 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
542 v = self.owner.speed * v_forward_y + self.cnt;
543 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
545 // * 10 so it will arrive in 0.1 sec
546 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
550 void spawnfunc_func_pendulum()
553 if (self.noise != "")
555 precache_sound(self.noise);
556 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
559 self.active = ACTIVE_ACTIVE;
561 // keys: angle, speed, phase, noise, freq
565 // not initializing self.dmg to 2, to allow damageless pendulum
567 if(self.dmg & (!self.message))
568 self.message = " was squished";
569 if(self.dmg && (!self.message2))
570 self.message2 = "was squished by";
571 if(self.dmg && (!self.dmgtime))
573 self.dmgtime2 = time;
575 self.blocked = generic_plat_blocked;
577 self.avelocity_z = 0.0000001;
578 if not(InitMovingBrushTrigger())
583 // find pendulum length (same formula as Q3A)
584 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
587 // copy initial angle
588 self.cnt = self.angles_z;
590 // wait for targets to spawn
591 controller = spawn();
592 controller.classname = "func_pendulum_controller";
593 controller.owner = self;
594 controller.nextthink = time + 1;
595 controller.think = func_pendulum_controller_think;
596 self.nextthink = self.ltime + 999999999;
597 self.think = SUB_Null;
599 //self.effects |= EF_LOWPRECISION;
601 // TODO make a reset function for this one
604 // button and multiple button
607 void() button_return;
611 self.state = STATE_TOP;
612 self.nextthink = self.ltime + self.wait;
613 self.think = button_return;
614 activator = self.enemy;
616 self.frame = 1; // use alternate textures
621 self.state = STATE_BOTTOM;
626 self.state = STATE_DOWN;
627 SUB_CalcMove (self.pos1, self.speed, button_done);
628 self.frame = 0; // use normal textures
630 self.takedamage = DAMAGE_YES; // can be shot again
634 void button_blocked()
636 // do nothing, just don't come all the way back out
642 self.health = self.max_health;
643 self.takedamage = DAMAGE_NO; // will be reset upon return
645 if (self.state == STATE_UP || self.state == STATE_TOP)
648 if (self.noise != "")
649 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
651 self.state = STATE_UP;
652 SUB_CalcMove (self.pos2, self.speed, button_wait);
657 self.health = self.max_health;
658 setorigin(self, self.pos1);
659 self.frame = 0; // use normal textures
660 self.state = STATE_BOTTOM;
662 self.takedamage = DAMAGE_YES; // can be shot again
667 // if (activator.classname != "player")
669 // dprint(activator.classname);
670 // dprint(" triggered a button\n");
673 if not (self.active == ACTIVE_ACTIVE)
676 self.enemy = activator;
682 // if (activator.classname != "player")
684 // dprint(activator.classname);
685 // dprint(" touched a button\n");
689 if not(other.iscreature)
691 if(other.velocity * self.movedir < 0)
695 self.enemy = other.owner;
699 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
701 if(self.spawnflags & DOOR_NOSPLASH)
702 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
704 self.health = self.health - damage;
705 if (self.health <= 0)
707 // if (activator.classname != "player")
709 // dprint(activator.classname);
710 // dprint(" killed a button\n");
712 self.enemy = damage_attacker;
718 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
719 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.
721 "angle" determines the opening direction
722 "target" all entities with a matching targetname will be used
723 "speed" override the default 40 speed
724 "wait" override the default 1 second wait (-1 = never return)
725 "lip" override the default 4 pixel lip remaining at end of move
726 "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
733 void spawnfunc_func_button()
737 if not(InitMovingBrushTrigger())
739 self.effects |= EF_LOWPRECISION;
741 self.blocked = button_blocked;
742 self.use = button_use;
744 // if (self.health == 0) // all buttons are now shootable
748 self.max_health = self.health;
749 self.event_damage = button_damage;
750 self.takedamage = DAMAGE_YES;
753 self.touch = button_touch;
763 precache_sound(self.noise);
765 self.active = ACTIVE_ACTIVE;
767 self.pos1 = self.origin;
768 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
769 self.flags |= FL_NOTARGET;
775 float DOOR_START_OPEN = 1;
776 float DOOR_DONT_LINK = 4;
777 float DOOR_TOGGLE = 32;
781 Doors are similar to buttons, but can spawn a fat trigger field around them
782 to open without a touch, and they link together to form simultanious
785 Door.owner is the master door. If there is only one door, it points to itself.
786 If multiple doors, all will point to a single one.
788 Door.enemy chains from the master door through all doors linked in the chain.
793 =============================================================================
797 =============================================================================
802 void() door_rotating_go_down;
803 void() door_rotating_go_up;
808 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
809 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
812 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
813 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
815 //Dont chamge direction for dead or dying stuff
816 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
819 if (self.state == STATE_DOWN)
820 if (self.classname == "door")
825 door_rotating_go_up ();
828 if (self.classname == "door")
833 door_rotating_go_down ();
837 //gib dying stuff just to make sure
838 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
839 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
843 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
844 // if a door has a negative wait, it would never come back if blocked,
845 // so let it just squash the object to death real fast
846 /* if (self.wait >= 0)
848 if (self.state == STATE_DOWN)
859 if (self.noise1 != "")
860 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
861 self.state = STATE_TOP;
862 if (self.spawnflags & DOOR_TOGGLE)
863 return; // don't come down automatically
864 if (self.classname == "door")
866 self.think = door_go_down;
869 self.think = door_rotating_go_down;
871 self.nextthink = self.ltime + self.wait;
874 void door_hit_bottom()
876 if (self.noise1 != "")
877 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
878 self.state = STATE_BOTTOM;
883 if (self.noise2 != "")
884 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
887 self.takedamage = DAMAGE_YES;
888 self.health = self.max_health;
891 self.state = STATE_DOWN;
892 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
897 if (self.state == STATE_UP)
898 return; // already going up
900 if (self.state == STATE_TOP)
901 { // reset top wait time
902 self.nextthink = self.ltime + self.wait;
906 if (self.noise2 != "")
907 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
908 self.state = STATE_UP;
909 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
912 oldmessage = self.message;
915 self.message = oldmessage;
921 =============================================================================
925 =============================================================================
928 float door_check_keys(void) {
938 if not(door.itemkeys)
941 // this door require a key
942 // only a player can have a key
943 if (other.classname != "player")
946 if (item_keys_usekey(door, other)) {
947 // some keys were used
948 if (other.key_door_messagetime <= time) {
949 play2(other, "misc/talk.wav");
950 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
951 other.key_door_messagetime = time + 2;
955 if (other.key_door_messagetime <= time) {
956 play2(other, "misc/talk.wav");
957 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
958 other.key_door_messagetime = time + 2;
963 // door is now unlocked
964 play2(other, "misc/talk.wav");
965 centerprint(other, "Door unlocked!");
977 if (self.owner != self)
978 objerror ("door_fire: self.owner != self");
982 if (self.spawnflags & DOOR_TOGGLE)
984 if (self.state == STATE_UP || self.state == STATE_TOP)
989 if (self.classname == "door")
995 door_rotating_go_down ();
998 } while ( (self != starte) && (self != world) );
1004 // trigger all paired doors
1008 if (self.classname == "door")
1013 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1014 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1016 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1017 self.pos2 = '0 0 0' - self.pos2;
1019 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1020 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1021 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1023 door_rotating_go_up ();
1027 } while ( (self != starte) && (self != world) );
1036 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1048 void door_trigger_touch()
1050 if (other.health < 1)
1051 if not(other.iscreature && other.deadflag == DEAD_NO)
1054 if (time < self.attack_finished_single)
1057 // check if door is locked
1058 if (!door_check_keys())
1061 self.attack_finished_single = time + 1;
1070 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1073 if(self.spawnflags & DOOR_NOSPLASH)
1074 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1076 self.health = self.health - damage;
1078 if (self.itemkeys) {
1079 // don't allow opening doors through damage if keys are required
1083 if (self.health <= 0)
1087 self.health = self.max_health;
1088 self.takedamage = DAMAGE_NO; // wil be reset upon return
1104 if(other.classname != "player")
1106 if (self.owner.attack_finished_single > time)
1109 self.owner.attack_finished_single = time + 2;
1111 if (!(self.owner.dmg) && (self.owner.message != ""))
1113 if (other.flags & FL_CLIENT)
1114 centerprint (other, self.owner.message);
1115 play2(other, "misc/talk.wav");
1120 void door_generic_plat_blocked()
1123 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1124 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1127 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1128 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1130 //Dont chamge direction for dead or dying stuff
1131 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1134 if (self.state == STATE_DOWN)
1135 door_rotating_go_up ();
1137 door_rotating_go_down ();
1140 //gib dying stuff just to make sure
1141 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1142 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1146 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1147 // if a door has a negative wait, it would never come back if blocked,
1148 // so let it just squash the object to death real fast
1149 /* if (self.wait >= 0)
1151 if (self.state == STATE_DOWN)
1152 door_rotating_go_up ();
1154 door_rotating_go_down ();
1160 void door_rotating_hit_top()
1162 if (self.noise1 != "")
1163 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1164 self.state = STATE_TOP;
1165 if (self.spawnflags & DOOR_TOGGLE)
1166 return; // don't come down automatically
1167 self.think = door_rotating_go_down;
1168 self.nextthink = self.ltime + self.wait;
1171 void door_rotating_hit_bottom()
1173 if (self.noise1 != "")
1174 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1175 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1177 self.pos2 = '0 0 0' - self.pos2;
1180 self.state = STATE_BOTTOM;
1183 void door_rotating_go_down()
1185 if (self.noise2 != "")
1186 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1187 if (self.max_health)
1189 self.takedamage = DAMAGE_YES;
1190 self.health = self.max_health;
1193 self.state = STATE_DOWN;
1194 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1197 void door_rotating_go_up()
1199 if (self.state == STATE_UP)
1200 return; // already going up
1202 if (self.state == STATE_TOP)
1203 { // reset top wait time
1204 self.nextthink = self.ltime + self.wait;
1207 if (self.noise2 != "")
1208 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1209 self.state = STATE_UP;
1210 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1213 oldmessage = self.message;
1216 self.message = oldmessage;
1223 =============================================================================
1227 =============================================================================
1231 entity spawn_field(vector fmins, vector fmaxs)
1237 trigger.classname = "doortriggerfield";
1238 trigger.movetype = MOVETYPE_NONE;
1239 trigger.solid = SOLID_TRIGGER;
1240 trigger.owner = self;
1241 trigger.touch = door_trigger_touch;
1245 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1250 float EntitiesTouching(entity e1, entity e2)
1252 if (e1.absmin_x > e2.absmax_x)
1254 if (e1.absmin_y > e2.absmax_y)
1256 if (e1.absmin_z > e2.absmax_z)
1258 if (e1.absmax_x < e2.absmin_x)
1260 if (e1.absmax_y < e2.absmin_y)
1262 if (e1.absmax_z < e2.absmin_z)
1278 vector cmins, cmaxs;
1281 return; // already linked by another door
1282 if (self.spawnflags & 4)
1284 self.owner = self.enemy = self;
1292 self.trigger_field = spawn_field(self.absmin, self.absmax);
1294 return; // don't want to link this door
1297 cmins = self.absmin;
1298 cmaxs = self.absmax;
1305 self.owner = starte; // master door
1308 starte.health = self.health;
1310 starte.targetname = self.targetname;
1311 if (self.message != "")
1312 starte.message = self.message;
1314 t = find(t, classname, self.classname);
1317 self.enemy = starte; // make the chain a loop
1319 // shootable, or triggered doors just needed the owner/enemy links,
1320 // they don't spawn a field
1331 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1336 if (EntitiesTouching(self,t))
1339 objerror ("cross connected doors");
1344 if (t.absmin_x < cmins_x)
1345 cmins_x = t.absmin_x;
1346 if (t.absmin_y < cmins_y)
1347 cmins_y = t.absmin_y;
1348 if (t.absmin_z < cmins_z)
1349 cmins_z = t.absmin_z;
1350 if (t.absmax_x > cmaxs_x)
1351 cmaxs_x = t.absmax_x;
1352 if (t.absmax_y > cmaxs_y)
1353 cmaxs_y = t.absmax_y;
1354 if (t.absmax_z > cmaxs_z)
1355 cmaxs_z = t.absmax_z;
1362 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1363 if two doors touch, they are assumed to be connected and operate as a unit.
1365 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1367 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).
1369 GOLD_KEY causes the door to open only if the activator holds a gold key.
1371 SILVER_KEY causes the door to open only if the activator holds a silver key.
1373 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1374 "angle" determines the opening direction
1375 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1376 "health" if set, door must be shot open
1377 "speed" movement speed (100 default)
1378 "wait" wait before returning (3 default, -1 = never return)
1379 "lip" lip remaining at end of move (8 default)
1380 "dmg" damage to inflict when blocked (2 default)
1387 FIXME: only one sound set available at the time being
1391 void door_init_startopen()
1393 setorigin (self, self.pos2);
1394 self.pos2 = self.pos1;
1395 self.pos1 = self.origin;
1400 setorigin(self, self.pos1);
1401 self.velocity = '0 0 0';
1402 self.state = STATE_BOTTOM;
1403 self.think = SUB_Null;
1406 // spawnflags require key (for now only func_door)
1407 #define SPAWNFLAGS_GOLD_KEY 8
1408 #define SPAWNFLAGS_SILVER_KEY 16
1409 void spawnfunc_func_door()
1411 // Quake 1 keys compatibility
1412 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1413 self.itemkeys |= ITEM_KEY_BIT(0);
1414 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1415 self.itemkeys |= ITEM_KEY_BIT(1);
1417 //if (!self.deathtype) // map makers can override this
1418 // self.deathtype = " got in the way";
1421 self.max_health = self.health;
1422 if not(InitMovingBrushTrigger())
1424 self.effects |= EF_LOWPRECISION;
1425 self.classname = "door";
1427 self.blocked = door_blocked;
1428 self.use = door_use;
1430 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1431 // if(self.spawnflags & 8)
1432 // self.dmg = 10000;
1434 if(self.dmg && (!self.message))
1435 self.message = "was squished";
1436 if(self.dmg && (!self.message2))
1437 self.message2 = "was squished by";
1439 if (self.sounds > 0)
1441 precache_sound ("plats/medplat1.wav");
1442 precache_sound ("plats/medplat2.wav");
1443 self.noise2 = "plats/medplat1.wav";
1444 self.noise1 = "plats/medplat2.wav";
1454 self.pos1 = self.origin;
1455 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1457 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1458 // but spawn in the open position
1459 if (self.spawnflags & DOOR_START_OPEN)
1460 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1462 self.state = STATE_BOTTOM;
1466 self.takedamage = DAMAGE_YES;
1467 self.event_damage = door_damage;
1473 self.touch = door_touch;
1475 // LinkDoors can't be done until all of the doors have been spawned, so
1476 // the sizes can be detected properly.
1477 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1479 self.reset = door_reset;
1482 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1483 if two doors touch, they are assumed to be connected and operate as a unit.
1485 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1487 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1488 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1489 must have set trigger_reverse to 1.
1490 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1492 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).
1494 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1495 "angle" determines the destination angle for opening. negative values reverse the direction.
1496 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1497 "health" if set, door must be shot open
1498 "speed" movement speed (100 default)
1499 "wait" wait before returning (3 default, -1 = never return)
1500 "dmg" damage to inflict when blocked (2 default)
1507 FIXME: only one sound set available at the time being
1510 void door_rotating_reset()
1512 self.angles = self.pos1;
1513 self.avelocity = '0 0 0';
1514 self.state = STATE_BOTTOM;
1515 self.think = SUB_Null;
1518 void door_rotating_init_startopen()
1520 self.angles = self.movedir;
1521 self.pos2 = '0 0 0';
1522 self.pos1 = self.movedir;
1526 void spawnfunc_func_door_rotating()
1529 //if (!self.deathtype) // map makers can override this
1530 // self.deathtype = " got in the way";
1532 // I abuse "movedir" for denoting the axis for now
1533 if (self.spawnflags & 64) // X (untested)
1534 self.movedir = '0 0 1';
1535 else if (self.spawnflags & 128) // Y (untested)
1536 self.movedir = '1 0 0';
1538 self.movedir = '0 1 0';
1540 if (self.angles_y==0) self.angles_y = 90;
1542 self.movedir = self.movedir * self.angles_y;
1543 self.angles = '0 0 0';
1545 self.max_health = self.health;
1546 self.avelocity = self.movedir;
1547 if not(InitMovingBrushTrigger())
1549 self.velocity = '0 0 0';
1550 //self.effects |= EF_LOWPRECISION;
1551 self.classname = "door_rotating";
1553 self.blocked = door_blocked;
1554 self.use = door_use;
1556 if(self.spawnflags & 8)
1559 if(self.dmg && (!self.message))
1560 self.message = "was squished";
1561 if(self.dmg && (!self.message2))
1562 self.message2 = "was squished by";
1564 if (self.sounds > 0)
1566 precache_sound ("plats/medplat1.wav");
1567 precache_sound ("plats/medplat2.wav");
1568 self.noise2 = "plats/medplat1.wav";
1569 self.noise1 = "plats/medplat2.wav";
1576 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1578 self.pos1 = '0 0 0';
1579 self.pos2 = self.movedir;
1581 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1582 // but spawn in the open position
1583 if (self.spawnflags & DOOR_START_OPEN)
1584 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1586 self.state = STATE_BOTTOM;
1590 self.takedamage = DAMAGE_YES;
1591 self.event_damage = door_damage;
1597 self.touch = door_touch;
1599 // LinkDoors can't be done until all of the doors have been spawned, so
1600 // the sizes can be detected properly.
1601 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1603 self.reset = door_rotating_reset;
1607 =============================================================================
1611 =============================================================================
1614 void() fd_secret_move1;
1615 void() fd_secret_move2;
1616 void() fd_secret_move3;
1617 void() fd_secret_move4;
1618 void() fd_secret_move5;
1619 void() fd_secret_move6;
1620 void() fd_secret_done;
1622 float SECRET_OPEN_ONCE = 1; // stays open
1623 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1624 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1625 float SECRET_NO_SHOOT = 8; // only opened by trigger
1626 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1629 void fd_secret_use()
1632 string message_save;
1634 self.health = 10000;
1635 self.bot_attack = TRUE;
1637 // exit if still moving around...
1638 if (self.origin != self.oldorigin)
1641 message_save = self.message;
1642 self.message = ""; // no more message
1643 SUB_UseTargets(); // fire all targets / killtargets
1644 self.message = message_save;
1646 self.velocity = '0 0 0';
1648 // Make a sound, wait a little...
1650 if (self.noise1 != "")
1651 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1652 self.nextthink = self.ltime + 0.1;
1654 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1655 makevectors(self.mangle);
1659 if (self.spawnflags & SECRET_1ST_DOWN)
1660 self.t_width = fabs(v_up * self.size);
1662 self.t_width = fabs(v_right * self.size);
1666 self.t_length = fabs(v_forward * self.size);
1668 if (self.spawnflags & SECRET_1ST_DOWN)
1669 self.dest1 = self.origin - v_up * self.t_width;
1671 self.dest1 = self.origin + v_right * (self.t_width * temp);
1673 self.dest2 = self.dest1 + v_forward * self.t_length;
1674 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1675 if (self.noise2 != "")
1676 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1679 // Wait after first movement...
1680 void fd_secret_move1()
1682 self.nextthink = self.ltime + 1.0;
1683 self.think = fd_secret_move2;
1684 if (self.noise3 != "")
1685 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1688 // Start moving sideways w/sound...
1689 void fd_secret_move2()
1691 if (self.noise2 != "")
1692 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1693 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1696 // Wait here until time to go back...
1697 void fd_secret_move3()
1699 if (self.noise3 != "")
1700 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1701 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1703 self.nextthink = self.ltime + self.wait;
1704 self.think = fd_secret_move4;
1709 void fd_secret_move4()
1711 if (self.noise2 != "")
1712 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1713 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1717 void fd_secret_move5()
1719 self.nextthink = self.ltime + 1.0;
1720 self.think = fd_secret_move6;
1721 if (self.noise3 != "")
1722 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1725 void fd_secret_move6()
1727 if (self.noise2 != "")
1728 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1729 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1732 void fd_secret_done()
1734 if (self.spawnflags&SECRET_YES_SHOOT)
1736 self.health = 10000;
1737 self.takedamage = DAMAGE_YES;
1738 //self.th_pain = fd_secret_use;
1740 if (self.noise3 != "")
1741 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1744 void secret_blocked()
1746 if (time < self.attack_finished_single)
1748 self.attack_finished_single = time + 0.5;
1749 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1761 if not(other.iscreature)
1763 if (self.attack_finished_single > time)
1766 self.attack_finished_single = time + 2;
1770 if (other.flags & FL_CLIENT)
1771 centerprint (other, self.message);
1772 play2(other, "misc/talk.wav");
1778 if (self.spawnflags&SECRET_YES_SHOOT)
1780 self.health = 10000;
1781 self.takedamage = DAMAGE_YES;
1783 setorigin(self, self.oldorigin);
1784 self.think = SUB_Null;
1787 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1788 Basic secret door. Slides back, then to the side. Angle determines direction.
1789 wait = # of seconds before coming back
1790 1st_left = 1st move is left of arrow
1791 1st_down = 1st move is down from arrow
1792 always_shoot = even if targeted, keep shootable
1793 t_width = override WIDTH to move back (or height if going down)
1794 t_length = override LENGTH to move sideways
1795 "dmg" damage to inflict when blocked (2 default)
1797 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1804 void spawnfunc_func_door_secret()
1806 /*if (!self.deathtype) // map makers can override this
1807 self.deathtype = " got in the way";*/
1813 self.mangle = self.angles;
1814 self.angles = '0 0 0';
1815 self.classname = "door";
1816 if not(InitMovingBrushTrigger())
1818 self.effects |= EF_LOWPRECISION;
1820 self.touch = secret_touch;
1821 self.blocked = secret_blocked;
1823 self.use = fd_secret_use;
1828 self.spawnflags |= SECRET_YES_SHOOT;
1830 if(self.spawnflags&SECRET_YES_SHOOT)
1832 self.health = 10000;
1833 self.takedamage = DAMAGE_YES;
1834 self.event_damage = fd_secret_use;
1836 self.oldorigin = self.origin;
1838 self.wait = 5; // 5 seconds before closing
1840 self.reset = secret_reset;
1844 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1845 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1846 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
1847 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1848 height: amplitude modifier (default 32)
1849 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1850 noise: path/name of looping .wav file to play.
1851 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1855 void func_fourier_controller_think()
1860 self.nextthink = time + 0.1;
1861 if not (self.owner.active == ACTIVE_ACTIVE)
1863 self.owner.velocity = '0 0 0';
1868 n = floor((tokenize_console(self.owner.netname)) / 5);
1869 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1871 v = self.owner.destvec;
1873 for(i = 0; i < n; ++i)
1875 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1876 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;
1879 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1880 // * 10 so it will arrive in 0.1 sec
1881 self.owner.velocity = (v - self.owner.origin) * 10;
1884 void spawnfunc_func_fourier()
1887 if (self.noise != "")
1889 precache_sound(self.noise);
1890 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1897 self.destvec = self.origin;
1898 self.cnt = 360 / self.speed;
1900 self.blocked = generic_plat_blocked;
1901 if(self.dmg & (!self.message))
1902 self.message = " was squished";
1903 if(self.dmg && (!self.message2))
1904 self.message2 = "was squished by";
1905 if(self.dmg && (!self.dmgtime))
1906 self.dmgtime = 0.25;
1907 self.dmgtime2 = time;
1909 if(self.netname == "")
1910 self.netname = "1 0 0 0 1";
1912 if not(InitMovingBrushTrigger())
1915 self.active = ACTIVE_ACTIVE;
1917 // wait for targets to spawn
1918 controller = spawn();
1919 controller.classname = "func_fourier_controller";
1920 controller.owner = self;
1921 controller.nextthink = time + 1;
1922 controller.think = func_fourier_controller_think;
1923 self.nextthink = self.ltime + 999999999;
1924 self.think = SUB_Null;
1926 // Savage: Reduce bandwith, critical on e.g. nexdm02
1927 self.effects |= EF_LOWPRECISION;
1929 // TODO make a reset function for this one
1932 // reusing some fields havocbots declared
1933 .entity wp00, wp01, wp02, wp03;
1935 .float targetfactor, target2factor, target3factor, target4factor;
1936 .vector targetnormal, target2normal, target3normal, target4normal;
1938 vector func_vectormamamam_origin(entity o, float t)
1950 p = e.origin + t * e.velocity;
1952 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1954 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1960 p = e.origin + t * e.velocity;
1962 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1964 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1970 p = e.origin + t * e.velocity;
1972 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1974 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1980 p = e.origin + t * e.velocity;
1982 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1984 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1990 void func_vectormamamam_controller_think()
1992 self.nextthink = time + 0.1;
1994 if not (self.owner.active == ACTIVE_ACTIVE)
1996 self.owner.velocity = '0 0 0';
2000 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2001 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2004 void func_vectormamamam_findtarget()
2006 if(self.target != "")
2007 self.wp00 = find(world, targetname, self.target);
2009 if(self.target2 != "")
2010 self.wp01 = find(world, targetname, self.target2);
2012 if(self.target3 != "")
2013 self.wp02 = find(world, targetname, self.target3);
2015 if(self.target4 != "")
2016 self.wp03 = find(world, targetname, self.target4);
2018 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2019 objerror("No reference entity found, so there is nothing to move. Aborting.");
2021 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2024 controller = spawn();
2025 controller.classname = "func_vectormamamam_controller";
2026 controller.owner = self;
2027 controller.nextthink = time + 1;
2028 controller.think = func_vectormamamam_controller_think;
2031 void spawnfunc_func_vectormamamam()
2033 if (self.noise != "")
2035 precache_sound(self.noise);
2036 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2039 if(!self.targetfactor)
2040 self.targetfactor = 1;
2042 if(!self.target2factor)
2043 self.target2factor = 1;
2045 if(!self.target3factor)
2046 self.target3factor = 1;
2048 if(!self.target4factor)
2049 self.target4factor = 1;
2051 if(vlen(self.targetnormal))
2052 self.targetnormal = normalize(self.targetnormal);
2054 if(vlen(self.target2normal))
2055 self.target2normal = normalize(self.target2normal);
2057 if(vlen(self.target3normal))
2058 self.target3normal = normalize(self.target3normal);
2060 if(vlen(self.target4normal))
2061 self.target4normal = normalize(self.target4normal);
2063 self.blocked = generic_plat_blocked;
2064 if(self.dmg & (!self.message))
2065 self.message = " was squished";
2066 if(self.dmg && (!self.message2))
2067 self.message2 = "was squished by";
2068 if(self.dmg && (!self.dmgtime))
2069 self.dmgtime = 0.25;
2070 self.dmgtime2 = time;
2072 if(self.netname == "")
2073 self.netname = "1 0 0 0 1";
2075 if not(InitMovingBrushTrigger())
2078 // wait for targets to spawn
2079 self.nextthink = self.ltime + 999999999;
2080 self.think = SUB_Null;
2082 // Savage: Reduce bandwith, critical on e.g. nexdm02
2083 self.effects |= EF_LOWPRECISION;
2085 self.active = ACTIVE_ACTIVE;
2087 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2090 void conveyor_think()
2094 // set myself as current conveyor where possible
2095 for(e = world; (e = findentity(e, conveyor, self)); )
2100 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2101 if(!e.conveyor.state)
2104 vector emin = e.absmin;
2105 vector emax = e.absmax;
2106 if(self.solid == SOLID_BSP)
2111 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2112 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2116 for(e = world; (e = findentity(e, conveyor, self)); )
2118 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2119 continue; // done in SV_PlayerPhysics
2121 setorigin(e, e.origin + self.movedir * sys_frametime);
2122 move_out_of_solid(e);
2123 UpdateCSQCProjectile(e);
2125 // stupid conveyor code
2126 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2127 if(trace_fraction > 0)
2128 setorigin(e, trace_endpos);
2133 self.nextthink = time;
2138 self.state = !self.state;
2141 void conveyor_reset()
2143 self.state = (self.spawnflags & 1);
2146 void conveyor_init()
2150 self.movedir = self.movedir * self.speed;
2151 self.think = conveyor_think;
2152 self.nextthink = time;
2155 self.use = conveyor_use;
2156 self.reset = conveyor_reset;
2163 void spawnfunc_trigger_conveyor()
2170 void spawnfunc_func_conveyor()
2173 InitMovingBrushTrigger();
2174 self.movetype = MOVETYPE_NONE;