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;
167 self.think = multi_wait;
168 self.nextthink = time + self.wait;
172 { // we can't just remove (self) here, because this is a touch function
173 // called wheil C code is looping through area links...
174 self.touch = SUB_Null;
180 self.goalentity = other;
181 self.enemy = activator;
187 if not(self.spawnflags & 2)
189 if not(other.iscreature)
193 if(self.team == other.team)
197 // if the trigger has an angles field, check player's facing direction
198 if (self.movedir != '0 0 0')
200 makevectors (other.angles);
201 if (v_forward * self.movedir < 0)
202 return; // not facing the right way
208 self.goalentity = other;
212 void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
214 if (!self.takedamage)
216 self.health = self.health - damage;
217 if (self.health <= 0)
219 self.enemy = attacker;
220 self.goalentity = inflictor;
227 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
228 self.touch = multi_touch;
231 self.health = self.max_health;
232 self.takedamage = DAMAGE_YES;
233 self.solid = SOLID_BBOX;
235 self.think = SUB_Null;
236 self.team = self.team_saved;
239 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
240 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.
241 If "delay" is set, the trigger waits some time after activating before firing.
242 "wait" : Seconds between triggerings. (.2 default)
243 If notouch is set, the trigger is only fired by other entities, not by touching.
244 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
250 set "message" to text string
252 void spawnfunc_trigger_multiple()
254 self.reset = multi_reset;
255 if (self.sounds == 1)
257 precache_sound ("misc/secret.wav");
258 self.noise = "misc/secret.wav";
260 else if (self.sounds == 2)
262 precache_sound ("misc/talk.wav");
263 self.noise = "misc/talk.wav";
265 else if (self.sounds == 3)
267 precache_sound ("misc/trigger1.wav");
268 self.noise = "misc/trigger1.wav";
271 self.use = multi_use;
275 self.team_saved = self.team;
279 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
280 objerror ("health and notouch don't make sense\n");
281 self.max_health = self.health;
282 self.event_damage = multi_eventdamage;
283 self.takedamage = DAMAGE_YES;
284 self.solid = SOLID_BBOX;
285 setorigin (self, self.origin); // make sure it links into the world
289 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
291 self.touch = multi_touch;
292 setorigin (self, self.origin); // make sure it links into the world
298 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
299 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
300 "targetname". If "health" is set, the trigger must be killed to activate.
301 If notouch is set, the trigger is only fired by other entities, not by touching.
302 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
303 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.
309 set "message" to text string
311 void spawnfunc_trigger_once()
314 spawnfunc_trigger_multiple();
317 //=============================================================================
319 /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
320 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
322 void spawnfunc_trigger_relay()
324 self.use = SUB_UseTargets;
325 self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
330 self.think = SUB_UseTargets;
331 self.nextthink = self.wait;
336 self.think = SUB_Null;
339 void spawnfunc_trigger_delay()
344 self.use = delay_use;
345 self.reset = delay_reset;
348 //=============================================================================
353 self.count = self.count - 1;
359 if (activator.classname == "player"
360 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
363 centerprint (activator, "There are more to go...");
364 else if (self.count == 3)
365 centerprint (activator, "Only 3 more to go...");
366 else if (self.count == 2)
367 centerprint (activator, "Only 2 more to go...");
369 centerprint (activator, "Only 1 more to go...");
374 if (activator.classname == "player"
375 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
376 centerprint(activator, "Sequence completed!");
377 self.enemy = activator;
383 self.count = self.cnt;
387 /*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
388 Acts as an intermediary for an action that takes multiple inputs.
390 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
392 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
394 void spawnfunc_trigger_counter()
399 self.cnt = self.count;
401 self.use = counter_use;
402 self.reset = counter_reset;
405 .float triggerhurttime;
406 void trigger_hurt_touch()
408 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
409 if (other.iscreature)
411 if (other.takedamage)
412 if (other.triggerhurttime < time)
415 other.triggerhurttime = time + 1;
416 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
423 if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag
426 other.pain_finished = min(other.pain_finished, time + 2);
428 else if (other.classname == "rune") // reset runes
431 other.nextthink = min(other.nextthink, time + 1);
439 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
440 Any object touching this will be hurt
441 set dmg to damage amount
444 .entity trigger_hurt_next;
445 entity trigger_hurt_last;
446 entity trigger_hurt_first;
447 void spawnfunc_trigger_hurt()
450 self.touch = trigger_hurt_touch;
454 self.message = "was in the wrong place";
456 self.message2 = "was thrown into a world of hurt by";
458 if(!trigger_hurt_first)
459 trigger_hurt_first = self;
460 if(trigger_hurt_last)
461 trigger_hurt_last.trigger_hurt_next = self;
462 trigger_hurt_last = self;
465 float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
469 for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
470 if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
476 //////////////////////////////////////////////////////////////
480 //Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
482 //////////////////////////////////////////////////////////////
484 .float triggerhealtime;
485 void trigger_heal_touch()
487 // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
488 if (other.iscreature)
490 if (other.takedamage)
491 if (other.triggerhealtime < time)
494 other.triggerhealtime = time + 1;
496 if (other.health < self.max_health)
498 other.health = min(other.health + self.health, self.max_health);
499 other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
500 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
506 void spawnfunc_trigger_heal()
509 self.touch = trigger_heal_touch;
512 if (!self.max_health)
513 self.max_health = 200; //Max health topoff for field
515 self.noise = "misc/mediumhealth.wav";
516 precache_sound(self.noise);
520 //////////////////////////////////////////////////////////////
526 //////////////////////////////////////////////////////////////
530 // TODO add a way to do looped sounds with sound(); then complete this entity
531 .float volume, atten;
532 void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
534 void spawnfunc_target_speaker()
537 precache_sound (self.noise);
541 self.atten = ATTN_NORM;
542 else if(self.atten < 0)
546 self.use = target_speaker_use;
551 self.atten = ATTN_STATIC;
552 else if(self.atten < 0)
556 ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
561 void spawnfunc_func_stardust() {
562 self.effects = EF_STARDUST;
566 .float bgmscriptattack;
567 .float bgmscriptdecay;
568 .float bgmscriptsustain;
569 .float bgmscriptrelease;
570 float pointparticles_SendEntity(entity to, float fl)
572 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
574 // optional features to save space
576 if(self.spawnflags & 2)
577 fl |= 0x10; // absolute count on toggle-on
578 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
579 fl |= 0x20; // 4 bytes - saves CPU
580 if(self.waterlevel || self.count != 1)
581 fl |= 0x40; // 4 bytes - obscure features almost never used
582 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
583 fl |= 0x80; // 14 bytes - saves lots of space
585 WriteByte(MSG_ENTITY, fl);
589 WriteCoord(MSG_ENTITY, self.impulse);
591 WriteCoord(MSG_ENTITY, 0); // off
595 WriteCoord(MSG_ENTITY, self.origin_x);
596 WriteCoord(MSG_ENTITY, self.origin_y);
597 WriteCoord(MSG_ENTITY, self.origin_z);
601 if(self.model != "null")
603 WriteShort(MSG_ENTITY, self.modelindex);
606 WriteCoord(MSG_ENTITY, self.mins_x);
607 WriteCoord(MSG_ENTITY, self.mins_y);
608 WriteCoord(MSG_ENTITY, self.mins_z);
609 WriteCoord(MSG_ENTITY, self.maxs_x);
610 WriteCoord(MSG_ENTITY, self.maxs_y);
611 WriteCoord(MSG_ENTITY, self.maxs_z);
616 WriteShort(MSG_ENTITY, 0);
619 WriteCoord(MSG_ENTITY, self.maxs_x);
620 WriteCoord(MSG_ENTITY, self.maxs_y);
621 WriteCoord(MSG_ENTITY, self.maxs_z);
624 WriteShort(MSG_ENTITY, self.cnt);
627 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
628 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
632 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
633 WriteByte(MSG_ENTITY, self.count * 16.0);
635 WriteString(MSG_ENTITY, self.noise);
638 WriteByte(MSG_ENTITY, floor(self.atten * 64));
639 WriteByte(MSG_ENTITY, floor(self.volume * 255));
641 WriteString(MSG_ENTITY, self.bgmscript);
642 if(self.bgmscript != "")
644 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
645 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
646 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
647 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
653 void pointparticles_use()
655 self.state = !self.state;
659 void pointparticles_think()
661 if(self.origin != self.oldorigin)
664 self.oldorigin = self.origin;
666 self.nextthink = time;
669 void pointparticles_reset()
671 if(self.spawnflags & 1)
677 void spawnfunc_func_pointparticles()
680 setmodel(self, self.model);
682 precache_sound (self.noise);
684 if(!self.bgmscriptsustain)
685 self.bgmscriptsustain = 1;
686 else if(self.bgmscriptsustain < 0)
687 self.bgmscriptsustain = 0;
690 self.atten = ATTN_NORM;
691 else if(self.atten < 0)
702 setorigin(self, self.origin + self.mins);
703 setsize(self, '0 0 0', self.maxs - self.mins);
706 self.cnt = particleeffectnum(self.mdl);
708 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
712 self.use = pointparticles_use;
713 self.reset = pointparticles_reset;
718 self.think = pointparticles_think;
719 self.nextthink = time;
722 void spawnfunc_func_sparks()
724 // self.cnt is the amount of sparks that one burst will spawn
726 self.cnt = 25.0; // nice default value
729 // self.wait is the probability that a sparkthink will spawn a spark shower
730 // range: 0 - 1, but 0 makes little sense, so...
731 if(self.wait < 0.05) {
732 self.wait = 0.25; // nice default value
735 self.count = self.cnt;
738 self.velocity = '0 0 -1';
739 self.mdl = "TE_SPARK";
740 self.impulse = 10 * self.wait; // by default 2.5/sec
742 self.cnt = 0; // use mdl
744 spawnfunc_func_pointparticles();
747 float rainsnow_SendEntity(entity to, float sf)
749 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
750 WriteByte(MSG_ENTITY, self.state);
751 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
752 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
753 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
754 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
755 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
756 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
757 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
758 WriteShort(MSG_ENTITY, self.count);
759 WriteByte(MSG_ENTITY, self.cnt);
763 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
764 This is an invisible area like a trigger, which rain falls inside of.
768 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
770 sets color of rain (default 12 - white)
772 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
774 void spawnfunc_func_rain()
776 self.dest = self.velocity;
777 self.velocity = '0 0 0';
779 self.dest = '0 0 -700';
780 self.angles = '0 0 0';
781 self.movetype = MOVETYPE_NONE;
782 self.solid = SOLID_NOT;
783 SetBrushEntityModel();
788 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
791 if(self.count > 65535)
794 self.state = 1; // 1 is rain, 0 is snow
797 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
801 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
802 This is an invisible area like a trigger, which snow falls inside of.
806 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
808 sets color of rain (default 12 - white)
810 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
812 void spawnfunc_func_snow()
814 self.dest = self.velocity;
815 self.velocity = '0 0 0';
817 self.dest = '0 0 -300';
818 self.angles = '0 0 0';
819 self.movetype = MOVETYPE_NONE;
820 self.solid = SOLID_NOT;
821 SetBrushEntityModel();
826 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
829 if(self.count > 65535)
832 self.state = 0; // 1 is rain, 0 is snow
835 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
839 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
842 void misc_laser_aim()
847 if(self.spawnflags & 2)
849 if(self.enemy.origin != self.mangle)
851 self.mangle = self.enemy.origin;
857 a = vectoangles(self.enemy.origin - self.origin);
868 if(self.angles != self.mangle)
870 self.mangle = self.angles;
874 if(self.origin != self.oldorigin)
877 self.oldorigin = self.origin;
881 void misc_laser_init()
883 if(self.target != "")
884 self.enemy = find(world, targetname, self.target);
888 void misc_laser_think()
893 self.nextthink = time;
902 o = self.enemy.origin;
903 if not(self.spawnflags & 2)
904 o = self.origin + normalize(o - self.origin) * 32768;
908 makevectors(self.mangle);
909 o = self.origin + v_forward * 32768;
915 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
917 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
920 if(self.enemy.target != "") // DETECTOR laser
922 traceline(self.origin, o, MOVE_NORMAL, self);
923 if(trace_ent.iscreature)
925 self.pusher = trace_ent;
932 activator = self.pusher;
945 activator = self.pusher;
953 float laser_SendEntity(entity to, float fl)
955 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
956 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
957 if(self.spawnflags & 2)
961 if(self.scale != 1 || self.modelscale != 1)
963 WriteByte(MSG_ENTITY, fl);
966 WriteCoord(MSG_ENTITY, self.origin_x);
967 WriteCoord(MSG_ENTITY, self.origin_y);
968 WriteCoord(MSG_ENTITY, self.origin_z);
972 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
973 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
974 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
976 WriteByte(MSG_ENTITY, self.alpha * 255.0);
979 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
980 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
982 WriteShort(MSG_ENTITY, self.cnt + 1);
988 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
989 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
990 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
994 WriteAngle(MSG_ENTITY, self.mangle_x);
995 WriteAngle(MSG_ENTITY, self.mangle_y);
999 WriteByte(MSG_ENTITY, self.state);
1003 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
1004 Any object touching the beam will be hurt
1007 spawnfunc_target_position where the laser ends
1009 name of beam end effect to use
1011 color of the beam (default: red)
1013 damage per second (-1 for a laser that kills immediately)
1017 self.state = !self.state;
1018 self.SendFlags |= 4;
1024 if(self.spawnflags & 1)
1030 void spawnfunc_misc_laser()
1034 if(self.mdl == "none")
1038 self.cnt = particleeffectnum(self.mdl);
1041 self.cnt = particleeffectnum("laser_deadly");
1047 self.cnt = particleeffectnum("laser_deadly");
1054 if(self.colormod == '0 0 0')
1056 self.colormod = '1 0 0';
1058 self.message = "saw the light";
1060 self.message2 = "was pushed into a laser by";
1063 if(!self.modelscale)
1064 self.modelscale = 1;
1065 self.think = misc_laser_think;
1066 self.nextthink = time;
1067 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1069 self.mangle = self.angles;
1071 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1075 self.reset = laser_reset;
1077 self.use = laser_use;
1083 // tZorks trigger impulse / gravity
1087 .float lastpushtime;
1089 // targeted (directional) mode
1090 void trigger_impulse_touch1()
1093 float pushdeltatime;
1096 // FIXME: Better checking for what to push and not.
1097 if not(other.iscreature)
1098 if (other.classname != "corpse")
1099 if (other.classname != "body")
1100 if (other.classname != "gib")
1101 if (other.classname != "missile")
1102 if (other.classname != "rocket")
1103 if (other.classname != "casing")
1104 if (other.classname != "grenade")
1105 if (other.classname != "plasma")
1106 if (other.classname != "plasma_prim")
1107 if (other.classname != "plasma_chain")
1108 if (other.classname != "droppedweapon")
1109 if (other.classname != "nexball_basketball")
1110 if (other.classname != "nexball_football")
1113 if (other.deadflag && other.iscreature)
1118 targ = find(world, targetname, self.target);
1121 objerror("trigger_force without a (valid) .target!\n");
1126 if(self.falloff == 1)
1127 str = (str / self.radius) * self.strength;
1128 else if(self.falloff == 2)
1129 str = (1 - (str / self.radius)) * self.strength;
1131 str = self.strength;
1133 pushdeltatime = time - other.lastpushtime;
1134 if (pushdeltatime > 0.15) pushdeltatime = 0;
1135 other.lastpushtime = time;
1136 if(!pushdeltatime) return;
1138 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1139 other.flags &~= FL_ONGROUND;
1142 // Directionless (accelerator/decelerator) mode
1143 void trigger_impulse_touch2()
1145 float pushdeltatime;
1147 // FIXME: Better checking for what to push and not.
1148 if not(other.iscreature)
1149 if (other.classname != "corpse")
1150 if (other.classname != "body")
1151 if (other.classname != "gib")
1152 if (other.classname != "missile")
1153 if (other.classname != "rocket")
1154 if (other.classname != "casing")
1155 if (other.classname != "grenade")
1156 if (other.classname != "plasma")
1157 if (other.classname != "plasma_prim")
1158 if (other.classname != "plasma_chain")
1159 if (other.classname != "droppedweapon")
1160 if (other.classname != "nexball_basketball")
1161 if (other.classname != "nexball_football")
1164 if (other.deadflag && other.iscreature)
1169 pushdeltatime = time - other.lastpushtime;
1170 if (pushdeltatime > 0.15) pushdeltatime = 0;
1171 other.lastpushtime = time;
1172 if(!pushdeltatime) return;
1174 //if(self.strength > 1)
1175 other.velocity = other.velocity * (self.strength * pushdeltatime);
1177 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1180 // Spherical (gravity/repulsor) mode
1181 void trigger_impulse_touch3()
1183 float pushdeltatime;
1186 // FIXME: Better checking for what to push and not.
1187 if not(other.iscreature)
1188 if (other.classname != "corpse")
1189 if (other.classname != "body")
1190 if (other.classname != "gib")
1191 if (other.classname != "missile")
1192 if (other.classname != "rocket")
1193 if (other.classname != "casing")
1194 if (other.classname != "grenade")
1195 if (other.classname != "plasma")
1196 if (other.classname != "plasma_prim")
1197 if (other.classname != "plasma_chain")
1198 if (other.classname != "droppedweapon")
1199 if (other.classname != "nexball_basketball")
1200 if (other.classname != "nexball_football")
1203 if (other.deadflag && other.iscreature)
1208 pushdeltatime = time - other.lastpushtime;
1209 if (pushdeltatime > 0.15) pushdeltatime = 0;
1210 other.lastpushtime = time;
1211 if(!pushdeltatime) return;
1213 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1215 str = min(self.radius, vlen(self.origin - other.origin));
1217 if(self.falloff == 1)
1218 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1219 else if(self.falloff == 2)
1220 str = (str / self.radius) * self.strength; // 0 in the inside
1222 str = self.strength;
1224 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1227 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1228 -------- KEYS --------
1229 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1230 If not, this trigger acts like a damper/accelerator field.
1232 strength : This is how mutch force to add in the direction of .target each second
1233 when .target is set. If not, this is hoe mutch to slow down/accelerate
1234 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1236 radius : If set, act as a spherical device rather then a liniar one.
1238 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1240 -------- NOTES --------
1241 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1242 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1245 void spawnfunc_trigger_impulse()
1250 if(!self.strength) self.strength = 2000;
1251 setorigin(self, self.origin);
1252 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1253 self.touch = trigger_impulse_touch3;
1259 if(!self.strength) self.strength = 950;
1260 self.touch = trigger_impulse_touch1;
1264 if(!self.strength) self.strength = 0.9;
1265 self.touch = trigger_impulse_touch2;
1270 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1271 "Flip-flop" trigger gate... lets only every second trigger event through
1275 self.state = !self.state;
1280 void spawnfunc_trigger_flipflop()
1282 if(self.spawnflags & 1)
1284 self.use = flipflop_use;
1285 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1288 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1289 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1293 self.nextthink = time + self.wait;
1294 self.enemy = activator;
1300 void monoflop_fixed_use()
1304 self.nextthink = time + self.wait;
1306 self.enemy = activator;
1310 void monoflop_think()
1313 activator = self.enemy;
1317 void monoflop_reset()
1323 void spawnfunc_trigger_monoflop()
1327 if(self.spawnflags & 1)
1328 self.use = monoflop_fixed_use;
1330 self.use = monoflop_use;
1331 self.think = monoflop_think;
1333 self.reset = monoflop_reset;
1336 void multivibrator_send()
1341 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1343 newstate = (time < cyclestart + self.wait);
1346 if(self.state != newstate)
1348 self.state = newstate;
1351 self.nextthink = cyclestart + self.wait + 0.01;
1353 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1356 void multivibrator_toggle()
1358 if(self.nextthink == 0)
1360 multivibrator_send();
1373 void multivibrator_reset()
1375 if(!(self.spawnflags & 1))
1376 self.nextthink = 0; // wait for a trigger event
1378 self.nextthink = max(1, time);
1381 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1382 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1383 -------- KEYS --------
1384 target: trigger all entities with this targetname when it goes off
1385 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1386 phase: offset of the timing
1387 wait: "on" cycle time (default: 1)
1388 respawntime: "off" cycle time (default: same as wait)
1389 -------- SPAWNFLAGS --------
1390 START_ON: assume it is already turned on (when targeted)
1392 void spawnfunc_trigger_multivibrator()
1396 if(!self.respawntime)
1397 self.respawntime = self.wait;
1400 self.use = multivibrator_toggle;
1401 self.think = multivibrator_send;
1402 self.nextthink = time;
1405 multivibrator_reset();
1412 src = find(world, targetname, self.killtarget);
1413 dst = find(world, targetname, self.target);
1417 objerror("follow: could not find target/killtarget");
1421 if(self.spawnflags & 1)
1424 if(self.spawnflags & 2)
1426 setattachment(dst, src, self.message);
1430 attach_sameorigin(dst, src, self.message);
1435 if(self.spawnflags & 2)
1437 dst.movetype = MOVETYPE_FOLLOW;
1439 // dst.punchangle = '0 0 0'; // keep unchanged
1440 dst.view_ofs = dst.origin;
1441 dst.v_angle = dst.angles;
1445 follow_sameorigin(dst, src);
1452 void spawnfunc_misc_follow()
1454 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1459 void gamestart_use() {
1465 void spawnfunc_trigger_gamestart() {
1466 self.use = gamestart_use;
1467 self.reset2 = spawnfunc_trigger_gamestart;
1471 self.think = self.use;
1472 self.nextthink = game_starttime + self.wait;
1475 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1481 .entity voicescript; // attached voice script
1482 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1483 .float voicescript_nextthink; // time to play next voice
1484 .float voicescript_voiceend; // time when this voice ends
1486 void target_voicescript_clear(entity pl)
1488 pl.voicescript = world;
1491 void target_voicescript_use()
1493 if(activator.voicescript != self)
1495 activator.voicescript = self;
1496 activator.voicescript_index = 0;
1497 activator.voicescript_nextthink = time + self.delay;
1501 void target_voicescript_next(entity pl)
1506 vs = pl.voicescript;
1509 if(vs.message == "")
1511 if(pl.classname != "player")
1516 if(time >= pl.voicescript_voiceend)
1518 if(time >= pl.voicescript_nextthink)
1520 // get the next voice...
1521 n = tokenize_console(vs.message);
1523 if(pl.voicescript_index < vs.cnt)
1524 i = pl.voicescript_index * 2;
1525 else if(n > vs.cnt * 2)
1526 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1532 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1533 pl.voicescript_voiceend = time + stof(argv(i + 1));
1536 pl.voicescript = world;
1538 pl.voicescript_index += 1;
1539 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1544 void spawnfunc_target_voicescript()
1546 // netname: directory of the sound files
1547 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1548 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1549 // wait: average time between messages
1550 // delay: initial delay before the first message
1553 self.use = target_voicescript_use;
1555 n = tokenize_console(self.message);
1557 for(i = 0; i+1 < n; i += 2)
1564 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1570 void trigger_relay_teamcheck_use()
1574 if(self.spawnflags & 2)
1576 if(activator.team != self.team)
1581 if(activator.team == self.team)
1587 if(self.spawnflags & 1)
1592 void trigger_relay_teamcheck_reset()
1594 self.team = self.team_saved;
1597 void spawnfunc_trigger_relay_teamcheck()
1599 self.team_saved = self.team;
1600 self.use = trigger_relay_teamcheck_use;
1601 self.reset = trigger_relay_teamcheck_reset;
1606 void trigger_disablerelay_use()
1613 for(e = world; (e = find(e, targetname, self.target)); )
1615 if(e.use == SUB_UseTargets)
1617 e.use = SUB_DontUseTargets;
1620 else if(e.use == SUB_DontUseTargets)
1622 e.use = SUB_UseTargets;
1628 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1631 void spawnfunc_trigger_disablerelay()
1633 self.use = trigger_disablerelay_use;