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;
160 other = self.goalentity;
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.goalentity = other;
178 self.enemy = activator;
184 if not(self.spawnflags & 2)
186 if not(other.iscreature)
190 if(self.team == other.team)
194 // if the trigger has an angles field, check player's facing direction
195 if (self.movedir != '0 0 0')
197 makevectors (other.angles);
198 if (v_forward * self.movedir < 0)
199 return; // not facing the right way
205 self.goalentity = other;
209 void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
211 if (!self.takedamage)
213 self.health = self.health - damage;
214 if (self.health <= 0)
216 self.enemy = attacker;
217 self.goalentity = inflictor;
224 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
225 self.touch = multi_touch;
228 self.health = self.max_health;
229 self.takedamage = DAMAGE_YES;
230 self.solid = SOLID_BBOX;
232 self.think = SUB_Null;
233 self.team = self.team_saved;
236 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
237 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.
238 If "delay" is set, the trigger waits some time after activating before firing.
239 "wait" : Seconds between triggerings. (.2 default)
240 If notouch is set, the trigger is only fired by other entities, not by touching.
241 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
247 set "message" to text string
249 void spawnfunc_trigger_multiple()
251 self.reset = multi_reset;
252 if (self.sounds == 1)
254 precache_sound ("misc/secret.wav");
255 self.noise = "misc/secret.wav";
257 else if (self.sounds == 2)
259 precache_sound ("misc/talk.wav");
260 self.noise = "misc/talk.wav";
262 else if (self.sounds == 3)
264 precache_sound ("misc/trigger1.wav");
265 self.noise = "misc/trigger1.wav";
270 self.use = multi_use;
274 self.team_saved = self.team;
278 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
279 objerror ("health and notouch don't make sense\n");
280 self.max_health = self.health;
281 self.event_damage = multi_eventdamage;
282 self.takedamage = DAMAGE_YES;
283 self.solid = SOLID_BBOX;
284 setorigin (self, self.origin); // make sure it links into the world
288 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
290 self.touch = multi_touch;
291 setorigin (self, self.origin); // make sure it links into the world
297 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
298 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
299 "targetname". If "health" is set, the trigger must be killed to activate.
300 If notouch is set, the trigger is only fired by other entities, not by touching.
301 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
302 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.
308 set "message" to text string
310 void spawnfunc_trigger_once()
313 spawnfunc_trigger_multiple();
316 //=============================================================================
318 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
319 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
321 void spawnfunc_trigger_relay()
323 self.use = SUB_UseTargets;
324 self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
329 self.think = SUB_UseTargets;
330 self.nextthink = self.wait;
335 self.think = SUB_Null;
338 void spawnfunc_trigger_delay()
343 self.use = delay_use;
344 self.reset = delay_reset;
347 //=============================================================================
352 self.count = self.count - 1;
358 if (activator.classname == "player"
359 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
362 centerprint (activator, "There are more to go...");
363 else if (self.count == 3)
364 centerprint (activator, "Only 3 more to go...");
365 else if (self.count == 2)
366 centerprint (activator, "Only 2 more to go...");
368 centerprint (activator, "Only 1 more to go...");
373 if (activator.classname == "player"
374 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
375 centerprint(activator, "Sequence completed!");
376 self.enemy = activator;
382 self.count = self.cnt;
386 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
387 Acts as an intermediary for an action that takes multiple inputs.
389 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
391 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
393 void spawnfunc_trigger_counter()
398 self.cnt = self.count;
400 self.use = counter_use;
401 self.reset = counter_reset;
404 .float triggerhurttime;
405 void trigger_hurt_touch()
407 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
408 if (other.iscreature)
410 if (other.takedamage)
411 if (other.triggerhurttime < time)
414 other.triggerhurttime = time + 1;
415 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
422 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
425 other.pain_finished = min(other.pain_finished, time + 2);
427 else if (other.classname == "rune") // reset runes
430 other.nextthink = min(other.nextthink, time + 1);
438 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
439 Any object touching this will be hurt
440 set dmg to damage amount
443 .entity trigger_hurt_next;
444 entity trigger_hurt_last;
445 entity trigger_hurt_first;
446 void spawnfunc_trigger_hurt()
449 self.touch = trigger_hurt_touch;
453 self.message = "was in the wrong place";
455 self.message2 = "was thrown into a world of hurt by";
457 if(!trigger_hurt_first)
458 trigger_hurt_first = self;
459 if(trigger_hurt_last)
460 trigger_hurt_last.trigger_hurt_next = self;
461 trigger_hurt_last = self;
464 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
468 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
469 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
475 //////////////////////////////////////////////////////////////
479 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
481 //////////////////////////////////////////////////////////////
483 .float triggerhealtime;
484 void trigger_heal_touch()
486 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
487 if (other.iscreature)
489 if (other.takedamage)
490 if (other.triggerhealtime < time)
493 other.triggerhealtime = time + 1;
495 if (other.health < self.max_health)
497 other.health = min(other.health + self.health, self.max_health);
498 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
499 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
505 void spawnfunc_trigger_heal()
508 self.touch = trigger_heal_touch;
511 if (!self.max_health)
512 self.max_health = 200; //Max health topoff for field
514 self.noise = "misc/mediumhealth.wav";
515 precache_sound(self.noise);
519 //////////////////////////////////////////////////////////////
525 //////////////////////////////////////////////////////////////
529 // TODO add a way to do looped sounds with sound(); then complete this entity
530 .float volume, atten;
531 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
533 void spawnfunc_target_speaker()
536 precache_sound (self.noise);
540 self.atten = ATTN_NORM;
541 else if(self.atten < 0)
545 self.use = target_speaker_use;
550 self.atten = ATTN_STATIC;
551 else if(self.atten < 0)
555 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
560 void spawnfunc_func_stardust() {
561 self.effects = EF_STARDUST;
565 .float bgmscriptattack;
566 .float bgmscriptdecay;
567 .float bgmscriptsustain;
568 .float bgmscriptrelease;
569 float pointparticles_SendEntity(entity to, float fl)
571 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
573 // optional features to save space
575 if(self.spawnflags & 2)
576 fl |= 0x10; // absolute count on toggle-on
577 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
578 fl |= 0x20; // 4 bytes - saves CPU
579 if(self.waterlevel || self.count != 1)
580 fl |= 0x40; // 4 bytes - obscure features almost never used
581 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
582 fl |= 0x80; // 14 bytes - saves lots of space
584 WriteByte(MSG_ENTITY, fl);
588 WriteCoord(MSG_ENTITY, self.impulse);
590 WriteCoord(MSG_ENTITY, 0); // off
594 WriteCoord(MSG_ENTITY, self.origin_x);
595 WriteCoord(MSG_ENTITY, self.origin_y);
596 WriteCoord(MSG_ENTITY, self.origin_z);
600 if(self.model != "null")
602 WriteShort(MSG_ENTITY, self.modelindex);
605 WriteCoord(MSG_ENTITY, self.mins_x);
606 WriteCoord(MSG_ENTITY, self.mins_y);
607 WriteCoord(MSG_ENTITY, self.mins_z);
608 WriteCoord(MSG_ENTITY, self.maxs_x);
609 WriteCoord(MSG_ENTITY, self.maxs_y);
610 WriteCoord(MSG_ENTITY, self.maxs_z);
615 WriteShort(MSG_ENTITY, 0);
618 WriteCoord(MSG_ENTITY, self.maxs_x);
619 WriteCoord(MSG_ENTITY, self.maxs_y);
620 WriteCoord(MSG_ENTITY, self.maxs_z);
623 WriteShort(MSG_ENTITY, self.cnt);
626 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
627 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
631 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
632 WriteByte(MSG_ENTITY, self.count * 16.0);
634 WriteString(MSG_ENTITY, self.noise);
637 WriteByte(MSG_ENTITY, floor(self.atten * 64));
638 WriteByte(MSG_ENTITY, floor(self.volume * 255));
640 WriteString(MSG_ENTITY, self.bgmscript);
641 if(self.bgmscript != "")
643 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
644 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
645 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
646 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
652 void pointparticles_use()
654 self.state = !self.state;
658 void pointparticles_think()
660 if(self.origin != self.oldorigin)
663 self.oldorigin = self.origin;
665 self.nextthink = time;
668 void pointparticles_reset()
670 if(self.spawnflags & 1)
676 void spawnfunc_func_pointparticles()
679 setmodel(self, self.model);
681 precache_sound (self.noise);
683 if(!self.bgmscriptsustain)
684 self.bgmscriptsustain = 1;
685 else if(self.bgmscriptsustain < 0)
686 self.bgmscriptsustain = 0;
689 self.atten = ATTN_NORM;
690 else if(self.atten < 0)
701 setorigin(self, self.origin + self.mins);
702 setsize(self, '0 0 0', self.maxs - self.mins);
705 self.cnt = particleeffectnum(self.mdl);
707 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
711 self.use = pointparticles_use;
712 self.reset = pointparticles_reset;
717 self.think = pointparticles_think;
718 self.nextthink = time;
721 void spawnfunc_func_sparks()
723 // self.cnt is the amount of sparks that one burst will spawn
725 self.cnt = 25.0; // nice default value
728 // self.wait is the probability that a sparkthink will spawn a spark shower
729 // range: 0 - 1, but 0 makes little sense, so...
730 if(self.wait < 0.05) {
731 self.wait = 0.25; // nice default value
734 self.count = self.cnt;
737 self.velocity = '0 0 -1';
738 self.mdl = "TE_SPARK";
739 self.impulse = 10 * self.wait; // by default 2.5/sec
741 self.cnt = 0; // use mdl
743 spawnfunc_func_pointparticles();
746 float rainsnow_SendEntity(entity to, float sf)
748 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
749 WriteByte(MSG_ENTITY, self.state);
750 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
751 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
752 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
753 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
754 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
755 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
756 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
757 WriteShort(MSG_ENTITY, self.count);
758 WriteByte(MSG_ENTITY, self.cnt);
762 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
763 This is an invisible area like a trigger, which rain falls inside of.
767 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
769 sets color of rain (default 12 - white)
771 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
773 void spawnfunc_func_rain()
775 self.dest = self.velocity;
776 self.velocity = '0 0 0';
778 self.dest = '0 0 -700';
779 self.angles = '0 0 0';
780 self.movetype = MOVETYPE_NONE;
781 self.solid = SOLID_NOT;
782 SetBrushEntityModel();
787 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
790 if(self.count > 65535)
793 self.state = 1; // 1 is rain, 0 is snow
796 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
800 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
801 This is an invisible area like a trigger, which snow falls inside of.
805 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
807 sets color of rain (default 12 - white)
809 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
811 void spawnfunc_func_snow()
813 self.dest = self.velocity;
814 self.velocity = '0 0 0';
816 self.dest = '0 0 -300';
817 self.angles = '0 0 0';
818 self.movetype = MOVETYPE_NONE;
819 self.solid = SOLID_NOT;
820 SetBrushEntityModel();
825 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
828 if(self.count > 65535)
831 self.state = 0; // 1 is rain, 0 is snow
834 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
838 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
841 void misc_laser_aim()
846 if(self.spawnflags & 2)
848 if(self.enemy.origin != self.mangle)
850 self.mangle = self.enemy.origin;
856 a = vectoangles(self.enemy.origin - self.origin);
867 if(self.angles != self.mangle)
869 self.mangle = self.angles;
873 if(self.origin != self.oldorigin)
876 self.oldorigin = self.origin;
880 void misc_laser_init()
882 if(self.target != "")
883 self.enemy = find(world, targetname, self.target);
887 void misc_laser_think()
892 self.nextthink = time;
901 o = self.enemy.origin;
902 if not(self.spawnflags & 2)
903 o = self.origin + normalize(o - self.origin) * 32768;
907 makevectors(self.mangle);
908 o = self.origin + v_forward * 32768;
914 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
916 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
919 if(self.enemy.target != "") // DETECTOR laser
921 traceline(self.origin, o, MOVE_NORMAL, self);
922 if(trace_ent.iscreature)
924 self.pusher = trace_ent;
931 activator = self.pusher;
944 activator = self.pusher;
952 float laser_SendEntity(entity to, float fl)
954 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
955 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
956 if(self.spawnflags & 2)
960 if(self.scale != 1 || self.modelscale != 1)
962 WriteByte(MSG_ENTITY, fl);
965 WriteCoord(MSG_ENTITY, self.origin_x);
966 WriteCoord(MSG_ENTITY, self.origin_y);
967 WriteCoord(MSG_ENTITY, self.origin_z);
971 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
972 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
973 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
975 WriteByte(MSG_ENTITY, self.alpha * 255.0);
978 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
979 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
981 WriteShort(MSG_ENTITY, self.cnt + 1);
987 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
988 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
989 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
993 WriteAngle(MSG_ENTITY, self.mangle_x);
994 WriteAngle(MSG_ENTITY, self.mangle_y);
998 WriteByte(MSG_ENTITY, self.state);
1002 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
1003 Any object touching the beam will be hurt
1006 spawnfunc_target_position where the laser ends
1008 name of beam end effect to use
1010 color of the beam (default: red)
1012 damage per second (-1 for a laser that kills immediately)
1016 self.state = !self.state;
1017 self.SendFlags |= 4;
1023 if(self.spawnflags & 1)
1029 void spawnfunc_misc_laser()
1033 if(self.mdl == "none")
1037 self.cnt = particleeffectnum(self.mdl);
1040 self.cnt = particleeffectnum("laser_deadly");
1046 self.cnt = particleeffectnum("laser_deadly");
1053 if(self.colormod == '0 0 0')
1055 self.colormod = '1 0 0';
1057 self.message = "saw the light";
1059 self.message2 = "was pushed into a laser by";
1062 if(!self.modelscale)
1063 self.modelscale = 1;
1064 self.think = misc_laser_think;
1065 self.nextthink = time;
1066 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1068 self.mangle = self.angles;
1070 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1074 self.reset = laser_reset;
1076 self.use = laser_use;
1082 // tZorks trigger impulse / gravity
1086 .float lastpushtime;
1088 // targeted (directional) mode
1089 void trigger_impulse_touch1()
1092 float pushdeltatime;
1095 // FIXME: Better checking for what to push and not.
1096 if not(other.iscreature)
1097 if (other.classname != "corpse")
1098 if (other.classname != "body")
1099 if (other.classname != "gib")
1100 if (other.classname != "missile")
1101 if (other.classname != "rocket")
1102 if (other.classname != "casing")
1103 if (other.classname != "grenade")
1104 if (other.classname != "plasma")
1105 if (other.classname != "plasma_prim")
1106 if (other.classname != "plasma_chain")
1107 if (other.classname != "droppedweapon")
1108 if (other.classname != "nexball_basketball")
1109 if (other.classname != "nexball_football")
1112 if (other.deadflag && other.iscreature)
1117 targ = find(world, targetname, self.target);
1120 objerror("trigger_force without a (valid) .target!\n");
1125 if(self.falloff == 1)
1126 str = (str / self.radius) * self.strength;
1127 else if(self.falloff == 2)
1128 str = (1 - (str / self.radius)) * self.strength;
1130 str = self.strength;
1132 pushdeltatime = time - other.lastpushtime;
1133 if (pushdeltatime > 0.15) pushdeltatime = 0;
1134 other.lastpushtime = time;
1135 if(!pushdeltatime) return;
1137 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1138 other.flags &~= FL_ONGROUND;
1141 // Directionless (accelerator/decelerator) mode
1142 void trigger_impulse_touch2()
1144 float pushdeltatime;
1146 // FIXME: Better checking for what to push and not.
1147 if not(other.iscreature)
1148 if (other.classname != "corpse")
1149 if (other.classname != "body")
1150 if (other.classname != "gib")
1151 if (other.classname != "missile")
1152 if (other.classname != "rocket")
1153 if (other.classname != "casing")
1154 if (other.classname != "grenade")
1155 if (other.classname != "plasma")
1156 if (other.classname != "plasma_prim")
1157 if (other.classname != "plasma_chain")
1158 if (other.classname != "droppedweapon")
1159 if (other.classname != "nexball_basketball")
1160 if (other.classname != "nexball_football")
1163 if (other.deadflag && other.iscreature)
1168 pushdeltatime = time - other.lastpushtime;
1169 if (pushdeltatime > 0.15) pushdeltatime = 0;
1170 other.lastpushtime = time;
1171 if(!pushdeltatime) return;
1173 //if(self.strength > 1)
1174 other.velocity = other.velocity * (self.strength * pushdeltatime);
1176 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1179 // Spherical (gravity/repulsor) mode
1180 void trigger_impulse_touch3()
1182 float pushdeltatime;
1185 // FIXME: Better checking for what to push and not.
1186 if not(other.iscreature)
1187 if (other.classname != "corpse")
1188 if (other.classname != "body")
1189 if (other.classname != "gib")
1190 if (other.classname != "missile")
1191 if (other.classname != "rocket")
1192 if (other.classname != "casing")
1193 if (other.classname != "grenade")
1194 if (other.classname != "plasma")
1195 if (other.classname != "plasma_prim")
1196 if (other.classname != "plasma_chain")
1197 if (other.classname != "droppedweapon")
1198 if (other.classname != "nexball_basketball")
1199 if (other.classname != "nexball_football")
1202 if (other.deadflag && other.iscreature)
1207 pushdeltatime = time - other.lastpushtime;
1208 if (pushdeltatime > 0.15) pushdeltatime = 0;
1209 other.lastpushtime = time;
1210 if(!pushdeltatime) return;
1212 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1214 str = min(self.radius, vlen(self.origin - other.origin));
1216 if(self.falloff == 1)
1217 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1218 else if(self.falloff == 2)
1219 str = (str / self.radius) * self.strength; // 0 in the inside
1221 str = self.strength;
1223 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1226 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1227 -------- KEYS --------
1228 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1229 If not, this trigger acts like a damper/accelerator field.
1231 strength : This is how mutch force to add in the direction of .target each second
1232 when .target is set. If not, this is hoe mutch to slow down/accelerate
1233 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1235 radius : If set, act as a spherical device rather then a liniar one.
1237 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1239 -------- NOTES --------
1240 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1241 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1244 void spawnfunc_trigger_impulse()
1249 if(!self.strength) self.strength = 2000;
1250 setorigin(self, self.origin);
1251 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1252 self.touch = trigger_impulse_touch3;
1258 if(!self.strength) self.strength = 950;
1259 self.touch = trigger_impulse_touch1;
1263 if(!self.strength) self.strength = 0.9;
1264 self.touch = trigger_impulse_touch2;
1269 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1270 "Flip-flop" trigger gate... lets only every second trigger event through
1274 self.state = !self.state;
1279 void spawnfunc_trigger_flipflop()
1281 if(self.spawnflags & 1)
1283 self.use = flipflop_use;
1284 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1287 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1288 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1292 self.nextthink = time + self.wait;
1293 self.enemy = activator;
1299 void monoflop_fixed_use()
1303 self.nextthink = time + self.wait;
1305 self.enemy = activator;
1309 void monoflop_think()
1312 activator = self.enemy;
1316 void monoflop_reset()
1322 void spawnfunc_trigger_monoflop()
1326 if(self.spawnflags & 1)
1327 self.use = monoflop_fixed_use;
1329 self.use = monoflop_use;
1330 self.think = monoflop_think;
1332 self.reset = monoflop_reset;
1335 void multivibrator_send()
1340 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1342 newstate = (time < cyclestart + self.wait);
1345 if(self.state != newstate)
1347 self.state = newstate;
1350 self.nextthink = cyclestart + self.wait + 0.01;
1352 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1355 void multivibrator_toggle()
1357 if(self.nextthink == 0)
1359 multivibrator_send();
1372 void multivibrator_reset()
1374 if(!(self.spawnflags & 1))
1375 self.nextthink = 0; // wait for a trigger event
1377 self.nextthink = max(1, time);
1380 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1381 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1382 -------- KEYS --------
1383 target: trigger all entities with this targetname when it goes off
1384 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1385 phase: offset of the timing
1386 wait: "on" cycle time (default: 1)
1387 respawntime: "off" cycle time (default: same as wait)
1388 -------- SPAWNFLAGS --------
1389 START_ON: assume it is already turned on (when targeted)
1391 void spawnfunc_trigger_multivibrator()
1395 if(!self.respawntime)
1396 self.respawntime = self.wait;
1399 self.use = multivibrator_toggle;
1400 self.think = multivibrator_send;
1401 self.nextthink = time;
1404 multivibrator_reset();
1411 src = find(world, targetname, self.killtarget);
1412 dst = find(world, targetname, self.target);
1416 objerror("follow: could not find target/killtarget");
1420 if(self.spawnflags & 1)
1423 if(self.spawnflags & 2)
1425 setattachment(dst, src, self.message);
1429 attach_sameorigin(dst, src, self.message);
1434 if(self.spawnflags & 2)
1436 dst.movetype = MOVETYPE_FOLLOW;
1438 // dst.punchangle = '0 0 0'; // keep unchanged
1439 dst.view_ofs = dst.origin;
1440 dst.v_angle = dst.angles;
1444 follow_sameorigin(dst, src);
1451 void spawnfunc_misc_follow()
1453 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1458 void gamestart_use() {
1464 void spawnfunc_trigger_gamestart() {
1465 self.use = gamestart_use;
1466 self.reset2 = spawnfunc_trigger_gamestart;
1470 self.think = self.use;
1471 self.nextthink = game_starttime + self.wait;
1474 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1480 .entity voicescript; // attached voice script
1481 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1482 .float voicescript_nextthink; // time to play next voice
1483 .float voicescript_voiceend; // time when this voice ends
1485 void target_voicescript_clear(entity pl)
1487 pl.voicescript = world;
1490 void target_voicescript_use()
1492 if(activator.voicescript != self)
1494 activator.voicescript = self;
1495 activator.voicescript_index = 0;
1496 activator.voicescript_nextthink = time + self.delay;
1500 void target_voicescript_next(entity pl)
1505 vs = pl.voicescript;
1508 if(vs.message == "")
1510 if(pl.classname != "player")
1515 if(time >= pl.voicescript_voiceend)
1517 if(time >= pl.voicescript_nextthink)
1519 // get the next voice...
1520 n = tokenize_console(vs.message);
1522 if(pl.voicescript_index < vs.cnt)
1523 i = pl.voicescript_index * 2;
1524 else if(n > vs.cnt * 2)
1525 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1531 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1532 pl.voicescript_voiceend = time + stof(argv(i + 1));
1535 pl.voicescript = world;
1537 pl.voicescript_index += 1;
1538 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1543 void spawnfunc_target_voicescript()
1545 // netname: directory of the sound files
1546 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1547 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1548 // wait: average time between messages
1549 // delay: initial delay before the first message
1552 self.use = target_voicescript_use;
1554 n = tokenize_console(self.message);
1556 for(i = 0; i+1 < n; i += 2)
1563 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1569 void trigger_relay_teamcheck_use()
1573 if(self.spawnflags & 2)
1575 if(activator.team != self.team)
1580 if(activator.team == self.team)
1586 if(self.spawnflags & 1)
1591 void trigger_relay_teamcheck_reset()
1593 self.team = self.team_saved;
1596 void spawnfunc_trigger_relay_teamcheck()
1598 self.team_saved = self.team;
1599 self.use = trigger_relay_teamcheck_use;
1600 self.reset = trigger_relay_teamcheck_reset;
1605 void trigger_disablerelay_use()
1612 for(e = world; (e = find(e, targetname, self.target)); )
1614 if(e.use == SUB_UseTargets)
1616 e.use = SUB_DontUseTargets;
1619 else if(e.use == SUB_DontUseTargets)
1621 e.use = SUB_UseTargets;
1627 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1630 void spawnfunc_trigger_disablerelay()
1632 self.use = trigger_disablerelay_use;