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 = stemp.target; break;
90 case 1: s = stemp.target2; break;
91 case 2: s = stemp.target3; break;
92 case 3: s = stemp.target4; break;
96 for(t = world; (t = find(t, targetname, s)); )
99 //print(stemp.classname, " ", stemp.targetname, " -> ", t.classname, " ", t.targetname, "\n");
114 //=============================================================================
116 float SPAWNFLAG_NOMESSAGE = 1;
117 float SPAWNFLAG_NOTOUCH = 1;
119 // the wait time has passed, so set back up for another activation
124 self.health = self.max_health;
125 self.takedamage = DAMAGE_YES;
126 self.solid = SOLID_BBOX;
131 // the trigger was just touched/killed/used
132 // self.enemy should be set to the activator so it can be held through a delay
133 // so wait for the delay time before firing
136 if (self.nextthink > time)
138 return; // allready been triggered
141 if (self.classname == "trigger_secret")
143 if (self.enemy.classname != "player")
145 found_secrets = found_secrets + 1;
146 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
150 sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
152 // don't trigger again until reset
153 self.takedamage = DAMAGE_NO;
155 activator = self.enemy;
161 self.think = multi_wait;
162 self.nextthink = time + self.wait;
165 { // we can't just remove (self) here, because this is a touch function
166 // called wheil C code is looping through area links...
167 self.touch = SUB_Null;
169 self.nextthink = time + 0.1;
170 self.think = SUB_Remove;
176 self.enemy = activator;
182 if not(self.spawnflags & 2)
184 if not(other.iscreature)
188 if(self.team == other.team)
192 // if the trigger has an angles field, check player's facing direction
193 if (self.movedir != '0 0 0')
195 makevectors (other.angles);
196 if (v_forward * self.movedir < 0)
197 return; // not facing the right way
206 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
208 if (!self.takedamage)
210 self.health = self.health - damage;
211 if (self.health <= 0)
213 self.enemy = attacker;
218 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
219 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.
220 If "delay" is set, the trigger waits some time after activating before firing.
221 "wait" : Seconds between triggerings. (.2 default)
222 If notouch is set, the trigger is only fired by other entities, not by touching.
223 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
229 set "message" to text string
231 void spawnfunc_trigger_multiple()
233 if (self.sounds == 1)
235 precache_sound ("misc/secret.wav");
236 self.noise = "misc/secret.wav";
238 else if (self.sounds == 2)
240 precache_sound ("misc/talk.wav");
241 self.noise = "misc/talk.wav";
243 else if (self.sounds == 3)
245 precache_sound ("misc/trigger1.wav");
246 self.noise = "misc/trigger1.wav";
251 self.use = multi_use;
257 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
258 objerror ("health and notouch don't make sense\n");
259 self.max_health = self.health;
260 self.event_damage = multi_eventdamage;
261 self.takedamage = DAMAGE_YES;
262 self.solid = SOLID_BBOX;
263 setorigin (self, self.origin); // make sure it links into the world
267 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
269 self.touch = multi_touch;
270 setorigin (self, self.origin); // make sure it links into the world
276 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
277 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
278 "targetname". If "health" is set, the trigger must be killed to activate.
279 If notouch is set, the trigger is only fired by other entities, not by touching.
280 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
281 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.
287 set "message" to text string
289 void spawnfunc_trigger_once()
292 spawnfunc_trigger_multiple();
295 //=============================================================================
297 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
298 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
300 void spawnfunc_trigger_relay()
302 self.use = SUB_UseTargets;
307 self.think = SUB_UseTargets;
308 self.nextthink = self.wait;
311 void spawnfunc_trigger_delay()
316 self.use = delay_use;
319 //=============================================================================
324 self.count = self.count - 1;
330 if (activator.classname == "player"
331 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
334 centerprint (activator, "There are more to go...");
335 else if (self.count == 3)
336 centerprint (activator, "Only 3 more to go...");
337 else if (self.count == 2)
338 centerprint (activator, "Only 2 more to go...");
340 centerprint (activator, "Only 1 more to go...");
345 if (activator.classname == "player"
346 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
347 centerprint(activator, "Sequence completed!");
348 self.enemy = activator;
352 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
353 Acts as an intermediary for an action that takes multiple inputs.
355 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
357 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
359 void spawnfunc_trigger_counter()
365 self.use = counter_use;
368 .float triggerhurttime;
369 void trigger_hurt_touch()
371 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
372 if (other.iscreature)
374 if (other.takedamage)
375 if (other.triggerhurttime < time)
378 other.triggerhurttime = time + 1;
379 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
386 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
389 other.pain_finished = min(other.pain_finished, time + 2);
391 else if (other.classname == "rune") // reset runes
394 other.nextthink = min(other.nextthink, time + 1);
402 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
403 Any object touching this will be hurt
404 set dmg to damage amount
407 .entity trigger_hurt_next;
408 entity trigger_hurt_last;
409 entity trigger_hurt_first;
410 void spawnfunc_trigger_hurt()
413 self.touch = trigger_hurt_touch;
417 self.message = "was in the wrong place";
419 self.message2 = "was thrown into a world of hurt by";
421 if(!trigger_hurt_first)
422 trigger_hurt_first = self;
423 if(trigger_hurt_last)
424 trigger_hurt_last.trigger_hurt_next = self;
425 trigger_hurt_last = self;
428 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
432 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
433 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
439 //////////////////////////////////////////////////////////////
443 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
445 //////////////////////////////////////////////////////////////
447 .float triggerhealtime;
448 void trigger_heal_touch()
450 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
451 if (other.iscreature)
453 if (other.takedamage)
454 if (other.triggerhealtime < time)
457 other.triggerhealtime = time + 1;
459 if (other.health < self.max_health)
461 other.health = min(other.health + self.health, self.max_health);
462 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
463 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
469 void spawnfunc_trigger_heal()
472 self.touch = trigger_heal_touch;
475 if (!self.max_health)
476 self.max_health = 200; //Max health topoff for field
478 self.noise = "misc/mediumhealth.wav";
479 precache_sound(self.noise);
483 //////////////////////////////////////////////////////////////
489 //////////////////////////////////////////////////////////////
493 // TODO add a way to do looped sounds with sound(); then complete this entity
494 .float volume, atten;
495 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
497 void spawnfunc_target_speaker()
500 precache_sound (self.noise);
504 self.atten = ATTN_NORM;
505 else if(self.atten < 0)
509 self.use = target_speaker_use;
514 self.atten = ATTN_STATIC;
515 else if(self.atten < 0)
519 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
524 void spawnfunc_func_stardust() {
525 self.effects = EF_STARDUST;
528 float pointparticles_SendEntity(entity to, float fl)
530 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
531 WriteByte(MSG_ENTITY, fl);
535 WriteCoord(MSG_ENTITY, self.impulse);
537 WriteCoord(MSG_ENTITY, 0); // off
541 WriteCoord(MSG_ENTITY, self.origin_x);
542 WriteCoord(MSG_ENTITY, self.origin_y);
543 WriteCoord(MSG_ENTITY, self.origin_z);
547 if(self.model != "null")
549 WriteShort(MSG_ENTITY, self.modelindex);
550 WriteCoord(MSG_ENTITY, self.mins_x);
551 WriteCoord(MSG_ENTITY, self.mins_y);
552 WriteCoord(MSG_ENTITY, self.mins_z);
553 WriteCoord(MSG_ENTITY, self.maxs_x);
554 WriteCoord(MSG_ENTITY, self.maxs_y);
555 WriteCoord(MSG_ENTITY, self.maxs_z);
559 WriteShort(MSG_ENTITY, 0);
560 WriteCoord(MSG_ENTITY, self.maxs_x);
561 WriteCoord(MSG_ENTITY, self.maxs_y);
562 WriteCoord(MSG_ENTITY, self.maxs_z);
564 WriteShort(MSG_ENTITY, self.cnt);
565 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
566 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
567 WriteCoord(MSG_ENTITY, self.waterlevel);
568 WriteCoord(MSG_ENTITY, self.count);
569 WriteByte(MSG_ENTITY, self.glow_color);
570 WriteString(MSG_ENTITY, self.noise);
575 void pointparticles_use()
577 self.state = !self.state;
581 void pointparticles_think()
583 if(self.origin != self.oldorigin)
586 self.oldorigin = self.origin;
588 self.nextthink = time;
591 void spawnfunc_func_pointparticles()
594 setmodel(self, self.model);
596 precache_sound (self.noise);
598 self.effects = EF_NODEPTHTEST;
599 self.SendEntity = pointparticles_SendEntity;
602 vector misave, masave;
605 setmodel(self, "null");
606 setorigin(self, self.origin + misave);
607 setsize(self, '0 0 0', masave - misave);
610 self.cnt = particleeffectnum(self.mdl);
613 self.use = pointparticles_use;
614 if(self.spawnflags & 1)
621 self.think = pointparticles_think;
622 self.nextthink = time;
625 void spawnfunc_func_sparks()
627 // self.cnt is the amount of sparks that one burst will spawn
629 self.cnt = 25.0; // nice default value
632 // self.wait is the probability that a sparkthink will spawn a spark shower
633 // range: 0 - 1, but 0 makes little sense, so...
634 if(self.wait < 0.05) {
635 self.wait = 0.25; // nice default value
638 self.count = self.cnt;
641 self.velocity = '0 0 -1';
642 self.mdl = "TE_SPARK";
643 self.impulse = 10 * self.wait; // by default 2.5/sec
645 self.cnt = 0; // use mdl
647 spawnfunc_func_pointparticles();
650 float rainsnow_SendEntity(float to)
652 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
653 WriteByte(MSG_ENTITY, self.state);
654 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
655 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
656 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
657 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
658 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
659 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
660 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
661 WriteShort(MSG_ENTITY, self.count);
662 WriteByte(MSG_ENTITY, self.cnt);
666 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
667 This is an invisible area like a trigger, which rain falls inside of.
671 falling direction (should be something like '0 0 -700', 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_rain()
679 self.dest = self.velocity;
680 self.velocity = '0 0 0';
682 self.dest = '0 0 -700';
683 self.angles = '0 0 0';
684 self.movetype = MOVETYPE_NONE;
685 self.solid = SOLID_NOT;
686 SetBrushEntityModel();
691 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
694 if(self.count > 65535)
697 self.state = 1; // 1 is rain, 0 is snow
698 self.effects = EF_NODEPTHTEST;
699 self.SendEntity = rainsnow_SendEntity;
704 vector misave, masave;
707 setmodel(self, "null");
708 setsize(self, misave, masave);
713 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
714 This is an invisible area like a trigger, which snow falls inside of.
718 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
720 sets color of rain (default 12 - white)
722 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
724 void spawnfunc_func_snow()
726 self.dest = self.velocity;
727 self.velocity = '0 0 0';
729 self.dest = '0 0 -300';
730 self.angles = '0 0 0';
731 self.movetype = MOVETYPE_NONE;
732 self.solid = SOLID_NOT;
733 SetBrushEntityModel();
738 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
741 if(self.count > 65535)
744 self.state = 0; // 1 is rain, 0 is snow
745 self.effects = EF_NODEPTHTEST;
746 self.SendEntity = rainsnow_SendEntity;
751 vector misave, masave;
754 setmodel(self, "null");
755 setsize(self, misave, masave);
760 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
763 void misc_laser_aim()
768 if(self.spawnflags & 2)
770 if(self.enemy.origin != self.mangle)
772 self.mangle = self.enemy.origin;
778 a = vectoangles(self.enemy.origin - self.origin);
789 if(self.angles != self.mangle)
791 self.mangle = self.angles;
795 if(self.origin != self.oldorigin)
798 self.oldorigin = self.origin;
802 void misc_laser_init()
804 if(self.target != "")
805 self.enemy = find(world, targetname, self.target);
809 void misc_laser_think()
814 self.nextthink = time;
823 o = self.enemy.origin;
824 if not(self.spawnflags & 2)
825 o = self.origin + normalize(o - self.origin) * 32768;
829 makevectors(self.mangle);
830 o = self.origin + v_forward * 32768;
836 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
838 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
841 if(self.enemy.target != "") // DETECTOR laser
843 traceline(self.origin, o, MOVE_NORMAL, self);
844 if(trace_ent.iscreature)
846 self.pusher = trace_ent;
853 activator = self.pusher;
866 activator = self.pusher;
874 float laser_SendEntity(entity to, float fl)
876 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
877 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
878 if(self.spawnflags & 2)
882 if(self.scale != 1 || self.modelscale != 1)
884 WriteByte(MSG_ENTITY, fl);
887 WriteCoord(MSG_ENTITY, self.origin_x);
888 WriteCoord(MSG_ENTITY, self.origin_y);
889 WriteCoord(MSG_ENTITY, self.origin_z);
893 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
894 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
895 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
897 WriteByte(MSG_ENTITY, self.alpha * 255.0);
900 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
901 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
903 WriteShort(MSG_ENTITY, self.cnt + 1);
909 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
910 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
911 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
915 WriteCoord(MSG_ENTITY, self.mangle_x);
916 WriteCoord(MSG_ENTITY, self.mangle_y);
920 WriteByte(MSG_ENTITY, self.state);
924 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
925 Any object touching the beam will be hurt
928 spawnfunc_target_position where the laser ends
930 name of beam end effect to use
932 color of the beam (default: red)
934 damage per second (-1 for a laser that kills immediately)
938 self.state = !self.state;
943 void spawnfunc_misc_laser()
947 if(self.mdl == "none")
951 self.cnt = particleeffectnum(self.mdl);
954 self.cnt = particleeffectnum("laser_deadly");
960 self.cnt = particleeffectnum("laser_deadly");
967 if(self.colormod == '0 0 0')
969 self.colormod = '1 0 0';
971 self.message = "saw the light";
973 self.message2 = "was pushed into a laser by";
978 self.think = misc_laser_think;
979 self.nextthink = time;
980 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
982 self.effects = EF_NODEPTHTEST;
983 self.SendEntity = laser_SendEntity;
984 setmodel(self, "null");
985 self.mangle = self.angles;
989 self.use = laser_use;
990 if(self.spawnflags & 1)
999 // tZorks trigger impulse / gravity
1003 .float lastpushtime;
1005 // targeted (directional) mode
1006 void trigger_impulse_touch1()
1009 float pushdeltatime;
1012 // FIXME: Better checking for what to push and not.
1013 if not(other.iscreature)
1014 if (other.classname != "corpse")
1015 if (other.classname != "body")
1016 if (other.classname != "gib")
1017 if (other.classname != "missile")
1018 if (other.classname != "rocket")
1019 if (other.classname != "casing")
1020 if (other.classname != "grenade")
1021 if (other.classname != "plasma")
1022 if (other.classname != "plasma_prim")
1023 if (other.classname != "plasma_chain")
1024 if (other.classname != "droppedweapon")
1027 if (other.deadflag && other.iscreature)
1032 targ = find(world, targetname, self.target);
1035 objerror("trigger_force without a (valid) .target!\n");
1040 if(self.falloff == 1)
1041 str = (str / self.radius) * self.strength;
1042 else if(self.falloff == 2)
1043 str = (1 - (str / self.radius)) * self.strength;
1045 str = self.strength;
1047 pushdeltatime = time - other.lastpushtime;
1048 if (pushdeltatime > 0.15) pushdeltatime = 0;
1049 other.lastpushtime = time;
1050 if(!pushdeltatime) return;
1052 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1053 other.flags &~= FL_ONGROUND;
1056 // Directionless (accelerator/decelerator) mode
1057 void trigger_impulse_touch2()
1059 float pushdeltatime;
1061 // FIXME: Better checking for what to push and not.
1062 if not(other.iscreature)
1063 if (other.classname != "corpse")
1064 if (other.classname != "body")
1065 if (other.classname != "gib")
1066 if (other.classname != "missile")
1067 if (other.classname != "rocket")
1068 if (other.classname != "casing")
1069 if (other.classname != "grenade")
1070 if (other.classname != "plasma")
1071 if (other.classname != "plasma_prim")
1072 if (other.classname != "plasma_chain")
1073 if (other.classname != "droppedweapon")
1076 if (other.deadflag && other.iscreature)
1081 pushdeltatime = time - other.lastpushtime;
1082 if (pushdeltatime > 0.15) pushdeltatime = 0;
1083 other.lastpushtime = time;
1084 if(!pushdeltatime) return;
1086 //if(self.strength > 1)
1087 other.velocity = other.velocity * (self.strength * pushdeltatime);
1089 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1092 // Spherical (gravity/repulsor) mode
1093 void trigger_impulse_touch3()
1095 float pushdeltatime;
1098 // FIXME: Better checking for what to push and not.
1099 if not(other.iscreature)
1100 if (other.classname != "corpse")
1101 if (other.classname != "body")
1102 if (other.classname != "gib")
1103 if (other.classname != "missile")
1104 if (other.classname != "rocket")
1105 if (other.classname != "casing")
1106 if (other.classname != "grenade")
1107 if (other.classname != "plasma")
1108 if (other.classname != "plasma_prim")
1109 if (other.classname != "plasma_chain")
1110 if (other.classname != "droppedweapon")
1113 if (other.deadflag && other.iscreature)
1118 pushdeltatime = time - other.lastpushtime;
1119 if (pushdeltatime > 0.15) pushdeltatime = 0;
1120 other.lastpushtime = time;
1121 if(!pushdeltatime) return;
1123 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1125 str = min(self.radius, vlen(self.origin - other.origin));
1127 if(self.falloff == 1)
1128 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1129 else if(self.falloff == 2)
1130 str = (str / self.radius) * self.strength; // 0 in the inside
1132 str = self.strength;
1134 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1137 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1138 -------- KEYS --------
1139 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1140 If not, this trigger acts like a damper/accelerator field.
1142 strength : This is how mutch force to add in the direction of .target each second
1143 when .target is set. If not, this is hoe mutch to slow down/accelerate
1144 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1146 radius : If set, act as a spherical device rather then a liniar one.
1148 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1150 -------- NOTES --------
1151 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1152 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1155 void spawnfunc_trigger_impulse()
1160 if(!self.strength) self.strength = 2000;
1161 setorigin(self, self.origin);
1162 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1163 self.touch = trigger_impulse_touch3;
1169 if(!self.strength) self.strength = 950;
1170 self.touch = trigger_impulse_touch1;
1174 if(!self.strength) self.strength = 0.9;
1175 self.touch = trigger_impulse_touch2;
1180 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1181 "Flip-flop" trigger gate... lets only every second trigger event through
1185 self.state = !self.state;
1190 void spawnfunc_trigger_flipflop()
1192 if(self.spawnflags & 1)
1194 self.use = flipflop_use;
1197 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1198 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1202 self.nextthink = time + self.wait;
1203 self.enemy = activator;
1209 void monoflop_fixed_use()
1213 self.nextthink = time + self.wait;
1215 self.enemy = activator;
1219 void monoflop_think()
1222 activator = self.enemy;
1226 void spawnfunc_trigger_monoflop()
1230 if(self.spawnflags & 1)
1231 self.use = monoflop_fixed_use;
1233 self.use = monoflop_use;
1234 self.think = monoflop_think;
1238 void multivibrator_send()
1243 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1245 newstate = (time < cyclestart + self.wait);
1248 if(self.state != newstate)
1250 self.state = newstate;
1253 self.nextthink = cyclestart + self.wait + 0.01;
1255 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1258 void multivibrator_toggle()
1260 if(self.nextthink == 0)
1262 multivibrator_send();
1275 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1276 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1277 -------- KEYS --------
1278 target: trigger all entities with this targetname when it goes off
1279 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1280 phase: offset of the timing
1281 wait: "on" cycle time (default: 1)
1282 respawntime: "off" cycle time (default: same as wait)
1283 -------- SPAWNFLAGS --------
1284 START_ON: assume it is already turned on (when targeted)
1286 void spawnfunc_trigger_multivibrator()
1290 if(!self.respawntime)
1291 self.respawntime = self.wait;
1294 self.use = multivibrator_toggle;
1295 self.think = multivibrator_send;
1296 self.nextthink = time;
1300 if(!(self.spawnflags & 1))
1301 self.nextthink = 0; // wait for a trigger event
1304 self.nextthink = time;
1311 src = find(world, targetname, self.killtarget);
1312 dst = find(world, targetname, self.target);
1316 objerror("follow: could not find target/killtarget");
1320 dst.movetype = MOVETYPE_FOLLOW;
1322 dst.punchangle = src.angles;
1323 dst.view_ofs = dst.origin - src.origin;
1324 dst.v_angle = dst.angles - src.angles;
1329 void spawnfunc_misc_follow()
1331 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1336 void gamestart_use() {
1342 void spawnfunc_trigger_gamestart() {
1345 self.think = gamestart_use;
1346 self.nextthink = self.wait;
1349 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1355 .entity voicescript; // attached voice script
1356 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1357 .float voicescript_nextthink; // time to play next voice
1358 .float voicescript_voiceend; // time when this voice ends
1360 void target_voicescript_clear(entity pl)
1362 pl.voicescript = world;
1365 void target_voicescript_use()
1367 if(activator.voicescript != self)
1369 activator.voicescript = self;
1370 activator.voicescript_index = 0;
1371 activator.voicescript_nextthink = time + self.delay;
1375 void target_voicescript_next(entity pl)
1380 vs = pl.voicescript;
1383 if(vs.message == "")
1385 if(pl.classname != "player")
1390 if(time >= pl.voicescript_voiceend)
1392 if(time >= pl.voicescript_nextthink)
1394 // get the next voice...
1395 n = tokenize_sane(vs.message);
1397 if(pl.voicescript_index < vs.cnt)
1398 i = pl.voicescript_index * 2;
1399 else if(n > vs.cnt * 2)
1400 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1406 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1407 pl.voicescript_voiceend = time + stof(argv(i + 1));
1410 pl.voicescript = world;
1412 pl.voicescript_index += 1;
1413 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1418 void spawnfunc_target_voicescript()
1420 // netname: directory of the sound files
1421 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1422 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1423 // wait: average time between messages
1424 // delay: initial delay before the first message
1427 self.use = target_voicescript_use;
1429 n = tokenize_sane(self.message);
1431 for(i = 0; i+1 < n; i += 2)
1438 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));