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 (other.classname != "player")
179 if(self.team == other.team)
182 // if the trigger has an angles field, check player's facing direction
183 if (self.movedir != '0 0 0')
185 makevectors (other.angles);
186 if (v_forward * self.movedir < 0)
187 return; // not facing the right way
196 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
198 if (!self.takedamage)
200 self.health = self.health - damage;
201 if (self.health <= 0)
203 self.enemy = attacker;
208 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
209 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.
210 If "delay" is set, the trigger waits some time after activating before firing.
211 "wait" : Seconds between triggerings. (.2 default)
212 If notouch is set, the trigger is only fired by other entities, not by touching.
213 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
219 set "message" to text string
221 void spawnfunc_trigger_multiple()
223 if (self.sounds == 1)
225 precache_sound ("misc/secret.wav");
226 self.noise = "misc/secret.wav";
228 else if (self.sounds == 2)
230 precache_sound ("misc/talk.wav");
231 self.noise = "misc/talk.wav";
233 else if (self.sounds == 3)
235 precache_sound ("misc/trigger1.wav");
236 self.noise = "misc/trigger1.wav";
241 self.use = multi_use;
247 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
248 objerror ("health and notouch don't make sense\n");
249 self.max_health = self.health;
250 self.event_damage = multi_eventdamage;
251 self.takedamage = DAMAGE_YES;
252 self.solid = SOLID_BBOX;
253 setorigin (self, self.origin); // make sure it links into the world
257 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
259 self.touch = multi_touch;
260 setorigin (self, self.origin); // make sure it links into the world
266 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
267 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
268 "targetname". If "health" is set, the trigger must be killed to activate.
269 If notouch is set, the trigger is only fired by other entities, not by touching.
270 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
271 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.
277 set "message" to text string
279 void spawnfunc_trigger_once()
282 spawnfunc_trigger_multiple();
285 //=============================================================================
287 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
288 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
290 void spawnfunc_trigger_relay()
292 self.use = SUB_UseTargets;
297 self.think = SUB_UseTargets;
298 self.nextthink = self.wait;
301 void spawnfunc_trigger_delay()
306 self.use = delay_use;
309 //=============================================================================
314 self.count = self.count - 1;
320 if (activator.classname == "player"
321 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
324 centerprint (activator, "There are more to go...");
325 else if (self.count == 3)
326 centerprint (activator, "Only 3 more to go...");
327 else if (self.count == 2)
328 centerprint (activator, "Only 2 more to go...");
330 centerprint (activator, "Only 1 more to go...");
335 if (activator.classname == "player"
336 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
337 centerprint(activator, "Sequence completed!");
338 self.enemy = activator;
342 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
343 Acts as an intermediary for an action that takes multiple inputs.
345 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
347 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
349 void spawnfunc_trigger_counter()
355 self.use = counter_use;
358 .float triggerhurttime;
359 void trigger_hurt_touch()
361 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
364 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
367 other.pain_finished = min(other.pain_finished, time + 2);
369 else if (other.classname == "rune") // reset runes
372 other.nextthink = min(other.nextthink, time + 1);
376 if (other.takedamage)
377 if (other.triggerhurttime < time)
380 other.triggerhurttime = time + 1;
381 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
387 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
388 Any object touching this will be hurt
389 set dmg to damage amount
392 .entity trigger_hurt_next;
393 entity trigger_hurt_last;
394 entity trigger_hurt_first;
395 void spawnfunc_trigger_hurt()
398 self.touch = trigger_hurt_touch;
402 self.message = "was in the wrong place.";
404 if(!trigger_hurt_first)
405 trigger_hurt_first = self;
406 if(trigger_hurt_last)
407 trigger_hurt_last.trigger_hurt_next = self;
408 trigger_hurt_last = self;
411 float trace_hits_box_a0, trace_hits_box_a1;
413 float trace_hits_box_1d(float end, float thmi, float thma)
417 // just check if x is in range
425 // do the trace with respect to x
426 // 0 -> end has to stay in thmi -> thma
427 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
428 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
429 if(trace_hits_box_a0 > trace_hits_box_a1)
435 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
440 // now it is a trace from 0 to end
442 trace_hits_box_a0 = 0;
443 trace_hits_box_a1 = 1;
445 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
447 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
449 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
455 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
457 return trace_hits_box(start, end, thmi - ma, thma - mi);
460 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
464 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
465 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
472 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);}
474 void spawnfunc_target_speaker()
477 precache_sound (self.noise);
479 self.use = target_speaker_use;
481 ambientsound (self.origin, self.noise, VOL_BASE, ATTN_STATIC);
485 void spawnfunc_func_stardust() {
486 self.effects = EF_STARDUST;
489 float pointparticles_SendEntity(entity to, float fl)
491 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
492 WriteByte(MSG_ENTITY, fl);
496 WriteCoord(MSG_ENTITY, self.impulse);
498 WriteCoord(MSG_ENTITY, 0); // off
502 WriteCoord(MSG_ENTITY, self.origin_x);
503 WriteCoord(MSG_ENTITY, self.origin_y);
504 WriteCoord(MSG_ENTITY, self.origin_z);
508 if(self.modelindex != 4.2)
510 WriteShort(MSG_ENTITY, self.modelindex);
511 WriteCoord(MSG_ENTITY, self.mins_x);
512 WriteCoord(MSG_ENTITY, self.mins_y);
513 WriteCoord(MSG_ENTITY, self.mins_z);
514 WriteCoord(MSG_ENTITY, self.maxs_x);
515 WriteCoord(MSG_ENTITY, self.maxs_y);
516 WriteCoord(MSG_ENTITY, self.maxs_z);
520 WriteShort(MSG_ENTITY, 0);
521 WriteCoord(MSG_ENTITY, self.maxs_x);
522 WriteCoord(MSG_ENTITY, self.maxs_y);
523 WriteCoord(MSG_ENTITY, self.maxs_z);
525 WriteShort(MSG_ENTITY, self.cnt);
526 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
527 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
528 WriteCoord(MSG_ENTITY, self.waterlevel);
529 WriteCoord(MSG_ENTITY, self.count);
530 WriteByte(MSG_ENTITY, self.glow_color);
531 WriteString(MSG_ENTITY, self.noise);
536 void pointparticles_use()
538 self.state = !self.state;
542 void pointparticles_think()
544 if(self.origin != self.oldorigin)
547 self.oldorigin = self.origin;
549 self.nextthink = time;
552 void spawnfunc_func_pointparticles()
555 setmodel(self, self.model);
557 precache_sound (self.noise);
559 self.effects = EF_NODEPTHTEST;
560 self.SendEntity = pointparticles_SendEntity;
564 self.modelindex = 4.2;
565 self.origin += self.mins;
566 self.maxs -= self.mins;
568 self.model = "net_entity";
570 self.cnt = particleeffectnum(self.mdl);
573 self.use = pointparticles_use;
574 if(self.spawnflags & 1)
581 self.think = pointparticles_think;
582 self.nextthink = time;
585 void spawnfunc_func_sparks()
587 // self.cnt is the amount of sparks that one burst will spawn
589 self.cnt = 25.0; // nice default value
592 // self.wait is the probability that a sparkthink will spawn a spark shower
593 // range: 0 - 1, but 0 makes little sense, so...
594 if(self.wait < 0.05) {
595 self.wait = 0.25; // nice default value
598 self.count = self.cnt;
601 self.velocity = '0 0 -1';
602 self.mdl = "TE_SPARK";
603 self.impulse = 0.1 / self.wait;
606 spawnfunc_func_pointparticles();
609 float rainsnow_SendEntity(float to)
611 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
612 WriteByte(MSG_ENTITY, self.state);
613 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
614 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
615 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
616 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
617 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
618 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
619 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
620 WriteShort(MSG_ENTITY, self.count);
621 WriteByte(MSG_ENTITY, self.cnt);
625 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
626 This is an invisible area like a trigger, which rain falls inside of.
630 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
632 sets color of rain (default 12 - white)
634 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
636 void spawnfunc_func_rain()
638 self.dest = self.velocity;
639 self.velocity = '0 0 0';
641 self.dest = '0 0 -700';
642 self.angles = '0 0 0';
643 self.movetype = MOVETYPE_NONE;
644 self.solid = SOLID_NOT;
645 SetBrushEntityModel();
651 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
654 if(self.count > 65535)
657 self.state = 1; // 1 is rain, 0 is snow
658 self.effects = EF_NODEPTHTEST;
659 self.SendEntity = rainsnow_SendEntity;
662 self.model = "net_entity";
666 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
667 This is an invisible area like a trigger, which snow falls inside of.
671 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
673 sets color of rain (default 12 - white)
675 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
677 void spawnfunc_func_snow()
679 self.dest = self.velocity;
680 self.velocity = '0 0 0';
682 self.dest = '0 0 -300';
683 self.angles = '0 0 0';
684 self.movetype = MOVETYPE_NONE;
685 self.solid = SOLID_NOT;
686 SetBrushEntityModel();
692 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
695 if(self.count > 65535)
698 self.state = 0; // 1 is rain, 0 is snow
699 self.effects = EF_NODEPTHTEST;
700 self.SendEntity = rainsnow_SendEntity;
703 self.model = "net_entity";
707 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
709 void misc_laser_aim()
714 if(self.spawnflags & 2)
716 if(self.enemy.origin != self.mangle)
718 self.mangle = self.enemy.origin;
724 a = vectoangles(self.enemy.origin - self.origin);
735 if(self.angles != self.mangle)
737 self.mangle = self.angles;
741 if(self.origin != self.oldorigin)
744 self.oldorigin = self.origin;
748 void misc_laser_init()
750 if(self.target != "")
751 self.enemy = find(world, targetname, self.target);
755 void misc_laser_think()
760 self.nextthink = time;
769 o = self.enemy.origin;
770 if not(self.spawnflags & 2)
771 o = self.origin + normalize(o - self.origin) * 32768;
775 makevectors(self.mangle);
776 o = self.origin + v_forward * 32768;
782 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
784 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
787 if(self.enemy.target != "") // DETECTOR laser
789 traceline(self.origin, o, MOVE_NORMAL, self);
790 if(trace_ent.classname == "player")
792 self.pusher = trace_ent;
799 activator = self.pusher;
812 activator = self.pusher;
820 float laser_SendEntity(entity to, float fl)
822 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
823 fl = fl - (fl & 0xC0); // use that bit to indicate finite length laser
824 if(self.spawnflags & 2)
828 WriteByte(MSG_ENTITY, fl);
831 WriteCoord(MSG_ENTITY, self.origin_x);
832 WriteCoord(MSG_ENTITY, self.origin_y);
833 WriteCoord(MSG_ENTITY, self.origin_z);
837 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
838 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
839 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
841 WriteByte(MSG_ENTITY, self.alpha * 255.0);
842 WriteShort(MSG_ENTITY, self.cnt + 1);
848 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
849 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
850 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
854 WriteCoord(MSG_ENTITY, self.mangle_x);
855 WriteCoord(MSG_ENTITY, self.mangle_y);
859 WriteByte(MSG_ENTITY, self.state);
863 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
864 Any object touching the beam will be hurt
867 spawnfunc_target_position where the laser ends
869 name of beam end effect to use
871 color of the beam (default: red)
873 damage per second (-1 for a laser that kills immediately)
877 self.state = !self.state;
882 void spawnfunc_misc_laser()
886 if(self.mdl == "none")
889 self.cnt = particleeffectnum(self.mdl);
894 self.cnt = particleeffectnum("laser_deadly");
899 if(self.colormod == '0 0 0')
901 self.colormod = '1 0 0';
903 self.message = "saw the light";
904 self.think = misc_laser_think;
905 self.nextthink = time;
906 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
908 self.effects = EF_NODEPTHTEST;
909 self.SendEntity = laser_SendEntity;
912 self.model = "net_entity";
913 self.mangle = self.angles;
917 self.use = laser_use;
918 if(self.spawnflags & 1)
927 // tZorks trigger impulse / gravity
933 // targeted (directional) mode
934 void trigger_impulse_touch1()
940 // FIXME: Better checking for what to push and not.
941 if (other.classname != "player")
942 if (other.classname != "corpse")
943 if (other.classname != "body")
944 if (other.classname != "gib")
945 if (other.classname != "missile")
946 if (other.classname != "casing")
947 if (other.classname != "grenade")
948 if (other.classname != "plasma")
949 if (other.classname != "plasma_prim")
950 if (other.classname != "plasma_chain")
951 if (other.classname != "droppedweapon")
954 if (other.deadflag && other.classname == "player")
959 targ = find(world, targetname, self.target);
962 objerror("trigger_force without a (valid) .target!\n");
967 if(self.falloff == 1)
968 str = (str / self.radius) * self.strength;
969 else if(self.falloff == 2)
970 str = (1 - (str / self.radius)) * self.strength;
974 pushdeltatime = time - other.lastpushtime;
975 if (pushdeltatime > 0.15) pushdeltatime = 0;
976 other.lastpushtime = time;
977 if(!pushdeltatime) return;
979 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
982 // Directionless (accelerator/decelerator) mode
983 void trigger_impulse_touch2()
987 // FIXME: Better checking for what to push and not.
988 if (other.classname != "player")
989 if (other.classname != "corpse")
990 if (other.classname != "body")
991 if (other.classname != "gib")
992 if (other.classname != "missile")
993 if (other.classname != "casing")
994 if (other.classname != "grenade")
995 if (other.classname != "plasma")
996 if (other.classname != "plasma_prim")
997 if (other.classname != "plasma_chain")
998 if (other.classname != "droppedweapon")
1001 if (other.deadflag && other.classname == "player")
1006 pushdeltatime = time - other.lastpushtime;
1007 if (pushdeltatime > 0.15) pushdeltatime = 0;
1008 other.lastpushtime = time;
1009 if(!pushdeltatime) return;
1011 //if(self.strength > 1)
1012 other.velocity = other.velocity * (self.strength * pushdeltatime);
1014 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1017 // Spherical (gravity/repulsor) mode
1018 void trigger_impulse_touch3()
1020 float pushdeltatime;
1023 // FIXME: Better checking for what to push and not.
1024 if (other.classname != "player")
1025 if (other.classname != "corpse")
1026 if (other.classname != "body")
1027 if (other.classname != "gib")
1028 if (other.classname != "missile")
1029 if (other.classname != "casing")
1030 if (other.classname != "grenade")
1031 if (other.classname != "plasma")
1032 if (other.classname != "plasma_prim")
1033 if (other.classname != "plasma_chain")
1034 if (other.classname != "droppedweapon")
1037 if (other.deadflag && other.classname == "player")
1042 pushdeltatime = time - other.lastpushtime;
1043 if (pushdeltatime > 0.15) pushdeltatime = 0;
1044 other.lastpushtime = time;
1045 if(!pushdeltatime) return;
1047 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1049 str = min(self.radius, vlen(self.origin - other.origin));
1051 if(self.falloff == 1)
1052 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1053 else if(self.falloff == 2)
1054 str = (str / self.radius) * self.strength; // 0 in the inside
1056 str = self.strength;
1058 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1061 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1062 -------- KEYS --------
1063 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1064 If not, this trigger acts like a damper/accelerator field.
1066 strength : This is how mutch force to add in the direction of .target each second
1067 when .target is set. If not, this is hoe mutch to slow down/accelerate
1068 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1070 radius : If set, act as a spherical device rather then a liniar one.
1072 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1074 -------- NOTES --------
1075 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1076 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1079 void spawnfunc_trigger_impulse()
1084 if(!self.strength) self.strength = 2000;
1085 setorigin(self, self.origin);
1086 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1087 self.touch = trigger_impulse_touch3;
1093 if(!self.strength) self.strength = 950;
1094 self.touch = trigger_impulse_touch1;
1098 if(!self.strength) self.strength = 0.9;
1099 self.touch = trigger_impulse_touch2;
1104 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1105 "Flip-flop" trigger gate... lets only every second trigger event through
1109 self.state = !self.state;
1114 void spawnfunc_trigger_flipflop()
1116 if(self.spawnflags & 1)
1118 self.use = flipflop_use;
1121 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1122 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1126 self.nextthink = time + self.wait;
1132 void monoflop_fixed_use()
1136 self.nextthink = time + self.wait;
1141 void monoflop_think()
1147 void spawnfunc_trigger_monoflop()
1151 if(self.spawnflags & 1)
1152 self.use = monoflop_fixed_use;
1154 self.use = monoflop_use;
1155 self.think = monoflop_think;
1159 void multivibrator_send()
1164 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1166 newstate = (time < cyclestart + self.wait);
1168 if(self.state != newstate)
1170 self.state = newstate;
1173 self.nextthink = cyclestart + self.wait + 0.01;
1175 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1178 void multivibrator_toggle()
1180 if(self.nextthink == 0)
1182 multivibrator_send();
1195 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1196 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1197 -------- KEYS --------
1198 target: trigger all entities with this targetname when it goes off
1199 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1200 phase: offset of the timing
1201 wait: "on" cycle time (default: 1)
1202 respawntime: "off" cycle time (default: same as wait)
1203 -------- SPAWNFLAGS --------
1204 START_ON: assume it is already turned on (when targeted)
1206 void spawnfunc_trigger_multivibrator()
1210 if(!self.respawntime)
1211 self.respawntime = self.wait;
1214 self.use = multivibrator_toggle;
1215 self.think = multivibrator_send;
1216 self.nextthink = time;
1220 if(!(self.spawnflags & 1))
1221 self.nextthink = 0; // wait for a trigger event
1224 self.nextthink = time;
1231 src = find(world, targetname, self.killtarget);
1232 dst = find(world, targetname, self.target);
1236 objerror("follow: could not find target/killtarget");
1240 dst.movetype = MOVETYPE_FOLLOW;
1242 dst.punchangle = src.angles;
1243 dst.view_ofs = dst.origin - src.origin;
1244 dst.v_angle = dst.angles - src.angles;
1249 void spawnfunc_misc_follow()
1251 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);