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);
563 // optional features to save space
565 if(self.noise || self.bgmscript)
566 fl |= 0x10; // 2 bytes
567 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
568 fl |= 0x20; // 4 bytes - saves CPU
569 if(self.glow_color || self.waterlevel || self.count != 1)
570 fl |= 0x40; // 4 bytes - obscure features almost never used
571 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
572 fl |= 0x80; // 14 bytes - saves lots of space
574 WriteByte(MSG_ENTITY, fl);
578 WriteCoord(MSG_ENTITY, self.impulse);
580 WriteCoord(MSG_ENTITY, 0); // off
584 WriteCoord(MSG_ENTITY, self.origin_x);
585 WriteCoord(MSG_ENTITY, self.origin_y);
586 WriteCoord(MSG_ENTITY, self.origin_z);
590 if(self.model != "null")
592 WriteShort(MSG_ENTITY, self.modelindex);
595 WriteCoord(MSG_ENTITY, self.mins_x);
596 WriteCoord(MSG_ENTITY, self.mins_y);
597 WriteCoord(MSG_ENTITY, self.mins_z);
598 WriteCoord(MSG_ENTITY, self.maxs_x);
599 WriteCoord(MSG_ENTITY, self.maxs_y);
600 WriteCoord(MSG_ENTITY, self.maxs_z);
605 WriteShort(MSG_ENTITY, 0);
608 WriteCoord(MSG_ENTITY, self.maxs_x);
609 WriteCoord(MSG_ENTITY, self.maxs_y);
610 WriteCoord(MSG_ENTITY, self.maxs_z);
613 WriteShort(MSG_ENTITY, self.cnt);
616 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
617 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
621 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
622 WriteByte(MSG_ENTITY, self.count * 16.0);
623 WriteByte(MSG_ENTITY, self.glow_color);
627 WriteString(MSG_ENTITY, self.noise);
630 WriteByte(MSG_ENTITY, floor(self.atten * 64));
631 WriteByte(MSG_ENTITY, floor(self.volume * 255));
633 WriteString(MSG_ENTITY, self.bgmscript);
634 if(self.bgmscript != "")
635 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 255));
641 void pointparticles_use()
643 self.state = !self.state;
647 void pointparticles_think()
649 if(self.origin != self.oldorigin)
652 self.oldorigin = self.origin;
654 self.nextthink = time;
657 void pointparticles_reset()
659 if(self.spawnflags & 1)
665 void spawnfunc_func_pointparticles()
668 setmodel(self, self.model);
670 precache_sound (self.noise);
673 self.atten = ATTN_NORM;
674 else if(self.atten < 0)
683 setorigin(self, self.origin + self.mins);
684 setsize(self, '0 0 0', self.maxs - self.mins);
687 self.cnt = particleeffectnum(self.mdl);
689 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
693 self.use = pointparticles_use;
694 self.reset = pointparticles_reset;
699 self.think = pointparticles_think;
700 self.nextthink = time;
703 void spawnfunc_func_sparks()
705 // self.cnt is the amount of sparks that one burst will spawn
707 self.cnt = 25.0; // nice default value
710 // self.wait is the probability that a sparkthink will spawn a spark shower
711 // range: 0 - 1, but 0 makes little sense, so...
712 if(self.wait < 0.05) {
713 self.wait = 0.25; // nice default value
716 self.count = self.cnt;
719 self.velocity = '0 0 -1';
720 self.mdl = "TE_SPARK";
721 self.impulse = 10 * self.wait; // by default 2.5/sec
723 self.cnt = 0; // use mdl
725 spawnfunc_func_pointparticles();
728 float rainsnow_SendEntity(entity to, float sf)
730 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
731 WriteByte(MSG_ENTITY, self.state);
732 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
733 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
734 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
735 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
736 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
737 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
738 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
739 WriteShort(MSG_ENTITY, self.count);
740 WriteByte(MSG_ENTITY, self.cnt);
744 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
745 This is an invisible area like a trigger, which rain falls inside of.
749 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
751 sets color of rain (default 12 - white)
753 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
755 void spawnfunc_func_rain()
757 self.dest = self.velocity;
758 self.velocity = '0 0 0';
760 self.dest = '0 0 -700';
761 self.angles = '0 0 0';
762 self.movetype = MOVETYPE_NONE;
763 self.solid = SOLID_NOT;
764 SetBrushEntityModel();
769 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
772 if(self.count > 65535)
775 self.state = 1; // 1 is rain, 0 is snow
778 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
782 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
783 This is an invisible area like a trigger, which snow falls inside of.
787 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
789 sets color of rain (default 12 - white)
791 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
793 void spawnfunc_func_snow()
795 self.dest = self.velocity;
796 self.velocity = '0 0 0';
798 self.dest = '0 0 -300';
799 self.angles = '0 0 0';
800 self.movetype = MOVETYPE_NONE;
801 self.solid = SOLID_NOT;
802 SetBrushEntityModel();
807 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
810 if(self.count > 65535)
813 self.state = 0; // 1 is rain, 0 is snow
816 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
820 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
823 void misc_laser_aim()
828 if(self.spawnflags & 2)
830 if(self.enemy.origin != self.mangle)
832 self.mangle = self.enemy.origin;
838 a = vectoangles(self.enemy.origin - self.origin);
849 if(self.angles != self.mangle)
851 self.mangle = self.angles;
855 if(self.origin != self.oldorigin)
858 self.oldorigin = self.origin;
862 void misc_laser_init()
864 if(self.target != "")
865 self.enemy = find(world, targetname, self.target);
869 void misc_laser_think()
874 self.nextthink = time;
883 o = self.enemy.origin;
884 if not(self.spawnflags & 2)
885 o = self.origin + normalize(o - self.origin) * 32768;
889 makevectors(self.mangle);
890 o = self.origin + v_forward * 32768;
896 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
898 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
901 if(self.enemy.target != "") // DETECTOR laser
903 traceline(self.origin, o, MOVE_NORMAL, self);
904 if(trace_ent.iscreature)
906 self.pusher = trace_ent;
913 activator = self.pusher;
926 activator = self.pusher;
934 float laser_SendEntity(entity to, float fl)
936 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
937 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
938 if(self.spawnflags & 2)
942 if(self.scale != 1 || self.modelscale != 1)
944 WriteByte(MSG_ENTITY, fl);
947 WriteCoord(MSG_ENTITY, self.origin_x);
948 WriteCoord(MSG_ENTITY, self.origin_y);
949 WriteCoord(MSG_ENTITY, self.origin_z);
953 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
954 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
955 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
957 WriteByte(MSG_ENTITY, self.alpha * 255.0);
960 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
961 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
963 WriteShort(MSG_ENTITY, self.cnt + 1);
969 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
970 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
971 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
975 WriteAngle(MSG_ENTITY, self.mangle_x);
976 WriteAngle(MSG_ENTITY, self.mangle_y);
980 WriteByte(MSG_ENTITY, self.state);
984 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
985 Any object touching the beam will be hurt
988 spawnfunc_target_position where the laser ends
990 name of beam end effect to use
992 color of the beam (default: red)
994 damage per second (-1 for a laser that kills immediately)
998 self.state = !self.state;
1005 if(self.spawnflags & 1)
1011 void spawnfunc_misc_laser()
1015 if(self.mdl == "none")
1019 self.cnt = particleeffectnum(self.mdl);
1022 self.cnt = particleeffectnum("laser_deadly");
1028 self.cnt = particleeffectnum("laser_deadly");
1035 if(self.colormod == '0 0 0')
1037 self.colormod = '1 0 0';
1039 self.message = "saw the light";
1041 self.message2 = "was pushed into a laser by";
1044 if(!self.modelscale)
1045 self.modelscale = 1;
1046 self.think = misc_laser_think;
1047 self.nextthink = time;
1048 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1050 self.mangle = self.angles;
1052 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1056 self.reset = laser_reset;
1058 self.use = laser_use;
1064 // tZorks trigger impulse / gravity
1068 .float lastpushtime;
1070 // targeted (directional) mode
1071 void trigger_impulse_touch1()
1074 float pushdeltatime;
1077 // FIXME: Better checking for what to push and not.
1078 if not(other.iscreature)
1079 if (other.classname != "corpse")
1080 if (other.classname != "body")
1081 if (other.classname != "gib")
1082 if (other.classname != "missile")
1083 if (other.classname != "rocket")
1084 if (other.classname != "casing")
1085 if (other.classname != "grenade")
1086 if (other.classname != "plasma")
1087 if (other.classname != "plasma_prim")
1088 if (other.classname != "plasma_chain")
1089 if (other.classname != "droppedweapon")
1092 if (other.deadflag && other.iscreature)
1097 targ = find(world, targetname, self.target);
1100 objerror("trigger_force without a (valid) .target!\n");
1105 if(self.falloff == 1)
1106 str = (str / self.radius) * self.strength;
1107 else if(self.falloff == 2)
1108 str = (1 - (str / self.radius)) * self.strength;
1110 str = self.strength;
1112 pushdeltatime = time - other.lastpushtime;
1113 if (pushdeltatime > 0.15) pushdeltatime = 0;
1114 other.lastpushtime = time;
1115 if(!pushdeltatime) return;
1117 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1118 other.flags &~= FL_ONGROUND;
1121 // Directionless (accelerator/decelerator) mode
1122 void trigger_impulse_touch2()
1124 float pushdeltatime;
1126 // FIXME: Better checking for what to push and not.
1127 if not(other.iscreature)
1128 if (other.classname != "corpse")
1129 if (other.classname != "body")
1130 if (other.classname != "gib")
1131 if (other.classname != "missile")
1132 if (other.classname != "rocket")
1133 if (other.classname != "casing")
1134 if (other.classname != "grenade")
1135 if (other.classname != "plasma")
1136 if (other.classname != "plasma_prim")
1137 if (other.classname != "plasma_chain")
1138 if (other.classname != "droppedweapon")
1141 if (other.deadflag && other.iscreature)
1146 pushdeltatime = time - other.lastpushtime;
1147 if (pushdeltatime > 0.15) pushdeltatime = 0;
1148 other.lastpushtime = time;
1149 if(!pushdeltatime) return;
1151 //if(self.strength > 1)
1152 other.velocity = other.velocity * (self.strength * pushdeltatime);
1154 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1157 // Spherical (gravity/repulsor) mode
1158 void trigger_impulse_touch3()
1160 float pushdeltatime;
1163 // FIXME: Better checking for what to push and not.
1164 if not(other.iscreature)
1165 if (other.classname != "corpse")
1166 if (other.classname != "body")
1167 if (other.classname != "gib")
1168 if (other.classname != "missile")
1169 if (other.classname != "rocket")
1170 if (other.classname != "casing")
1171 if (other.classname != "grenade")
1172 if (other.classname != "plasma")
1173 if (other.classname != "plasma_prim")
1174 if (other.classname != "plasma_chain")
1175 if (other.classname != "droppedweapon")
1178 if (other.deadflag && other.iscreature)
1183 pushdeltatime = time - other.lastpushtime;
1184 if (pushdeltatime > 0.15) pushdeltatime = 0;
1185 other.lastpushtime = time;
1186 if(!pushdeltatime) return;
1188 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1190 str = min(self.radius, vlen(self.origin - other.origin));
1192 if(self.falloff == 1)
1193 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1194 else if(self.falloff == 2)
1195 str = (str / self.radius) * self.strength; // 0 in the inside
1197 str = self.strength;
1199 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1202 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1203 -------- KEYS --------
1204 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1205 If not, this trigger acts like a damper/accelerator field.
1207 strength : This is how mutch force to add in the direction of .target each second
1208 when .target is set. If not, this is hoe mutch to slow down/accelerate
1209 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1211 radius : If set, act as a spherical device rather then a liniar one.
1213 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1215 -------- NOTES --------
1216 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1217 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1220 void spawnfunc_trigger_impulse()
1225 if(!self.strength) self.strength = 2000;
1226 setorigin(self, self.origin);
1227 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1228 self.touch = trigger_impulse_touch3;
1234 if(!self.strength) self.strength = 950;
1235 self.touch = trigger_impulse_touch1;
1239 if(!self.strength) self.strength = 0.9;
1240 self.touch = trigger_impulse_touch2;
1245 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1246 "Flip-flop" trigger gate... lets only every second trigger event through
1250 self.state = !self.state;
1255 void spawnfunc_trigger_flipflop()
1257 if(self.spawnflags & 1)
1259 self.use = flipflop_use;
1260 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1263 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1264 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1268 self.nextthink = time + self.wait;
1269 self.enemy = activator;
1275 void monoflop_fixed_use()
1279 self.nextthink = time + self.wait;
1281 self.enemy = activator;
1285 void monoflop_think()
1288 activator = self.enemy;
1292 void monoflop_reset()
1298 void spawnfunc_trigger_monoflop()
1302 if(self.spawnflags & 1)
1303 self.use = monoflop_fixed_use;
1305 self.use = monoflop_use;
1306 self.think = monoflop_think;
1308 self.reset = monoflop_reset;
1311 void multivibrator_send()
1316 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1318 newstate = (time < cyclestart + self.wait);
1321 if(self.state != newstate)
1323 self.state = newstate;
1326 self.nextthink = cyclestart + self.wait + 0.01;
1328 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1331 void multivibrator_toggle()
1333 if(self.nextthink == 0)
1335 multivibrator_send();
1348 void multivibrator_reset()
1350 if(!(self.spawnflags & 1))
1351 self.nextthink = 0; // wait for a trigger event
1353 self.nextthink = max(1, time);
1356 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1357 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1358 -------- KEYS --------
1359 target: trigger all entities with this targetname when it goes off
1360 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1361 phase: offset of the timing
1362 wait: "on" cycle time (default: 1)
1363 respawntime: "off" cycle time (default: same as wait)
1364 -------- SPAWNFLAGS --------
1365 START_ON: assume it is already turned on (when targeted)
1367 void spawnfunc_trigger_multivibrator()
1371 if(!self.respawntime)
1372 self.respawntime = self.wait;
1375 self.use = multivibrator_toggle;
1376 self.think = multivibrator_send;
1377 self.nextthink = time;
1380 multivibrator_reset();
1387 src = find(world, targetname, self.killtarget);
1388 dst = find(world, targetname, self.target);
1392 objerror("follow: could not find target/killtarget");
1396 if(self.spawnflags & 1)
1399 if(self.spawnflags & 2)
1401 setattachment(dst, src, self.message);
1405 attach_sameorigin(dst, src, self.message);
1410 if(self.spawnflags & 2)
1412 dst.movetype = MOVETYPE_FOLLOW;
1414 // dst.punchangle = '0 0 0'; // keep unchanged
1415 dst.view_ofs = dst.origin;
1416 dst.v_angle = dst.angles;
1420 follow_sameorigin(dst, src);
1427 void spawnfunc_misc_follow()
1429 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1434 void gamestart_use() {
1440 void spawnfunc_trigger_gamestart() {
1441 self.use = gamestart_use;
1442 self.reset2 = spawnfunc_trigger_gamestart;
1446 self.think = self.use;
1447 self.nextthink = game_starttime + self.wait;
1450 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1456 .entity voicescript; // attached voice script
1457 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1458 .float voicescript_nextthink; // time to play next voice
1459 .float voicescript_voiceend; // time when this voice ends
1461 void target_voicescript_clear(entity pl)
1463 pl.voicescript = world;
1466 void target_voicescript_use()
1468 if(activator.voicescript != self)
1470 activator.voicescript = self;
1471 activator.voicescript_index = 0;
1472 activator.voicescript_nextthink = time + self.delay;
1476 void target_voicescript_next(entity pl)
1481 vs = pl.voicescript;
1484 if(vs.message == "")
1486 if(pl.classname != "player")
1491 if(time >= pl.voicescript_voiceend)
1493 if(time >= pl.voicescript_nextthink)
1495 // get the next voice...
1496 n = tokenize_sane(vs.message);
1498 if(pl.voicescript_index < vs.cnt)
1499 i = pl.voicescript_index * 2;
1500 else if(n > vs.cnt * 2)
1501 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1507 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1508 pl.voicescript_voiceend = time + stof(argv(i + 1));
1511 pl.voicescript = world;
1513 pl.voicescript_index += 1;
1514 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1519 void spawnfunc_target_voicescript()
1521 // netname: directory of the sound files
1522 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1523 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1524 // wait: average time between messages
1525 // delay: initial delay before the first message
1528 self.use = target_voicescript_use;
1530 n = tokenize_sane(self.message);
1532 for(i = 0; i+1 < n; i += 2)
1539 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1545 void trigger_relay_teamcheck_use()
1549 if(self.spawnflags & 2)
1551 if(activator.team != self.team)
1556 if(activator.team == self.team)
1562 if(self.spawnflags & 1)
1567 void trigger_relay_teamcheck_reset()
1569 self.team = self.team_saved;
1572 void spawnfunc_trigger_relay_teamcheck()
1574 self.team_saved = self.team;
1575 self.use = trigger_relay_teamcheck_use;
1576 self.reset = trigger_relay_teamcheck_reset;
1581 void trigger_disablerelay_use()
1588 for(e = world; (e = find(e, targetname, self.target)); )
1590 if(e.use == SUB_UseTargets)
1592 e.use = SUB_DontUseTargets;
1595 else if(e.use == SUB_DontUseTargets)
1597 e.use = SUB_UseTargets;
1603 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1606 void spawnfunc_trigger_disablerelay()
1608 self.use = trigger_disablerelay_use;