2 void generic_plat_blocked()
4 if(self.dmg && other.takedamage != DAMAGE_NO) {
5 if(self.dmgtime2 < time) {
6 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
7 self.dmgtime2 = time + self.dmgtime;
10 // Gib dead/dying stuff
11 if(other.deadflag != DEAD_NO)
12 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
18 float STATE_BOTTOM = 1;
22 .entity trigger_field;
24 void() plat_center_touch;
25 void() plat_outside_touch;
26 void() plat_trigger_use;
30 float PLAT_LOW_TRIGGER = 1;
32 void plat_spawn_inside_trigger()
38 trigger.touch = plat_center_touch;
39 trigger.movetype = MOVETYPE_NONE;
40 trigger.solid = SOLID_TRIGGER;
43 tmin = self.absmin + '25 25 0';
44 tmax = self.absmax - '25 25 -8';
45 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
46 if (self.spawnflags & PLAT_LOW_TRIGGER)
49 if (self.size_x <= 50)
51 tmin_x = (self.mins_x + self.maxs_x) / 2;
54 if (self.size_y <= 50)
56 tmin_y = (self.mins_y + self.maxs_y) / 2;
64 setsize (trigger, tmin, tmax);
68 // otherwise, something is fishy...
70 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
75 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
77 self.think = plat_go_down;
78 self.nextthink = self.ltime + 3;
81 void plat_hit_bottom()
83 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
89 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
91 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
101 void plat_center_touch()
103 if not(other.iscreature)
106 if (other.health <= 0)
112 else if (self.state == 1)
113 self.nextthink = self.ltime + 1; // delay going down
116 void plat_outside_touch()
118 if not(other.iscreature)
121 if (other.health <= 0)
129 void plat_trigger_use()
132 return; // already activated
139 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
140 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
142 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
143 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
144 // Gib dead/dying stuff
145 if(other.deadflag != DEAD_NO)
146 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
151 else if (self.state == 3)
153 // when in other states, then the plat_crush event came delayed after
154 // plat state already had changed
155 // this isn't a bug per se!
163 objerror ("plat_use: not in up state");
167 .string sound1, sound2;
173 setorigin (self, self.pos1);
179 setorigin (self, self.pos2);
181 self.use = plat_trigger_use;
185 void spawnfunc_path_corner() { }
186 void spawnfunc_func_plat()
188 if (self.sounds == 0)
191 if(self.spawnflags & 4)
194 if(self.dmg && (!self.message))
195 self.message = "was squished";
196 if(self.dmg && (!self.message2))
197 self.message2 = "was squished by";
199 if (self.sounds == 1)
201 precache_sound ("plats/plat1.wav");
202 precache_sound ("plats/plat2.wav");
203 self.noise = "plats/plat1.wav";
204 self.noise1 = "plats/plat2.wav";
207 if (self.sounds == 2)
209 precache_sound ("plats/medplat1.wav");
210 precache_sound ("plats/medplat2.wav");
211 self.noise = "plats/medplat1.wav";
212 self.noise1 = "plats/medplat2.wav";
217 precache_sound (self.sound1);
218 self.noise = self.sound1;
222 precache_sound (self.sound2);
223 self.noise1 = self.sound2;
226 self.mangle = self.angles;
227 self.angles = '0 0 0';
229 self.classname = "plat";
230 if not(InitMovingBrushTrigger())
232 self.effects |= EF_LOWPRECISION;
233 setsize (self, self.mins , self.maxs);
235 self.blocked = plat_crush;
242 self.height = self.size_z - self.lip;
244 self.pos1 = self.origin;
245 self.pos2 = self.origin;
246 self.pos2_z = self.origin_z - self.height;
248 self.reset = plat_reset;
251 plat_spawn_inside_trigger (); // the "start moving" trigger
259 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
267 self.think = train_next;
268 self.nextthink = self.ltime + self.wait;
284 targ = find(world, targetname, self.target);
285 self.target = targ.target;
286 if (self.spawnflags & 1)
288 cp = find(world, target, targ.targetname); // get the previous corner first
289 cp = find(world, targetname, cp.target2); // now get its second target (the control point)
290 if(cp.targetname == "")
291 cp_org = targ.origin - self.mins; // no control point found, assume a straight line to the destination
293 cp_org = cp.origin - self.mins;
296 objerror("train_next: no next target");
297 self.wait = targ.wait;
303 if (self.spawnflags & 1)
304 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, targ.speed, train_wait);
306 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
310 if (self.spawnflags & 1)
311 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, self.speed, train_wait);
313 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
317 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
320 void func_train_find()
323 targ = find(world, targetname, self.target);
324 self.target = targ.target;
326 objerror("func_train_find: no next target");
327 setorigin(self, targ.origin - self.mins);
328 self.nextthink = self.ltime + 1;
329 self.think = train_next;
332 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
333 Ridable platform, targets spawnfunc_path_corner path to follow.
334 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
335 target : targetname of first spawnfunc_path_corner (starts here)
337 void spawnfunc_func_train()
339 if (self.noise != "")
340 precache_sound(self.noise);
343 objerror("func_train without a target");
347 if not(InitMovingBrushTrigger())
349 self.effects |= EF_LOWPRECISION;
351 // wait for targets to spawn
352 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
354 self.blocked = generic_plat_blocked;
355 if(self.dmg & (!self.message))
356 self.message = " was squished";
357 if(self.dmg && (!self.message2))
358 self.message2 = "was squished by";
359 if(self.dmg && (!self.dmgtime))
361 self.dmgtime2 = time;
363 // TODO make a reset function for this one
366 void func_rotating_setactive(float astate)
369 if (astate == ACTIVE_TOGGLE)
371 if(self.active == ACTIVE_ACTIVE)
372 self.active = ACTIVE_NOT;
374 self.active = ACTIVE_ACTIVE;
377 self.active = astate;
379 if(self.active == ACTIVE_NOT)
380 self.avelocity = '0 0 0';
382 self.avelocity = self.pos1;
385 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
386 Brush model that spins in place on one axis (default Z).
387 speed : speed to rotate (in degrees per second)
388 noise : path/name of looping .wav file to play.
389 dmg : Do this mutch dmg every .dmgtime intervall when blocked
393 void spawnfunc_func_rotating()
395 if (self.noise != "")
397 precache_sound(self.noise);
398 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
401 self.active = ACTIVE_ACTIVE;
402 self.setactive = func_rotating_setactive;
406 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
407 if (self.spawnflags & 4) // X (untested)
408 self.avelocity = '0 0 1' * self.speed;
409 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
410 else if (self.spawnflags & 8) // Y (untested)
411 self.avelocity = '1 0 0' * self.speed;
412 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
414 self.avelocity = '0 1 0' * self.speed;
416 self.pos1 = self.avelocity;
418 if(self.dmg & (!self.message))
419 self.message = " was squished";
420 if(self.dmg && (!self.message2))
421 self.message2 = "was squished by";
424 if(self.dmg && (!self.dmgtime))
427 self.dmgtime2 = time;
429 if not(InitMovingBrushTrigger())
431 // no EF_LOWPRECISION here, as rounding angles is bad
433 self.blocked = generic_plat_blocked;
435 // wait for targets to spawn
436 self.nextthink = self.ltime + 999999999;
437 self.think = SUB_Null;
439 // TODO make a reset function for this one
443 void func_bobbing_controller_think()
446 self.nextthink = time + 0.1;
448 if not (self.owner.active == ACTIVE_ACTIVE)
450 self.owner.velocity = '0 0 0';
454 // calculate sinewave using makevectors
455 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
456 v = self.owner.destvec + self.owner.movedir * v_forward_y;
457 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
458 // * 10 so it will arrive in 0.1 sec
459 self.owner.velocity = (v - self.owner.origin) * 10;
462 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
463 Brush model that moves back and forth on one axis (default Z).
464 speed : how long one cycle takes in seconds (default 4)
465 height : how far the cycle moves (default 32)
466 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
467 noise : path/name of looping .wav file to play.
468 dmg : Do this mutch dmg every .dmgtime intervall when blocked
471 void spawnfunc_func_bobbing()
474 if (self.noise != "")
476 precache_sound(self.noise);
477 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
483 // center of bobbing motion
484 self.destvec = self.origin;
485 // time scale to get degrees
486 self.cnt = 360 / self.speed;
488 self.active = ACTIVE_ACTIVE;
490 // damage when blocked
491 self.blocked = generic_plat_blocked;
492 if(self.dmg & (!self.message))
493 self.message = " was squished";
494 if(self.dmg && (!self.message2))
495 self.message2 = "was squished by";
496 if(self.dmg && (!self.dmgtime))
498 self.dmgtime2 = time;
501 if (self.spawnflags & 1) // X
502 self.movedir = '1 0 0' * self.height;
503 else if (self.spawnflags & 2) // Y
504 self.movedir = '0 1 0' * self.height;
506 self.movedir = '0 0 1' * self.height;
508 if not(InitMovingBrushTrigger())
511 // wait for targets to spawn
512 controller = spawn();
513 controller.classname = "func_bobbing_controller";
514 controller.owner = self;
515 controller.nextthink = time + 1;
516 controller.think = func_bobbing_controller_think;
517 self.nextthink = self.ltime + 999999999;
518 self.think = SUB_Null;
520 // Savage: Reduce bandwith, critical on e.g. nexdm02
521 self.effects |= EF_LOWPRECISION;
523 // TODO make a reset function for this one
527 void func_pendulum_controller_think()
530 self.nextthink = time + 0.1;
532 if not (self.owner.active == ACTIVE_ACTIVE)
534 self.owner.avelocity_x = 0;
538 // calculate sinewave using makevectors
539 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
540 v = self.owner.speed * v_forward_y + self.cnt;
541 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
543 // * 10 so it will arrive in 0.1 sec
544 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
548 void spawnfunc_func_pendulum()
551 if (self.noise != "")
553 precache_sound(self.noise);
554 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
557 self.active = ACTIVE_ACTIVE;
559 // keys: angle, speed, phase, noise, freq
563 // not initializing self.dmg to 2, to allow damageless pendulum
565 if(self.dmg & (!self.message))
566 self.message = " was squished";
567 if(self.dmg && (!self.message2))
568 self.message2 = "was squished by";
569 if(self.dmg && (!self.dmgtime))
571 self.dmgtime2 = time;
573 self.blocked = generic_plat_blocked;
575 self.avelocity_z = 0.0000001;
576 if not(InitMovingBrushTrigger())
581 // find pendulum length (same formula as Q3A)
582 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
585 // copy initial angle
586 self.cnt = self.angles_z;
588 // wait for targets to spawn
589 controller = spawn();
590 controller.classname = "func_pendulum_controller";
591 controller.owner = self;
592 controller.nextthink = time + 1;
593 controller.think = func_pendulum_controller_think;
594 self.nextthink = self.ltime + 999999999;
595 self.think = SUB_Null;
597 //self.effects |= EF_LOWPRECISION;
599 // TODO make a reset function for this one
602 // button and multiple button
605 void() button_return;
609 self.state = STATE_TOP;
610 self.nextthink = self.ltime + self.wait;
611 self.think = button_return;
612 activator = self.enemy;
614 self.frame = 1; // use alternate textures
619 self.state = STATE_BOTTOM;
624 self.state = STATE_DOWN;
625 SUB_CalcMove (self.pos1, self.speed, button_done);
626 self.frame = 0; // use normal textures
628 self.takedamage = DAMAGE_YES; // can be shot again
632 void button_blocked()
634 // do nothing, just don't come all the way back out
640 self.health = self.max_health;
641 self.takedamage = DAMAGE_NO; // will be reset upon return
643 if (self.state == STATE_UP || self.state == STATE_TOP)
646 if (self.noise != "")
647 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
649 self.state = STATE_UP;
650 SUB_CalcMove (self.pos2, self.speed, button_wait);
655 self.health = self.max_health;
656 setorigin(self, self.pos1);
657 self.frame = 0; // use normal textures
658 self.state = STATE_BOTTOM;
660 self.takedamage = DAMAGE_YES; // can be shot again
665 // if (activator.classname != "player")
667 // dprint(activator.classname);
668 // dprint(" triggered a button\n");
671 if not (self.active == ACTIVE_ACTIVE)
674 self.enemy = activator;
680 // if (activator.classname != "player")
682 // dprint(activator.classname);
683 // dprint(" touched a button\n");
687 if not(other.iscreature)
689 if(other.velocity * self.movedir < 0)
693 self.enemy = other.owner;
697 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
699 if(self.spawnflags & DOOR_NOSPLASH)
700 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
702 self.health = self.health - damage;
703 if (self.health <= 0)
705 // if (activator.classname != "player")
707 // dprint(activator.classname);
708 // dprint(" killed a button\n");
710 self.enemy = damage_attacker;
716 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
717 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.
719 "angle" determines the opening direction
720 "target" all entities with a matching targetname will be used
721 "speed" override the default 40 speed
722 "wait" override the default 1 second wait (-1 = never return)
723 "lip" override the default 4 pixel lip remaining at end of move
724 "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
731 void spawnfunc_func_button()
735 if not(InitMovingBrushTrigger())
737 self.effects |= EF_LOWPRECISION;
739 self.blocked = button_blocked;
740 self.use = button_use;
742 // if (self.health == 0) // all buttons are now shootable
746 self.max_health = self.health;
747 self.event_damage = button_damage;
748 self.takedamage = DAMAGE_YES;
751 self.touch = button_touch;
761 precache_sound(self.noise);
763 self.active = ACTIVE_ACTIVE;
765 self.pos1 = self.origin;
766 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
767 self.flags |= FL_NOTARGET;
773 float DOOR_START_OPEN = 1;
774 float DOOR_DONT_LINK = 4;
775 float DOOR_TOGGLE = 32;
779 Doors are similar to buttons, but can spawn a fat trigger field around them
780 to open without a touch, and they link together to form simultanious
783 Door.owner is the master door. If there is only one door, it points to itself.
784 If multiple doors, all will point to a single one.
786 Door.enemy chains from the master door through all doors linked in the chain.
791 =============================================================================
795 =============================================================================
800 void() door_rotating_go_down;
801 void() door_rotating_go_up;
806 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
807 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
810 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
811 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
813 //Dont chamge direction for dead or dying stuff
814 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
817 if (self.state == STATE_DOWN)
818 if (self.classname == "door")
823 door_rotating_go_up ();
826 if (self.classname == "door")
831 door_rotating_go_down ();
835 //gib dying stuff just to make sure
836 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
837 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
841 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
842 // if a door has a negative wait, it would never come back if blocked,
843 // so let it just squash the object to death real fast
844 /* if (self.wait >= 0)
846 if (self.state == STATE_DOWN)
857 if (self.noise1 != "")
858 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
859 self.state = STATE_TOP;
860 if (self.spawnflags & DOOR_TOGGLE)
861 return; // don't come down automatically
862 if (self.classname == "door")
864 self.think = door_go_down;
867 self.think = door_rotating_go_down;
869 self.nextthink = self.ltime + self.wait;
872 void door_hit_bottom()
874 if (self.noise1 != "")
875 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
876 self.state = STATE_BOTTOM;
881 if (self.noise2 != "")
882 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
885 self.takedamage = DAMAGE_YES;
886 self.health = self.max_health;
889 self.state = STATE_DOWN;
890 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
895 if (self.state == STATE_UP)
896 return; // already going up
898 if (self.state == STATE_TOP)
899 { // reset top wait time
900 self.nextthink = self.ltime + self.wait;
904 if (self.noise2 != "")
905 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
906 self.state = STATE_UP;
907 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
910 oldmessage = self.message;
913 self.message = oldmessage;
919 =============================================================================
923 =============================================================================
926 float door_check_keys(void) {
936 if not(door.itemkeys)
939 // this door require a key
940 // only a player can have a key
941 if (other.classname != "player")
944 if (item_keys_usekey(door, other)) {
945 // some keys were used
946 if (other.key_door_messagetime <= time) {
947 play2(other, "misc/talk.wav");
948 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
949 other.key_door_messagetime = time + 2;
953 if (other.key_door_messagetime <= time) {
954 play2(other, "misc/talk.wav");
955 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
956 other.key_door_messagetime = time + 2;
961 // door is now unlocked
962 play2(other, "misc/talk.wav");
963 centerprint(other, "Door unlocked!");
975 if (self.owner != self)
976 objerror ("door_fire: self.owner != self");
980 if (self.spawnflags & DOOR_TOGGLE)
982 if (self.state == STATE_UP || self.state == STATE_TOP)
987 if (self.classname == "door")
993 door_rotating_go_down ();
996 } while ( (self != starte) && (self != world) );
1002 // trigger all paired doors
1006 if (self.classname == "door")
1011 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1012 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1014 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1015 self.pos2 = '0 0 0' - self.pos2;
1017 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1018 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1019 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1021 door_rotating_go_up ();
1025 } while ( (self != starte) && (self != world) );
1034 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1046 void door_trigger_touch()
1048 if (other.health < 1)
1049 if not(other.iscreature && other.deadflag == DEAD_NO)
1052 if (time < self.attack_finished_single)
1055 // check if door is locked
1056 if (!door_check_keys())
1059 self.attack_finished_single = time + 1;
1068 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1071 if(self.spawnflags & DOOR_NOSPLASH)
1072 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1074 self.health = self.health - damage;
1076 if (self.itemkeys) {
1077 // don't allow opening doors through damage if keys are required
1081 if (self.health <= 0)
1085 self.health = self.max_health;
1086 self.takedamage = DAMAGE_NO; // wil be reset upon return
1102 if(other.classname != "player")
1104 if (self.owner.attack_finished_single > time)
1107 self.owner.attack_finished_single = time + 2;
1109 if (!(self.owner.dmg) && (self.owner.message != ""))
1111 if (other.flags & FL_CLIENT)
1112 centerprint (other, self.owner.message);
1113 play2(other, "misc/talk.wav");
1118 void door_generic_plat_blocked()
1121 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1122 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1125 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1126 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1128 //Dont chamge direction for dead or dying stuff
1129 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1132 if (self.state == STATE_DOWN)
1133 door_rotating_go_up ();
1135 door_rotating_go_down ();
1138 //gib dying stuff just to make sure
1139 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1140 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1144 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1145 // if a door has a negative wait, it would never come back if blocked,
1146 // so let it just squash the object to death real fast
1147 /* if (self.wait >= 0)
1149 if (self.state == STATE_DOWN)
1150 door_rotating_go_up ();
1152 door_rotating_go_down ();
1158 void door_rotating_hit_top()
1160 if (self.noise1 != "")
1161 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1162 self.state = STATE_TOP;
1163 if (self.spawnflags & DOOR_TOGGLE)
1164 return; // don't come down automatically
1165 self.think = door_rotating_go_down;
1166 self.nextthink = self.ltime + self.wait;
1169 void door_rotating_hit_bottom()
1171 if (self.noise1 != "")
1172 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1173 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1175 self.pos2 = '0 0 0' - self.pos2;
1178 self.state = STATE_BOTTOM;
1181 void door_rotating_go_down()
1183 if (self.noise2 != "")
1184 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1185 if (self.max_health)
1187 self.takedamage = DAMAGE_YES;
1188 self.health = self.max_health;
1191 self.state = STATE_DOWN;
1192 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1195 void door_rotating_go_up()
1197 if (self.state == STATE_UP)
1198 return; // already going up
1200 if (self.state == STATE_TOP)
1201 { // reset top wait time
1202 self.nextthink = self.ltime + self.wait;
1205 if (self.noise2 != "")
1206 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1207 self.state = STATE_UP;
1208 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1211 oldmessage = self.message;
1214 self.message = oldmessage;
1221 =============================================================================
1225 =============================================================================
1229 entity spawn_field(vector fmins, vector fmaxs)
1235 trigger.classname = "doortriggerfield";
1236 trigger.movetype = MOVETYPE_NONE;
1237 trigger.solid = SOLID_TRIGGER;
1238 trigger.owner = self;
1239 trigger.touch = door_trigger_touch;
1243 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1248 float EntitiesTouching(entity e1, entity e2)
1250 if (e1.absmin_x > e2.absmax_x)
1252 if (e1.absmin_y > e2.absmax_y)
1254 if (e1.absmin_z > e2.absmax_z)
1256 if (e1.absmax_x < e2.absmin_x)
1258 if (e1.absmax_y < e2.absmin_y)
1260 if (e1.absmax_z < e2.absmin_z)
1276 vector cmins, cmaxs;
1279 return; // already linked by another door
1280 if (self.spawnflags & 4)
1282 self.owner = self.enemy = self;
1290 self.trigger_field = spawn_field(self.absmin, self.absmax);
1292 return; // don't want to link this door
1295 cmins = self.absmin;
1296 cmaxs = self.absmax;
1303 self.owner = starte; // master door
1306 starte.health = self.health;
1308 starte.targetname = self.targetname;
1309 if (self.message != "")
1310 starte.message = self.message;
1312 t = find(t, classname, self.classname);
1315 self.enemy = starte; // make the chain a loop
1317 // shootable, or triggered doors just needed the owner/enemy links,
1318 // they don't spawn a field
1329 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1334 if (EntitiesTouching(self,t))
1337 objerror ("cross connected doors");
1342 if (t.absmin_x < cmins_x)
1343 cmins_x = t.absmin_x;
1344 if (t.absmin_y < cmins_y)
1345 cmins_y = t.absmin_y;
1346 if (t.absmin_z < cmins_z)
1347 cmins_z = t.absmin_z;
1348 if (t.absmax_x > cmaxs_x)
1349 cmaxs_x = t.absmax_x;
1350 if (t.absmax_y > cmaxs_y)
1351 cmaxs_y = t.absmax_y;
1352 if (t.absmax_z > cmaxs_z)
1353 cmaxs_z = t.absmax_z;
1360 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1361 if two doors touch, they are assumed to be connected and operate as a unit.
1363 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1365 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).
1367 GOLD_KEY causes the door to open only if the activator holds a gold key.
1369 SILVER_KEY causes the door to open only if the activator holds a silver key.
1371 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1372 "angle" determines the opening direction
1373 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1374 "health" if set, door must be shot open
1375 "speed" movement speed (100 default)
1376 "wait" wait before returning (3 default, -1 = never return)
1377 "lip" lip remaining at end of move (8 default)
1378 "dmg" damage to inflict when blocked (2 default)
1385 FIXME: only one sound set available at the time being
1389 void door_init_startopen()
1391 setorigin (self, self.pos2);
1392 self.pos2 = self.pos1;
1393 self.pos1 = self.origin;
1398 setorigin(self, self.pos1);
1399 self.velocity = '0 0 0';
1400 self.state = STATE_BOTTOM;
1401 self.think = SUB_Null;
1404 // spawnflags require key (for now only func_door)
1405 #define SPAWNFLAGS_GOLD_KEY 8
1406 #define SPAWNFLAGS_SILVER_KEY 16
1407 void spawnfunc_func_door()
1409 // Quake 1 keys compatibility
1410 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1411 self.itemkeys |= ITEM_KEY_BIT(0);
1412 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1413 self.itemkeys |= ITEM_KEY_BIT(1);
1415 //if (!self.deathtype) // map makers can override this
1416 // self.deathtype = " got in the way";
1419 self.max_health = self.health;
1420 if not(InitMovingBrushTrigger())
1422 self.effects |= EF_LOWPRECISION;
1423 self.classname = "door";
1425 self.blocked = door_blocked;
1426 self.use = door_use;
1428 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1429 // if(self.spawnflags & 8)
1430 // self.dmg = 10000;
1432 if(self.dmg && (!self.message))
1433 self.message = "was squished";
1434 if(self.dmg && (!self.message2))
1435 self.message2 = "was squished by";
1437 if (self.sounds > 0)
1439 precache_sound ("plats/medplat1.wav");
1440 precache_sound ("plats/medplat2.wav");
1441 self.noise2 = "plats/medplat1.wav";
1442 self.noise1 = "plats/medplat2.wav";
1452 self.pos1 = self.origin;
1453 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1455 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1456 // but spawn in the open position
1457 if (self.spawnflags & DOOR_START_OPEN)
1458 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1460 self.state = STATE_BOTTOM;
1464 self.takedamage = DAMAGE_YES;
1465 self.event_damage = door_damage;
1471 self.touch = door_touch;
1473 // LinkDoors can't be done until all of the doors have been spawned, so
1474 // the sizes can be detected properly.
1475 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1477 self.reset = door_reset;
1480 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1481 if two doors touch, they are assumed to be connected and operate as a unit.
1483 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1485 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1486 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1487 must have set trigger_reverse to 1.
1488 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1490 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).
1492 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1493 "angle" determines the destination angle for opening. negative values reverse the direction.
1494 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1495 "health" if set, door must be shot open
1496 "speed" movement speed (100 default)
1497 "wait" wait before returning (3 default, -1 = never return)
1498 "dmg" damage to inflict when blocked (2 default)
1505 FIXME: only one sound set available at the time being
1508 void door_rotating_reset()
1510 self.angles = self.pos1;
1511 self.avelocity = '0 0 0';
1512 self.state = STATE_BOTTOM;
1513 self.think = SUB_Null;
1516 void door_rotating_init_startopen()
1518 self.angles = self.movedir;
1519 self.pos2 = '0 0 0';
1520 self.pos1 = self.movedir;
1524 void spawnfunc_func_door_rotating()
1527 //if (!self.deathtype) // map makers can override this
1528 // self.deathtype = " got in the way";
1530 // I abuse "movedir" for denoting the axis for now
1531 if (self.spawnflags & 64) // X (untested)
1532 self.movedir = '0 0 1';
1533 else if (self.spawnflags & 128) // Y (untested)
1534 self.movedir = '1 0 0';
1536 self.movedir = '0 1 0';
1538 if (self.angles_y==0) self.angles_y = 90;
1540 self.movedir = self.movedir * self.angles_y;
1541 self.angles = '0 0 0';
1543 self.max_health = self.health;
1544 self.avelocity = self.movedir;
1545 if not(InitMovingBrushTrigger())
1547 self.velocity = '0 0 0';
1548 //self.effects |= EF_LOWPRECISION;
1549 self.classname = "door_rotating";
1551 self.blocked = door_blocked;
1552 self.use = door_use;
1554 if(self.spawnflags & 8)
1557 if(self.dmg && (!self.message))
1558 self.message = "was squished";
1559 if(self.dmg && (!self.message2))
1560 self.message2 = "was squished by";
1562 if (self.sounds > 0)
1564 precache_sound ("plats/medplat1.wav");
1565 precache_sound ("plats/medplat2.wav");
1566 self.noise2 = "plats/medplat1.wav";
1567 self.noise1 = "plats/medplat2.wav";
1574 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1576 self.pos1 = '0 0 0';
1577 self.pos2 = self.movedir;
1579 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1580 // but spawn in the open position
1581 if (self.spawnflags & DOOR_START_OPEN)
1582 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1584 self.state = STATE_BOTTOM;
1588 self.takedamage = DAMAGE_YES;
1589 self.event_damage = door_damage;
1595 self.touch = door_touch;
1597 // LinkDoors can't be done until all of the doors have been spawned, so
1598 // the sizes can be detected properly.
1599 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1601 self.reset = door_rotating_reset;
1605 =============================================================================
1609 =============================================================================
1612 void() fd_secret_move1;
1613 void() fd_secret_move2;
1614 void() fd_secret_move3;
1615 void() fd_secret_move4;
1616 void() fd_secret_move5;
1617 void() fd_secret_move6;
1618 void() fd_secret_done;
1620 float SECRET_OPEN_ONCE = 1; // stays open
1621 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1622 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1623 float SECRET_NO_SHOOT = 8; // only opened by trigger
1624 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1627 void fd_secret_use()
1630 string message_save;
1632 self.health = 10000;
1633 self.bot_attack = TRUE;
1635 // exit if still moving around...
1636 if (self.origin != self.oldorigin)
1639 message_save = self.message;
1640 self.message = ""; // no more message
1641 SUB_UseTargets(); // fire all targets / killtargets
1642 self.message = message_save;
1644 self.velocity = '0 0 0';
1646 // Make a sound, wait a little...
1648 if (self.noise1 != "")
1649 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1650 self.nextthink = self.ltime + 0.1;
1652 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1653 makevectors(self.mangle);
1657 if (self.spawnflags & SECRET_1ST_DOWN)
1658 self.t_width = fabs(v_up * self.size);
1660 self.t_width = fabs(v_right * self.size);
1664 self.t_length = fabs(v_forward * self.size);
1666 if (self.spawnflags & SECRET_1ST_DOWN)
1667 self.dest1 = self.origin - v_up * self.t_width;
1669 self.dest1 = self.origin + v_right * (self.t_width * temp);
1671 self.dest2 = self.dest1 + v_forward * self.t_length;
1672 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1673 if (self.noise2 != "")
1674 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1677 // Wait after first movement...
1678 void fd_secret_move1()
1680 self.nextthink = self.ltime + 1.0;
1681 self.think = fd_secret_move2;
1682 if (self.noise3 != "")
1683 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1686 // Start moving sideways w/sound...
1687 void fd_secret_move2()
1689 if (self.noise2 != "")
1690 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1691 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1694 // Wait here until time to go back...
1695 void fd_secret_move3()
1697 if (self.noise3 != "")
1698 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1699 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1701 self.nextthink = self.ltime + self.wait;
1702 self.think = fd_secret_move4;
1707 void fd_secret_move4()
1709 if (self.noise2 != "")
1710 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1711 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1715 void fd_secret_move5()
1717 self.nextthink = self.ltime + 1.0;
1718 self.think = fd_secret_move6;
1719 if (self.noise3 != "")
1720 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1723 void fd_secret_move6()
1725 if (self.noise2 != "")
1726 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1727 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1730 void fd_secret_done()
1732 if (self.spawnflags&SECRET_YES_SHOOT)
1734 self.health = 10000;
1735 self.takedamage = DAMAGE_YES;
1736 //self.th_pain = fd_secret_use;
1738 if (self.noise3 != "")
1739 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1742 void secret_blocked()
1744 if (time < self.attack_finished_single)
1746 self.attack_finished_single = time + 0.5;
1747 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1759 if not(other.iscreature)
1761 if (self.attack_finished_single > time)
1764 self.attack_finished_single = time + 2;
1768 if (other.flags & FL_CLIENT)
1769 centerprint (other, self.message);
1770 play2(other, "misc/talk.wav");
1776 if (self.spawnflags&SECRET_YES_SHOOT)
1778 self.health = 10000;
1779 self.takedamage = DAMAGE_YES;
1781 setorigin(self, self.oldorigin);
1782 self.think = SUB_Null;
1785 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1786 Basic secret door. Slides back, then to the side. Angle determines direction.
1787 wait = # of seconds before coming back
1788 1st_left = 1st move is left of arrow
1789 1st_down = 1st move is down from arrow
1790 always_shoot = even if targeted, keep shootable
1791 t_width = override WIDTH to move back (or height if going down)
1792 t_length = override LENGTH to move sideways
1793 "dmg" damage to inflict when blocked (2 default)
1795 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1802 void spawnfunc_func_door_secret()
1804 /*if (!self.deathtype) // map makers can override this
1805 self.deathtype = " got in the way";*/
1811 self.mangle = self.angles;
1812 self.angles = '0 0 0';
1813 self.classname = "door";
1814 if not(InitMovingBrushTrigger())
1816 self.effects |= EF_LOWPRECISION;
1818 self.touch = secret_touch;
1819 self.blocked = secret_blocked;
1821 self.use = fd_secret_use;
1826 self.spawnflags |= SECRET_YES_SHOOT;
1828 if(self.spawnflags&SECRET_YES_SHOOT)
1830 self.health = 10000;
1831 self.takedamage = DAMAGE_YES;
1832 self.event_damage = fd_secret_use;
1834 self.oldorigin = self.origin;
1836 self.wait = 5; // 5 seconds before closing
1838 self.reset = secret_reset;
1842 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1843 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1844 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
1845 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1846 height: amplitude modifier (default 32)
1847 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1848 noise: path/name of looping .wav file to play.
1849 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1853 void func_fourier_controller_think()
1858 self.nextthink = time + 0.1;
1859 if not (self.owner.active == ACTIVE_ACTIVE)
1861 self.owner.velocity = '0 0 0';
1866 n = floor((tokenize_console(self.owner.netname)) / 5);
1867 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1869 v = self.owner.destvec;
1871 for(i = 0; i < n; ++i)
1873 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1874 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;
1877 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1878 // * 10 so it will arrive in 0.1 sec
1879 self.owner.velocity = (v - self.owner.origin) * 10;
1882 void spawnfunc_func_fourier()
1885 if (self.noise != "")
1887 precache_sound(self.noise);
1888 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1895 self.destvec = self.origin;
1896 self.cnt = 360 / self.speed;
1898 self.blocked = generic_plat_blocked;
1899 if(self.dmg & (!self.message))
1900 self.message = " was squished";
1901 if(self.dmg && (!self.message2))
1902 self.message2 = "was squished by";
1903 if(self.dmg && (!self.dmgtime))
1904 self.dmgtime = 0.25;
1905 self.dmgtime2 = time;
1907 if(self.netname == "")
1908 self.netname = "1 0 0 0 1";
1910 if not(InitMovingBrushTrigger())
1913 self.active = ACTIVE_ACTIVE;
1915 // wait for targets to spawn
1916 controller = spawn();
1917 controller.classname = "func_fourier_controller";
1918 controller.owner = self;
1919 controller.nextthink = time + 1;
1920 controller.think = func_fourier_controller_think;
1921 self.nextthink = self.ltime + 999999999;
1922 self.think = SUB_Null;
1924 // Savage: Reduce bandwith, critical on e.g. nexdm02
1925 self.effects |= EF_LOWPRECISION;
1927 // TODO make a reset function for this one
1930 // reusing some fields havocbots declared
1931 .entity wp00, wp01, wp02, wp03;
1933 .float targetfactor, target2factor, target3factor, target4factor;
1934 .vector targetnormal, target2normal, target3normal, target4normal;
1936 vector func_vectormamamam_origin(entity o, float t)
1948 p = e.origin + t * e.velocity;
1950 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1952 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1958 p = e.origin + t * e.velocity;
1960 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1962 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1968 p = e.origin + t * e.velocity;
1970 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1972 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1978 p = e.origin + t * e.velocity;
1980 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1982 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1988 void func_vectormamamam_controller_think()
1990 self.nextthink = time + 0.1;
1992 if not (self.owner.active == ACTIVE_ACTIVE)
1994 self.owner.velocity = '0 0 0';
1998 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1999 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2002 void func_vectormamamam_findtarget()
2004 if(self.target != "")
2005 self.wp00 = find(world, targetname, self.target);
2007 if(self.target2 != "")
2008 self.wp01 = find(world, targetname, self.target2);
2010 if(self.target3 != "")
2011 self.wp02 = find(world, targetname, self.target3);
2013 if(self.target4 != "")
2014 self.wp03 = find(world, targetname, self.target4);
2016 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2017 objerror("No reference entity found, so there is nothing to move. Aborting.");
2019 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2022 controller = spawn();
2023 controller.classname = "func_vectormamamam_controller";
2024 controller.owner = self;
2025 controller.nextthink = time + 1;
2026 controller.think = func_vectormamamam_controller_think;
2029 void spawnfunc_func_vectormamamam()
2031 if (self.noise != "")
2033 precache_sound(self.noise);
2034 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2037 if(!self.targetfactor)
2038 self.targetfactor = 1;
2040 if(!self.target2factor)
2041 self.target2factor = 1;
2043 if(!self.target3factor)
2044 self.target3factor = 1;
2046 if(!self.target4factor)
2047 self.target4factor = 1;
2049 if(vlen(self.targetnormal))
2050 self.targetnormal = normalize(self.targetnormal);
2052 if(vlen(self.target2normal))
2053 self.target2normal = normalize(self.target2normal);
2055 if(vlen(self.target3normal))
2056 self.target3normal = normalize(self.target3normal);
2058 if(vlen(self.target4normal))
2059 self.target4normal = normalize(self.target4normal);
2061 self.blocked = generic_plat_blocked;
2062 if(self.dmg & (!self.message))
2063 self.message = " was squished";
2064 if(self.dmg && (!self.message2))
2065 self.message2 = "was squished by";
2066 if(self.dmg && (!self.dmgtime))
2067 self.dmgtime = 0.25;
2068 self.dmgtime2 = time;
2070 if(self.netname == "")
2071 self.netname = "1 0 0 0 1";
2073 if not(InitMovingBrushTrigger())
2076 // wait for targets to spawn
2077 self.nextthink = self.ltime + 999999999;
2078 self.think = SUB_Null;
2080 // Savage: Reduce bandwith, critical on e.g. nexdm02
2081 self.effects |= EF_LOWPRECISION;
2083 self.active = ACTIVE_ACTIVE;
2085 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2088 void conveyor_think()
2092 // set myself as current conveyor where possible
2093 for(e = world; (e = findentity(e, conveyor, self)); )
2098 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2099 if(!e.conveyor.state)
2102 vector emin = e.absmin;
2103 vector emax = e.absmax;
2104 if(self.solid == SOLID_BSP)
2109 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2110 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2114 for(e = world; (e = findentity(e, conveyor, self)); )
2116 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2117 continue; // done in SV_PlayerPhysics
2119 setorigin(e, e.origin + self.movedir * sys_frametime);
2120 move_out_of_solid(e);
2121 UpdateCSQCProjectile(e);
2123 // stupid conveyor code
2124 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2125 if(trace_fraction > 0)
2126 setorigin(e, trace_endpos);
2131 self.nextthink = time;
2136 self.state = !self.state;
2139 void conveyor_reset()
2141 self.state = (self.spawnflags & 1);
2144 void conveyor_init()
2148 self.movedir = self.movedir * self.speed;
2149 self.think = conveyor_think;
2150 self.nextthink = time;
2153 self.use = conveyor_use;
2154 self.reset = conveyor_reset;
2161 void spawnfunc_trigger_conveyor()
2168 void spawnfunc_func_conveyor()
2171 InitMovingBrushTrigger();
2172 self.movetype = MOVETYPE_NONE;