1 void SUB_DontUseTargets()
10 activator = self.enemy;
16 ==============================
19 the global "activator" should be set to the entity that initiated the firing.
21 If self.delay is set, a DelayedUse entity will be created that will actually
22 do the SUB_UseTargets after that many seconds have passed.
24 Centerprints any self.message to the activator.
26 Removes all entities with a targetname that match self.killtarget,
27 and removes them, so some events can remove other triggers.
29 Search for (string)targetname in all entities that
30 match (string)self.target and call their .use function
32 ==============================
36 local entity t, stemp, otemp, act;
45 // create a temp object to fire at a later time
47 t.classname = "DelayedUse";
48 t.nextthink = time + self.delay;
51 t.message = self.message;
52 t.killtarget = self.killtarget;
53 t.target = self.target;
61 if (activator.classname == "player" && self.message != "")
63 if(clienttype(activator) == CLIENTTYPE_REAL)
65 centerprint (activator, self.message);
67 play2(activator, "misc/talk.wav");
72 // kill the killtagets
77 for(t = world; (t = find(t, targetname, s)); )
88 for(i = 0; i < 4; ++i)
93 case 0: s = stemp.target; break;
94 case 1: s = stemp.target2; break;
95 case 2: s = stemp.target3; break;
96 case 3: s = stemp.target4; break;
100 for(t = world; (t = find(t, targetname, s)); )
103 //print(stemp.classname, " ", stemp.targetname, " -> ", t.classname, " ", t.targetname, "\n");
118 //=============================================================================
120 float SPAWNFLAG_NOMESSAGE = 1;
121 float SPAWNFLAG_NOTOUCH = 1;
123 // the wait time has passed, so set back up for another activation
128 self.health = self.max_health;
129 self.takedamage = DAMAGE_YES;
130 self.solid = SOLID_BBOX;
135 // the trigger was just touched/killed/used
136 // self.enemy should be set to the activator so it can be held through a delay
137 // so wait for the delay time before firing
140 if (self.nextthink > time)
142 return; // allready been triggered
145 if (self.classname == "trigger_secret")
147 if (self.enemy.classname != "player")
149 found_secrets = found_secrets + 1;
150 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
154 sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
156 // don't trigger again until reset
157 self.takedamage = DAMAGE_NO;
159 activator = self.enemy;
165 self.think = multi_wait;
166 self.nextthink = time + self.wait;
169 { // we can't just remove (self) here, because this is a touch function
170 // called wheil C code is looping through area links...
171 self.touch = SUB_Null;
173 self.nextthink = time + 0.1;
174 self.think = SUB_Remove;
180 self.enemy = activator;
186 if not(self.spawnflags & 2)
188 if not(other.iscreature)
192 if(self.team == other.team)
196 // if the trigger has an angles field, check player's facing direction
197 if (self.movedir != '0 0 0')
199 makevectors (other.angles);
200 if (v_forward * self.movedir < 0)
201 return; // not facing the right way
210 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
212 if (!self.takedamage)
214 self.health = self.health - damage;
215 if (self.health <= 0)
217 self.enemy = attacker;
222 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
223 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.
224 If "delay" is set, the trigger waits some time after activating before firing.
225 "wait" : Seconds between triggerings. (.2 default)
226 If notouch is set, the trigger is only fired by other entities, not by touching.
227 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
233 set "message" to text string
235 void spawnfunc_trigger_multiple()
237 if (self.sounds == 1)
239 precache_sound ("misc/secret.wav");
240 self.noise = "misc/secret.wav";
242 else if (self.sounds == 2)
244 precache_sound ("misc/talk.wav");
245 self.noise = "misc/talk.wav";
247 else if (self.sounds == 3)
249 precache_sound ("misc/trigger1.wav");
250 self.noise = "misc/trigger1.wav";
255 self.use = multi_use;
259 self.team_saved = self.team;
263 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
264 objerror ("health and notouch don't make sense\n");
265 self.max_health = self.health;
266 self.event_damage = multi_eventdamage;
267 self.takedamage = DAMAGE_YES;
268 self.solid = SOLID_BBOX;
269 setorigin (self, self.origin); // make sure it links into the world
273 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
275 self.touch = multi_touch;
276 setorigin (self, self.origin); // make sure it links into the world
282 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
283 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
284 "targetname". If "health" is set, the trigger must be killed to activate.
285 If notouch is set, the trigger is only fired by other entities, not by touching.
286 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
287 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.
293 set "message" to text string
295 void spawnfunc_trigger_once()
298 spawnfunc_trigger_multiple();
301 //=============================================================================
303 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
304 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
306 void spawnfunc_trigger_relay()
308 self.use = SUB_UseTargets;
313 self.think = SUB_UseTargets;
314 self.nextthink = self.wait;
317 void spawnfunc_trigger_delay()
322 self.use = delay_use;
325 //=============================================================================
330 self.count = self.count - 1;
336 if (activator.classname == "player"
337 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
340 centerprint (activator, "There are more to go...");
341 else if (self.count == 3)
342 centerprint (activator, "Only 3 more to go...");
343 else if (self.count == 2)
344 centerprint (activator, "Only 2 more to go...");
346 centerprint (activator, "Only 1 more to go...");
351 if (activator.classname == "player"
352 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
353 centerprint(activator, "Sequence completed!");
354 self.enemy = activator;
358 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
359 Acts as an intermediary for an action that takes multiple inputs.
361 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
363 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
365 void spawnfunc_trigger_counter()
371 self.use = counter_use;
374 .float triggerhurttime;
375 void trigger_hurt_touch()
377 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
378 if (other.iscreature)
380 if (other.takedamage)
381 if (other.triggerhurttime < time)
384 other.triggerhurttime = time + 1;
385 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
392 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
395 other.pain_finished = min(other.pain_finished, time + 2);
397 else if (other.classname == "rune") // reset runes
400 other.nextthink = min(other.nextthink, time + 1);
408 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
409 Any object touching this will be hurt
410 set dmg to damage amount
413 .entity trigger_hurt_next;
414 entity trigger_hurt_last;
415 entity trigger_hurt_first;
416 void spawnfunc_trigger_hurt()
419 self.touch = trigger_hurt_touch;
423 self.message = "was in the wrong place";
425 self.message2 = "was thrown into a world of hurt by";
427 if(!trigger_hurt_first)
428 trigger_hurt_first = self;
429 if(trigger_hurt_last)
430 trigger_hurt_last.trigger_hurt_next = self;
431 trigger_hurt_last = self;
434 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
438 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
439 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
445 //////////////////////////////////////////////////////////////
449 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
451 //////////////////////////////////////////////////////////////
453 .float triggerhealtime;
454 void trigger_heal_touch()
456 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
457 if (other.iscreature)
459 if (other.takedamage)
460 if (other.triggerhealtime < time)
463 other.triggerhealtime = time + 1;
465 if (other.health < self.max_health)
467 other.health = min(other.health + self.health, self.max_health);
468 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
469 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
475 void spawnfunc_trigger_heal()
478 self.touch = trigger_heal_touch;
481 if (!self.max_health)
482 self.max_health = 200; //Max health topoff for field
484 self.noise = "misc/mediumhealth.wav";
485 precache_sound(self.noise);
489 //////////////////////////////////////////////////////////////
495 //////////////////////////////////////////////////////////////
499 // TODO add a way to do looped sounds with sound(); then complete this entity
500 .float volume, atten;
501 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
503 void spawnfunc_target_speaker()
506 precache_sound (self.noise);
510 self.atten = ATTN_NORM;
511 else if(self.atten < 0)
515 self.use = target_speaker_use;
520 self.atten = ATTN_STATIC;
521 else if(self.atten < 0)
525 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
530 void spawnfunc_func_stardust() {
531 self.effects = EF_STARDUST;
534 float pointparticles_SendEntity(entity to, float fl)
536 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
537 WriteByte(MSG_ENTITY, fl);
541 WriteCoord(MSG_ENTITY, self.impulse);
543 WriteCoord(MSG_ENTITY, 0); // off
547 WriteCoord(MSG_ENTITY, self.origin_x);
548 WriteCoord(MSG_ENTITY, self.origin_y);
549 WriteCoord(MSG_ENTITY, self.origin_z);
553 if(self.model != "null")
555 WriteShort(MSG_ENTITY, self.modelindex);
556 WriteCoord(MSG_ENTITY, self.mins_x);
557 WriteCoord(MSG_ENTITY, self.mins_y);
558 WriteCoord(MSG_ENTITY, self.mins_z);
559 WriteCoord(MSG_ENTITY, self.maxs_x);
560 WriteCoord(MSG_ENTITY, self.maxs_y);
561 WriteCoord(MSG_ENTITY, self.maxs_z);
565 WriteShort(MSG_ENTITY, 0);
566 WriteCoord(MSG_ENTITY, self.maxs_x);
567 WriteCoord(MSG_ENTITY, self.maxs_y);
568 WriteCoord(MSG_ENTITY, self.maxs_z);
570 WriteShort(MSG_ENTITY, self.cnt);
571 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
572 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
573 WriteCoord(MSG_ENTITY, self.waterlevel);
574 WriteCoord(MSG_ENTITY, self.count);
575 WriteByte(MSG_ENTITY, self.glow_color);
576 WriteString(MSG_ENTITY, self.noise);
581 void pointparticles_use()
583 self.state = !self.state;
587 void pointparticles_think()
589 if(self.origin != self.oldorigin)
592 self.oldorigin = self.origin;
594 self.nextthink = time;
597 void spawnfunc_func_pointparticles()
600 setmodel(self, self.model);
602 precache_sound (self.noise);
604 self.effects = EF_NODEPTHTEST;
605 self.SendEntity = pointparticles_SendEntity;
608 vector misave, masave;
611 setmodel(self, "null");
612 setorigin(self, self.origin + misave);
613 setsize(self, '0 0 0', masave - misave);
616 self.cnt = particleeffectnum(self.mdl);
619 self.use = pointparticles_use;
620 if(self.spawnflags & 1)
627 self.think = pointparticles_think;
628 self.nextthink = time;
631 void spawnfunc_func_sparks()
633 // self.cnt is the amount of sparks that one burst will spawn
635 self.cnt = 25.0; // nice default value
638 // self.wait is the probability that a sparkthink will spawn a spark shower
639 // range: 0 - 1, but 0 makes little sense, so...
640 if(self.wait < 0.05) {
641 self.wait = 0.25; // nice default value
644 self.count = self.cnt;
647 self.velocity = '0 0 -1';
648 self.mdl = "TE_SPARK";
649 self.impulse = 10 * self.wait; // by default 2.5/sec
651 self.cnt = 0; // use mdl
653 spawnfunc_func_pointparticles();
656 float rainsnow_SendEntity(float to)
658 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
659 WriteByte(MSG_ENTITY, self.state);
660 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
661 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
662 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
663 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
664 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
665 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
666 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
667 WriteShort(MSG_ENTITY, self.count);
668 WriteByte(MSG_ENTITY, self.cnt);
672 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
673 This is an invisible area like a trigger, which rain falls inside of.
677 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
679 sets color of rain (default 12 - white)
681 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
683 void spawnfunc_func_rain()
685 self.dest = self.velocity;
686 self.velocity = '0 0 0';
688 self.dest = '0 0 -700';
689 self.angles = '0 0 0';
690 self.movetype = MOVETYPE_NONE;
691 self.solid = SOLID_NOT;
692 SetBrushEntityModel();
697 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
700 if(self.count > 65535)
703 self.state = 1; // 1 is rain, 0 is snow
704 self.effects = EF_NODEPTHTEST;
705 self.SendEntity = rainsnow_SendEntity;
710 vector misave, masave;
713 setmodel(self, "null");
714 setsize(self, misave, masave);
719 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
720 This is an invisible area like a trigger, which snow falls inside of.
724 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
726 sets color of rain (default 12 - white)
728 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
730 void spawnfunc_func_snow()
732 self.dest = self.velocity;
733 self.velocity = '0 0 0';
735 self.dest = '0 0 -300';
736 self.angles = '0 0 0';
737 self.movetype = MOVETYPE_NONE;
738 self.solid = SOLID_NOT;
739 SetBrushEntityModel();
744 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
747 if(self.count > 65535)
750 self.state = 0; // 1 is rain, 0 is snow
751 self.effects = EF_NODEPTHTEST;
752 self.SendEntity = rainsnow_SendEntity;
757 vector misave, masave;
760 setmodel(self, "null");
761 setsize(self, misave, masave);
766 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
769 void misc_laser_aim()
774 if(self.spawnflags & 2)
776 if(self.enemy.origin != self.mangle)
778 self.mangle = self.enemy.origin;
784 a = vectoangles(self.enemy.origin - self.origin);
795 if(self.angles != self.mangle)
797 self.mangle = self.angles;
801 if(self.origin != self.oldorigin)
804 self.oldorigin = self.origin;
808 void misc_laser_init()
810 if(self.target != "")
811 self.enemy = find(world, targetname, self.target);
815 void misc_laser_think()
820 self.nextthink = time;
829 o = self.enemy.origin;
830 if not(self.spawnflags & 2)
831 o = self.origin + normalize(o - self.origin) * 32768;
835 makevectors(self.mangle);
836 o = self.origin + v_forward * 32768;
842 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
844 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
847 if(self.enemy.target != "") // DETECTOR laser
849 traceline(self.origin, o, MOVE_NORMAL, self);
850 if(trace_ent.iscreature)
852 self.pusher = trace_ent;
859 activator = self.pusher;
872 activator = self.pusher;
880 float laser_SendEntity(entity to, float fl)
882 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
883 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
884 if(self.spawnflags & 2)
888 if(self.scale != 1 || self.modelscale != 1)
890 WriteByte(MSG_ENTITY, fl);
893 WriteCoord(MSG_ENTITY, self.origin_x);
894 WriteCoord(MSG_ENTITY, self.origin_y);
895 WriteCoord(MSG_ENTITY, self.origin_z);
899 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
900 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
901 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
903 WriteByte(MSG_ENTITY, self.alpha * 255.0);
906 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
907 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
909 WriteShort(MSG_ENTITY, self.cnt + 1);
915 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
916 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
917 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
921 WriteCoord(MSG_ENTITY, self.mangle_x);
922 WriteCoord(MSG_ENTITY, self.mangle_y);
926 WriteByte(MSG_ENTITY, self.state);
930 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
931 Any object touching the beam will be hurt
934 spawnfunc_target_position where the laser ends
936 name of beam end effect to use
938 color of the beam (default: red)
940 damage per second (-1 for a laser that kills immediately)
944 self.state = !self.state;
949 void spawnfunc_misc_laser()
953 if(self.mdl == "none")
957 self.cnt = particleeffectnum(self.mdl);
960 self.cnt = particleeffectnum("laser_deadly");
966 self.cnt = particleeffectnum("laser_deadly");
973 if(self.colormod == '0 0 0')
975 self.colormod = '1 0 0';
977 self.message = "saw the light";
979 self.message2 = "was pushed into a laser by";
984 self.think = misc_laser_think;
985 self.nextthink = time;
986 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
988 self.effects = EF_NODEPTHTEST;
989 self.SendEntity = laser_SendEntity;
990 setmodel(self, "null");
991 self.mangle = self.angles;
995 self.use = laser_use;
996 if(self.spawnflags & 1)
1005 // tZorks trigger impulse / gravity
1009 .float lastpushtime;
1011 // targeted (directional) mode
1012 void trigger_impulse_touch1()
1015 float pushdeltatime;
1018 // FIXME: Better checking for what to push and not.
1019 if not(other.iscreature)
1020 if (other.classname != "corpse")
1021 if (other.classname != "body")
1022 if (other.classname != "gib")
1023 if (other.classname != "missile")
1024 if (other.classname != "rocket")
1025 if (other.classname != "casing")
1026 if (other.classname != "grenade")
1027 if (other.classname != "plasma")
1028 if (other.classname != "plasma_prim")
1029 if (other.classname != "plasma_chain")
1030 if (other.classname != "droppedweapon")
1033 if (other.deadflag && other.iscreature)
1038 targ = find(world, targetname, self.target);
1041 objerror("trigger_force without a (valid) .target!\n");
1046 if(self.falloff == 1)
1047 str = (str / self.radius) * self.strength;
1048 else if(self.falloff == 2)
1049 str = (1 - (str / self.radius)) * self.strength;
1051 str = self.strength;
1053 pushdeltatime = time - other.lastpushtime;
1054 if (pushdeltatime > 0.15) pushdeltatime = 0;
1055 other.lastpushtime = time;
1056 if(!pushdeltatime) return;
1058 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1059 other.flags &~= FL_ONGROUND;
1062 // Directionless (accelerator/decelerator) mode
1063 void trigger_impulse_touch2()
1065 float pushdeltatime;
1067 // FIXME: Better checking for what to push and not.
1068 if not(other.iscreature)
1069 if (other.classname != "corpse")
1070 if (other.classname != "body")
1071 if (other.classname != "gib")
1072 if (other.classname != "missile")
1073 if (other.classname != "rocket")
1074 if (other.classname != "casing")
1075 if (other.classname != "grenade")
1076 if (other.classname != "plasma")
1077 if (other.classname != "plasma_prim")
1078 if (other.classname != "plasma_chain")
1079 if (other.classname != "droppedweapon")
1082 if (other.deadflag && other.iscreature)
1087 pushdeltatime = time - other.lastpushtime;
1088 if (pushdeltatime > 0.15) pushdeltatime = 0;
1089 other.lastpushtime = time;
1090 if(!pushdeltatime) return;
1092 //if(self.strength > 1)
1093 other.velocity = other.velocity * (self.strength * pushdeltatime);
1095 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1098 // Spherical (gravity/repulsor) mode
1099 void trigger_impulse_touch3()
1101 float pushdeltatime;
1104 // FIXME: Better checking for what to push and not.
1105 if not(other.iscreature)
1106 if (other.classname != "corpse")
1107 if (other.classname != "body")
1108 if (other.classname != "gib")
1109 if (other.classname != "missile")
1110 if (other.classname != "rocket")
1111 if (other.classname != "casing")
1112 if (other.classname != "grenade")
1113 if (other.classname != "plasma")
1114 if (other.classname != "plasma_prim")
1115 if (other.classname != "plasma_chain")
1116 if (other.classname != "droppedweapon")
1119 if (other.deadflag && other.iscreature)
1124 pushdeltatime = time - other.lastpushtime;
1125 if (pushdeltatime > 0.15) pushdeltatime = 0;
1126 other.lastpushtime = time;
1127 if(!pushdeltatime) return;
1129 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1131 str = min(self.radius, vlen(self.origin - other.origin));
1133 if(self.falloff == 1)
1134 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1135 else if(self.falloff == 2)
1136 str = (str / self.radius) * self.strength; // 0 in the inside
1138 str = self.strength;
1140 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1143 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1144 -------- KEYS --------
1145 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1146 If not, this trigger acts like a damper/accelerator field.
1148 strength : This is how mutch force to add in the direction of .target each second
1149 when .target is set. If not, this is hoe mutch to slow down/accelerate
1150 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1152 radius : If set, act as a spherical device rather then a liniar one.
1154 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1156 -------- NOTES --------
1157 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1158 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1161 void spawnfunc_trigger_impulse()
1166 if(!self.strength) self.strength = 2000;
1167 setorigin(self, self.origin);
1168 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1169 self.touch = trigger_impulse_touch3;
1175 if(!self.strength) self.strength = 950;
1176 self.touch = trigger_impulse_touch1;
1180 if(!self.strength) self.strength = 0.9;
1181 self.touch = trigger_impulse_touch2;
1186 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1187 "Flip-flop" trigger gate... lets only every second trigger event through
1191 self.state = !self.state;
1196 void spawnfunc_trigger_flipflop()
1198 if(self.spawnflags & 1)
1200 self.use = flipflop_use;
1203 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1204 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1208 self.nextthink = time + self.wait;
1209 self.enemy = activator;
1215 void monoflop_fixed_use()
1219 self.nextthink = time + self.wait;
1221 self.enemy = activator;
1225 void monoflop_think()
1228 activator = self.enemy;
1232 void spawnfunc_trigger_monoflop()
1236 if(self.spawnflags & 1)
1237 self.use = monoflop_fixed_use;
1239 self.use = monoflop_use;
1240 self.think = monoflop_think;
1244 void multivibrator_send()
1249 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1251 newstate = (time < cyclestart + self.wait);
1254 if(self.state != newstate)
1256 self.state = newstate;
1259 self.nextthink = cyclestart + self.wait + 0.01;
1261 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1264 void multivibrator_toggle()
1266 if(self.nextthink == 0)
1268 multivibrator_send();
1281 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1282 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1283 -------- KEYS --------
1284 target: trigger all entities with this targetname when it goes off
1285 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1286 phase: offset of the timing
1287 wait: "on" cycle time (default: 1)
1288 respawntime: "off" cycle time (default: same as wait)
1289 -------- SPAWNFLAGS --------
1290 START_ON: assume it is already turned on (when targeted)
1292 void spawnfunc_trigger_multivibrator()
1296 if(!self.respawntime)
1297 self.respawntime = self.wait;
1300 self.use = multivibrator_toggle;
1301 self.think = multivibrator_send;
1302 self.nextthink = time;
1306 if(!(self.spawnflags & 1))
1307 self.nextthink = 0; // wait for a trigger event
1310 self.nextthink = time;
1317 src = find(world, targetname, self.killtarget);
1318 dst = find(world, targetname, self.target);
1322 objerror("follow: could not find target/killtarget");
1326 dst.movetype = MOVETYPE_FOLLOW;
1328 dst.punchangle = src.angles;
1329 dst.view_ofs = dst.origin - src.origin;
1330 dst.v_angle = dst.angles - src.angles;
1335 void spawnfunc_misc_follow()
1337 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1342 void gamestart_use() {
1348 void spawnfunc_trigger_gamestart() {
1349 self.use = gamestart_use;
1353 self.think = self.use;
1354 self.nextthink = game_starttime + self.wait;
1357 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1363 .entity voicescript; // attached voice script
1364 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1365 .float voicescript_nextthink; // time to play next voice
1366 .float voicescript_voiceend; // time when this voice ends
1368 void target_voicescript_clear(entity pl)
1370 pl.voicescript = world;
1373 void target_voicescript_use()
1375 if(activator.voicescript != self)
1377 activator.voicescript = self;
1378 activator.voicescript_index = 0;
1379 activator.voicescript_nextthink = time + self.delay;
1383 void target_voicescript_next(entity pl)
1388 vs = pl.voicescript;
1391 if(vs.message == "")
1393 if(pl.classname != "player")
1398 if(time >= pl.voicescript_voiceend)
1400 if(time >= pl.voicescript_nextthink)
1402 // get the next voice...
1403 n = tokenize_sane(vs.message);
1405 if(pl.voicescript_index < vs.cnt)
1406 i = pl.voicescript_index * 2;
1407 else if(n > vs.cnt * 2)
1408 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1414 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1415 pl.voicescript_voiceend = time + stof(argv(i + 1));
1418 pl.voicescript = world;
1420 pl.voicescript_index += 1;
1421 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1426 void spawnfunc_target_voicescript()
1428 // netname: directory of the sound files
1429 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1430 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1431 // wait: average time between messages
1432 // delay: initial delay before the first message
1435 self.use = target_voicescript_use;
1437 n = tokenize_sane(self.message);
1439 for(i = 0; i+1 < n; i += 2)
1446 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1452 void trigger_relay_teamcheck_use()
1456 if(self.spawnflags & 2)
1458 if(activator.team != self.team)
1463 if(activator.team == self.team)
1469 if(self.spawnflags & 1)
1474 void spawnfunc_trigger_relay_teamcheck()
1476 self.team_saved = self.team;
1477 self.use = trigger_relay_teamcheck_use;
1482 void trigger_disablerelay_use()
1489 for(e = world; (e = find(e, targetname, self.target)); )
1491 if(e.use == SUB_UseTargets)
1493 e.use = SUB_DontUseTargets;
1496 else if(e.use == SUB_DontUseTargets)
1498 e.use = SUB_UseTargets;
1504 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1507 void spawnfunc_trigger_disablerelay()
1509 self.use = trigger_disablerelay_use;