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 ==============================
30 void() SUB_UseTargets =
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);
108 void() spawnfunc_trigger_reactivate =
110 self.solid = SOLID_TRIGGER;
113 //=============================================================================
115 float SPAWNFLAG_NOMESSAGE = 1;
116 float SPAWNFLAG_NOTOUCH = 1;
118 // the wait time has passed, so set back up for another activation
123 self.health = self.max_health;
124 self.takedamage = DAMAGE_YES;
125 self.solid = SOLID_BBOX;
130 // the trigger was just touched/killed/used
131 // self.enemy should be set to the activator so it can be held through a delay
132 // so wait for the delay time before firing
133 void() multi_trigger =
135 if (self.nextthink > time)
137 return; // allready been triggered
140 if (self.classname == "trigger_secret")
142 if (self.enemy.classname != "player")
144 found_secrets = found_secrets + 1;
145 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
149 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
151 // don't trigger again until reset
152 self.takedamage = DAMAGE_NO;
154 activator = self.enemy;
160 self.think = multi_wait;
161 self.nextthink = time + self.wait;
164 { // we can't just remove (self) here, because this is a touch function
165 // called wheil C code is looping through area links...
166 self.touch = SUB_Null;
168 self.nextthink = time + 0.1;
169 self.think = SUB_Remove;
175 self.enemy = activator;
181 if (other.classname != "player")
185 if(self.team == other.team)
188 // if the trigger has an angles field, check player's facing direction
189 if (self.movedir != '0 0 0')
191 makevectors (other.angles);
192 if (v_forward * self.movedir < 0)
193 return; // not facing the right way
200 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
202 if (!self.takedamage)
204 self.health = self.health - damage;
205 if (self.health <= 0)
207 self.enemy = attacker;
212 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
213 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.
214 If "delay" is set, the trigger waits some time after activating before firing.
215 "wait" : Seconds between triggerings. (.2 default)
216 If notouch is set, the trigger is only fired by other entities, not by touching.
217 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
223 set "message" to text string
225 void() spawnfunc_trigger_multiple =
227 if (self.sounds == 1)
229 precache_sound ("misc/secret.wav");
230 self.noise = "misc/secret.wav";
232 else if (self.sounds == 2)
234 precache_sound ("misc/talk.wav");
235 self.noise = "misc/talk.wav";
237 else if (self.sounds == 3)
239 precache_sound ("misc/trigger1.wav");
240 self.noise = "misc/trigger1.wav";
245 self.use = multi_use;
251 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
252 objerror ("health and notouch don't make sense\n");
253 self.max_health = self.health;
254 self.event_damage = multi_eventdamage;
255 self.takedamage = DAMAGE_YES;
256 self.solid = SOLID_BBOX;
257 setorigin (self, self.origin); // make sure it links into the world
261 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
263 self.touch = multi_touch;
269 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
270 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
271 "targetname". If "health" is set, the trigger must be killed to activate.
272 If notouch is set, the trigger is only fired by other entities, not by touching.
273 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
274 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.
280 set "message" to text string
282 void() spawnfunc_trigger_once =
285 spawnfunc_trigger_multiple();
288 //=============================================================================
290 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
291 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
293 void() spawnfunc_trigger_relay =
295 self.use = SUB_UseTargets;
300 self.think = SUB_UseTargets;
301 self.nextthink = self.wait;
304 void() spawnfunc_trigger_delay
309 self.use = delay_use;
312 //=============================================================================
317 self.count = self.count - 1;
323 if (activator.classname == "player"
324 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
327 centerprint (activator, "There are more to go...");
328 else if (self.count == 3)
329 centerprint (activator, "Only 3 more to go...");
330 else if (self.count == 2)
331 centerprint (activator, "Only 2 more to go...");
333 centerprint (activator, "Only 1 more to go...");
338 if (activator.classname == "player"
339 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
340 centerprint(activator, "Sequence completed!");
341 self.enemy = activator;
345 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
346 Acts as an intermediary for an action that takes multiple inputs.
348 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
350 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
352 void() spawnfunc_trigger_counter =
358 self.use = counter_use;
361 .float triggerhurttime;
362 void() spawnfunc_hurt_touch =
366 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
367 other.pain_finished = min(other.pain_finished, time + 2);
368 else if (other.classname == "rune") // reset runes
369 other.nextthink = min(other.nextthink, time + 1);
372 if (other.takedamage)
373 if (other.triggerhurttime < time)
375 other.triggerhurttime = time + 1;
376 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
382 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
383 Any object touching this will be hurt
384 set dmg to damage amount
387 void() spawnfunc_trigger_hurt =
390 self.touch = spawnfunc_hurt_touch;
394 self.message = "was in the wrong place.";
397 //void() target_speaker_use = {sound(self, CHAN_VOICE, self.noise, 1, 1);}
398 //void() spawnfunc_target_speaker = {self.use = target_speaker_use;}
400 void() spawnfunc_target_speaker =
403 precache_sound (self.noise);
404 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
410 void() spawnfunc_func_stardust {
411 self.effects = EF_STARDUST;
418 self.nextthink = time + 0.1;
420 if(random() < self.wait) {
421 te_spark(self.origin,'0 0 -1',self.cnt);
428 self.think = sparksthink;
429 self.nextthink = time + 0.2;
431 // self.cnt is the amount of sparks that one burst will spawn
433 self.cnt = 25.0; // nice default value
436 // self.wait is the probability that a sparkthink will spawn a spark shower
437 // range: 0 - 1, but 0 makes little sense, so...
438 if(self.wait < 0.05) {
439 self.wait = 0.25; // nice default value
444 precache_sound (self.noise);
445 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
451 self.nextthink = time + 0.1;
452 te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
453 // te_particlesnow(self.absmin, self.absmax, self.dest * 0.25, self.count, self.cnt);
454 // WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
455 // WriteByte (MSG_BROADCAST, TE_PARTICLERAIN);
456 // WriteVec (MSG_BROADCAST, self.absmin);
457 // WriteVec (MSG_BROADCAST, self.absmax);
458 // WriteVec (MSG_BROADCAST, self.dest);
459 // WriteShort (MSG_BROADCAST, self.count);
460 // WriteByte (MSG_BROADCAST, self.cnt);
463 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
464 This is an invisible area like a trigger, which rain falls inside of.
468 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
470 sets color of rain (default 12 - white)
472 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
474 void() spawnfunc_func_rain =
476 self.dest = self.velocity;
477 self.velocity = '0 0 0';
479 self.dest = '0 0 -700';
480 self.angles = '0 0 0';
481 self.movetype = MOVETYPE_NONE;
482 self.solid = SOLID_NOT;
484 setmodel(self, self.model); // no precision needed
485 setorigin(self, self.origin);
486 setsize(self, self.mins, self.maxs);
492 self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
498 // convert from per second to per 0.1 sec,
499 self.count = ceil(self.count * 0.1);
500 self.think = rain_think;
501 self.nextthink = time + 0.5;
507 self.nextthink = time + 0.1 + random() * 0.05;
508 te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
509 // WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
510 // WriteByte (MSG_BROADCAST, TE_PARTICLESNOW);
511 // WriteVec (MSG_BROADCAST, self.absmin);
512 // WriteVec (MSG_BROADCAST, self.absmax);
513 // WriteVec (MSG_BROADCAST, self.dest);
514 // WriteShort (MSG_BROADCAST, self.count);
515 // WriteByte (MSG_BROADCAST, self.cnt);
518 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
519 This is an invisible area like a trigger, which snow falls inside of.
523 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
525 sets color of rain (default 12 - white)
527 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
529 void() spawnfunc_func_snow =
531 self.dest = self.velocity;
532 self.velocity = '0 0 0';
534 self.dest = '0 0 -300';
535 self.angles = '0 0 0';
536 self.movetype = MOVETYPE_NONE;
537 self.solid = SOLID_NOT;
539 setmodel(self, self.model); // no precision needed
540 setorigin(self, self.origin);
541 setsize(self, self.mins, self.maxs);
547 self.count = 0.1 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
553 // convert from per second to per 0.1 sec,
554 self.count = ceil(self.count * 0.1);
555 self.think = snow_think;
556 self.nextthink = time + 0.5;
560 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
561 void() misc_laser_think =
566 self.enemy = find(world, targetname, self.target);
571 o = self.enemy.origin;
575 makevectors(self.angles);
576 o = self.origin + v_forward * MAX_SHOT_DISTANCE;
582 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
584 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
587 if(time > self.ltime)
589 traceline(self.origin, o, MOVE_WORLDONLY, self);
590 trailparticles(self, self.cnt, self.origin, trace_endpos);
591 pointparticles(self.lip, trace_endpos, trace_plane_normal, 256 * frametime);
592 self.ltime = time + self.wait;
594 self.nextthink = time;
596 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ?
597 Any object touching the beam will be hurt
600 spawnfunc_target_position where the laser ends
602 name of beam effect to use
604 damage per second (-1 for a laser that kills immediately)
606 delay between sending the particle effect
608 void() spawnfunc_misc_laser =
612 self.cnt = particleeffectnum(self.mdl);
613 self.lip = particleeffectnum(strcat(self.mdl, "_end"));
617 self.cnt = particleeffectnum("misc_laser_beam");
618 self.lip = particleeffectnum("misc_laser_beam_end");
623 self.message = "saw the light";
624 self.think = misc_laser_think;
625 self.nextthink = time;
630 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 (valif) .target!\n");
661 pushdeltatime = time - other.lastpushtime;
662 if (pushdeltatime > 0.15) pushdeltatime = 0;
663 other.lastpushtime = time;
664 if(!pushdeltatime) return;
666 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
669 void trigger_impulse_touch2()
672 // FIXME: Better checking for what to push and not.
673 if (other.classname != "player")
674 if (other.classname != "corpse")
675 if (other.classname != "body")
676 if (other.classname != "gib")
677 if (other.classname != "missile")
678 if (other.classname != "casing")
679 if (other.classname != "grenade")
680 if (other.classname != "plasma")
681 if (other.classname != "plasma_prim")
682 if (other.classname != "plasma_chain")
683 if (other.classname != "droppedweapon")
686 if (other.deadflag && other.classname == "player")
689 other.velocity = other.velocity * self.strength;
693 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
694 -------- KEYS --------
695 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
696 If not, this trigger acts like a damper/accelerator field.
698 strength : This is how mutch force to add in the direction of .target each second
699 when .target is set. If not, this is hoe mutch to slow down/accelerate
700 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
702 -------- NOTES --------
703 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
704 in directional push mode. For damper/accelerator mode this is no nessesary (and has no effect).
707 void spawnfunc_trigger_impulse()
712 if(!self.strength) self.strength = 950;
713 self.touch = trigger_impulse_touch1;
717 if(!self.strength) self.strength = 0.9;
718 self.touch = trigger_impulse_touch2;