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;
558 .float bgmscriptdecay;
559 float pointparticles_SendEntity(entity to, float fl)
561 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
562 WriteByte(MSG_ENTITY, fl);
566 WriteCoord(MSG_ENTITY, self.impulse);
568 WriteCoord(MSG_ENTITY, 0); // off
572 WriteCoord(MSG_ENTITY, self.origin_x);
573 WriteCoord(MSG_ENTITY, self.origin_y);
574 WriteCoord(MSG_ENTITY, self.origin_z);
578 if(self.model != "null")
580 WriteShort(MSG_ENTITY, self.modelindex);
581 WriteCoord(MSG_ENTITY, self.mins_x);
582 WriteCoord(MSG_ENTITY, self.mins_y);
583 WriteCoord(MSG_ENTITY, self.mins_z);
584 WriteCoord(MSG_ENTITY, self.maxs_x);
585 WriteCoord(MSG_ENTITY, self.maxs_y);
586 WriteCoord(MSG_ENTITY, self.maxs_z);
590 WriteShort(MSG_ENTITY, 0);
591 WriteCoord(MSG_ENTITY, self.maxs_x);
592 WriteCoord(MSG_ENTITY, self.maxs_y);
593 WriteCoord(MSG_ENTITY, self.maxs_z);
595 WriteShort(MSG_ENTITY, self.cnt);
596 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
597 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
598 WriteCoord(MSG_ENTITY, self.waterlevel);
599 WriteCoord(MSG_ENTITY, self.count);
600 WriteByte(MSG_ENTITY, self.glow_color);
601 WriteString(MSG_ENTITY, self.noise);
604 WriteByte(MSG_ENTITY, floor(self.atten * 64));
605 WriteByte(MSG_ENTITY, floor(self.volume * 255));
607 WriteString(MSG_ENTITY, self.bgmscript);
608 if(self.bgmscript != "")
609 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 255));
614 void pointparticles_use()
616 self.state = !self.state;
620 void pointparticles_think()
622 if(self.origin != self.oldorigin)
625 self.oldorigin = self.origin;
627 self.nextthink = time;
630 void pointparticles_reset()
632 if(self.spawnflags & 1)
638 void spawnfunc_func_pointparticles()
641 setmodel(self, self.model);
643 precache_sound (self.noise);
646 self.atten = ATTN_NORM;
647 else if(self.atten < 0)
654 setorigin(self, self.origin + self.mins);
655 setsize(self, '0 0 0', self.maxs - self.mins);
658 self.cnt = particleeffectnum(self.mdl);
660 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
664 self.use = pointparticles_use;
665 self.reset = pointparticles_reset;
670 self.think = pointparticles_think;
671 self.nextthink = time;
674 void spawnfunc_func_sparks()
676 // self.cnt is the amount of sparks that one burst will spawn
678 self.cnt = 25.0; // nice default value
681 // self.wait is the probability that a sparkthink will spawn a spark shower
682 // range: 0 - 1, but 0 makes little sense, so...
683 if(self.wait < 0.05) {
684 self.wait = 0.25; // nice default value
687 self.count = self.cnt;
690 self.velocity = '0 0 -1';
691 self.mdl = "TE_SPARK";
692 self.impulse = 10 * self.wait; // by default 2.5/sec
694 self.cnt = 0; // use mdl
696 spawnfunc_func_pointparticles();
699 float rainsnow_SendEntity(entity to, float sf)
701 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
702 WriteByte(MSG_ENTITY, self.state);
703 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
704 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
705 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
706 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
707 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
708 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
709 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
710 WriteShort(MSG_ENTITY, self.count);
711 WriteByte(MSG_ENTITY, self.cnt);
715 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
716 This is an invisible area like a trigger, which rain falls inside of.
720 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
722 sets color of rain (default 12 - white)
724 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
726 void spawnfunc_func_rain()
728 self.dest = self.velocity;
729 self.velocity = '0 0 0';
731 self.dest = '0 0 -700';
732 self.angles = '0 0 0';
733 self.movetype = MOVETYPE_NONE;
734 self.solid = SOLID_NOT;
735 SetBrushEntityModel();
740 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
743 if(self.count > 65535)
746 self.state = 1; // 1 is rain, 0 is snow
749 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
753 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
754 This is an invisible area like a trigger, which snow falls inside of.
758 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
760 sets color of rain (default 12 - white)
762 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
764 void spawnfunc_func_snow()
766 self.dest = self.velocity;
767 self.velocity = '0 0 0';
769 self.dest = '0 0 -300';
770 self.angles = '0 0 0';
771 self.movetype = MOVETYPE_NONE;
772 self.solid = SOLID_NOT;
773 SetBrushEntityModel();
778 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
781 if(self.count > 65535)
784 self.state = 0; // 1 is rain, 0 is snow
787 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
791 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
794 void misc_laser_aim()
799 if(self.spawnflags & 2)
801 if(self.enemy.origin != self.mangle)
803 self.mangle = self.enemy.origin;
809 a = vectoangles(self.enemy.origin - self.origin);
820 if(self.angles != self.mangle)
822 self.mangle = self.angles;
826 if(self.origin != self.oldorigin)
829 self.oldorigin = self.origin;
833 void misc_laser_init()
835 if(self.target != "")
836 self.enemy = find(world, targetname, self.target);
840 void misc_laser_think()
845 self.nextthink = time;
854 o = self.enemy.origin;
855 if not(self.spawnflags & 2)
856 o = self.origin + normalize(o - self.origin) * 32768;
860 makevectors(self.mangle);
861 o = self.origin + v_forward * 32768;
867 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
869 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
872 if(self.enemy.target != "") // DETECTOR laser
874 traceline(self.origin, o, MOVE_NORMAL, self);
875 if(trace_ent.iscreature)
877 self.pusher = trace_ent;
884 activator = self.pusher;
897 activator = self.pusher;
905 float laser_SendEntity(entity to, float fl)
907 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
908 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
909 if(self.spawnflags & 2)
913 if(self.scale != 1 || self.modelscale != 1)
915 WriteByte(MSG_ENTITY, fl);
918 WriteCoord(MSG_ENTITY, self.origin_x);
919 WriteCoord(MSG_ENTITY, self.origin_y);
920 WriteCoord(MSG_ENTITY, self.origin_z);
924 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
925 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
926 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
928 WriteByte(MSG_ENTITY, self.alpha * 255.0);
931 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
932 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
934 WriteShort(MSG_ENTITY, self.cnt + 1);
940 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
941 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
942 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
946 WriteAngle(MSG_ENTITY, self.mangle_x);
947 WriteAngle(MSG_ENTITY, self.mangle_y);
951 WriteByte(MSG_ENTITY, self.state);
955 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
956 Any object touching the beam will be hurt
959 spawnfunc_target_position where the laser ends
961 name of beam end effect to use
963 color of the beam (default: red)
965 damage per second (-1 for a laser that kills immediately)
969 self.state = !self.state;
976 if(self.spawnflags & 1)
982 void spawnfunc_misc_laser()
986 if(self.mdl == "none")
990 self.cnt = particleeffectnum(self.mdl);
993 self.cnt = particleeffectnum("laser_deadly");
999 self.cnt = particleeffectnum("laser_deadly");
1006 if(self.colormod == '0 0 0')
1008 self.colormod = '1 0 0';
1010 self.message = "saw the light";
1012 self.message2 = "was pushed into a laser by";
1015 if(!self.modelscale)
1016 self.modelscale = 1;
1017 self.think = misc_laser_think;
1018 self.nextthink = time;
1019 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1021 self.mangle = self.angles;
1023 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1027 self.reset = laser_reset;
1029 self.use = laser_use;
1035 // tZorks trigger impulse / gravity
1039 .float lastpushtime;
1041 // targeted (directional) mode
1042 void trigger_impulse_touch1()
1045 float pushdeltatime;
1048 // FIXME: Better checking for what to push and not.
1049 if not(other.iscreature)
1050 if (other.classname != "corpse")
1051 if (other.classname != "body")
1052 if (other.classname != "gib")
1053 if (other.classname != "missile")
1054 if (other.classname != "rocket")
1055 if (other.classname != "casing")
1056 if (other.classname != "grenade")
1057 if (other.classname != "plasma")
1058 if (other.classname != "plasma_prim")
1059 if (other.classname != "plasma_chain")
1060 if (other.classname != "droppedweapon")
1063 if (other.deadflag && other.iscreature)
1068 targ = find(world, targetname, self.target);
1071 objerror("trigger_force without a (valid) .target!\n");
1076 if(self.falloff == 1)
1077 str = (str / self.radius) * self.strength;
1078 else if(self.falloff == 2)
1079 str = (1 - (str / self.radius)) * self.strength;
1081 str = self.strength;
1083 pushdeltatime = time - other.lastpushtime;
1084 if (pushdeltatime > 0.15) pushdeltatime = 0;
1085 other.lastpushtime = time;
1086 if(!pushdeltatime) return;
1088 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1089 other.flags &~= FL_ONGROUND;
1092 // Directionless (accelerator/decelerator) mode
1093 void trigger_impulse_touch2()
1095 float pushdeltatime;
1097 // FIXME: Better checking for what to push and not.
1098 if not(other.iscreature)
1099 if (other.classname != "corpse")
1100 if (other.classname != "body")
1101 if (other.classname != "gib")
1102 if (other.classname != "missile")
1103 if (other.classname != "rocket")
1104 if (other.classname != "casing")
1105 if (other.classname != "grenade")
1106 if (other.classname != "plasma")
1107 if (other.classname != "plasma_prim")
1108 if (other.classname != "plasma_chain")
1109 if (other.classname != "droppedweapon")
1112 if (other.deadflag && other.iscreature)
1117 pushdeltatime = time - other.lastpushtime;
1118 if (pushdeltatime > 0.15) pushdeltatime = 0;
1119 other.lastpushtime = time;
1120 if(!pushdeltatime) return;
1122 //if(self.strength > 1)
1123 other.velocity = other.velocity * (self.strength * pushdeltatime);
1125 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1128 // Spherical (gravity/repulsor) mode
1129 void trigger_impulse_touch3()
1131 float pushdeltatime;
1134 // FIXME: Better checking for what to push and not.
1135 if not(other.iscreature)
1136 if (other.classname != "corpse")
1137 if (other.classname != "body")
1138 if (other.classname != "gib")
1139 if (other.classname != "missile")
1140 if (other.classname != "rocket")
1141 if (other.classname != "casing")
1142 if (other.classname != "grenade")
1143 if (other.classname != "plasma")
1144 if (other.classname != "plasma_prim")
1145 if (other.classname != "plasma_chain")
1146 if (other.classname != "droppedweapon")
1149 if (other.deadflag && other.iscreature)
1154 pushdeltatime = time - other.lastpushtime;
1155 if (pushdeltatime > 0.15) pushdeltatime = 0;
1156 other.lastpushtime = time;
1157 if(!pushdeltatime) return;
1159 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1161 str = min(self.radius, vlen(self.origin - other.origin));
1163 if(self.falloff == 1)
1164 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1165 else if(self.falloff == 2)
1166 str = (str / self.radius) * self.strength; // 0 in the inside
1168 str = self.strength;
1170 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1173 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1174 -------- KEYS --------
1175 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1176 If not, this trigger acts like a damper/accelerator field.
1178 strength : This is how mutch force to add in the direction of .target each second
1179 when .target is set. If not, this is hoe mutch to slow down/accelerate
1180 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1182 radius : If set, act as a spherical device rather then a liniar one.
1184 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1186 -------- NOTES --------
1187 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1188 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1191 void spawnfunc_trigger_impulse()
1196 if(!self.strength) self.strength = 2000;
1197 setorigin(self, self.origin);
1198 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1199 self.touch = trigger_impulse_touch3;
1205 if(!self.strength) self.strength = 950;
1206 self.touch = trigger_impulse_touch1;
1210 if(!self.strength) self.strength = 0.9;
1211 self.touch = trigger_impulse_touch2;
1216 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1217 "Flip-flop" trigger gate... lets only every second trigger event through
1221 self.state = !self.state;
1226 void spawnfunc_trigger_flipflop()
1228 if(self.spawnflags & 1)
1230 self.use = flipflop_use;
1231 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1234 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1235 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1239 self.nextthink = time + self.wait;
1240 self.enemy = activator;
1246 void monoflop_fixed_use()
1250 self.nextthink = time + self.wait;
1252 self.enemy = activator;
1256 void monoflop_think()
1259 activator = self.enemy;
1263 void monoflop_reset()
1269 void spawnfunc_trigger_monoflop()
1273 if(self.spawnflags & 1)
1274 self.use = monoflop_fixed_use;
1276 self.use = monoflop_use;
1277 self.think = monoflop_think;
1279 self.reset = monoflop_reset;
1282 void multivibrator_send()
1287 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1289 newstate = (time < cyclestart + self.wait);
1292 if(self.state != newstate)
1294 self.state = newstate;
1297 self.nextthink = cyclestart + self.wait + 0.01;
1299 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1302 void multivibrator_toggle()
1304 if(self.nextthink == 0)
1306 multivibrator_send();
1319 void multivibrator_reset()
1321 if(!(self.spawnflags & 1))
1322 self.nextthink = 0; // wait for a trigger event
1324 self.nextthink = max(1, time);
1327 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1328 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1329 -------- KEYS --------
1330 target: trigger all entities with this targetname when it goes off
1331 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1332 phase: offset of the timing
1333 wait: "on" cycle time (default: 1)
1334 respawntime: "off" cycle time (default: same as wait)
1335 -------- SPAWNFLAGS --------
1336 START_ON: assume it is already turned on (when targeted)
1338 void spawnfunc_trigger_multivibrator()
1342 if(!self.respawntime)
1343 self.respawntime = self.wait;
1346 self.use = multivibrator_toggle;
1347 self.think = multivibrator_send;
1348 self.nextthink = time;
1351 multivibrator_reset();
1358 src = find(world, targetname, self.killtarget);
1359 dst = find(world, targetname, self.target);
1363 objerror("follow: could not find target/killtarget");
1367 if(self.spawnflags & 1)
1370 if(self.spawnflags & 2)
1372 setattachment(dst, src, self.message);
1376 attach_sameorigin(dst, src, self.message);
1381 if(self.spawnflags & 2)
1383 dst.movetype = MOVETYPE_FOLLOW;
1385 // dst.punchangle = '0 0 0'; // keep unchanged
1386 dst.view_ofs = dst.origin;
1387 dst.v_angle = dst.angles;
1391 follow_sameorigin(dst, src);
1398 void spawnfunc_misc_follow()
1400 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1405 void gamestart_use() {
1411 void spawnfunc_trigger_gamestart() {
1412 self.use = gamestart_use;
1413 self.reset2 = spawnfunc_trigger_gamestart;
1417 self.think = self.use;
1418 self.nextthink = game_starttime + self.wait;
1421 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1427 .entity voicescript; // attached voice script
1428 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1429 .float voicescript_nextthink; // time to play next voice
1430 .float voicescript_voiceend; // time when this voice ends
1432 void target_voicescript_clear(entity pl)
1434 pl.voicescript = world;
1437 void target_voicescript_use()
1439 if(activator.voicescript != self)
1441 activator.voicescript = self;
1442 activator.voicescript_index = 0;
1443 activator.voicescript_nextthink = time + self.delay;
1447 void target_voicescript_next(entity pl)
1452 vs = pl.voicescript;
1455 if(vs.message == "")
1457 if(pl.classname != "player")
1462 if(time >= pl.voicescript_voiceend)
1464 if(time >= pl.voicescript_nextthink)
1466 // get the next voice...
1467 n = tokenize_sane(vs.message);
1469 if(pl.voicescript_index < vs.cnt)
1470 i = pl.voicescript_index * 2;
1471 else if(n > vs.cnt * 2)
1472 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1478 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1479 pl.voicescript_voiceend = time + stof(argv(i + 1));
1482 pl.voicescript = world;
1484 pl.voicescript_index += 1;
1485 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1490 void spawnfunc_target_voicescript()
1492 // netname: directory of the sound files
1493 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1494 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1495 // wait: average time between messages
1496 // delay: initial delay before the first message
1499 self.use = target_voicescript_use;
1501 n = tokenize_sane(self.message);
1503 for(i = 0; i+1 < n; i += 2)
1510 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1516 void trigger_relay_teamcheck_use()
1520 if(self.spawnflags & 2)
1522 if(activator.team != self.team)
1527 if(activator.team == self.team)
1533 if(self.spawnflags & 1)
1538 void trigger_relay_teamcheck_reset()
1540 self.team = self.team_saved;
1543 void spawnfunc_trigger_relay_teamcheck()
1545 self.team_saved = self.team;
1546 self.use = trigger_relay_teamcheck_use;
1547 self.reset = trigger_relay_teamcheck_reset;
1552 void trigger_disablerelay_use()
1559 for(e = world; (e = find(e, targetname, self.target)); )
1561 if(e.use == SUB_UseTargets)
1563 e.use = SUB_DontUseTargets;
1566 else if(e.use == SUB_DontUseTargets)
1568 e.use = SUB_UseTargets;
1574 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1577 void spawnfunc_trigger_disablerelay()
1579 self.use = trigger_disablerelay_use;