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);
602 WriteByte(MSG_ENTITY, floor(self.atten * 64));
603 WriteByte(MSG_ENTITY, floor(self.volume * 255));
609 void pointparticles_use()
611 self.state = !self.state;
615 void pointparticles_think()
617 if(self.origin != self.oldorigin)
620 self.oldorigin = self.origin;
622 self.nextthink = time;
625 void pointparticles_reset()
627 if(self.spawnflags & 1)
633 void spawnfunc_func_pointparticles()
636 setmodel(self, self.model);
638 precache_sound (self.noise);
641 self.atten = ATTN_NORM;
642 else if(self.atten < 0)
649 setorigin(self, self.origin + self.mins);
650 setsize(self, '0 0 0', self.maxs - self.mins);
653 self.cnt = particleeffectnum(self.mdl);
655 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
659 self.use = pointparticles_use;
660 self.reset = pointparticles_reset;
665 self.think = pointparticles_think;
666 self.nextthink = time;
669 void spawnfunc_func_sparks()
671 // self.cnt is the amount of sparks that one burst will spawn
673 self.cnt = 25.0; // nice default value
676 // self.wait is the probability that a sparkthink will spawn a spark shower
677 // range: 0 - 1, but 0 makes little sense, so...
678 if(self.wait < 0.05) {
679 self.wait = 0.25; // nice default value
682 self.count = self.cnt;
685 self.velocity = '0 0 -1';
686 self.mdl = "TE_SPARK";
687 self.impulse = 10 * self.wait; // by default 2.5/sec
689 self.cnt = 0; // use mdl
691 spawnfunc_func_pointparticles();
694 float rainsnow_SendEntity(entity to, float sf)
696 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
697 WriteByte(MSG_ENTITY, self.state);
698 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
699 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
700 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
701 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
702 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
703 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
704 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
705 WriteShort(MSG_ENTITY, self.count);
706 WriteByte(MSG_ENTITY, self.cnt);
710 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
711 This is an invisible area like a trigger, which rain falls inside of.
715 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
717 sets color of rain (default 12 - white)
719 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
721 void spawnfunc_func_rain()
723 self.dest = self.velocity;
724 self.velocity = '0 0 0';
726 self.dest = '0 0 -700';
727 self.angles = '0 0 0';
728 self.movetype = MOVETYPE_NONE;
729 self.solid = SOLID_NOT;
730 SetBrushEntityModel();
735 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
738 if(self.count > 65535)
741 self.state = 1; // 1 is rain, 0 is snow
744 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
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
782 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
786 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
789 void misc_laser_aim()
794 if(self.spawnflags & 2)
796 if(self.enemy.origin != self.mangle)
798 self.mangle = self.enemy.origin;
804 a = vectoangles(self.enemy.origin - self.origin);
815 if(self.angles != self.mangle)
817 self.mangle = self.angles;
821 if(self.origin != self.oldorigin)
824 self.oldorigin = self.origin;
828 void misc_laser_init()
830 if(self.target != "")
831 self.enemy = find(world, targetname, self.target);
835 void misc_laser_think()
840 self.nextthink = time;
849 o = self.enemy.origin;
850 if not(self.spawnflags & 2)
851 o = self.origin + normalize(o - self.origin) * 32768;
855 makevectors(self.mangle);
856 o = self.origin + v_forward * 32768;
862 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
864 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
867 if(self.enemy.target != "") // DETECTOR laser
869 traceline(self.origin, o, MOVE_NORMAL, self);
870 if(trace_ent.iscreature)
872 self.pusher = trace_ent;
879 activator = self.pusher;
892 activator = self.pusher;
900 float laser_SendEntity(entity to, float fl)
902 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
903 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
904 if(self.spawnflags & 2)
908 if(self.scale != 1 || self.modelscale != 1)
910 WriteByte(MSG_ENTITY, fl);
913 WriteCoord(MSG_ENTITY, self.origin_x);
914 WriteCoord(MSG_ENTITY, self.origin_y);
915 WriteCoord(MSG_ENTITY, self.origin_z);
919 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
920 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
921 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
923 WriteByte(MSG_ENTITY, self.alpha * 255.0);
926 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
927 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
929 WriteShort(MSG_ENTITY, self.cnt + 1);
935 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
936 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
937 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
941 WriteAngle(MSG_ENTITY, self.mangle_x);
942 WriteAngle(MSG_ENTITY, self.mangle_y);
946 WriteByte(MSG_ENTITY, self.state);
950 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
951 Any object touching the beam will be hurt
954 spawnfunc_target_position where the laser ends
956 name of beam end effect to use
958 color of the beam (default: red)
960 damage per second (-1 for a laser that kills immediately)
964 self.state = !self.state;
971 if(self.spawnflags & 1)
977 void spawnfunc_misc_laser()
981 if(self.mdl == "none")
985 self.cnt = particleeffectnum(self.mdl);
988 self.cnt = particleeffectnum("laser_deadly");
994 self.cnt = particleeffectnum("laser_deadly");
1001 if(self.colormod == '0 0 0')
1003 self.colormod = '1 0 0';
1005 self.message = "saw the light";
1007 self.message2 = "was pushed into a laser by";
1010 if(!self.modelscale)
1011 self.modelscale = 1;
1012 self.think = misc_laser_think;
1013 self.nextthink = time;
1014 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1016 self.mangle = self.angles;
1018 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1022 self.reset = laser_reset;
1024 self.use = laser_use;
1030 // tZorks trigger impulse / gravity
1034 .float lastpushtime;
1036 // targeted (directional) mode
1037 void trigger_impulse_touch1()
1040 float pushdeltatime;
1043 // FIXME: Better checking for what to push and not.
1044 if not(other.iscreature)
1045 if (other.classname != "corpse")
1046 if (other.classname != "body")
1047 if (other.classname != "gib")
1048 if (other.classname != "missile")
1049 if (other.classname != "rocket")
1050 if (other.classname != "casing")
1051 if (other.classname != "grenade")
1052 if (other.classname != "plasma")
1053 if (other.classname != "plasma_prim")
1054 if (other.classname != "plasma_chain")
1055 if (other.classname != "droppedweapon")
1058 if (other.deadflag && other.iscreature)
1063 targ = find(world, targetname, self.target);
1066 objerror("trigger_force without a (valid) .target!\n");
1071 if(self.falloff == 1)
1072 str = (str / self.radius) * self.strength;
1073 else if(self.falloff == 2)
1074 str = (1 - (str / self.radius)) * self.strength;
1076 str = self.strength;
1078 pushdeltatime = time - other.lastpushtime;
1079 if (pushdeltatime > 0.15) pushdeltatime = 0;
1080 other.lastpushtime = time;
1081 if(!pushdeltatime) return;
1083 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1084 other.flags &~= FL_ONGROUND;
1087 // Directionless (accelerator/decelerator) mode
1088 void trigger_impulse_touch2()
1090 float pushdeltatime;
1092 // FIXME: Better checking for what to push and not.
1093 if not(other.iscreature)
1094 if (other.classname != "corpse")
1095 if (other.classname != "body")
1096 if (other.classname != "gib")
1097 if (other.classname != "missile")
1098 if (other.classname != "rocket")
1099 if (other.classname != "casing")
1100 if (other.classname != "grenade")
1101 if (other.classname != "plasma")
1102 if (other.classname != "plasma_prim")
1103 if (other.classname != "plasma_chain")
1104 if (other.classname != "droppedweapon")
1107 if (other.deadflag && other.iscreature)
1112 pushdeltatime = time - other.lastpushtime;
1113 if (pushdeltatime > 0.15) pushdeltatime = 0;
1114 other.lastpushtime = time;
1115 if(!pushdeltatime) return;
1117 //if(self.strength > 1)
1118 other.velocity = other.velocity * (self.strength * pushdeltatime);
1120 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1123 // Spherical (gravity/repulsor) mode
1124 void trigger_impulse_touch3()
1126 float pushdeltatime;
1129 // FIXME: Better checking for what to push and not.
1130 if not(other.iscreature)
1131 if (other.classname != "corpse")
1132 if (other.classname != "body")
1133 if (other.classname != "gib")
1134 if (other.classname != "missile")
1135 if (other.classname != "rocket")
1136 if (other.classname != "casing")
1137 if (other.classname != "grenade")
1138 if (other.classname != "plasma")
1139 if (other.classname != "plasma_prim")
1140 if (other.classname != "plasma_chain")
1141 if (other.classname != "droppedweapon")
1144 if (other.deadflag && other.iscreature)
1149 pushdeltatime = time - other.lastpushtime;
1150 if (pushdeltatime > 0.15) pushdeltatime = 0;
1151 other.lastpushtime = time;
1152 if(!pushdeltatime) return;
1154 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1156 str = min(self.radius, vlen(self.origin - other.origin));
1158 if(self.falloff == 1)
1159 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1160 else if(self.falloff == 2)
1161 str = (str / self.radius) * self.strength; // 0 in the inside
1163 str = self.strength;
1165 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1168 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1169 -------- KEYS --------
1170 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1171 If not, this trigger acts like a damper/accelerator field.
1173 strength : This is how mutch force to add in the direction of .target each second
1174 when .target is set. If not, this is hoe mutch to slow down/accelerate
1175 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1177 radius : If set, act as a spherical device rather then a liniar one.
1179 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1181 -------- NOTES --------
1182 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1183 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1186 void spawnfunc_trigger_impulse()
1191 if(!self.strength) self.strength = 2000;
1192 setorigin(self, self.origin);
1193 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1194 self.touch = trigger_impulse_touch3;
1200 if(!self.strength) self.strength = 950;
1201 self.touch = trigger_impulse_touch1;
1205 if(!self.strength) self.strength = 0.9;
1206 self.touch = trigger_impulse_touch2;
1211 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1212 "Flip-flop" trigger gate... lets only every second trigger event through
1216 self.state = !self.state;
1221 void spawnfunc_trigger_flipflop()
1223 if(self.spawnflags & 1)
1225 self.use = flipflop_use;
1226 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1229 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1230 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1234 self.nextthink = time + self.wait;
1235 self.enemy = activator;
1241 void monoflop_fixed_use()
1245 self.nextthink = time + self.wait;
1247 self.enemy = activator;
1251 void monoflop_think()
1254 activator = self.enemy;
1258 void monoflop_reset()
1264 void spawnfunc_trigger_monoflop()
1268 if(self.spawnflags & 1)
1269 self.use = monoflop_fixed_use;
1271 self.use = monoflop_use;
1272 self.think = monoflop_think;
1274 self.reset = monoflop_reset;
1277 void multivibrator_send()
1282 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1284 newstate = (time < cyclestart + self.wait);
1287 if(self.state != newstate)
1289 self.state = newstate;
1292 self.nextthink = cyclestart + self.wait + 0.01;
1294 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1297 void multivibrator_toggle()
1299 if(self.nextthink == 0)
1301 multivibrator_send();
1314 void multivibrator_reset()
1316 if(!(self.spawnflags & 1))
1317 self.nextthink = 0; // wait for a trigger event
1319 self.nextthink = max(1, time);
1322 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1323 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1324 -------- KEYS --------
1325 target: trigger all entities with this targetname when it goes off
1326 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1327 phase: offset of the timing
1328 wait: "on" cycle time (default: 1)
1329 respawntime: "off" cycle time (default: same as wait)
1330 -------- SPAWNFLAGS --------
1331 START_ON: assume it is already turned on (when targeted)
1333 void spawnfunc_trigger_multivibrator()
1337 if(!self.respawntime)
1338 self.respawntime = self.wait;
1341 self.use = multivibrator_toggle;
1342 self.think = multivibrator_send;
1343 self.nextthink = time;
1346 multivibrator_reset();
1353 src = find(world, targetname, self.killtarget);
1354 dst = find(world, targetname, self.target);
1358 objerror("follow: could not find target/killtarget");
1362 if(self.spawnflags & 1)
1365 if(self.spawnflags & 2)
1367 setattachment(dst, src, self.message);
1371 attach_sameorigin(dst, src, self.message);
1376 if(self.spawnflags & 2)
1378 dst.movetype = MOVETYPE_FOLLOW;
1380 // dst.punchangle = '0 0 0'; // keep unchanged
1381 dst.view_ofs = dst.origin;
1382 dst.v_angle = dst.angles;
1386 follow_sameorigin(dst, src);
1393 void spawnfunc_misc_follow()
1395 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1400 void gamestart_use() {
1406 void spawnfunc_trigger_gamestart() {
1407 self.use = gamestart_use;
1408 self.reset2 = spawnfunc_trigger_gamestart;
1412 self.think = self.use;
1413 self.nextthink = game_starttime + self.wait;
1416 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1422 .entity voicescript; // attached voice script
1423 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1424 .float voicescript_nextthink; // time to play next voice
1425 .float voicescript_voiceend; // time when this voice ends
1427 void target_voicescript_clear(entity pl)
1429 pl.voicescript = world;
1432 void target_voicescript_use()
1434 if(activator.voicescript != self)
1436 activator.voicescript = self;
1437 activator.voicescript_index = 0;
1438 activator.voicescript_nextthink = time + self.delay;
1442 void target_voicescript_next(entity pl)
1447 vs = pl.voicescript;
1450 if(vs.message == "")
1452 if(pl.classname != "player")
1457 if(time >= pl.voicescript_voiceend)
1459 if(time >= pl.voicescript_nextthink)
1461 // get the next voice...
1462 n = tokenize_sane(vs.message);
1464 if(pl.voicescript_index < vs.cnt)
1465 i = pl.voicescript_index * 2;
1466 else if(n > vs.cnt * 2)
1467 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1473 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1474 pl.voicescript_voiceend = time + stof(argv(i + 1));
1477 pl.voicescript = world;
1479 pl.voicescript_index += 1;
1480 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1485 void spawnfunc_target_voicescript()
1487 // netname: directory of the sound files
1488 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1489 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1490 // wait: average time between messages
1491 // delay: initial delay before the first message
1494 self.use = target_voicescript_use;
1496 n = tokenize_sane(self.message);
1498 for(i = 0; i+1 < n; i += 2)
1505 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1511 void trigger_relay_teamcheck_use()
1515 if(self.spawnflags & 2)
1517 if(activator.team != self.team)
1522 if(activator.team == self.team)
1528 if(self.spawnflags & 1)
1533 void trigger_relay_teamcheck_reset()
1535 self.team = self.team_saved;
1538 void spawnfunc_trigger_relay_teamcheck()
1540 self.team_saved = self.team;
1541 self.use = trigger_relay_teamcheck_use;
1542 self.reset = trigger_relay_teamcheck_reset;
1547 void trigger_disablerelay_use()
1554 for(e = world; (e = find(e, targetname, self.target)); )
1556 if(e.use == SUB_UseTargets)
1558 e.use = SUB_DontUseTargets;
1561 else if(e.use == SUB_DontUseTargets)
1563 e.use = SUB_UseTargets;
1569 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1572 void spawnfunc_trigger_disablerelay()
1574 self.use = trigger_disablerelay_use;