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;
177 self.enemy = activator;
183 if not(self.spawnflags & 2)
185 if not(other.iscreature)
189 if(self.team == other.team)
193 // if the trigger has an angles field, check player's facing direction
194 if (self.movedir != '0 0 0')
196 makevectors (other.angles);
197 if (v_forward * self.movedir < 0)
198 return; // not facing the right way
207 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
209 if (!self.takedamage)
211 self.health = self.health - damage;
212 if (self.health <= 0)
214 self.enemy = attacker;
221 self.touch = multi_touch;
222 self.health = self.max_health;
223 self.takedamage = DAMAGE_YES;
224 self.solid = SOLID_BBOX;
225 self.think = SUB_Null;
226 self.team = self.team_saved;
229 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
230 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.
231 If "delay" is set, the trigger waits some time after activating before firing.
232 "wait" : Seconds between triggerings. (.2 default)
233 If notouch is set, the trigger is only fired by other entities, not by touching.
234 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
240 set "message" to text string
242 void spawnfunc_trigger_multiple()
244 self.reset = multi_reset;
245 if (self.sounds == 1)
247 precache_sound ("misc/secret.wav");
248 self.noise = "misc/secret.wav";
250 else if (self.sounds == 2)
252 precache_sound ("misc/talk.wav");
253 self.noise = "misc/talk.wav";
255 else if (self.sounds == 3)
257 precache_sound ("misc/trigger1.wav");
258 self.noise = "misc/trigger1.wav";
263 self.use = multi_use;
267 self.team_saved = self.team;
271 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
272 objerror ("health and notouch don't make sense\n");
273 self.max_health = self.health;
274 self.event_damage = multi_eventdamage;
275 self.takedamage = DAMAGE_YES;
276 self.solid = SOLID_BBOX;
277 setorigin (self, self.origin); // make sure it links into the world
281 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
283 self.touch = multi_touch;
284 setorigin (self, self.origin); // make sure it links into the world
290 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
291 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
292 "targetname". If "health" is set, the trigger must be killed to activate.
293 If notouch is set, the trigger is only fired by other entities, not by touching.
294 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
295 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.
301 set "message" to text string
303 void spawnfunc_trigger_once()
306 spawnfunc_trigger_multiple();
309 //=============================================================================
311 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
312 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
314 void spawnfunc_trigger_relay()
316 self.use = SUB_UseTargets;
317 self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
322 self.think = SUB_UseTargets;
323 self.nextthink = self.wait;
328 self.think = SUB_Null;
331 void spawnfunc_trigger_delay()
336 self.use = delay_use;
337 self.reset = delay_reset;
340 //=============================================================================
345 self.count = self.count - 1;
351 if (activator.classname == "player"
352 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
355 centerprint (activator, "There are more to go...");
356 else if (self.count == 3)
357 centerprint (activator, "Only 3 more to go...");
358 else if (self.count == 2)
359 centerprint (activator, "Only 2 more to go...");
361 centerprint (activator, "Only 1 more to go...");
366 if (activator.classname == "player"
367 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
368 centerprint(activator, "Sequence completed!");
369 self.enemy = activator;
375 self.count = self.cnt;
379 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
380 Acts as an intermediary for an action that takes multiple inputs.
382 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
384 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
386 void spawnfunc_trigger_counter()
391 self.cnt = self.count;
393 self.use = counter_use;
394 self.reset = counter_reset;
397 .float triggerhurttime;
398 void trigger_hurt_touch()
400 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
401 if (other.iscreature)
403 if (other.takedamage)
404 if (other.triggerhurttime < time)
407 other.triggerhurttime = time + 1;
408 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
415 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
418 other.pain_finished = min(other.pain_finished, time + 2);
420 else if (other.classname == "rune") // reset runes
423 other.nextthink = min(other.nextthink, time + 1);
431 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
432 Any object touching this will be hurt
433 set dmg to damage amount
436 .entity trigger_hurt_next;
437 entity trigger_hurt_last;
438 entity trigger_hurt_first;
439 void spawnfunc_trigger_hurt()
442 self.touch = trigger_hurt_touch;
446 self.message = "was in the wrong place";
448 self.message2 = "was thrown into a world of hurt by";
450 if(!trigger_hurt_first)
451 trigger_hurt_first = self;
452 if(trigger_hurt_last)
453 trigger_hurt_last.trigger_hurt_next = self;
454 trigger_hurt_last = self;
457 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
461 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
462 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
468 //////////////////////////////////////////////////////////////
472 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
474 //////////////////////////////////////////////////////////////
476 .float triggerhealtime;
477 void trigger_heal_touch()
479 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
480 if (other.iscreature)
482 if (other.takedamage)
483 if (other.triggerhealtime < time)
486 other.triggerhealtime = time + 1;
488 if (other.health < self.max_health)
490 other.health = min(other.health + self.health, self.max_health);
491 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
492 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
498 void spawnfunc_trigger_heal()
501 self.touch = trigger_heal_touch;
504 if (!self.max_health)
505 self.max_health = 200; //Max health topoff for field
507 self.noise = "misc/mediumhealth.wav";
508 precache_sound(self.noise);
512 //////////////////////////////////////////////////////////////
518 //////////////////////////////////////////////////////////////
522 // TODO add a way to do looped sounds with sound(); then complete this entity
523 .float volume, atten;
524 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
526 void spawnfunc_target_speaker()
529 precache_sound (self.noise);
533 self.atten = ATTN_NORM;
534 else if(self.atten < 0)
538 self.use = target_speaker_use;
543 self.atten = ATTN_STATIC;
544 else if(self.atten < 0)
548 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
553 void spawnfunc_func_stardust() {
554 self.effects = EF_STARDUST;
557 float pointparticles_SendEntity(entity to, float fl)
559 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
560 WriteByte(MSG_ENTITY, fl);
564 WriteCoord(MSG_ENTITY, self.impulse);
566 WriteCoord(MSG_ENTITY, 0); // off
570 WriteCoord(MSG_ENTITY, self.origin_x);
571 WriteCoord(MSG_ENTITY, self.origin_y);
572 WriteCoord(MSG_ENTITY, self.origin_z);
576 if(self.model != "null")
578 WriteShort(MSG_ENTITY, self.modelindex);
579 WriteCoord(MSG_ENTITY, self.mins_x);
580 WriteCoord(MSG_ENTITY, self.mins_y);
581 WriteCoord(MSG_ENTITY, self.mins_z);
582 WriteCoord(MSG_ENTITY, self.maxs_x);
583 WriteCoord(MSG_ENTITY, self.maxs_y);
584 WriteCoord(MSG_ENTITY, self.maxs_z);
588 WriteShort(MSG_ENTITY, 0);
589 WriteCoord(MSG_ENTITY, self.maxs_x);
590 WriteCoord(MSG_ENTITY, self.maxs_y);
591 WriteCoord(MSG_ENTITY, self.maxs_z);
593 WriteShort(MSG_ENTITY, self.cnt);
594 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
595 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
596 WriteCoord(MSG_ENTITY, self.waterlevel);
597 WriteCoord(MSG_ENTITY, self.count);
598 WriteByte(MSG_ENTITY, self.glow_color);
599 WriteString(MSG_ENTITY, self.noise);
604 void pointparticles_use()
606 self.state = !self.state;
610 void pointparticles_think()
612 if(self.origin != self.oldorigin)
615 self.oldorigin = self.origin;
617 self.nextthink = time;
620 void pointparticles_reset()
622 if(self.spawnflags & 1)
628 void spawnfunc_func_pointparticles()
631 setmodel(self, self.model);
633 precache_sound (self.noise);
637 setorigin(self, self.origin + self.mins);
638 setsize(self, '0 0 0', self.maxs - self.mins);
641 self.cnt = particleeffectnum(self.mdl);
643 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
647 self.use = pointparticles_use;
648 self.reset = pointparticles_reset;
653 self.think = pointparticles_think;
654 self.nextthink = time;
657 void spawnfunc_func_sparks()
659 // self.cnt is the amount of sparks that one burst will spawn
661 self.cnt = 25.0; // nice default value
664 // self.wait is the probability that a sparkthink will spawn a spark shower
665 // range: 0 - 1, but 0 makes little sense, so...
666 if(self.wait < 0.05) {
667 self.wait = 0.25; // nice default value
670 self.count = self.cnt;
673 self.velocity = '0 0 -1';
674 self.mdl = "TE_SPARK";
675 self.impulse = 10 * self.wait; // by default 2.5/sec
677 self.cnt = 0; // use mdl
679 spawnfunc_func_pointparticles();
682 float rainsnow_SendEntity(entity to, float sf)
684 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
685 WriteByte(MSG_ENTITY, self.state);
686 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
687 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
688 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
689 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
690 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
691 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
692 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
693 WriteShort(MSG_ENTITY, self.count);
694 WriteByte(MSG_ENTITY, self.cnt);
698 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
699 This is an invisible area like a trigger, which rain falls inside of.
703 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
705 sets color of rain (default 12 - white)
707 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
709 void spawnfunc_func_rain()
711 self.dest = self.velocity;
712 self.velocity = '0 0 0';
714 self.dest = '0 0 -700';
715 self.angles = '0 0 0';
716 self.movetype = MOVETYPE_NONE;
717 self.solid = SOLID_NOT;
718 SetBrushEntityModel();
723 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
726 if(self.count > 65535)
729 self.state = 1; // 1 is rain, 0 is snow
732 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
736 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
737 This is an invisible area like a trigger, which snow falls inside of.
741 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
743 sets color of rain (default 12 - white)
745 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
747 void spawnfunc_func_snow()
749 self.dest = self.velocity;
750 self.velocity = '0 0 0';
752 self.dest = '0 0 -300';
753 self.angles = '0 0 0';
754 self.movetype = MOVETYPE_NONE;
755 self.solid = SOLID_NOT;
756 SetBrushEntityModel();
761 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
764 if(self.count > 65535)
767 self.state = 0; // 1 is rain, 0 is snow
770 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
774 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
777 void misc_laser_aim()
782 if(self.spawnflags & 2)
784 if(self.enemy.origin != self.mangle)
786 self.mangle = self.enemy.origin;
792 a = vectoangles(self.enemy.origin - self.origin);
803 if(self.angles != self.mangle)
805 self.mangle = self.angles;
809 if(self.origin != self.oldorigin)
812 self.oldorigin = self.origin;
816 void misc_laser_init()
818 if(self.target != "")
819 self.enemy = find(world, targetname, self.target);
823 void misc_laser_think()
828 self.nextthink = time;
837 o = self.enemy.origin;
838 if not(self.spawnflags & 2)
839 o = self.origin + normalize(o - self.origin) * 32768;
843 makevectors(self.mangle);
844 o = self.origin + v_forward * 32768;
850 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
852 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
855 if(self.enemy.target != "") // DETECTOR laser
857 traceline(self.origin, o, MOVE_NORMAL, self);
858 if(trace_ent.iscreature)
860 self.pusher = trace_ent;
867 activator = self.pusher;
880 activator = self.pusher;
888 float laser_SendEntity(entity to, float fl)
890 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
891 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
892 if(self.spawnflags & 2)
896 if(self.scale != 1 || self.modelscale != 1)
898 WriteByte(MSG_ENTITY, fl);
901 WriteCoord(MSG_ENTITY, self.origin_x);
902 WriteCoord(MSG_ENTITY, self.origin_y);
903 WriteCoord(MSG_ENTITY, self.origin_z);
907 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
908 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
909 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
911 WriteByte(MSG_ENTITY, self.alpha * 255.0);
914 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
915 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
917 WriteShort(MSG_ENTITY, self.cnt + 1);
923 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
924 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
925 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
929 WriteAngle(MSG_ENTITY, self.mangle_x);
930 WriteAngle(MSG_ENTITY, self.mangle_y);
934 WriteByte(MSG_ENTITY, self.state);
938 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
939 Any object touching the beam will be hurt
942 spawnfunc_target_position where the laser ends
944 name of beam end effect to use
946 color of the beam (default: red)
948 damage per second (-1 for a laser that kills immediately)
952 self.state = !self.state;
959 if(self.spawnflags & 1)
965 void spawnfunc_misc_laser()
969 if(self.mdl == "none")
973 self.cnt = particleeffectnum(self.mdl);
976 self.cnt = particleeffectnum("laser_deadly");
982 self.cnt = particleeffectnum("laser_deadly");
989 if(self.colormod == '0 0 0')
991 self.colormod = '1 0 0';
993 self.message = "saw the light";
995 self.message2 = "was pushed into a laser by";
1000 self.think = misc_laser_think;
1001 self.nextthink = time;
1002 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1004 self.mangle = self.angles;
1006 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1010 self.reset = laser_reset;
1012 self.use = laser_use;
1018 // tZorks trigger impulse / gravity
1022 .float lastpushtime;
1024 // targeted (directional) mode
1025 void trigger_impulse_touch1()
1028 float pushdeltatime;
1031 // FIXME: Better checking for what to push and not.
1032 if not(other.iscreature)
1033 if (other.classname != "corpse")
1034 if (other.classname != "body")
1035 if (other.classname != "gib")
1036 if (other.classname != "missile")
1037 if (other.classname != "rocket")
1038 if (other.classname != "casing")
1039 if (other.classname != "grenade")
1040 if (other.classname != "plasma")
1041 if (other.classname != "plasma_prim")
1042 if (other.classname != "plasma_chain")
1043 if (other.classname != "droppedweapon")
1046 if (other.deadflag && other.iscreature)
1051 targ = find(world, targetname, self.target);
1054 objerror("trigger_force without a (valid) .target!\n");
1059 if(self.falloff == 1)
1060 str = (str / self.radius) * self.strength;
1061 else if(self.falloff == 2)
1062 str = (1 - (str / self.radius)) * self.strength;
1064 str = self.strength;
1066 pushdeltatime = time - other.lastpushtime;
1067 if (pushdeltatime > 0.15) pushdeltatime = 0;
1068 other.lastpushtime = time;
1069 if(!pushdeltatime) return;
1071 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1072 other.flags &~= FL_ONGROUND;
1075 // Directionless (accelerator/decelerator) mode
1076 void trigger_impulse_touch2()
1078 float pushdeltatime;
1080 // FIXME: Better checking for what to push and not.
1081 if not(other.iscreature)
1082 if (other.classname != "corpse")
1083 if (other.classname != "body")
1084 if (other.classname != "gib")
1085 if (other.classname != "missile")
1086 if (other.classname != "rocket")
1087 if (other.classname != "casing")
1088 if (other.classname != "grenade")
1089 if (other.classname != "plasma")
1090 if (other.classname != "plasma_prim")
1091 if (other.classname != "plasma_chain")
1092 if (other.classname != "droppedweapon")
1095 if (other.deadflag && other.iscreature)
1100 pushdeltatime = time - other.lastpushtime;
1101 if (pushdeltatime > 0.15) pushdeltatime = 0;
1102 other.lastpushtime = time;
1103 if(!pushdeltatime) return;
1105 //if(self.strength > 1)
1106 other.velocity = other.velocity * (self.strength * pushdeltatime);
1108 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1111 // Spherical (gravity/repulsor) mode
1112 void trigger_impulse_touch3()
1114 float pushdeltatime;
1117 // FIXME: Better checking for what to push and not.
1118 if not(other.iscreature)
1119 if (other.classname != "corpse")
1120 if (other.classname != "body")
1121 if (other.classname != "gib")
1122 if (other.classname != "missile")
1123 if (other.classname != "rocket")
1124 if (other.classname != "casing")
1125 if (other.classname != "grenade")
1126 if (other.classname != "plasma")
1127 if (other.classname != "plasma_prim")
1128 if (other.classname != "plasma_chain")
1129 if (other.classname != "droppedweapon")
1132 if (other.deadflag && other.iscreature)
1137 pushdeltatime = time - other.lastpushtime;
1138 if (pushdeltatime > 0.15) pushdeltatime = 0;
1139 other.lastpushtime = time;
1140 if(!pushdeltatime) return;
1142 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1144 str = min(self.radius, vlen(self.origin - other.origin));
1146 if(self.falloff == 1)
1147 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1148 else if(self.falloff == 2)
1149 str = (str / self.radius) * self.strength; // 0 in the inside
1151 str = self.strength;
1153 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1156 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1157 -------- KEYS --------
1158 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1159 If not, this trigger acts like a damper/accelerator field.
1161 strength : This is how mutch force to add in the direction of .target each second
1162 when .target is set. If not, this is hoe mutch to slow down/accelerate
1163 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1165 radius : If set, act as a spherical device rather then a liniar one.
1167 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1169 -------- NOTES --------
1170 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1171 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1174 void spawnfunc_trigger_impulse()
1179 if(!self.strength) self.strength = 2000;
1180 setorigin(self, self.origin);
1181 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1182 self.touch = trigger_impulse_touch3;
1188 if(!self.strength) self.strength = 950;
1189 self.touch = trigger_impulse_touch1;
1193 if(!self.strength) self.strength = 0.9;
1194 self.touch = trigger_impulse_touch2;
1199 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1200 "Flip-flop" trigger gate... lets only every second trigger event through
1204 self.state = !self.state;
1209 void spawnfunc_trigger_flipflop()
1211 if(self.spawnflags & 1)
1213 self.use = flipflop_use;
1214 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1217 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1218 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1222 self.nextthink = time + self.wait;
1223 self.enemy = activator;
1229 void monoflop_fixed_use()
1233 self.nextthink = time + self.wait;
1235 self.enemy = activator;
1239 void monoflop_think()
1242 activator = self.enemy;
1246 void monoflop_reset()
1252 void spawnfunc_trigger_monoflop()
1256 if(self.spawnflags & 1)
1257 self.use = monoflop_fixed_use;
1259 self.use = monoflop_use;
1260 self.think = monoflop_think;
1262 self.reset = monoflop_reset;
1265 void multivibrator_send()
1270 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1272 newstate = (time < cyclestart + self.wait);
1275 if(self.state != newstate)
1277 self.state = newstate;
1280 self.nextthink = cyclestart + self.wait + 0.01;
1282 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1285 void multivibrator_toggle()
1287 if(self.nextthink == 0)
1289 multivibrator_send();
1302 void multivibrator_reset()
1304 if(!(self.spawnflags & 1))
1305 self.nextthink = 0; // wait for a trigger event
1307 self.nextthink = max(1, time);
1310 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1311 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1312 -------- KEYS --------
1313 target: trigger all entities with this targetname when it goes off
1314 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1315 phase: offset of the timing
1316 wait: "on" cycle time (default: 1)
1317 respawntime: "off" cycle time (default: same as wait)
1318 -------- SPAWNFLAGS --------
1319 START_ON: assume it is already turned on (when targeted)
1321 void spawnfunc_trigger_multivibrator()
1325 if(!self.respawntime)
1326 self.respawntime = self.wait;
1329 self.use = multivibrator_toggle;
1330 self.think = multivibrator_send;
1331 self.nextthink = time;
1334 multivibrator_reset();
1341 src = find(world, targetname, self.killtarget);
1342 dst = find(world, targetname, self.target);
1346 objerror("follow: could not find target/killtarget");
1350 dst.movetype = MOVETYPE_FOLLOW;
1352 dst.punchangle = src.angles;
1353 dst.view_ofs = dst.origin - src.origin;
1354 dst.v_angle = dst.angles - src.angles;
1359 void spawnfunc_misc_follow()
1361 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1366 void gamestart_use() {
1372 void spawnfunc_trigger_gamestart() {
1373 self.use = gamestart_use;
1374 self.reset2 = spawnfunc_trigger_gamestart;
1378 self.think = self.use;
1379 self.nextthink = game_starttime + self.wait;
1382 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1388 .entity voicescript; // attached voice script
1389 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1390 .float voicescript_nextthink; // time to play next voice
1391 .float voicescript_voiceend; // time when this voice ends
1393 void target_voicescript_clear(entity pl)
1395 pl.voicescript = world;
1398 void target_voicescript_use()
1400 if(activator.voicescript != self)
1402 activator.voicescript = self;
1403 activator.voicescript_index = 0;
1404 activator.voicescript_nextthink = time + self.delay;
1408 void target_voicescript_next(entity pl)
1413 vs = pl.voicescript;
1416 if(vs.message == "")
1418 if(pl.classname != "player")
1423 if(time >= pl.voicescript_voiceend)
1425 if(time >= pl.voicescript_nextthink)
1427 // get the next voice...
1428 n = tokenize_sane(vs.message);
1430 if(pl.voicescript_index < vs.cnt)
1431 i = pl.voicescript_index * 2;
1432 else if(n > vs.cnt * 2)
1433 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1439 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1440 pl.voicescript_voiceend = time + stof(argv(i + 1));
1443 pl.voicescript = world;
1445 pl.voicescript_index += 1;
1446 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1451 void spawnfunc_target_voicescript()
1453 // netname: directory of the sound files
1454 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1455 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1456 // wait: average time between messages
1457 // delay: initial delay before the first message
1460 self.use = target_voicescript_use;
1462 n = tokenize_sane(self.message);
1464 for(i = 0; i+1 < n; i += 2)
1471 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1477 void trigger_relay_teamcheck_use()
1481 if(self.spawnflags & 2)
1483 if(activator.team != self.team)
1488 if(activator.team == self.team)
1494 if(self.spawnflags & 1)
1499 void trigger_relay_teamcheck_reset()
1501 self.team = self.team_saved;
1504 void spawnfunc_trigger_relay_teamcheck()
1506 self.team_saved = self.team;
1507 self.use = trigger_relay_teamcheck_use;
1508 self.reset = trigger_relay_teamcheck_reset;
1513 void trigger_disablerelay_use()
1520 for(e = world; (e = find(e, targetname, self.target)); )
1522 if(e.use == SUB_UseTargets)
1524 e.use = SUB_DontUseTargets;
1527 else if(e.use == SUB_DontUseTargets)
1529 e.use = SUB_UseTargets;
1535 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1538 void spawnfunc_trigger_disablerelay()
1540 self.use = trigger_disablerelay_use;