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;
41 // create a temp object to fire at a later time
43 t.classname = "DelayedUse";
44 t.nextthink = time + self.delay;
47 t.message = self.message;
48 t.killtarget = self.killtarget;
49 t.target = self.target;
57 if (activator.classname == "player" && self.message != "")
59 if(clienttype(activator) == CLIENTTYPE_REAL)
61 centerprint (activator, self.message);
63 play2(activator, "misc/talk.wav");
68 // kill the killtagets
73 for(t = world; (t = find(t, targetname, s)); )
84 for(i = 0; i < 4; ++i)
89 case 0: s = self.target; break;
90 case 1: s = self.target2; break;
91 case 2: s = self.target3; break;
92 case 3: s = self.target4; break;
96 for(t = world; (t = find(t, targetname, s)); )
113 //=============================================================================
115 float SPAWNFLAG_NOMESSAGE = 1;
116 float SPAWNFLAG_NOTOUCH = 1;
118 // the wait time has passed, so set back up for another activation
123 self.health = self.max_health;
124 self.takedamage = DAMAGE_YES;
125 self.solid = SOLID_BBOX;
130 // the trigger was just touched/killed/used
131 // self.enemy should be set to the activator so it can be held through a delay
132 // so wait for the delay time before firing
135 if (self.nextthink > time)
137 return; // allready been triggered
140 if (self.classname == "trigger_secret")
142 if (self.enemy.classname != "player")
144 found_secrets = found_secrets + 1;
145 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
149 sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
151 // don't trigger again until reset
152 self.takedamage = DAMAGE_NO;
154 activator = self.enemy;
160 self.think = multi_wait;
161 self.nextthink = time + self.wait;
164 { // we can't just remove (self) here, because this is a touch function
165 // called wheil C code is looping through area links...
166 self.touch = SUB_Null;
168 self.nextthink = time + 0.1;
169 self.think = SUB_Remove;
175 self.enemy = activator;
181 if not(self.spawnflags & 2)
183 if not(other.iscreature)
187 if(self.team == other.team)
191 // if the trigger has an angles field, check player's facing direction
192 if (self.movedir != '0 0 0')
194 makevectors (other.angles);
195 if (v_forward * self.movedir < 0)
196 return; // not facing the right way
205 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
207 if (!self.takedamage)
209 self.health = self.health - damage;
210 if (self.health <= 0)
212 self.enemy = attacker;
217 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
218 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.
219 If "delay" is set, the trigger waits some time after activating before firing.
220 "wait" : Seconds between triggerings. (.2 default)
221 If notouch is set, the trigger is only fired by other entities, not by touching.
222 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
228 set "message" to text string
230 void spawnfunc_trigger_multiple()
232 if (self.sounds == 1)
234 precache_sound ("misc/secret.wav");
235 self.noise = "misc/secret.wav";
237 else if (self.sounds == 2)
239 precache_sound ("misc/talk.wav");
240 self.noise = "misc/talk.wav";
242 else if (self.sounds == 3)
244 precache_sound ("misc/trigger1.wav");
245 self.noise = "misc/trigger1.wav";
250 self.use = multi_use;
256 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
257 objerror ("health and notouch don't make sense\n");
258 self.max_health = self.health;
259 self.event_damage = multi_eventdamage;
260 self.takedamage = DAMAGE_YES;
261 self.solid = SOLID_BBOX;
262 setorigin (self, self.origin); // make sure it links into the world
266 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
268 self.touch = multi_touch;
269 setorigin (self, self.origin); // make sure it links into the world
275 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
276 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
277 "targetname". If "health" is set, the trigger must be killed to activate.
278 If notouch is set, the trigger is only fired by other entities, not by touching.
279 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
280 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.
286 set "message" to text string
288 void spawnfunc_trigger_once()
291 spawnfunc_trigger_multiple();
294 //=============================================================================
296 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
297 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
299 void spawnfunc_trigger_relay()
301 self.use = SUB_UseTargets;
306 self.think = SUB_UseTargets;
307 self.nextthink = self.wait;
310 void spawnfunc_trigger_delay()
315 self.use = delay_use;
318 //=============================================================================
323 self.count = self.count - 1;
329 if (activator.classname == "player"
330 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
333 centerprint (activator, "There are more to go...");
334 else if (self.count == 3)
335 centerprint (activator, "Only 3 more to go...");
336 else if (self.count == 2)
337 centerprint (activator, "Only 2 more to go...");
339 centerprint (activator, "Only 1 more to go...");
344 if (activator.classname == "player"
345 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
346 centerprint(activator, "Sequence completed!");
347 self.enemy = activator;
351 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
352 Acts as an intermediary for an action that takes multiple inputs.
354 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
356 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
358 void spawnfunc_trigger_counter()
364 self.use = counter_use;
367 .float triggerhurttime;
368 void trigger_hurt_touch()
370 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
371 if (other.iscreature)
373 if (other.takedamage)
374 if (other.triggerhurttime < time)
377 other.triggerhurttime = time + 1;
378 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
385 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
388 other.pain_finished = min(other.pain_finished, time + 2);
390 else if (other.classname == "rune") // reset runes
393 other.nextthink = min(other.nextthink, time + 1);
401 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
402 Any object touching this will be hurt
403 set dmg to damage amount
406 .entity trigger_hurt_next;
407 entity trigger_hurt_last;
408 entity trigger_hurt_first;
409 void spawnfunc_trigger_hurt()
412 self.touch = trigger_hurt_touch;
416 self.message = "was in the wrong place";
418 self.message2 = "was thrown into a world of hurt by";
420 if(!trigger_hurt_first)
421 trigger_hurt_first = self;
422 if(trigger_hurt_last)
423 trigger_hurt_last.trigger_hurt_next = self;
424 trigger_hurt_last = self;
427 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
431 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
432 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
438 //////////////////////////////////////////////////////////////
442 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
444 //////////////////////////////////////////////////////////////
446 .float triggerhealtime;
447 void trigger_heal_touch()
449 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
450 if (other.iscreature)
452 if (other.takedamage)
453 if (other.triggerhealtime < time)
456 other.triggerhealtime = time + 1;
458 if (other.health < self.max_health)
460 other.health = min(other.health + self.health, self.max_health);
461 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
462 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
468 void spawnfunc_trigger_heal()
471 self.touch = trigger_heal_touch;
474 if (!self.max_health)
475 self.max_health = 200; //Max health topoff for field
477 self.noise = "misc/mediumhealth.wav";
478 precache_sound(self.noise);
482 //////////////////////////////////////////////////////////////
488 //////////////////////////////////////////////////////////////
492 // TODO add a way to do looped sounds with sound(); then complete this entity
493 .float volume, atten;
494 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
496 void spawnfunc_target_speaker()
499 precache_sound (self.noise);
503 self.atten = ATTN_NORM;
504 else if(self.atten < 0)
508 self.use = target_speaker_use;
513 self.atten = ATTN_STATIC;
514 else if(self.atten < 0)
518 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
523 void spawnfunc_func_stardust() {
524 self.effects = EF_STARDUST;
527 float pointparticles_SendEntity(entity to, float fl)
529 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
530 WriteByte(MSG_ENTITY, fl);
534 WriteCoord(MSG_ENTITY, self.impulse);
536 WriteCoord(MSG_ENTITY, 0); // off
540 WriteCoord(MSG_ENTITY, self.origin_x);
541 WriteCoord(MSG_ENTITY, self.origin_y);
542 WriteCoord(MSG_ENTITY, self.origin_z);
546 if(self.modelindex != 4.2)
548 WriteShort(MSG_ENTITY, self.modelindex);
549 WriteCoord(MSG_ENTITY, self.mins_x);
550 WriteCoord(MSG_ENTITY, self.mins_y);
551 WriteCoord(MSG_ENTITY, self.mins_z);
552 WriteCoord(MSG_ENTITY, self.maxs_x);
553 WriteCoord(MSG_ENTITY, self.maxs_y);
554 WriteCoord(MSG_ENTITY, self.maxs_z);
558 WriteShort(MSG_ENTITY, 0);
559 WriteCoord(MSG_ENTITY, self.maxs_x);
560 WriteCoord(MSG_ENTITY, self.maxs_y);
561 WriteCoord(MSG_ENTITY, self.maxs_z);
563 WriteShort(MSG_ENTITY, self.cnt);
564 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
565 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
566 WriteCoord(MSG_ENTITY, self.waterlevel);
567 WriteCoord(MSG_ENTITY, self.count);
568 WriteByte(MSG_ENTITY, self.glow_color);
569 WriteString(MSG_ENTITY, self.noise);
574 void pointparticles_use()
576 self.state = !self.state;
580 void pointparticles_think()
582 if(self.origin != self.oldorigin)
585 self.oldorigin = self.origin;
587 self.nextthink = time;
590 void spawnfunc_func_pointparticles()
593 setmodel(self, self.model);
595 precache_sound (self.noise);
597 self.effects = EF_NODEPTHTEST;
598 self.SendEntity = pointparticles_SendEntity;
602 self.modelindex = 4.2;
603 self.origin += self.mins;
604 self.maxs = self.maxs - self.mins;
606 self.model = "net_entity";
608 self.cnt = particleeffectnum(self.mdl);
611 self.use = pointparticles_use;
612 if(self.spawnflags & 1)
619 self.think = pointparticles_think;
620 self.nextthink = time;
623 void spawnfunc_func_sparks()
625 // self.cnt is the amount of sparks that one burst will spawn
627 self.cnt = 25.0; // nice default value
630 // self.wait is the probability that a sparkthink will spawn a spark shower
631 // range: 0 - 1, but 0 makes little sense, so...
632 if(self.wait < 0.05) {
633 self.wait = 0.25; // nice default value
636 self.count = self.cnt;
639 self.velocity = '0 0 -1';
640 self.mdl = "TE_SPARK";
641 self.impulse = 10 * self.wait; // by default 2.5/sec
643 self.cnt = 0; // use mdl
645 spawnfunc_func_pointparticles();
648 float rainsnow_SendEntity(float to)
650 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
651 WriteByte(MSG_ENTITY, self.state);
652 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
653 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
654 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
655 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
656 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
657 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
658 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
659 WriteShort(MSG_ENTITY, self.count);
660 WriteByte(MSG_ENTITY, self.cnt);
664 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
665 This is an invisible area like a trigger, which rain falls inside of.
669 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
671 sets color of rain (default 12 - white)
673 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
675 void spawnfunc_func_rain()
677 self.dest = self.velocity;
678 self.velocity = '0 0 0';
680 self.dest = '0 0 -700';
681 self.angles = '0 0 0';
682 self.movetype = MOVETYPE_NONE;
683 self.solid = SOLID_NOT;
684 SetBrushEntityModel();
690 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
693 if(self.count > 65535)
696 self.state = 1; // 1 is rain, 0 is snow
697 self.effects = EF_NODEPTHTEST;
698 self.SendEntity = rainsnow_SendEntity;
701 self.model = "net_entity";
705 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
706 This is an invisible area like a trigger, which snow falls inside of.
710 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
712 sets color of rain (default 12 - white)
714 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
716 void spawnfunc_func_snow()
718 self.dest = self.velocity;
719 self.velocity = '0 0 0';
721 self.dest = '0 0 -300';
722 self.angles = '0 0 0';
723 self.movetype = MOVETYPE_NONE;
724 self.solid = SOLID_NOT;
725 SetBrushEntityModel();
731 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
734 if(self.count > 65535)
737 self.state = 0; // 1 is rain, 0 is snow
738 self.effects = EF_NODEPTHTEST;
739 self.SendEntity = rainsnow_SendEntity;
742 self.model = "net_entity";
746 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
748 void misc_laser_aim()
753 if(self.spawnflags & 2)
755 if(self.enemy.origin != self.mangle)
757 self.mangle = self.enemy.origin;
763 a = vectoangles(self.enemy.origin - self.origin);
774 if(self.angles != self.mangle)
776 self.mangle = self.angles;
780 if(self.origin != self.oldorigin)
783 self.oldorigin = self.origin;
787 void misc_laser_init()
789 if(self.target != "")
790 self.enemy = find(world, targetname, self.target);
794 void misc_laser_think()
799 self.nextthink = time;
808 o = self.enemy.origin;
809 if not(self.spawnflags & 2)
810 o = self.origin + normalize(o - self.origin) * 32768;
814 makevectors(self.mangle);
815 o = self.origin + v_forward * 32768;
821 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
823 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
826 if(self.enemy.target != "") // DETECTOR laser
828 traceline(self.origin, o, MOVE_NORMAL, self);
829 if(trace_ent.iscreature)
831 self.pusher = trace_ent;
838 activator = self.pusher;
851 activator = self.pusher;
859 float laser_SendEntity(entity to, float fl)
861 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
862 fl = fl - (fl & 0xC0); // use that bit to indicate finite length laser
863 if(self.spawnflags & 2)
867 WriteByte(MSG_ENTITY, fl);
870 WriteCoord(MSG_ENTITY, self.origin_x);
871 WriteCoord(MSG_ENTITY, self.origin_y);
872 WriteCoord(MSG_ENTITY, self.origin_z);
876 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
877 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
878 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
880 WriteByte(MSG_ENTITY, self.alpha * 255.0);
881 WriteShort(MSG_ENTITY, self.cnt + 1);
887 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
888 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
889 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
893 WriteCoord(MSG_ENTITY, self.mangle_x);
894 WriteCoord(MSG_ENTITY, self.mangle_y);
898 WriteByte(MSG_ENTITY, self.state);
902 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
903 Any object touching the beam will be hurt
906 spawnfunc_target_position where the laser ends
908 name of beam end effect to use
910 color of the beam (default: red)
912 damage per second (-1 for a laser that kills immediately)
916 self.state = !self.state;
921 void spawnfunc_misc_laser()
925 if(self.mdl == "none")
928 self.cnt = particleeffectnum(self.mdl);
933 self.cnt = particleeffectnum("laser_deadly");
938 if(self.colormod == '0 0 0')
940 self.colormod = '1 0 0';
942 self.message = "saw the light";
944 self.message2 = "was pushed into a laser by";
945 self.think = misc_laser_think;
946 self.nextthink = time;
947 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
949 self.effects = EF_NODEPTHTEST;
950 self.SendEntity = laser_SendEntity;
953 self.model = "net_entity";
954 self.mangle = self.angles;
958 self.use = laser_use;
959 if(self.spawnflags & 1)
968 // tZorks trigger impulse / gravity
974 // targeted (directional) mode
975 void trigger_impulse_touch1()
981 // FIXME: Better checking for what to push and not.
982 if not(other.iscreature)
983 if (other.classname != "corpse")
984 if (other.classname != "body")
985 if (other.classname != "gib")
986 if (other.classname != "missile")
987 if (other.classname != "rocket")
988 if (other.classname != "casing")
989 if (other.classname != "grenade")
990 if (other.classname != "plasma")
991 if (other.classname != "plasma_prim")
992 if (other.classname != "plasma_chain")
993 if (other.classname != "droppedweapon")
996 if (other.deadflag && other.iscreature)
1001 targ = find(world, targetname, self.target);
1004 objerror("trigger_force without a (valid) .target!\n");
1009 if(self.falloff == 1)
1010 str = (str / self.radius) * self.strength;
1011 else if(self.falloff == 2)
1012 str = (1 - (str / self.radius)) * self.strength;
1014 str = self.strength;
1016 pushdeltatime = time - other.lastpushtime;
1017 if (pushdeltatime > 0.15) pushdeltatime = 0;
1018 other.lastpushtime = time;
1019 if(!pushdeltatime) return;
1021 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1024 // Directionless (accelerator/decelerator) mode
1025 void trigger_impulse_touch2()
1027 float pushdeltatime;
1029 // FIXME: Better checking for what to push and not.
1030 if not(other.iscreature)
1031 if (other.classname != "corpse")
1032 if (other.classname != "body")
1033 if (other.classname != "gib")
1034 if (other.classname != "missile")
1035 if (other.classname != "rocket")
1036 if (other.classname != "casing")
1037 if (other.classname != "grenade")
1038 if (other.classname != "plasma")
1039 if (other.classname != "plasma_prim")
1040 if (other.classname != "plasma_chain")
1041 if (other.classname != "droppedweapon")
1044 if (other.deadflag && other.iscreature)
1049 pushdeltatime = time - other.lastpushtime;
1050 if (pushdeltatime > 0.15) pushdeltatime = 0;
1051 other.lastpushtime = time;
1052 if(!pushdeltatime) return;
1054 //if(self.strength > 1)
1055 other.velocity = other.velocity * (self.strength * pushdeltatime);
1057 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1060 // Spherical (gravity/repulsor) mode
1061 void trigger_impulse_touch3()
1063 float pushdeltatime;
1066 // FIXME: Better checking for what to push and not.
1067 if not(other.iscreature)
1068 if (other.classname != "corpse")
1069 if (other.classname != "body")
1070 if (other.classname != "gib")
1071 if (other.classname != "missile")
1072 if (other.classname != "rocket")
1073 if (other.classname != "casing")
1074 if (other.classname != "grenade")
1075 if (other.classname != "plasma")
1076 if (other.classname != "plasma_prim")
1077 if (other.classname != "plasma_chain")
1078 if (other.classname != "droppedweapon")
1081 if (other.deadflag && other.iscreature)
1086 pushdeltatime = time - other.lastpushtime;
1087 if (pushdeltatime > 0.15) pushdeltatime = 0;
1088 other.lastpushtime = time;
1089 if(!pushdeltatime) return;
1091 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1093 str = min(self.radius, vlen(self.origin - other.origin));
1095 if(self.falloff == 1)
1096 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1097 else if(self.falloff == 2)
1098 str = (str / self.radius) * self.strength; // 0 in the inside
1100 str = self.strength;
1102 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1105 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1106 -------- KEYS --------
1107 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1108 If not, this trigger acts like a damper/accelerator field.
1110 strength : This is how mutch force to add in the direction of .target each second
1111 when .target is set. If not, this is hoe mutch to slow down/accelerate
1112 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1114 radius : If set, act as a spherical device rather then a liniar one.
1116 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1118 -------- NOTES --------
1119 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1120 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1123 void spawnfunc_trigger_impulse()
1128 if(!self.strength) self.strength = 2000;
1129 setorigin(self, self.origin);
1130 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1131 self.touch = trigger_impulse_touch3;
1137 if(!self.strength) self.strength = 950;
1138 self.touch = trigger_impulse_touch1;
1142 if(!self.strength) self.strength = 0.9;
1143 self.touch = trigger_impulse_touch2;
1148 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1149 "Flip-flop" trigger gate... lets only every second trigger event through
1153 self.state = !self.state;
1158 void spawnfunc_trigger_flipflop()
1160 if(self.spawnflags & 1)
1162 self.use = flipflop_use;
1165 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1166 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1170 self.nextthink = time + self.wait;
1171 self.enemy = activator;
1177 void monoflop_fixed_use()
1181 self.nextthink = time + self.wait;
1183 self.enemy = activator;
1187 void monoflop_think()
1190 activator = self.enemy;
1194 void spawnfunc_trigger_monoflop()
1198 if(self.spawnflags & 1)
1199 self.use = monoflop_fixed_use;
1201 self.use = monoflop_use;
1202 self.think = monoflop_think;
1206 void multivibrator_send()
1211 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1213 newstate = (time < cyclestart + self.wait);
1216 if(self.state != newstate)
1218 self.state = newstate;
1221 self.nextthink = cyclestart + self.wait + 0.01;
1223 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1226 void multivibrator_toggle()
1228 if(self.nextthink == 0)
1230 multivibrator_send();
1243 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1244 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1245 -------- KEYS --------
1246 target: trigger all entities with this targetname when it goes off
1247 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1248 phase: offset of the timing
1249 wait: "on" cycle time (default: 1)
1250 respawntime: "off" cycle time (default: same as wait)
1251 -------- SPAWNFLAGS --------
1252 START_ON: assume it is already turned on (when targeted)
1254 void spawnfunc_trigger_multivibrator()
1258 if(!self.respawntime)
1259 self.respawntime = self.wait;
1262 self.use = multivibrator_toggle;
1263 self.think = multivibrator_send;
1264 self.nextthink = time;
1268 if(!(self.spawnflags & 1))
1269 self.nextthink = 0; // wait for a trigger event
1272 self.nextthink = time;
1279 src = find(world, targetname, self.killtarget);
1280 dst = find(world, targetname, self.target);
1284 objerror("follow: could not find target/killtarget");
1288 dst.movetype = MOVETYPE_FOLLOW;
1290 dst.punchangle = src.angles;
1291 dst.view_ofs = dst.origin - src.origin;
1292 dst.v_angle = dst.angles - src.angles;
1297 void spawnfunc_misc_follow()
1299 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1305 .entity voicescript; // attached voice script
1306 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1307 .float voicescript_nextthink; // time to play next voice
1308 .float voicescript_voiceend; // time when this voice ends
1310 void target_voicescript_clear(entity pl)
1312 pl.voicescript = world;
1315 void target_voicescript_use()
1317 if(activator.voicescript != self)
1319 activator.voicescript = self;
1320 activator.voicescript_index = 0;
1321 activator.voicescript_nextthink = time + self.delay;
1325 void target_voicescript_next(entity pl)
1330 vs = pl.voicescript;
1333 if(vs.message == "")
1335 if(pl.classname != "player")
1340 if(time >= pl.voicescript_voiceend)
1342 if(time >= pl.voicescript_nextthink)
1344 // get the next voice...
1345 n = tokenize_sane(vs.message);
1347 if(pl.voicescript_index < vs.cnt)
1348 i = pl.voicescript_index * 2;
1349 else if(n > vs.cnt * 2)
1350 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1356 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1357 pl.voicescript_voiceend = time + stof(argv(i + 1));
1360 pl.voicescript = world;
1362 pl.voicescript_index += 1;
1363 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1368 void spawnfunc_target_voicescript()
1370 // netname: directory of the sound files
1371 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1372 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1373 // wait: average time between messages
1374 // delay: initial delay before the first message
1377 self.use = target_voicescript_use;
1379 n = tokenize_sane(self.message);
1381 for(i = 0; i+1 < n; i += 2)
1388 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));