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;
60 setsize (trigger, tmin, tmax);
65 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
67 self.think = plat_go_down;
68 self.nextthink = self.ltime + 3;
71 void plat_hit_bottom()
73 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
79 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
81 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
86 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
88 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
91 void plat_center_touch()
93 if not(other.iscreature)
96 if (other.health <= 0)
102 else if (self.state == 1)
103 self.nextthink = self.ltime + 1; // delay going down
106 void plat_outside_touch()
108 if not(other.iscreature)
111 if (other.health <= 0)
119 void plat_trigger_use()
122 return; // already activated
129 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
130 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
132 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
133 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
134 // Gib dead/dying stuff
135 if(other.deadflag != DEAD_NO)
136 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
141 else if (self.state == 3)
143 // when in other states, then the plat_crush event came delayed after
144 // plat state already had changed
145 // this isn't a bug per se!
153 objerror ("plat_use: not in up state");
157 .string sound1, sound2;
163 setorigin (self, self.pos1);
169 setorigin (self, self.pos2);
171 self.use = plat_trigger_use;
175 void spawnfunc_path_corner() { }
176 void spawnfunc_func_plat()
178 if (self.sounds == 0)
181 if(self.spawnflags & 4)
184 if(self.dmg && (!self.message))
185 self.message = "was squished";
186 if(self.dmg && (!self.message2))
187 self.message2 = "was squished by";
189 if (self.sounds == 1)
191 precache_sound ("plats/plat1.wav");
192 precache_sound ("plats/plat2.wav");
193 self.noise = "plats/plat1.wav";
194 self.noise1 = "plats/plat2.wav";
197 if (self.sounds == 2)
199 precache_sound ("plats/medplat1.wav");
200 precache_sound ("plats/medplat2.wav");
201 self.noise = "plats/medplat1.wav";
202 self.noise1 = "plats/medplat2.wav";
207 precache_sound (self.sound1);
208 self.noise = self.sound1;
212 precache_sound (self.sound2);
213 self.noise1 = self.sound2;
216 self.mangle = self.angles;
217 self.angles = '0 0 0';
219 self.classname = "plat";
220 if not(InitMovingBrushTrigger())
222 self.effects |= EF_LOWPRECISION;
223 setsize (self, self.mins , self.maxs);
225 self.blocked = plat_crush;
232 self.height = self.size_z - self.lip;
234 self.pos1 = self.origin;
235 self.pos2 = self.origin;
236 self.pos2_z = self.origin_z - self.height;
238 plat_spawn_inside_trigger (); // the "start moving" trigger
240 self.reset = plat_reset;
249 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
257 self.think = train_next;
258 self.nextthink = self.ltime + self.wait;
272 targ = find(world, targetname, self.target);
274 self.target = targ.target;
276 objerror("train_next: no next target");
277 self.wait = targ.wait;
282 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
284 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
287 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
290 void func_train_find()
293 targ = find(world, targetname, self.target);
294 self.target = targ.target;
296 objerror("func_train_find: no next target");
297 setorigin(self, targ.origin - self.mins);
298 self.nextthink = self.ltime + 1;
299 self.think = train_next;
302 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
303 Ridable platform, targets spawnfunc_path_corner path to follow.
304 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
305 target : targetname of first spawnfunc_path_corner (starts here)
307 void spawnfunc_func_train()
309 if (self.noise != "")
310 precache_sound(self.noise);
313 objerror("func_train without a target");
317 if not(InitMovingBrushTrigger())
319 self.effects |= EF_LOWPRECISION;
321 // wait for targets to spawn
322 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
324 self.blocked = generic_plat_blocked;
325 if(self.dmg & (!self.message))
326 self.message = " was squished";
327 if(self.dmg && (!self.message2))
328 self.message2 = "was squished by";
329 if(self.dmg && (!self.dmgtime))
331 self.dmgtime2 = time;
333 // TODO make a reset function for this one
336 void func_rotating_setactive(float astate)
339 if (astate == ACTIVE_TOGGLE)
341 if(self.active == ACTIVE_ACTIVE)
342 self.active = ACTIVE_NOT;
344 self.active = ACTIVE_ACTIVE;
347 self.active = astate;
349 if(self.active == ACTIVE_NOT)
350 self.avelocity = '0 0 0';
352 self.avelocity = self.pos1;
355 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
356 Brush model that spins in place on one axis (default Z).
357 speed : speed to rotate (in degrees per second)
358 noise : path/name of looping .wav file to play.
359 dmg : Do this mutch dmg every .dmgtime intervall when blocked
363 void spawnfunc_func_rotating()
365 if (self.noise != "")
367 precache_sound(self.noise);
368 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
371 self.active = ACTIVE_ACTIVE;
372 self.setactive = func_rotating_setactive;
376 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
377 if (self.spawnflags & 4) // X (untested)
378 self.avelocity = '0 0 1' * self.speed;
379 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
380 else if (self.spawnflags & 8) // Y (untested)
381 self.avelocity = '1 0 0' * self.speed;
382 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
384 self.avelocity = '0 1 0' * self.speed;
386 self.pos1 = self.avelocity;
388 if(self.dmg & (!self.message))
389 self.message = " was squished";
390 if(self.dmg && (!self.message2))
391 self.message2 = "was squished by";
394 if(self.dmg && (!self.dmgtime))
397 self.dmgtime2 = time;
399 if not(InitMovingBrushTrigger())
401 // no EF_LOWPRECISION here, as rounding angles is bad
403 self.blocked = generic_plat_blocked;
405 // wait for targets to spawn
406 self.nextthink = self.ltime + 999999999;
407 self.think = SUB_Null;
409 // TODO make a reset function for this one
413 void func_bobbing_controller_think()
416 self.nextthink = time + 0.1;
418 if not (self.owner.active == ACTIVE_ACTIVE)
420 self.owner.velocity = '0 0 0';
424 // calculate sinewave using makevectors
425 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
426 v = self.owner.destvec + self.owner.movedir * v_forward_y;
427 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
428 // * 10 so it will arrive in 0.1 sec
429 self.owner.velocity = (v - self.owner.origin) * 10;
432 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
433 Brush model that moves back and forth on one axis (default Z).
434 speed : how long one cycle takes in seconds (default 4)
435 height : how far the cycle moves (default 32)
436 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
437 noise : path/name of looping .wav file to play.
438 dmg : Do this mutch dmg every .dmgtime intervall when blocked
441 void spawnfunc_func_bobbing()
444 if (self.noise != "")
446 precache_sound(self.noise);
447 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
453 // center of bobbing motion
454 self.destvec = self.origin;
455 // time scale to get degrees
456 self.cnt = 360 / self.speed;
458 self.active = ACTIVE_ACTIVE;
460 // damage when blocked
461 self.blocked = generic_plat_blocked;
462 if(self.dmg & (!self.message))
463 self.message = " was squished";
464 if(self.dmg && (!self.message2))
465 self.message2 = "was squished by";
466 if(self.dmg && (!self.dmgtime))
468 self.dmgtime2 = time;
471 if (self.spawnflags & 1) // X
472 self.movedir = '1 0 0' * self.height;
473 else if (self.spawnflags & 2) // Y
474 self.movedir = '0 1 0' * self.height;
476 self.movedir = '0 0 1' * self.height;
478 if not(InitMovingBrushTrigger())
481 // wait for targets to spawn
482 controller = spawn();
483 controller.classname = "func_bobbing_controller";
484 controller.owner = self;
485 controller.nextthink = time + 1;
486 controller.think = func_bobbing_controller_think;
487 self.nextthink = self.ltime + 999999999;
488 self.think = SUB_Null;
490 // Savage: Reduce bandwith, critical on e.g. nexdm02
491 self.effects |= EF_LOWPRECISION;
493 // TODO make a reset function for this one
497 void func_pendulum_controller_think()
500 self.nextthink = time + 0.1;
502 if not (self.owner.active == ACTIVE_ACTIVE)
504 self.owner.avelocity_x = 0;
508 // calculate sinewave using makevectors
509 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
510 v = self.owner.speed * v_forward_y + self.cnt;
511 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
513 // * 10 so it will arrive in 0.1 sec
514 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
518 void spawnfunc_func_pendulum()
521 if (self.noise != "")
523 precache_sound(self.noise);
524 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
527 self.active = ACTIVE_ACTIVE;
529 // keys: angle, speed, phase, noise, freq
533 // not initializing self.dmg to 2, to allow damageless pendulum
535 if(self.dmg & (!self.message))
536 self.message = " was squished";
537 if(self.dmg && (!self.message2))
538 self.message2 = "was squished by";
539 if(self.dmg && (!self.dmgtime))
541 self.dmgtime2 = time;
543 self.blocked = generic_plat_blocked;
545 self.avelocity_z = 0.0000001;
546 if not(InitMovingBrushTrigger())
551 // find pendulum length (same formula as Q3A)
552 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
555 // copy initial angle
556 self.cnt = self.angles_z;
558 // wait for targets to spawn
559 controller = spawn();
560 controller.classname = "func_pendulum_controller";
561 controller.owner = self;
562 controller.nextthink = time + 1;
563 controller.think = func_pendulum_controller_think;
564 self.nextthink = self.ltime + 999999999;
565 self.think = SUB_Null;
567 //self.effects |= EF_LOWPRECISION;
569 // TODO make a reset function for this one
572 // button and multiple button
575 void() button_return;
579 self.state = STATE_TOP;
580 self.nextthink = self.ltime + self.wait;
581 self.think = button_return;
582 activator = self.enemy;
584 self.frame = 1; // use alternate textures
589 self.state = STATE_BOTTOM;
594 self.state = STATE_DOWN;
595 SUB_CalcMove (self.pos1, self.speed, button_done);
596 self.frame = 0; // use normal textures
598 self.takedamage = DAMAGE_YES; // can be shot again
602 void button_blocked()
604 // do nothing, just don't come all the way back out
610 self.health = self.max_health;
611 self.takedamage = DAMAGE_NO; // will be reset upon return
613 if (self.state == STATE_UP || self.state == STATE_TOP)
616 if (self.noise != "")
617 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
619 self.state = STATE_UP;
620 SUB_CalcMove (self.pos2, self.speed, button_wait);
625 self.health = self.max_health;
626 setorigin(self, self.pos1);
627 self.frame = 0; // use normal textures
628 self.state = STATE_BOTTOM;
630 self.takedamage = DAMAGE_YES; // can be shot again
635 // if (activator.classname != "player")
637 // dprint(activator.classname);
638 // dprint(" triggered a button\n");
641 if not (self.active == ACTIVE_ACTIVE)
644 self.enemy = activator;
650 // if (activator.classname != "player")
652 // dprint(activator.classname);
653 // dprint(" touched a button\n");
657 if not(other.iscreature)
659 if(other.velocity * self.movedir < 0)
663 self.enemy = other.owner;
667 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
669 if(self.spawnflags & DOOR_NOSPLASH)
670 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
672 self.health = self.health - damage;
673 if (self.health <= 0)
675 // if (activator.classname != "player")
677 // dprint(activator.classname);
678 // dprint(" killed a button\n");
680 self.enemy = damage_attacker;
686 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
687 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.
689 "angle" determines the opening direction
690 "target" all entities with a matching targetname will be used
691 "speed" override the default 40 speed
692 "wait" override the default 1 second wait (-1 = never return)
693 "lip" override the default 4 pixel lip remaining at end of move
694 "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
701 void spawnfunc_func_button()
705 if not(InitMovingBrushTrigger())
707 self.effects |= EF_LOWPRECISION;
709 self.blocked = button_blocked;
710 self.use = button_use;
712 // if (self.health == 0) // all buttons are now shootable
716 self.max_health = self.health;
717 self.event_damage = button_damage;
718 self.takedamage = DAMAGE_YES;
721 self.touch = button_touch;
731 precache_sound(self.noise);
733 self.active = ACTIVE_ACTIVE;
735 self.pos1 = self.origin;
736 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
737 self.flags |= FL_NOTARGET;
743 float DOOR_START_OPEN = 1;
744 float DOOR_DONT_LINK = 4;
745 float DOOR_TOGGLE = 32;
749 Doors are similar to buttons, but can spawn a fat trigger field around them
750 to open without a touch, and they link together to form simultanious
753 Door.owner is the master door. If there is only one door, it points to itself.
754 If multiple doors, all will point to a single one.
756 Door.enemy chains from the master door through all doors linked in the chain.
761 =============================================================================
765 =============================================================================
770 void() door_rotating_go_down;
771 void() door_rotating_go_up;
776 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
777 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
780 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
781 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
783 //Dont chamge direction for dead or dying stuff
784 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
787 if (self.state == STATE_DOWN)
788 if (self.classname == "door")
793 door_rotating_go_up ();
796 if (self.classname == "door")
801 door_rotating_go_down ();
805 //gib dying stuff just to make sure
806 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
807 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
811 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
812 // if a door has a negative wait, it would never come back if blocked,
813 // so let it just squash the object to death real fast
814 /* if (self.wait >= 0)
816 if (self.state == STATE_DOWN)
827 if (self.noise1 != "")
828 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
829 self.state = STATE_TOP;
830 if (self.spawnflags & DOOR_TOGGLE)
831 return; // don't come down automatically
832 if (self.classname == "door")
834 self.think = door_go_down;
837 self.think = door_rotating_go_down;
839 self.nextthink = self.ltime + self.wait;
842 void door_hit_bottom()
844 if (self.noise1 != "")
845 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
846 self.state = STATE_BOTTOM;
851 if (self.noise2 != "")
852 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
855 self.takedamage = DAMAGE_YES;
856 self.health = self.max_health;
859 self.state = STATE_DOWN;
860 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
865 if (self.state == STATE_UP)
866 return; // already going up
868 if (self.state == STATE_TOP)
869 { // reset top wait time
870 self.nextthink = self.ltime + self.wait;
874 if (self.noise2 != "")
875 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
876 self.state = STATE_UP;
877 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
880 oldmessage = self.message;
883 self.message = oldmessage;
889 =============================================================================
893 =============================================================================
896 float door_check_keys(void) {
906 if not(door.itemkeys)
909 // this door require a key
910 // only a player can have a key
911 if (other.classname != "player")
914 if (item_keys_usekey(door, other)) {
915 // some keys were used
916 if (other.key_door_messagetime <= time) {
917 play2(other, "misc/talk.wav");
918 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
919 other.key_door_messagetime = time + 2;
923 if (other.key_door_messagetime <= time) {
924 play2(other, "misc/talk.wav");
925 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
926 other.key_door_messagetime = time + 2;
931 // door is now unlocked
932 play2(other, "misc/talk.wav");
933 centerprint(other, "Door unlocked!");
945 if (self.owner != self)
946 objerror ("door_fire: self.owner != self");
950 if (self.spawnflags & DOOR_TOGGLE)
952 if (self.state == STATE_UP || self.state == STATE_TOP)
957 if (self.classname == "door")
963 door_rotating_go_down ();
966 } while ( (self != starte) && (self != world) );
972 // trigger all paired doors
976 if (self.classname == "door")
981 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
982 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
984 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
985 self.pos2 = '0 0 0' - self.pos2;
987 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
988 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
989 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
991 door_rotating_go_up ();
995 } while ( (self != starte) && (self != world) );
1004 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1016 void door_trigger_touch()
1018 if (other.health < 1)
1019 if not(other.iscreature && other.deadflag == DEAD_NO)
1022 if (time < self.attack_finished_single)
1025 // check if door is locked
1026 if (!door_check_keys())
1029 self.attack_finished_single = time + 1;
1038 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1041 if(self.spawnflags & DOOR_NOSPLASH)
1042 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1044 self.health = self.health - damage;
1046 if (self.itemkeys) {
1047 // don't allow opening doors through damage if keys are required
1051 if (self.health <= 0)
1055 self.health = self.max_health;
1056 self.takedamage = DAMAGE_NO; // wil be reset upon return
1072 if(other.classname != "player")
1074 if (self.owner.attack_finished_single > time)
1077 self.owner.attack_finished_single = time + 2;
1079 if (!(self.owner.dmg) && (self.owner.message != ""))
1081 if (other.flags & FL_CLIENT)
1082 centerprint (other, self.owner.message);
1083 play2(other, "misc/talk.wav");
1088 void door_generic_plat_blocked()
1091 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1092 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1095 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1096 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1098 //Dont chamge direction for dead or dying stuff
1099 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1102 if (self.state == STATE_DOWN)
1103 door_rotating_go_up ();
1105 door_rotating_go_down ();
1108 //gib dying stuff just to make sure
1109 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1110 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1114 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1115 // if a door has a negative wait, it would never come back if blocked,
1116 // so let it just squash the object to death real fast
1117 /* if (self.wait >= 0)
1119 if (self.state == STATE_DOWN)
1120 door_rotating_go_up ();
1122 door_rotating_go_down ();
1128 void door_rotating_hit_top()
1130 if (self.noise1 != "")
1131 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1132 self.state = STATE_TOP;
1133 if (self.spawnflags & DOOR_TOGGLE)
1134 return; // don't come down automatically
1135 self.think = door_rotating_go_down;
1136 self.nextthink = self.ltime + self.wait;
1139 void door_rotating_hit_bottom()
1141 if (self.noise1 != "")
1142 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1143 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1145 self.pos2 = '0 0 0' - self.pos2;
1148 self.state = STATE_BOTTOM;
1151 void door_rotating_go_down()
1153 if (self.noise2 != "")
1154 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1155 if (self.max_health)
1157 self.takedamage = DAMAGE_YES;
1158 self.health = self.max_health;
1161 self.state = STATE_DOWN;
1162 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1165 void door_rotating_go_up()
1167 if (self.state == STATE_UP)
1168 return; // already going up
1170 if (self.state == STATE_TOP)
1171 { // reset top wait time
1172 self.nextthink = self.ltime + self.wait;
1175 if (self.noise2 != "")
1176 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1177 self.state = STATE_UP;
1178 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1181 oldmessage = self.message;
1184 self.message = oldmessage;
1191 =============================================================================
1195 =============================================================================
1199 entity spawn_field(vector fmins, vector fmaxs)
1205 trigger.classname = "doortriggerfield";
1206 trigger.movetype = MOVETYPE_NONE;
1207 trigger.solid = SOLID_TRIGGER;
1208 trigger.owner = self;
1209 trigger.touch = door_trigger_touch;
1213 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1218 float EntitiesTouching(entity e1, entity e2)
1220 if (e1.absmin_x > e2.absmax_x)
1222 if (e1.absmin_y > e2.absmax_y)
1224 if (e1.absmin_z > e2.absmax_z)
1226 if (e1.absmax_x < e2.absmin_x)
1228 if (e1.absmax_y < e2.absmin_y)
1230 if (e1.absmax_z < e2.absmin_z)
1246 vector cmins, cmaxs;
1249 return; // already linked by another door
1250 if (self.spawnflags & 4)
1252 self.owner = self.enemy = self;
1260 self.trigger_field = spawn_field(self.absmin, self.absmax);
1262 return; // don't want to link this door
1265 cmins = self.absmin;
1266 cmaxs = self.absmax;
1273 self.owner = starte; // master door
1276 starte.health = self.health;
1278 starte.targetname = self.targetname;
1279 if (self.message != "")
1280 starte.message = self.message;
1282 t = find(t, classname, self.classname);
1285 self.enemy = starte; // make the chain a loop
1287 // shootable, or triggered doors just needed the owner/enemy links,
1288 // they don't spawn a field
1299 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1304 if (EntitiesTouching(self,t))
1307 objerror ("cross connected doors");
1312 if (t.absmin_x < cmins_x)
1313 cmins_x = t.absmin_x;
1314 if (t.absmin_y < cmins_y)
1315 cmins_y = t.absmin_y;
1316 if (t.absmin_z < cmins_z)
1317 cmins_z = t.absmin_z;
1318 if (t.absmax_x > cmaxs_x)
1319 cmaxs_x = t.absmax_x;
1320 if (t.absmax_y > cmaxs_y)
1321 cmaxs_y = t.absmax_y;
1322 if (t.absmax_z > cmaxs_z)
1323 cmaxs_z = t.absmax_z;
1330 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1331 if two doors touch, they are assumed to be connected and operate as a unit.
1333 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1335 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).
1337 GOLD_KEY causes the door to open only if the activator holds a gold key.
1339 SILVER_KEY causes the door to open only if the activator holds a silver key.
1341 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1342 "angle" determines the opening direction
1343 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1344 "health" if set, door must be shot open
1345 "speed" movement speed (100 default)
1346 "wait" wait before returning (3 default, -1 = never return)
1347 "lip" lip remaining at end of move (8 default)
1348 "dmg" damage to inflict when blocked (2 default)
1355 FIXME: only one sound set available at the time being
1359 void door_init_startopen()
1361 setorigin (self, self.pos2);
1362 self.pos2 = self.pos1;
1363 self.pos1 = self.origin;
1368 setorigin(self, self.pos1);
1369 self.velocity = '0 0 0';
1370 self.state = STATE_BOTTOM;
1371 self.think = SUB_Null;
1374 // spawnflags require key (for now only func_door)
1375 #define SPAWNFLAGS_GOLD_KEY 8
1376 #define SPAWNFLAGS_SILVER_KEY 16
1377 void spawnfunc_func_door()
1379 // Quake 1 keys compatibility
1380 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1381 self.itemkeys |= ITEM_KEY_BIT(0);
1382 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1383 self.itemkeys |= ITEM_KEY_BIT(1);
1385 //if (!self.deathtype) // map makers can override this
1386 // self.deathtype = " got in the way";
1389 self.max_health = self.health;
1390 if not(InitMovingBrushTrigger())
1392 self.effects |= EF_LOWPRECISION;
1393 self.classname = "door";
1395 self.blocked = door_blocked;
1396 self.use = door_use;
1398 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1399 // if(self.spawnflags & 8)
1400 // self.dmg = 10000;
1402 if(self.dmg && (!self.message))
1403 self.message = "was squished";
1404 if(self.dmg && (!self.message2))
1405 self.message2 = "was squished by";
1407 if (self.sounds > 0)
1409 precache_sound ("plats/medplat1.wav");
1410 precache_sound ("plats/medplat2.wav");
1411 self.noise2 = "plats/medplat1.wav";
1412 self.noise1 = "plats/medplat2.wav";
1422 self.pos1 = self.origin;
1423 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1425 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1426 // but spawn in the open position
1427 if (self.spawnflags & DOOR_START_OPEN)
1428 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1430 self.state = STATE_BOTTOM;
1434 self.takedamage = DAMAGE_YES;
1435 self.event_damage = door_damage;
1441 self.touch = door_touch;
1443 // LinkDoors can't be done until all of the doors have been spawned, so
1444 // the sizes can be detected properly.
1445 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1447 self.reset = door_reset;
1450 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1451 if two doors touch, they are assumed to be connected and operate as a unit.
1453 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1455 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1456 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1457 must have set trigger_reverse to 1.
1458 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1460 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).
1462 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1463 "angle" determines the destination angle for opening. negative values reverse the direction.
1464 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1465 "health" if set, door must be shot open
1466 "speed" movement speed (100 default)
1467 "wait" wait before returning (3 default, -1 = never return)
1468 "dmg" damage to inflict when blocked (2 default)
1475 FIXME: only one sound set available at the time being
1478 void door_rotating_reset()
1480 self.angles = self.pos1;
1481 self.avelocity = '0 0 0';
1482 self.state = STATE_BOTTOM;
1483 self.think = SUB_Null;
1486 void door_rotating_init_startopen()
1488 self.angles = self.movedir;
1489 self.pos2 = '0 0 0';
1490 self.pos1 = self.movedir;
1494 void spawnfunc_func_door_rotating()
1497 //if (!self.deathtype) // map makers can override this
1498 // self.deathtype = " got in the way";
1500 // I abuse "movedir" for denoting the axis for now
1501 if (self.spawnflags & 64) // X (untested)
1502 self.movedir = '0 0 1';
1503 else if (self.spawnflags & 128) // Y (untested)
1504 self.movedir = '1 0 0';
1506 self.movedir = '0 1 0';
1508 if (self.angles_y==0) self.angles_y = 90;
1510 self.movedir = self.movedir * self.angles_y;
1511 self.angles = '0 0 0';
1513 self.max_health = self.health;
1514 self.avelocity = self.movedir;
1515 if not(InitMovingBrushTrigger())
1517 self.velocity = '0 0 0';
1518 //self.effects |= EF_LOWPRECISION;
1519 self.classname = "door_rotating";
1521 self.blocked = door_blocked;
1522 self.use = door_use;
1524 if(self.spawnflags & 8)
1527 if(self.dmg && (!self.message))
1528 self.message = "was squished";
1529 if(self.dmg && (!self.message2))
1530 self.message2 = "was squished by";
1532 if (self.sounds > 0)
1534 precache_sound ("plats/medplat1.wav");
1535 precache_sound ("plats/medplat2.wav");
1536 self.noise2 = "plats/medplat1.wav";
1537 self.noise1 = "plats/medplat2.wav";
1544 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1546 self.pos1 = '0 0 0';
1547 self.pos2 = self.movedir;
1549 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1550 // but spawn in the open position
1551 if (self.spawnflags & DOOR_START_OPEN)
1552 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1554 self.state = STATE_BOTTOM;
1558 self.takedamage = DAMAGE_YES;
1559 self.event_damage = door_damage;
1565 self.touch = door_touch;
1567 // LinkDoors can't be done until all of the doors have been spawned, so
1568 // the sizes can be detected properly.
1569 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1571 self.reset = door_rotating_reset;
1575 =============================================================================
1579 =============================================================================
1582 void() fd_secret_move1;
1583 void() fd_secret_move2;
1584 void() fd_secret_move3;
1585 void() fd_secret_move4;
1586 void() fd_secret_move5;
1587 void() fd_secret_move6;
1588 void() fd_secret_done;
1590 float SECRET_OPEN_ONCE = 1; // stays open
1591 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1592 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1593 float SECRET_NO_SHOOT = 8; // only opened by trigger
1594 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1597 void fd_secret_use()
1600 string message_save;
1602 self.health = 10000;
1603 self.bot_attack = TRUE;
1605 // exit if still moving around...
1606 if (self.origin != self.oldorigin)
1609 message_save = self.message;
1610 self.message = ""; // no more message
1611 SUB_UseTargets(); // fire all targets / killtargets
1612 self.message = message_save;
1614 self.velocity = '0 0 0';
1616 // Make a sound, wait a little...
1618 if (self.noise1 != "")
1619 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1620 self.nextthink = self.ltime + 0.1;
1622 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1623 makevectors(self.mangle);
1627 if (self.spawnflags & SECRET_1ST_DOWN)
1628 self.t_width = fabs(v_up * self.size);
1630 self.t_width = fabs(v_right * self.size);
1634 self.t_length = fabs(v_forward * self.size);
1636 if (self.spawnflags & SECRET_1ST_DOWN)
1637 self.dest1 = self.origin - v_up * self.t_width;
1639 self.dest1 = self.origin + v_right * (self.t_width * temp);
1641 self.dest2 = self.dest1 + v_forward * self.t_length;
1642 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1643 if (self.noise2 != "")
1644 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1647 // Wait after first movement...
1648 void fd_secret_move1()
1650 self.nextthink = self.ltime + 1.0;
1651 self.think = fd_secret_move2;
1652 if (self.noise3 != "")
1653 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1656 // Start moving sideways w/sound...
1657 void fd_secret_move2()
1659 if (self.noise2 != "")
1660 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1661 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1664 // Wait here until time to go back...
1665 void fd_secret_move3()
1667 if (self.noise3 != "")
1668 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1669 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1671 self.nextthink = self.ltime + self.wait;
1672 self.think = fd_secret_move4;
1677 void fd_secret_move4()
1679 if (self.noise2 != "")
1680 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1681 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1685 void fd_secret_move5()
1687 self.nextthink = self.ltime + 1.0;
1688 self.think = fd_secret_move6;
1689 if (self.noise3 != "")
1690 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1693 void fd_secret_move6()
1695 if (self.noise2 != "")
1696 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1697 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1700 void fd_secret_done()
1702 if (self.spawnflags&SECRET_YES_SHOOT)
1704 self.health = 10000;
1705 self.takedamage = DAMAGE_YES;
1706 //self.th_pain = fd_secret_use;
1708 if (self.noise3 != "")
1709 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1712 void secret_blocked()
1714 if (time < self.attack_finished_single)
1716 self.attack_finished_single = time + 0.5;
1717 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1729 if not(other.iscreature)
1731 if (self.attack_finished_single > time)
1734 self.attack_finished_single = time + 2;
1738 if (other.flags & FL_CLIENT)
1739 centerprint (other, self.message);
1740 play2(other, "misc/talk.wav");
1746 if (self.spawnflags&SECRET_YES_SHOOT)
1748 self.health = 10000;
1749 self.takedamage = DAMAGE_YES;
1751 setorigin(self, self.oldorigin);
1752 self.think = SUB_Null;
1755 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1756 Basic secret door. Slides back, then to the side. Angle determines direction.
1757 wait = # of seconds before coming back
1758 1st_left = 1st move is left of arrow
1759 1st_down = 1st move is down from arrow
1760 always_shoot = even if targeted, keep shootable
1761 t_width = override WIDTH to move back (or height if going down)
1762 t_length = override LENGTH to move sideways
1763 "dmg" damage to inflict when blocked (2 default)
1765 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1772 void spawnfunc_func_door_secret()
1774 /*if (!self.deathtype) // map makers can override this
1775 self.deathtype = " got in the way";*/
1781 self.mangle = self.angles;
1782 self.angles = '0 0 0';
1783 self.classname = "door";
1784 if not(InitMovingBrushTrigger())
1786 self.effects |= EF_LOWPRECISION;
1788 self.touch = secret_touch;
1789 self.blocked = secret_blocked;
1791 self.use = fd_secret_use;
1796 self.spawnflags |= SECRET_YES_SHOOT;
1798 if(self.spawnflags&SECRET_YES_SHOOT)
1800 self.health = 10000;
1801 self.takedamage = DAMAGE_YES;
1802 self.event_damage = fd_secret_use;
1804 self.oldorigin = self.origin;
1806 self.wait = 5; // 5 seconds before closing
1808 self.reset = secret_reset;
1812 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1813 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1814 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
1815 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1816 height: amplitude modifier (default 32)
1817 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1818 noise: path/name of looping .wav file to play.
1819 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1823 void func_fourier_controller_think()
1828 self.nextthink = time + 0.1;
1829 if not (self.owner.active == ACTIVE_ACTIVE)
1831 self.owner.velocity = '0 0 0';
1836 n = floor((tokenize_console(self.owner.netname)) / 5);
1837 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1839 v = self.owner.destvec;
1841 for(i = 0; i < n; ++i)
1843 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1844 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;
1847 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1848 // * 10 so it will arrive in 0.1 sec
1849 self.owner.velocity = (v - self.owner.origin) * 10;
1852 void spawnfunc_func_fourier()
1855 if (self.noise != "")
1857 precache_sound(self.noise);
1858 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1865 self.destvec = self.origin;
1866 self.cnt = 360 / self.speed;
1868 self.blocked = generic_plat_blocked;
1869 if(self.dmg & (!self.message))
1870 self.message = " was squished";
1871 if(self.dmg && (!self.message2))
1872 self.message2 = "was squished by";
1873 if(self.dmg && (!self.dmgtime))
1874 self.dmgtime = 0.25;
1875 self.dmgtime2 = time;
1877 if(self.netname == "")
1878 self.netname = "1 0 0 0 1";
1880 if not(InitMovingBrushTrigger())
1883 self.active = ACTIVE_ACTIVE;
1885 // wait for targets to spawn
1886 controller = spawn();
1887 controller.classname = "func_fourier_controller";
1888 controller.owner = self;
1889 controller.nextthink = time + 1;
1890 controller.think = func_fourier_controller_think;
1891 self.nextthink = self.ltime + 999999999;
1892 self.think = SUB_Null;
1894 // Savage: Reduce bandwith, critical on e.g. nexdm02
1895 self.effects |= EF_LOWPRECISION;
1897 // TODO make a reset function for this one
1900 // reusing some fields havocbots declared
1901 .entity wp00, wp01, wp02, wp03;
1903 .float targetfactor, target2factor, target3factor, target4factor;
1904 .vector targetnormal, target2normal, target3normal, target4normal;
1906 vector func_vectormamamam_origin(entity o, float t)
1918 p = e.origin + t * e.velocity;
1920 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1922 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1928 p = e.origin + t * e.velocity;
1930 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1932 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1938 p = e.origin + t * e.velocity;
1940 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1942 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1948 p = e.origin + t * e.velocity;
1950 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1952 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1958 void func_vectormamamam_controller_think()
1960 self.nextthink = time + 0.1;
1962 if not (self.owner.active == ACTIVE_ACTIVE)
1964 self.owner.velocity = '0 0 0';
1968 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1969 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1972 void func_vectormamamam_findtarget()
1974 if(self.target != "")
1975 self.wp00 = find(world, targetname, self.target);
1977 if(self.target2 != "")
1978 self.wp01 = find(world, targetname, self.target2);
1980 if(self.target3 != "")
1981 self.wp02 = find(world, targetname, self.target3);
1983 if(self.target4 != "")
1984 self.wp03 = find(world, targetname, self.target4);
1986 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1987 objerror("No reference entity found, so there is nothing to move. Aborting.");
1989 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
1992 controller = spawn();
1993 controller.classname = "func_vectormamamam_controller";
1994 controller.owner = self;
1995 controller.nextthink = time + 1;
1996 controller.think = func_vectormamamam_controller_think;
1999 void spawnfunc_func_vectormamamam()
2001 if (self.noise != "")
2003 precache_sound(self.noise);
2004 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2007 if(!self.targetfactor)
2008 self.targetfactor = 1;
2010 if(!self.target2factor)
2011 self.target2factor = 1;
2013 if(!self.target3factor)
2014 self.target3factor = 1;
2016 if(!self.target4factor)
2017 self.target4factor = 1;
2019 if(vlen(self.targetnormal))
2020 self.targetnormal = normalize(self.targetnormal);
2022 if(vlen(self.target2normal))
2023 self.target2normal = normalize(self.target2normal);
2025 if(vlen(self.target3normal))
2026 self.target3normal = normalize(self.target3normal);
2028 if(vlen(self.target4normal))
2029 self.target4normal = normalize(self.target4normal);
2031 self.blocked = generic_plat_blocked;
2032 if(self.dmg & (!self.message))
2033 self.message = " was squished";
2034 if(self.dmg && (!self.message2))
2035 self.message2 = "was squished by";
2036 if(self.dmg && (!self.dmgtime))
2037 self.dmgtime = 0.25;
2038 self.dmgtime2 = time;
2040 if(self.netname == "")
2041 self.netname = "1 0 0 0 1";
2043 if not(InitMovingBrushTrigger())
2046 // wait for targets to spawn
2047 self.nextthink = self.ltime + 999999999;
2048 self.think = SUB_Null;
2050 // Savage: Reduce bandwith, critical on e.g. nexdm02
2051 self.effects |= EF_LOWPRECISION;
2053 self.active = ACTIVE_ACTIVE;
2055 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2058 void conveyor_think()
2062 // set myself as current conveyor where possible
2063 for(e = world; (e = findentity(e, conveyor, self)); )
2068 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5); e; e = e.chain)
2069 if(!e.conveyor.state)
2070 if(e.movetype != MOVETYPE_NONE)
2072 vector emin = e.absmin;
2073 vector emax = e.absmax;
2074 if(self.solid == SOLID_BSP)
2079 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2080 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2084 for(e = world; (e = findentity(e, conveyor, self)); )
2086 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2087 continue; // done in SV_PlayerPhysics
2089 // stupid conveyor code
2090 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2091 if(trace_fraction > 0)
2092 setorigin(e, trace_endpos);
2096 self.nextthink = time;
2101 self.state = !self.state;
2104 void conveyor_reset()
2106 self.state = (self.spawnflags & 1);
2109 void conveyor_init()
2113 self.movedir = self.movedir * self.speed;
2114 self.think = conveyor_think;
2115 self.nextthink = time;
2118 self.use = conveyor_use;
2119 self.reset = conveyor_reset;
2126 void spawnfunc_trigger_conveyor()
2133 void spawnfunc_func_conveyor()
2136 InitMovingBrushTrigger();
2137 self.movetype = MOVETYPE_NONE;