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);
635 self.effects = EF_NODEPTHTEST;
636 self.SendEntity = pointparticles_SendEntity;
639 vector misave, masave;
642 setmodel(self, "null");
643 setorigin(self, self.origin + misave);
644 setsize(self, '0 0 0', masave - misave);
647 self.cnt = particleeffectnum(self.mdl);
650 self.use = pointparticles_use;
651 self.reset = pointparticles_reset;
656 self.think = pointparticles_think;
657 self.nextthink = time;
660 void spawnfunc_func_sparks()
662 // self.cnt is the amount of sparks that one burst will spawn
664 self.cnt = 25.0; // nice default value
667 // self.wait is the probability that a sparkthink will spawn a spark shower
668 // range: 0 - 1, but 0 makes little sense, so...
669 if(self.wait < 0.05) {
670 self.wait = 0.25; // nice default value
673 self.count = self.cnt;
676 self.velocity = '0 0 -1';
677 self.mdl = "TE_SPARK";
678 self.impulse = 10 * self.wait; // by default 2.5/sec
680 self.cnt = 0; // use mdl
682 spawnfunc_func_pointparticles();
685 float rainsnow_SendEntity(float to)
687 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
688 WriteByte(MSG_ENTITY, self.state);
689 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
690 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
691 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
692 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
693 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
694 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
695 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
696 WriteShort(MSG_ENTITY, self.count);
697 WriteByte(MSG_ENTITY, self.cnt);
701 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
702 This is an invisible area like a trigger, which rain falls inside of.
706 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
708 sets color of rain (default 12 - white)
710 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
712 void spawnfunc_func_rain()
714 self.dest = self.velocity;
715 self.velocity = '0 0 0';
717 self.dest = '0 0 -700';
718 self.angles = '0 0 0';
719 self.movetype = MOVETYPE_NONE;
720 self.solid = SOLID_NOT;
721 SetBrushEntityModel();
726 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
729 if(self.count > 65535)
732 self.state = 1; // 1 is rain, 0 is snow
733 self.effects = EF_NODEPTHTEST;
734 self.SendEntity = rainsnow_SendEntity;
739 vector misave, masave;
742 setmodel(self, "null");
743 setsize(self, misave, masave);
748 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
749 This is an invisible area like a trigger, which snow falls inside of.
753 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
755 sets color of rain (default 12 - white)
757 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
759 void spawnfunc_func_snow()
761 self.dest = self.velocity;
762 self.velocity = '0 0 0';
764 self.dest = '0 0 -300';
765 self.angles = '0 0 0';
766 self.movetype = MOVETYPE_NONE;
767 self.solid = SOLID_NOT;
768 SetBrushEntityModel();
773 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
776 if(self.count > 65535)
779 self.state = 0; // 1 is rain, 0 is snow
780 self.effects = EF_NODEPTHTEST;
781 self.SendEntity = rainsnow_SendEntity;
786 vector misave, masave;
789 setmodel(self, "null");
790 setsize(self, misave, masave);
795 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
798 void misc_laser_aim()
803 if(self.spawnflags & 2)
805 if(self.enemy.origin != self.mangle)
807 self.mangle = self.enemy.origin;
813 a = vectoangles(self.enemy.origin - self.origin);
824 if(self.angles != self.mangle)
826 self.mangle = self.angles;
830 if(self.origin != self.oldorigin)
833 self.oldorigin = self.origin;
837 void misc_laser_init()
839 if(self.target != "")
840 self.enemy = find(world, targetname, self.target);
844 void misc_laser_think()
849 self.nextthink = time;
858 o = self.enemy.origin;
859 if not(self.spawnflags & 2)
860 o = self.origin + normalize(o - self.origin) * 32768;
864 makevectors(self.mangle);
865 o = self.origin + v_forward * 32768;
871 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
873 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
876 if(self.enemy.target != "") // DETECTOR laser
878 traceline(self.origin, o, MOVE_NORMAL, self);
879 if(trace_ent.iscreature)
881 self.pusher = trace_ent;
888 activator = self.pusher;
901 activator = self.pusher;
909 float laser_SendEntity(entity to, float fl)
911 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
912 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
913 if(self.spawnflags & 2)
917 if(self.scale != 1 || self.modelscale != 1)
919 WriteByte(MSG_ENTITY, fl);
922 WriteCoord(MSG_ENTITY, self.origin_x);
923 WriteCoord(MSG_ENTITY, self.origin_y);
924 WriteCoord(MSG_ENTITY, self.origin_z);
928 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
929 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
930 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
932 WriteByte(MSG_ENTITY, self.alpha * 255.0);
935 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
936 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
938 WriteShort(MSG_ENTITY, self.cnt + 1);
944 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
945 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
946 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
950 WriteAngle(MSG_ENTITY, self.mangle_x);
951 WriteAngle(MSG_ENTITY, self.mangle_y);
955 WriteByte(MSG_ENTITY, self.state);
959 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
960 Any object touching the beam will be hurt
963 spawnfunc_target_position where the laser ends
965 name of beam end effect to use
967 color of the beam (default: red)
969 damage per second (-1 for a laser that kills immediately)
973 self.state = !self.state;
980 if(self.spawnflags & 1)
986 void spawnfunc_misc_laser()
990 if(self.mdl == "none")
994 self.cnt = particleeffectnum(self.mdl);
997 self.cnt = particleeffectnum("laser_deadly");
1003 self.cnt = particleeffectnum("laser_deadly");
1010 if(self.colormod == '0 0 0')
1012 self.colormod = '1 0 0';
1014 self.message = "saw the light";
1016 self.message2 = "was pushed into a laser by";
1019 if(!self.modelscale)
1020 self.modelscale = 1;
1021 self.think = misc_laser_think;
1022 self.nextthink = time;
1023 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1025 self.effects = EF_NODEPTHTEST;
1026 self.SendEntity = laser_SendEntity;
1027 setmodel(self, "null");
1028 self.mangle = self.angles;
1032 self.reset = laser_reset;
1034 self.use = laser_use;
1040 // tZorks trigger impulse / gravity
1044 .float lastpushtime;
1046 // targeted (directional) mode
1047 void trigger_impulse_touch1()
1050 float pushdeltatime;
1053 // FIXME: Better checking for what to push and not.
1054 if not(other.iscreature)
1055 if (other.classname != "corpse")
1056 if (other.classname != "body")
1057 if (other.classname != "gib")
1058 if (other.classname != "missile")
1059 if (other.classname != "rocket")
1060 if (other.classname != "casing")
1061 if (other.classname != "grenade")
1062 if (other.classname != "plasma")
1063 if (other.classname != "plasma_prim")
1064 if (other.classname != "plasma_chain")
1065 if (other.classname != "droppedweapon")
1068 if (other.deadflag && other.iscreature)
1073 targ = find(world, targetname, self.target);
1076 objerror("trigger_force without a (valid) .target!\n");
1081 if(self.falloff == 1)
1082 str = (str / self.radius) * self.strength;
1083 else if(self.falloff == 2)
1084 str = (1 - (str / self.radius)) * self.strength;
1086 str = self.strength;
1088 pushdeltatime = time - other.lastpushtime;
1089 if (pushdeltatime > 0.15) pushdeltatime = 0;
1090 other.lastpushtime = time;
1091 if(!pushdeltatime) return;
1093 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1094 other.flags &~= FL_ONGROUND;
1097 // Directionless (accelerator/decelerator) mode
1098 void trigger_impulse_touch2()
1100 float pushdeltatime;
1102 // FIXME: Better checking for what to push and not.
1103 if not(other.iscreature)
1104 if (other.classname != "corpse")
1105 if (other.classname != "body")
1106 if (other.classname != "gib")
1107 if (other.classname != "missile")
1108 if (other.classname != "rocket")
1109 if (other.classname != "casing")
1110 if (other.classname != "grenade")
1111 if (other.classname != "plasma")
1112 if (other.classname != "plasma_prim")
1113 if (other.classname != "plasma_chain")
1114 if (other.classname != "droppedweapon")
1117 if (other.deadflag && other.iscreature)
1122 pushdeltatime = time - other.lastpushtime;
1123 if (pushdeltatime > 0.15) pushdeltatime = 0;
1124 other.lastpushtime = time;
1125 if(!pushdeltatime) return;
1127 //if(self.strength > 1)
1128 other.velocity = other.velocity * (self.strength * pushdeltatime);
1130 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1133 // Spherical (gravity/repulsor) mode
1134 void trigger_impulse_touch3()
1136 float pushdeltatime;
1139 // FIXME: Better checking for what to push and not.
1140 if not(other.iscreature)
1141 if (other.classname != "corpse")
1142 if (other.classname != "body")
1143 if (other.classname != "gib")
1144 if (other.classname != "missile")
1145 if (other.classname != "rocket")
1146 if (other.classname != "casing")
1147 if (other.classname != "grenade")
1148 if (other.classname != "plasma")
1149 if (other.classname != "plasma_prim")
1150 if (other.classname != "plasma_chain")
1151 if (other.classname != "droppedweapon")
1154 if (other.deadflag && other.iscreature)
1159 pushdeltatime = time - other.lastpushtime;
1160 if (pushdeltatime > 0.15) pushdeltatime = 0;
1161 other.lastpushtime = time;
1162 if(!pushdeltatime) return;
1164 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1166 str = min(self.radius, vlen(self.origin - other.origin));
1168 if(self.falloff == 1)
1169 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1170 else if(self.falloff == 2)
1171 str = (str / self.radius) * self.strength; // 0 in the inside
1173 str = self.strength;
1175 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1178 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1179 -------- KEYS --------
1180 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1181 If not, this trigger acts like a damper/accelerator field.
1183 strength : This is how mutch force to add in the direction of .target each second
1184 when .target is set. If not, this is hoe mutch to slow down/accelerate
1185 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1187 radius : If set, act as a spherical device rather then a liniar one.
1189 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1191 -------- NOTES --------
1192 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1193 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1196 void spawnfunc_trigger_impulse()
1201 if(!self.strength) self.strength = 2000;
1202 setorigin(self, self.origin);
1203 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1204 self.touch = trigger_impulse_touch3;
1210 if(!self.strength) self.strength = 950;
1211 self.touch = trigger_impulse_touch1;
1215 if(!self.strength) self.strength = 0.9;
1216 self.touch = trigger_impulse_touch2;
1221 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1222 "Flip-flop" trigger gate... lets only every second trigger event through
1226 self.state = !self.state;
1231 void spawnfunc_trigger_flipflop()
1233 if(self.spawnflags & 1)
1235 self.use = flipflop_use;
1236 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1239 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1240 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1244 self.nextthink = time + self.wait;
1245 self.enemy = activator;
1251 void monoflop_fixed_use()
1255 self.nextthink = time + self.wait;
1257 self.enemy = activator;
1261 void monoflop_think()
1264 activator = self.enemy;
1268 void monoflop_reset()
1274 void spawnfunc_trigger_monoflop()
1278 if(self.spawnflags & 1)
1279 self.use = monoflop_fixed_use;
1281 self.use = monoflop_use;
1282 self.think = monoflop_think;
1284 self.reset = monoflop_reset;
1287 void multivibrator_send()
1292 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1294 newstate = (time < cyclestart + self.wait);
1297 if(self.state != newstate)
1299 self.state = newstate;
1302 self.nextthink = cyclestart + self.wait + 0.01;
1304 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1307 void multivibrator_toggle()
1309 if(self.nextthink == 0)
1311 multivibrator_send();
1324 void multivibrator_reset()
1326 if(!(self.spawnflags & 1))
1327 self.nextthink = 0; // wait for a trigger event
1329 self.nextthink = max(1, time);
1332 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1333 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1334 -------- KEYS --------
1335 target: trigger all entities with this targetname when it goes off
1336 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1337 phase: offset of the timing
1338 wait: "on" cycle time (default: 1)
1339 respawntime: "off" cycle time (default: same as wait)
1340 -------- SPAWNFLAGS --------
1341 START_ON: assume it is already turned on (when targeted)
1343 void spawnfunc_trigger_multivibrator()
1347 if(!self.respawntime)
1348 self.respawntime = self.wait;
1351 self.use = multivibrator_toggle;
1352 self.think = multivibrator_send;
1353 self.nextthink = time;
1356 multivibrator_reset();
1363 src = find(world, targetname, self.killtarget);
1364 dst = find(world, targetname, self.target);
1368 objerror("follow: could not find target/killtarget");
1372 dst.movetype = MOVETYPE_FOLLOW;
1374 dst.punchangle = src.angles;
1375 dst.view_ofs = dst.origin - src.origin;
1376 dst.v_angle = dst.angles - src.angles;
1381 void spawnfunc_misc_follow()
1383 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1388 void gamestart_use() {
1394 void spawnfunc_trigger_gamestart() {
1395 self.use = gamestart_use;
1399 self.think = self.use;
1400 self.nextthink = game_starttime + self.wait;
1403 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1409 .entity voicescript; // attached voice script
1410 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1411 .float voicescript_nextthink; // time to play next voice
1412 .float voicescript_voiceend; // time when this voice ends
1414 void target_voicescript_clear(entity pl)
1416 pl.voicescript = world;
1419 void target_voicescript_use()
1421 if(activator.voicescript != self)
1423 activator.voicescript = self;
1424 activator.voicescript_index = 0;
1425 activator.voicescript_nextthink = time + self.delay;
1429 void target_voicescript_next(entity pl)
1434 vs = pl.voicescript;
1437 if(vs.message == "")
1439 if(pl.classname != "player")
1444 if(time >= pl.voicescript_voiceend)
1446 if(time >= pl.voicescript_nextthink)
1448 // get the next voice...
1449 n = tokenize_sane(vs.message);
1451 if(pl.voicescript_index < vs.cnt)
1452 i = pl.voicescript_index * 2;
1453 else if(n > vs.cnt * 2)
1454 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1460 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1461 pl.voicescript_voiceend = time + stof(argv(i + 1));
1464 pl.voicescript = world;
1466 pl.voicescript_index += 1;
1467 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1472 void spawnfunc_target_voicescript()
1474 // netname: directory of the sound files
1475 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1476 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1477 // wait: average time between messages
1478 // delay: initial delay before the first message
1481 self.use = target_voicescript_use;
1483 n = tokenize_sane(self.message);
1485 for(i = 0; i+1 < n; i += 2)
1492 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1498 void trigger_relay_teamcheck_use()
1502 if(self.spawnflags & 2)
1504 if(activator.team != self.team)
1509 if(activator.team == self.team)
1515 if(self.spawnflags & 1)
1520 void trigger_relay_teamcheck_reset()
1522 self.team = self.team_saved;
1525 void spawnfunc_trigger_relay_teamcheck()
1527 self.team_saved = self.team;
1528 self.use = trigger_relay_teamcheck_use;
1529 self.reset = trigger_relay_teamcheck_reset;
1534 void trigger_disablerelay_use()
1541 for(e = world; (e = find(e, targetname, self.target)); )
1543 if(e.use == SUB_UseTargets)
1545 e.use = SUB_DontUseTargets;
1548 else if(e.use == SUB_DontUseTargets)
1550 e.use = SUB_UseTargets;
1556 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1559 void spawnfunc_trigger_disablerelay()
1561 self.use = trigger_disablerelay_use;