6 activator = self.enemy;
12 ==============================
15 the global "activator" should be set to the entity that initiated the firing.
17 If self.delay is set, a DelayedUse entity will be created that will actually
18 do the SUB_UseTargets after that many seconds have passed.
20 Centerprints any self.message to the activator.
22 Removes all entities with a targetname that match self.killtarget,
23 and removes them, so some events can remove other triggers.
25 Search for (string)targetname in all entities that
26 match (string)self.target and call their .use function
28 ==============================
32 local entity t, stemp, otemp, act;
39 // create a temp object to fire at a later time
41 t.classname = "DelayedUse";
42 t.nextthink = time + self.delay;
45 t.message = self.message;
46 t.killtarget = self.killtarget;
47 t.target = self.target;
55 if (activator.classname == "player" && self.message != "")
57 centerprint (activator, self.message);
59 play2(activator, "misc/talk.wav");
63 // kill the killtagets
70 t = find (t, targetname, self.killtarget);
86 t = find (t, targetname, self.target);
107 //=============================================================================
109 float SPAWNFLAG_NOMESSAGE = 1;
110 float SPAWNFLAG_NOTOUCH = 1;
112 // the wait time has passed, so set back up for another activation
117 self.health = self.max_health;
118 self.takedamage = DAMAGE_YES;
119 self.solid = SOLID_BBOX;
124 // the trigger was just touched/killed/used
125 // self.enemy should be set to the activator so it can be held through a delay
126 // so wait for the delay time before firing
129 if (self.nextthink > time)
131 return; // allready been triggered
134 if (self.classname == "trigger_secret")
136 if (self.enemy.classname != "player")
138 found_secrets = found_secrets + 1;
139 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
143 sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
145 // don't trigger again until reset
146 self.takedamage = DAMAGE_NO;
148 activator = self.enemy;
154 self.think = multi_wait;
155 self.nextthink = time + self.wait;
158 { // we can't just remove (self) here, because this is a touch function
159 // called wheil C code is looping through area links...
160 self.touch = SUB_Null;
162 self.nextthink = time + 0.1;
163 self.think = SUB_Remove;
169 self.enemy = activator;
175 if not(self.spawnflags & 2)
177 if (other.classname != "player")
181 if(self.team == other.team)
185 // if the trigger has an angles field, check player's facing direction
186 if (self.movedir != '0 0 0')
188 makevectors (other.angles);
189 if (v_forward * self.movedir < 0)
190 return; // not facing the right way
199 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
201 if (!self.takedamage)
203 self.health = self.health - damage;
204 if (self.health <= 0)
206 self.enemy = attacker;
211 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
212 Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time.
213 If "delay" is set, the trigger waits some time after activating before firing.
214 "wait" : Seconds between triggerings. (.2 default)
215 If notouch is set, the trigger is only fired by other entities, not by touching.
216 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
222 set "message" to text string
224 void spawnfunc_trigger_multiple()
226 if (self.sounds == 1)
228 precache_sound ("misc/secret.wav");
229 self.noise = "misc/secret.wav";
231 else if (self.sounds == 2)
233 precache_sound ("misc/talk.wav");
234 self.noise = "misc/talk.wav";
236 else if (self.sounds == 3)
238 precache_sound ("misc/trigger1.wav");
239 self.noise = "misc/trigger1.wav";
244 self.use = multi_use;
250 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
251 objerror ("health and notouch don't make sense\n");
252 self.max_health = self.health;
253 self.event_damage = multi_eventdamage;
254 self.takedamage = DAMAGE_YES;
255 self.solid = SOLID_BBOX;
256 setorigin (self, self.origin); // make sure it links into the world
260 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
262 self.touch = multi_touch;
263 setorigin (self, self.origin); // make sure it links into the world
269 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
270 Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
271 "targetname". If "health" is set, the trigger must be killed to activate.
272 If notouch is set, the trigger is only fired by other entities, not by touching.
273 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
274 if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
280 set "message" to text string
282 void spawnfunc_trigger_once()
285 spawnfunc_trigger_multiple();
288 //=============================================================================
290 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
291 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
293 void spawnfunc_trigger_relay()
295 self.use = SUB_UseTargets;
300 self.think = SUB_UseTargets;
301 self.nextthink = self.wait;
304 void spawnfunc_trigger_delay()
309 self.use = delay_use;
312 //=============================================================================
317 self.count = self.count - 1;
323 if (activator.classname == "player"
324 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
327 centerprint (activator, "There are more to go...");
328 else if (self.count == 3)
329 centerprint (activator, "Only 3 more to go...");
330 else if (self.count == 2)
331 centerprint (activator, "Only 2 more to go...");
333 centerprint (activator, "Only 1 more to go...");
338 if (activator.classname == "player"
339 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
340 centerprint(activator, "Sequence completed!");
341 self.enemy = activator;
345 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
346 Acts as an intermediary for an action that takes multiple inputs.
348 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
350 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
352 void spawnfunc_trigger_counter()
358 self.use = counter_use;
361 .float triggerhurttime;
362 void trigger_hurt_touch()
364 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
367 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
370 other.pain_finished = min(other.pain_finished, time + 2);
372 else if (other.classname == "rune") // reset runes
375 other.nextthink = min(other.nextthink, time + 1);
379 if (other.takedamage)
380 if (other.triggerhurttime < time)
383 other.triggerhurttime = time + 1;
384 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
390 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
391 Any object touching this will be hurt
392 set dmg to damage amount
395 .entity trigger_hurt_next;
396 entity trigger_hurt_last;
397 entity trigger_hurt_first;
398 void spawnfunc_trigger_hurt()
401 self.touch = trigger_hurt_touch;
405 self.message = "was in the wrong place.";
407 if(!trigger_hurt_first)
408 trigger_hurt_first = self;
409 if(trigger_hurt_last)
410 trigger_hurt_last.trigger_hurt_next = self;
411 trigger_hurt_last = self;
414 float trace_hits_box_a0, trace_hits_box_a1;
416 float trace_hits_box_1d(float end, float thmi, float thma)
420 // just check if x is in range
428 // do the trace with respect to x
429 // 0 -> end has to stay in thmi -> thma
430 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
431 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
432 if(trace_hits_box_a0 > trace_hits_box_a1)
438 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
443 // now it is a trace from 0 to end
445 trace_hits_box_a0 = 0;
446 trace_hits_box_a1 = 1;
448 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
450 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
452 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
458 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
460 return trace_hits_box(start, end, thmi - ma, thma - mi);
463 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
467 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
468 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
475 // TODO add a way to do looped sounds with sound(); then complete this entity
476 .float volume, atten;
477 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
479 void spawnfunc_target_speaker()
482 precache_sound (self.noise);
486 self.atten = ATTN_NORM;
487 else if(self.atten < 0)
491 self.use = target_speaker_use;
496 self.atten = ATTN_STATIC;
497 else if(self.atten < 0)
501 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
506 void spawnfunc_func_stardust() {
507 self.effects = EF_STARDUST;
510 float pointparticles_SendEntity(entity to, float fl)
512 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
513 WriteByte(MSG_ENTITY, fl);
517 WriteCoord(MSG_ENTITY, self.impulse);
519 WriteCoord(MSG_ENTITY, 0); // off
523 WriteCoord(MSG_ENTITY, self.origin_x);
524 WriteCoord(MSG_ENTITY, self.origin_y);
525 WriteCoord(MSG_ENTITY, self.origin_z);
529 if(self.modelindex != 4.2)
531 WriteShort(MSG_ENTITY, self.modelindex);
532 WriteCoord(MSG_ENTITY, self.mins_x);
533 WriteCoord(MSG_ENTITY, self.mins_y);
534 WriteCoord(MSG_ENTITY, self.mins_z);
535 WriteCoord(MSG_ENTITY, self.maxs_x);
536 WriteCoord(MSG_ENTITY, self.maxs_y);
537 WriteCoord(MSG_ENTITY, self.maxs_z);
541 WriteShort(MSG_ENTITY, 0);
542 WriteCoord(MSG_ENTITY, self.maxs_x);
543 WriteCoord(MSG_ENTITY, self.maxs_y);
544 WriteCoord(MSG_ENTITY, self.maxs_z);
546 WriteShort(MSG_ENTITY, self.cnt);
547 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
548 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
549 WriteCoord(MSG_ENTITY, self.waterlevel);
550 WriteCoord(MSG_ENTITY, self.count);
551 WriteByte(MSG_ENTITY, self.glow_color);
552 WriteString(MSG_ENTITY, self.noise);
557 void pointparticles_use()
559 self.state = !self.state;
563 void pointparticles_think()
565 if(self.origin != self.oldorigin)
568 self.oldorigin = self.origin;
570 self.nextthink = time;
573 void spawnfunc_func_pointparticles()
576 setmodel(self, self.model);
578 precache_sound (self.noise);
580 self.effects = EF_NODEPTHTEST;
581 self.SendEntity = pointparticles_SendEntity;
585 self.modelindex = 4.2;
586 self.origin += self.mins;
587 self.maxs -= self.mins;
589 self.model = "net_entity";
591 self.cnt = particleeffectnum(self.mdl);
594 self.use = pointparticles_use;
595 if(self.spawnflags & 1)
602 self.think = pointparticles_think;
603 self.nextthink = time;
606 void spawnfunc_func_sparks()
608 // self.cnt is the amount of sparks that one burst will spawn
610 self.cnt = 25.0; // nice default value
613 // self.wait is the probability that a sparkthink will spawn a spark shower
614 // range: 0 - 1, but 0 makes little sense, so...
615 if(self.wait < 0.05) {
616 self.wait = 0.25; // nice default value
619 self.count = self.cnt;
622 self.velocity = '0 0 -1';
623 self.mdl = "TE_SPARK";
624 self.impulse = 10 * self.wait; // by default 2.5/sec
626 self.cnt = 0; // use mdl
628 spawnfunc_func_pointparticles();
631 float rainsnow_SendEntity(float to)
633 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
634 WriteByte(MSG_ENTITY, self.state);
635 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
636 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
637 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
638 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
639 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
640 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
641 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
642 WriteShort(MSG_ENTITY, self.count);
643 WriteByte(MSG_ENTITY, self.cnt);
647 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
648 This is an invisible area like a trigger, which rain falls inside of.
652 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
654 sets color of rain (default 12 - white)
656 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
658 void spawnfunc_func_rain()
660 self.dest = self.velocity;
661 self.velocity = '0 0 0';
663 self.dest = '0 0 -700';
664 self.angles = '0 0 0';
665 self.movetype = MOVETYPE_NONE;
666 self.solid = SOLID_NOT;
667 SetBrushEntityModel();
673 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
676 if(self.count > 65535)
679 self.state = 1; // 1 is rain, 0 is snow
680 self.effects = EF_NODEPTHTEST;
681 self.SendEntity = rainsnow_SendEntity;
684 self.model = "net_entity";
688 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
689 This is an invisible area like a trigger, which snow falls inside of.
693 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
695 sets color of rain (default 12 - white)
697 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
699 void spawnfunc_func_snow()
701 self.dest = self.velocity;
702 self.velocity = '0 0 0';
704 self.dest = '0 0 -300';
705 self.angles = '0 0 0';
706 self.movetype = MOVETYPE_NONE;
707 self.solid = SOLID_NOT;
708 SetBrushEntityModel();
714 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
717 if(self.count > 65535)
720 self.state = 0; // 1 is rain, 0 is snow
721 self.effects = EF_NODEPTHTEST;
722 self.SendEntity = rainsnow_SendEntity;
725 self.model = "net_entity";
729 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
731 void misc_laser_aim()
736 if(self.spawnflags & 2)
738 if(self.enemy.origin != self.mangle)
740 self.mangle = self.enemy.origin;
746 a = vectoangles(self.enemy.origin - self.origin);
757 if(self.angles != self.mangle)
759 self.mangle = self.angles;
763 if(self.origin != self.oldorigin)
766 self.oldorigin = self.origin;
770 void misc_laser_init()
772 if(self.target != "")
773 self.enemy = find(world, targetname, self.target);
777 void misc_laser_think()
782 self.nextthink = time;
791 o = self.enemy.origin;
792 if not(self.spawnflags & 2)
793 o = self.origin + normalize(o - self.origin) * 32768;
797 makevectors(self.mangle);
798 o = self.origin + v_forward * 32768;
804 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
806 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
809 if(self.enemy.target != "") // DETECTOR laser
811 traceline(self.origin, o, MOVE_NORMAL, self);
812 if(trace_ent.classname == "player")
814 self.pusher = trace_ent;
821 activator = self.pusher;
834 activator = self.pusher;
842 float laser_SendEntity(entity to, float fl)
844 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
845 fl = fl - (fl & 0xC0); // use that bit to indicate finite length laser
846 if(self.spawnflags & 2)
850 WriteByte(MSG_ENTITY, fl);
853 WriteCoord(MSG_ENTITY, self.origin_x);
854 WriteCoord(MSG_ENTITY, self.origin_y);
855 WriteCoord(MSG_ENTITY, self.origin_z);
859 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
860 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
861 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
863 WriteByte(MSG_ENTITY, self.alpha * 255.0);
864 WriteShort(MSG_ENTITY, self.cnt + 1);
870 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
871 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
872 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
876 WriteCoord(MSG_ENTITY, self.mangle_x);
877 WriteCoord(MSG_ENTITY, self.mangle_y);
881 WriteByte(MSG_ENTITY, self.state);
885 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
886 Any object touching the beam will be hurt
889 spawnfunc_target_position where the laser ends
891 name of beam end effect to use
893 color of the beam (default: red)
895 damage per second (-1 for a laser that kills immediately)
899 self.state = !self.state;
904 void spawnfunc_misc_laser()
908 if(self.mdl == "none")
911 self.cnt = particleeffectnum(self.mdl);
916 self.cnt = particleeffectnum("laser_deadly");
921 if(self.colormod == '0 0 0')
923 self.colormod = '1 0 0';
925 self.message = "saw the light";
926 self.think = misc_laser_think;
927 self.nextthink = time;
928 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
930 self.effects = EF_NODEPTHTEST;
931 self.SendEntity = laser_SendEntity;
934 self.model = "net_entity";
935 self.mangle = self.angles;
939 self.use = laser_use;
940 if(self.spawnflags & 1)
949 // tZorks trigger impulse / gravity
955 // targeted (directional) mode
956 void trigger_impulse_touch1()
962 // FIXME: Better checking for what to push and not.
963 if (other.classname != "player")
964 if (other.classname != "corpse")
965 if (other.classname != "body")
966 if (other.classname != "gib")
967 if (other.classname != "missile")
968 if (other.classname != "casing")
969 if (other.classname != "grenade")
970 if (other.classname != "plasma")
971 if (other.classname != "plasma_prim")
972 if (other.classname != "plasma_chain")
973 if (other.classname != "droppedweapon")
976 if (other.deadflag && other.classname == "player")
981 targ = find(world, targetname, self.target);
984 objerror("trigger_force without a (valid) .target!\n");
989 if(self.falloff == 1)
990 str = (str / self.radius) * self.strength;
991 else if(self.falloff == 2)
992 str = (1 - (str / self.radius)) * self.strength;
996 pushdeltatime = time - other.lastpushtime;
997 if (pushdeltatime > 0.15) pushdeltatime = 0;
998 other.lastpushtime = time;
999 if(!pushdeltatime) return;
1001 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1004 // Directionless (accelerator/decelerator) mode
1005 void trigger_impulse_touch2()
1007 float pushdeltatime;
1009 // FIXME: Better checking for what to push and not.
1010 if (other.classname != "player")
1011 if (other.classname != "corpse")
1012 if (other.classname != "body")
1013 if (other.classname != "gib")
1014 if (other.classname != "missile")
1015 if (other.classname != "casing")
1016 if (other.classname != "grenade")
1017 if (other.classname != "plasma")
1018 if (other.classname != "plasma_prim")
1019 if (other.classname != "plasma_chain")
1020 if (other.classname != "droppedweapon")
1023 if (other.deadflag && other.classname == "player")
1028 pushdeltatime = time - other.lastpushtime;
1029 if (pushdeltatime > 0.15) pushdeltatime = 0;
1030 other.lastpushtime = time;
1031 if(!pushdeltatime) return;
1033 //if(self.strength > 1)
1034 other.velocity = other.velocity * (self.strength * pushdeltatime);
1036 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1039 // Spherical (gravity/repulsor) mode
1040 void trigger_impulse_touch3()
1042 float pushdeltatime;
1045 // FIXME: Better checking for what to push and not.
1046 if (other.classname != "player")
1047 if (other.classname != "corpse")
1048 if (other.classname != "body")
1049 if (other.classname != "gib")
1050 if (other.classname != "missile")
1051 if (other.classname != "casing")
1052 if (other.classname != "grenade")
1053 if (other.classname != "plasma")
1054 if (other.classname != "plasma_prim")
1055 if (other.classname != "plasma_chain")
1056 if (other.classname != "droppedweapon")
1059 if (other.deadflag && other.classname == "player")
1064 pushdeltatime = time - other.lastpushtime;
1065 if (pushdeltatime > 0.15) pushdeltatime = 0;
1066 other.lastpushtime = time;
1067 if(!pushdeltatime) return;
1069 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1071 str = min(self.radius, vlen(self.origin - other.origin));
1073 if(self.falloff == 1)
1074 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1075 else if(self.falloff == 2)
1076 str = (str / self.radius) * self.strength; // 0 in the inside
1078 str = self.strength;
1080 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1083 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1084 -------- KEYS --------
1085 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1086 If not, this trigger acts like a damper/accelerator field.
1088 strength : This is how mutch force to add in the direction of .target each second
1089 when .target is set. If not, this is hoe mutch to slow down/accelerate
1090 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1092 radius : If set, act as a spherical device rather then a liniar one.
1094 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1096 -------- NOTES --------
1097 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1098 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1101 void spawnfunc_trigger_impulse()
1106 if(!self.strength) self.strength = 2000;
1107 setorigin(self, self.origin);
1108 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1109 self.touch = trigger_impulse_touch3;
1115 if(!self.strength) self.strength = 950;
1116 self.touch = trigger_impulse_touch1;
1120 if(!self.strength) self.strength = 0.9;
1121 self.touch = trigger_impulse_touch2;
1126 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1127 "Flip-flop" trigger gate... lets only every second trigger event through
1131 self.state = !self.state;
1136 void spawnfunc_trigger_flipflop()
1138 if(self.spawnflags & 1)
1140 self.use = flipflop_use;
1143 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1144 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1148 self.nextthink = time + self.wait;
1154 void monoflop_fixed_use()
1158 self.nextthink = time + self.wait;
1163 void monoflop_think()
1169 void spawnfunc_trigger_monoflop()
1173 if(self.spawnflags & 1)
1174 self.use = monoflop_fixed_use;
1176 self.use = monoflop_use;
1177 self.think = monoflop_think;
1181 void multivibrator_send()
1186 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1188 newstate = (time < cyclestart + self.wait);
1190 if(self.state != newstate)
1192 self.state = newstate;
1195 self.nextthink = cyclestart + self.wait + 0.01;
1197 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1200 void multivibrator_toggle()
1202 if(self.nextthink == 0)
1204 multivibrator_send();
1217 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1218 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1219 -------- KEYS --------
1220 target: trigger all entities with this targetname when it goes off
1221 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1222 phase: offset of the timing
1223 wait: "on" cycle time (default: 1)
1224 respawntime: "off" cycle time (default: same as wait)
1225 -------- SPAWNFLAGS --------
1226 START_ON: assume it is already turned on (when targeted)
1228 void spawnfunc_trigger_multivibrator()
1232 if(!self.respawntime)
1233 self.respawntime = self.wait;
1236 self.use = multivibrator_toggle;
1237 self.think = multivibrator_send;
1238 self.nextthink = time;
1242 if(!(self.spawnflags & 1))
1243 self.nextthink = 0; // wait for a trigger event
1246 self.nextthink = time;
1253 src = find(world, targetname, self.killtarget);
1254 dst = find(world, targetname, self.target);
1258 objerror("follow: could not find target/killtarget");
1262 dst.movetype = MOVETYPE_FOLLOW;
1264 dst.punchangle = src.angles;
1265 dst.view_ofs = dst.origin - src.origin;
1266 dst.v_angle = dst.angles - src.angles;
1271 void spawnfunc_misc_follow()
1273 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);