6 activator = self.enemy;
12 ==============================
15 the global "activator" should be set to the entity that initiated the firing.
17 If self.delay is set, a DelayedUse entity will be created that will actually
18 do the SUB_UseTargets after that many seconds have passed.
20 Centerprints any self.message to the activator.
22 Removes all entities with a targetname that match self.killtarget,
23 and removes them, so some events can remove other triggers.
25 Search for (string)targetname in all entities that
26 match (string)self.target and call their .use function
28 ==============================
32 local entity t, stemp, otemp, act;
39 // create a temp object to fire at a later time
41 t.classname = "DelayedUse";
42 t.nextthink = time + self.delay;
45 t.message = self.message;
46 t.killtarget = self.killtarget;
47 t.target = self.target;
55 if (activator.classname == "player" && self.message != "")
57 centerprint (activator, self.message);
59 sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
63 // kill the killtagets
70 t = find (t, targetname, self.killtarget);
86 t = find (t, targetname, self.target);
107 //=============================================================================
109 float SPAWNFLAG_NOMESSAGE = 1;
110 float SPAWNFLAG_NOTOUCH = 1;
112 // the wait time has passed, so set back up for another activation
117 self.health = self.max_health;
118 self.takedamage = DAMAGE_YES;
119 self.solid = SOLID_BBOX;
124 // the trigger was just touched/killed/used
125 // self.enemy should be set to the activator so it can be held through a delay
126 // so wait for the delay time before firing
129 if (self.nextthink > time)
131 return; // allready been triggered
134 if (self.classname == "trigger_secret")
136 if (self.enemy.classname != "player")
138 found_secrets = found_secrets + 1;
139 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
143 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
145 // don't trigger again until reset
146 self.takedamage = DAMAGE_NO;
148 activator = self.enemy;
154 self.think = multi_wait;
155 self.nextthink = time + self.wait;
158 { // we can't just remove (self) here, because this is a touch function
159 // called wheil C code is looping through area links...
160 self.touch = SUB_Null;
162 self.nextthink = time + 0.1;
163 self.think = SUB_Remove;
169 self.enemy = activator;
175 if (other.classname != "player")
179 if(self.team == other.team)
182 // if the trigger has an angles field, check player's facing direction
183 if (self.movedir != '0 0 0')
185 makevectors (other.angles);
186 if (v_forward * self.movedir < 0)
187 return; // not facing the right way
194 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
196 if (!self.takedamage)
198 self.health = self.health - damage;
199 if (self.health <= 0)
201 self.enemy = attacker;
206 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
207 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.
208 If "delay" is set, the trigger waits some time after activating before firing.
209 "wait" : Seconds between triggerings. (.2 default)
210 If notouch is set, the trigger is only fired by other entities, not by touching.
211 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
217 set "message" to text string
219 void spawnfunc_trigger_multiple()
221 if (self.sounds == 1)
223 precache_sound ("misc/secret.wav");
224 self.noise = "misc/secret.wav";
226 else if (self.sounds == 2)
228 precache_sound ("misc/talk.wav");
229 self.noise = "misc/talk.wav";
231 else if (self.sounds == 3)
233 precache_sound ("misc/trigger1.wav");
234 self.noise = "misc/trigger1.wav";
239 self.use = multi_use;
245 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
246 objerror ("health and notouch don't make sense\n");
247 self.max_health = self.health;
248 self.event_damage = multi_eventdamage;
249 self.takedamage = DAMAGE_YES;
250 self.solid = SOLID_BBOX;
251 setorigin (self, self.origin); // make sure it links into the world
255 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
257 self.touch = multi_touch;
263 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
264 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
265 "targetname". If "health" is set, the trigger must be killed to activate.
266 If notouch is set, the trigger is only fired by other entities, not by touching.
267 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
268 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.
274 set "message" to text string
276 void spawnfunc_trigger_once()
279 spawnfunc_trigger_multiple();
282 //=============================================================================
284 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
285 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
287 void spawnfunc_trigger_relay()
289 self.use = SUB_UseTargets;
294 self.think = SUB_UseTargets;
295 self.nextthink = self.wait;
298 void spawnfunc_trigger_delay()
303 self.use = delay_use;
306 //=============================================================================
311 self.count = self.count - 1;
317 if (activator.classname == "player"
318 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
321 centerprint (activator, "There are more to go...");
322 else if (self.count == 3)
323 centerprint (activator, "Only 3 more to go...");
324 else if (self.count == 2)
325 centerprint (activator, "Only 2 more to go...");
327 centerprint (activator, "Only 1 more to go...");
332 if (activator.classname == "player"
333 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
334 centerprint(activator, "Sequence completed!");
335 self.enemy = activator;
339 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
340 Acts as an intermediary for an action that takes multiple inputs.
342 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
344 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
346 void spawnfunc_trigger_counter()
352 self.use = counter_use;
355 .float triggerhurttime;
356 void trigger_hurt_touch()
360 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
361 other.pain_finished = min(other.pain_finished, time + 2);
362 else if (other.classname == "rune") // reset runes
363 other.nextthink = min(other.nextthink, time + 1);
366 if (other.takedamage)
367 if (other.triggerhurttime < time)
369 other.triggerhurttime = time + 1;
370 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
376 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
377 Any object touching this will be hurt
378 set dmg to damage amount
381 void spawnfunc_trigger_hurt()
384 self.touch = trigger_hurt_touch;
388 self.message = "was in the wrong place.";
391 void target_speaker_use() {sound(self, CHAN_VOICE, self.noise, 1, 1);}
393 void spawnfunc_target_speaker()
396 precache_sound (self.noise);
398 self.use = target_speaker_use;
400 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
404 void spawnfunc_func_stardust() {
405 self.effects = EF_STARDUST;
412 self.nextthink = time + 0.1;
414 if(random() < self.wait) {
415 te_spark(self.origin,'0 0 -1',self.cnt);
422 self.think = sparksthink;
423 self.nextthink = time + 0.2;
425 // self.cnt is the amount of sparks that one burst will spawn
427 self.cnt = 25.0; // nice default value
430 // self.wait is the probability that a sparkthink will spawn a spark shower
431 // range: 0 - 1, but 0 makes little sense, so...
432 if(self.wait < 0.05) {
433 self.wait = 0.25; // nice default value
438 precache_sound (self.noise);
439 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
445 self.nextthink = time + 0.1;
446 te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
447 // te_particlesnow(self.absmin, self.absmax, self.dest * 0.25, self.count, self.cnt);
448 // WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
449 // WriteByte (MSG_BROADCAST, TE_PARTICLERAIN);
450 // WriteVec (MSG_BROADCAST, self.absmin);
451 // WriteVec (MSG_BROADCAST, self.absmax);
452 // WriteVec (MSG_BROADCAST, self.dest);
453 // WriteShort (MSG_BROADCAST, self.count);
454 // WriteByte (MSG_BROADCAST, self.cnt);
457 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
458 This is an invisible area like a trigger, which rain falls inside of.
462 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
464 sets color of rain (default 12 - white)
466 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
468 void spawnfunc_func_rain()
470 self.dest = self.velocity;
471 self.velocity = '0 0 0';
473 self.dest = '0 0 -700';
474 self.angles = '0 0 0';
475 self.movetype = MOVETYPE_NONE;
476 self.solid = SOLID_NOT;
478 setmodel(self, self.model); // no precision needed
479 setorigin(self, self.origin);
480 setsize(self, self.mins, self.maxs);
486 self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
492 // convert from per second to per 0.1 sec,
493 self.count = ceil(self.count * 0.1);
494 self.think = rain_think;
495 self.nextthink = time + 0.5;
501 self.nextthink = time + 0.1 + random() * 0.05;
502 te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
503 // WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
504 // WriteByte (MSG_BROADCAST, TE_PARTICLESNOW);
505 // WriteVec (MSG_BROADCAST, self.absmin);
506 // WriteVec (MSG_BROADCAST, self.absmax);
507 // WriteVec (MSG_BROADCAST, self.dest);
508 // WriteShort (MSG_BROADCAST, self.count);
509 // WriteByte (MSG_BROADCAST, self.cnt);
512 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
513 This is an invisible area like a trigger, which snow falls inside of.
517 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
519 sets color of rain (default 12 - white)
521 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
523 void spawnfunc_func_snow()
525 self.dest = self.velocity;
526 self.velocity = '0 0 0';
528 self.dest = '0 0 -300';
529 self.angles = '0 0 0';
530 self.movetype = MOVETYPE_NONE;
531 self.solid = SOLID_NOT;
533 setmodel(self, self.model); // no precision needed
534 setorigin(self, self.origin);
535 setsize(self, self.mins, self.maxs);
541 self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
547 // convert from per second to per 0.1 sec,
548 self.count = ceil(self.count * 0.1);
549 self.think = snow_think;
550 self.nextthink = time + 0.5;
554 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
555 void misc_laser_think()
560 self.enemy = find(world, targetname, self.target);
565 o = self.enemy.origin;
569 makevectors(self.angles);
570 o = self.origin + v_forward * MAX_SHOT_DISTANCE;
576 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
578 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
581 if(time > self.ltime)
583 traceline(self.origin, o, MOVE_WORLDONLY, self);
584 trailparticles(self, self.cnt, self.origin, trace_endpos);
585 pointparticles(self.lip, trace_endpos, trace_plane_normal, 256 * frametime);
586 self.ltime = time + self.wait;
588 self.nextthink = time;
590 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ?
591 Any object touching the beam will be hurt
594 spawnfunc_target_position where the laser ends
596 name of beam effect to use
598 damage per second (-1 for a laser that kills immediately)
600 delay between sending the particle effect
602 void spawnfunc_misc_laser()
606 self.cnt = particleeffectnum(self.mdl);
607 self.lip = particleeffectnum(strcat(self.mdl, "_end"));
611 self.cnt = particleeffectnum("misc_laser_beam");
612 self.lip = particleeffectnum("misc_laser_beam_end");
617 self.message = "saw the light";
618 self.think = misc_laser_think;
619 self.nextthink = time;
622 // tZorks trigger impulse / gravity
628 // targeted (directional) mode
629 void trigger_impulse_touch1()
635 // FIXME: Better checking for what to push and not.
636 if (other.classname != "player")
637 if (other.classname != "corpse")
638 if (other.classname != "body")
639 if (other.classname != "gib")
640 if (other.classname != "missile")
641 if (other.classname != "casing")
642 if (other.classname != "grenade")
643 if (other.classname != "plasma")
644 if (other.classname != "plasma_prim")
645 if (other.classname != "plasma_chain")
646 if (other.classname != "droppedweapon")
649 if (other.deadflag && other.classname == "player")
652 targ = find(world, targetname, self.target);
655 objerror("trigger_force without a (valid) .target!\n");
660 if(self.falloff == 1)
661 str = (str / self.radius) * self.strength;
662 else if(self.falloff == 2)
663 str = (1 - (str / self.radius)) * self.strength;
667 pushdeltatime = time - other.lastpushtime;
668 if (pushdeltatime > 0.15) pushdeltatime = 0;
669 other.lastpushtime = time;
670 if(!pushdeltatime) return;
672 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
675 // Directionless (accelerator/decelerator) mode
676 void trigger_impulse_touch2()
680 // FIXME: Better checking for what to push and not.
681 if (other.classname != "player")
682 if (other.classname != "corpse")
683 if (other.classname != "body")
684 if (other.classname != "gib")
685 if (other.classname != "missile")
686 if (other.classname != "casing")
687 if (other.classname != "grenade")
688 if (other.classname != "plasma")
689 if (other.classname != "plasma_prim")
690 if (other.classname != "plasma_chain")
691 if (other.classname != "droppedweapon")
694 if (other.deadflag && other.classname == "player")
697 pushdeltatime = time - other.lastpushtime;
698 if (pushdeltatime > 0.15) pushdeltatime = 0;
699 other.lastpushtime = time;
700 if(!pushdeltatime) return;
702 //if(self.strength > 1)
703 other.velocity = other.velocity * (self.strength * pushdeltatime);
705 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
708 // Spherical (gravity/repulsor) mode
709 void trigger_impulse_touch3()
714 // FIXME: Better checking for what to push and not.
715 if (other.classname != "player")
716 if (other.classname != "corpse")
717 if (other.classname != "body")
718 if (other.classname != "gib")
719 if (other.classname != "missile")
720 if (other.classname != "casing")
721 if (other.classname != "grenade")
722 if (other.classname != "plasma")
723 if (other.classname != "plasma_prim")
724 if (other.classname != "plasma_chain")
725 if (other.classname != "droppedweapon")
728 if (other.deadflag && other.classname == "player")
731 pushdeltatime = time - other.lastpushtime;
732 if (pushdeltatime > 0.15) pushdeltatime = 0;
733 other.lastpushtime = time;
734 if(!pushdeltatime) return;
736 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
738 if(self.falloff == 1)
739 str = (str / self.radius) * self.strength;
740 else if(self.falloff == 2)
741 str = (1 - (str / self.radius)) * self.strength;
745 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
748 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
749 -------- KEYS --------
750 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
751 If not, this trigger acts like a damper/accelerator field.
753 strength : This is how mutch force to add in the direction of .target each second
754 when .target is set. If not, this is hoe mutch to slow down/accelerate
755 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
757 radius : If set, act as a spherical device rather then a liniar one.
759 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
761 -------- NOTES --------
762 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
763 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
766 void spawnfunc_trigger_impulse()
771 if(!self.strength) self.strength = 2000;
772 setorigin(self, self.origin);
773 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
774 self.touch = trigger_impulse_touch3;
780 if(!self.strength) self.strength = 950;
781 self.touch = trigger_impulse_touch1;
785 if(!self.strength) self.strength = 0.9;
786 self.touch = trigger_impulse_touch2;