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 bgmscriptattack;
559 .float bgmscriptdecay;
560 .float bgmscriptsustain;
561 .float bgmscriptrelease;
562 float pointparticles_SendEntity(entity to, float fl)
564 WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
566 // optional features to save space
568 if(self.spawnflags & 2)
569 fl |= 0x10; // absolute count on toggle-on
570 if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
571 fl |= 0x20; // 4 bytes - saves CPU
572 if(self.waterlevel || self.count != 1)
573 fl |= 0x40; // 4 bytes - obscure features almost never used
574 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
575 fl |= 0x80; // 14 bytes - saves lots of space
577 WriteByte(MSG_ENTITY, fl);
581 WriteCoord(MSG_ENTITY, self.impulse);
583 WriteCoord(MSG_ENTITY, 0); // off
587 WriteCoord(MSG_ENTITY, self.origin_x);
588 WriteCoord(MSG_ENTITY, self.origin_y);
589 WriteCoord(MSG_ENTITY, self.origin_z);
593 if(self.model != "null")
595 WriteShort(MSG_ENTITY, self.modelindex);
598 WriteCoord(MSG_ENTITY, self.mins_x);
599 WriteCoord(MSG_ENTITY, self.mins_y);
600 WriteCoord(MSG_ENTITY, self.mins_z);
601 WriteCoord(MSG_ENTITY, self.maxs_x);
602 WriteCoord(MSG_ENTITY, self.maxs_y);
603 WriteCoord(MSG_ENTITY, self.maxs_z);
608 WriteShort(MSG_ENTITY, 0);
611 WriteCoord(MSG_ENTITY, self.maxs_x);
612 WriteCoord(MSG_ENTITY, self.maxs_y);
613 WriteCoord(MSG_ENTITY, self.maxs_z);
616 WriteShort(MSG_ENTITY, self.cnt);
619 WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
620 WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
624 WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
625 WriteByte(MSG_ENTITY, self.count * 16.0);
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 != "")
636 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
637 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
638 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
639 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
645 void pointparticles_use()
647 self.state = !self.state;
651 void pointparticles_think()
653 if(self.origin != self.oldorigin)
656 self.oldorigin = self.origin;
658 self.nextthink = time;
661 void pointparticles_reset()
663 if(self.spawnflags & 1)
669 void spawnfunc_func_pointparticles()
672 setmodel(self, self.model);
674 precache_sound (self.noise);
676 if(!self.bgmscriptsustain)
677 self.bgmscriptsustain = 1;
678 else if(self.bgmscriptsustain < 0)
679 self.bgmscriptsustain = 0;
682 self.atten = ATTN_NORM;
683 else if(self.atten < 0)
694 setorigin(self, self.origin + self.mins);
695 setsize(self, '0 0 0', self.maxs - self.mins);
698 self.cnt = particleeffectnum(self.mdl);
700 Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
704 self.use = pointparticles_use;
705 self.reset = pointparticles_reset;
710 self.think = pointparticles_think;
711 self.nextthink = time;
714 float func_clientwall_send (entity to, float sf)
717 if(self.angles != '0 0 0')
719 if(self.solid && (self.mins != '0 0 0' || self.maxs != '0 0 0'))
721 if(self.colormap != 0)
724 WriteByte(MSG_ENTITY, ENT_CLIENT_WALL);
725 WriteByte(MSG_ENTITY, sf);
730 WriteShort(MSG_ENTITY, self.colormap);
735 WriteCoord(MSG_ENTITY, self.origin_x);
736 WriteCoord(MSG_ENTITY, self.origin_y);
737 WriteCoord(MSG_ENTITY, self.origin_z);
744 WriteAngle(MSG_ENTITY, self.angles_x);
745 WriteAngle(MSG_ENTITY, self.angles_y);
746 WriteAngle(MSG_ENTITY, self.angles_z);
752 WriteShort(MSG_ENTITY, self.modelindex);
753 WriteByte(MSG_ENTITY, self.solid);
754 WriteByte(MSG_ENTITY, floor(self.scale * 16));
757 WriteCoord(MSG_ENTITY, self.mins_x);
758 WriteCoord(MSG_ENTITY, self.mins_y);
759 WriteCoord(MSG_ENTITY, self.mins_z);
760 WriteCoord(MSG_ENTITY, self.maxs_x);
761 WriteCoord(MSG_ENTITY, self.maxs_y);
762 WriteCoord(MSG_ENTITY, self.maxs_z);
764 WriteString(MSG_ENTITY, self.bgmscript);
765 if(self.bgmscript != "")
767 WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
768 WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
769 WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
770 WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
771 WriteCoord(MSG_ENTITY, self.movedir_x);
772 WriteCoord(MSG_ENTITY, self.movedir_y);
773 WriteCoord(MSG_ENTITY, self.movedir_z);
774 WriteByte(MSG_ENTITY, floor(self.lip * 255));
781 void func_clientwall_use (void)
786 self.colormap = (activator.team - 1) * 0x11;
788 self.colormap = 0x00;
791 self.colormap = floor(random() * 256);
792 self.colormap |= 1024; // RENDER_COLORMAPPED
796 void spawnfunc_func_clientillusionary()
798 SetBrushEntityModelNoLOD(); // LOD can't be done the CEFC way
799 self.use = func_clientwall_use;
801 if(!self.bgmscriptsustain)
802 self.bgmscriptsustain = 1;
803 else if(self.bgmscriptsustain < 0)
804 self.bgmscriptsustain = 0;
806 Net_LinkEntity(self, TRUE, 0, func_clientwall_send);
809 void spawnfunc_func_clientwall()
811 SetBrushEntityModelNoLOD(); // LOD can't be done the CEFC way
813 self.solid = SOLID_BSP;
814 self.use = func_clientwall_use;
816 if(!self.bgmscriptsustain)
817 self.bgmscriptsustain = 1;
818 else if(self.bgmscriptsustain < 0)
819 self.bgmscriptsustain = 0;
821 Net_LinkEntity(self, TRUE, 0, func_clientwall_send);
824 void spawnfunc_func_sparks()
826 // self.cnt is the amount of sparks that one burst will spawn
828 self.cnt = 25.0; // nice default value
831 // self.wait is the probability that a sparkthink will spawn a spark shower
832 // range: 0 - 1, but 0 makes little sense, so...
833 if(self.wait < 0.05) {
834 self.wait = 0.25; // nice default value
837 self.count = self.cnt;
840 self.velocity = '0 0 -1';
841 self.mdl = "TE_SPARK";
842 self.impulse = 10 * self.wait; // by default 2.5/sec
844 self.cnt = 0; // use mdl
846 spawnfunc_func_pointparticles();
849 float rainsnow_SendEntity(entity to, float sf)
851 WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
852 WriteByte(MSG_ENTITY, self.state);
853 WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
854 WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
855 WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
856 WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
857 WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
858 WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
859 WriteShort(MSG_ENTITY, compressShortVector(self.dest));
860 WriteShort(MSG_ENTITY, self.count);
861 WriteByte(MSG_ENTITY, self.cnt);
865 /*QUAKED spawnfunc_func_rain (0 .5 .8) ?
866 This is an invisible area like a trigger, which rain falls inside of.
870 falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
872 sets color of rain (default 12 - white)
874 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
876 void spawnfunc_func_rain()
878 self.dest = self.velocity;
879 self.velocity = '0 0 0';
881 self.dest = '0 0 -700';
882 self.angles = '0 0 0';
883 self.movetype = MOVETYPE_NONE;
884 self.solid = SOLID_NOT;
885 SetBrushEntityModel();
890 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
893 if(self.count > 65535)
896 self.state = 1; // 1 is rain, 0 is snow
899 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
903 /*QUAKED spawnfunc_func_snow (0 .5 .8) ?
904 This is an invisible area like a trigger, which snow falls inside of.
908 falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
910 sets color of rain (default 12 - white)
912 adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
914 void spawnfunc_func_snow()
916 self.dest = self.velocity;
917 self.velocity = '0 0 0';
919 self.dest = '0 0 -300';
920 self.angles = '0 0 0';
921 self.movetype = MOVETYPE_NONE;
922 self.solid = SOLID_NOT;
923 SetBrushEntityModel();
928 self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
931 if(self.count > 65535)
934 self.state = 0; // 1 is rain, 0 is snow
937 Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity);
941 void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float deathtype);
944 void misc_laser_aim()
949 if(self.spawnflags & 2)
951 if(self.enemy.origin != self.mangle)
953 self.mangle = self.enemy.origin;
959 a = vectoangles(self.enemy.origin - self.origin);
970 if(self.angles != self.mangle)
972 self.mangle = self.angles;
976 if(self.origin != self.oldorigin)
979 self.oldorigin = self.origin;
983 void misc_laser_init()
985 if(self.target != "")
986 self.enemy = find(world, targetname, self.target);
990 void misc_laser_think()
995 self.nextthink = time;
1004 o = self.enemy.origin;
1005 if not(self.spawnflags & 2)
1006 o = self.origin + normalize(o - self.origin) * 32768;
1010 makevectors(self.mangle);
1011 o = self.origin + v_forward * 32768;
1017 FireRailgunBullet(self.origin, o, 100000, 0, DEATH_HURTTRIGGER);
1019 FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, DEATH_HURTTRIGGER);
1022 if(self.enemy.target != "") // DETECTOR laser
1024 traceline(self.origin, o, MOVE_NORMAL, self);
1025 if(trace_ent.iscreature)
1027 self.pusher = trace_ent;
1034 activator = self.pusher;
1047 activator = self.pusher;
1055 float laser_SendEntity(entity to, float fl)
1057 WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
1058 fl = fl - (fl & 0xE0); // use that bit to indicate finite length laser
1059 if(self.spawnflags & 2)
1063 if(self.scale != 1 || self.modelscale != 1)
1065 WriteByte(MSG_ENTITY, fl);
1068 WriteCoord(MSG_ENTITY, self.origin_x);
1069 WriteCoord(MSG_ENTITY, self.origin_y);
1070 WriteCoord(MSG_ENTITY, self.origin_z);
1074 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
1075 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
1076 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
1078 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1081 WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
1082 WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
1084 WriteShort(MSG_ENTITY, self.cnt + 1);
1090 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
1091 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
1092 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
1096 WriteAngle(MSG_ENTITY, self.mangle_x);
1097 WriteAngle(MSG_ENTITY, self.mangle_y);
1101 WriteByte(MSG_ENTITY, self.state);
1105 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
1106 Any object touching the beam will be hurt
1109 spawnfunc_target_position where the laser ends
1111 name of beam end effect to use
1113 color of the beam (default: red)
1115 damage per second (-1 for a laser that kills immediately)
1119 self.state = !self.state;
1120 self.SendFlags |= 4;
1126 if(self.spawnflags & 1)
1132 void spawnfunc_misc_laser()
1136 if(self.mdl == "none")
1140 self.cnt = particleeffectnum(self.mdl);
1143 self.cnt = particleeffectnum("laser_deadly");
1149 self.cnt = particleeffectnum("laser_deadly");
1156 if(self.colormod == '0 0 0')
1158 self.colormod = '1 0 0';
1160 self.message = "saw the light";
1162 self.message2 = "was pushed into a laser by";
1165 if(!self.modelscale)
1166 self.modelscale = 1;
1167 self.think = misc_laser_think;
1168 self.nextthink = time;
1169 InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
1171 self.mangle = self.angles;
1173 Net_LinkEntity(self, FALSE, 0, laser_SendEntity);
1177 self.reset = laser_reset;
1179 self.use = laser_use;
1185 // tZorks trigger impulse / gravity
1189 .float lastpushtime;
1191 // targeted (directional) mode
1192 void trigger_impulse_touch1()
1195 float pushdeltatime;
1198 // FIXME: Better checking for what to push and not.
1199 if not(other.iscreature)
1200 if (other.classname != "corpse")
1201 if (other.classname != "body")
1202 if (other.classname != "gib")
1203 if (other.classname != "missile")
1204 if (other.classname != "rocket")
1205 if (other.classname != "casing")
1206 if (other.classname != "grenade")
1207 if (other.classname != "plasma")
1208 if (other.classname != "plasma_prim")
1209 if (other.classname != "plasma_chain")
1210 if (other.classname != "droppedweapon")
1213 if (other.deadflag && other.iscreature)
1218 targ = find(world, targetname, self.target);
1221 objerror("trigger_force without a (valid) .target!\n");
1226 if(self.falloff == 1)
1227 str = (str / self.radius) * self.strength;
1228 else if(self.falloff == 2)
1229 str = (1 - (str / self.radius)) * self.strength;
1231 str = self.strength;
1233 pushdeltatime = time - other.lastpushtime;
1234 if (pushdeltatime > 0.15) pushdeltatime = 0;
1235 other.lastpushtime = time;
1236 if(!pushdeltatime) return;
1238 other.velocity = other.velocity + normalize(targ.origin - self.origin) * self.strength * pushdeltatime;
1239 other.flags &~= FL_ONGROUND;
1242 // Directionless (accelerator/decelerator) mode
1243 void trigger_impulse_touch2()
1245 float pushdeltatime;
1247 // FIXME: Better checking for what to push and not.
1248 if not(other.iscreature)
1249 if (other.classname != "corpse")
1250 if (other.classname != "body")
1251 if (other.classname != "gib")
1252 if (other.classname != "missile")
1253 if (other.classname != "rocket")
1254 if (other.classname != "casing")
1255 if (other.classname != "grenade")
1256 if (other.classname != "plasma")
1257 if (other.classname != "plasma_prim")
1258 if (other.classname != "plasma_chain")
1259 if (other.classname != "droppedweapon")
1262 if (other.deadflag && other.iscreature)
1267 pushdeltatime = time - other.lastpushtime;
1268 if (pushdeltatime > 0.15) pushdeltatime = 0;
1269 other.lastpushtime = time;
1270 if(!pushdeltatime) return;
1272 //if(self.strength > 1)
1273 other.velocity = other.velocity * (self.strength * pushdeltatime);
1275 // other.velocity = other.velocity - (other.velocity * self.strength * pushdeltatime);
1278 // Spherical (gravity/repulsor) mode
1279 void trigger_impulse_touch3()
1281 float pushdeltatime;
1284 // FIXME: Better checking for what to push and not.
1285 if not(other.iscreature)
1286 if (other.classname != "corpse")
1287 if (other.classname != "body")
1288 if (other.classname != "gib")
1289 if (other.classname != "missile")
1290 if (other.classname != "rocket")
1291 if (other.classname != "casing")
1292 if (other.classname != "grenade")
1293 if (other.classname != "plasma")
1294 if (other.classname != "plasma_prim")
1295 if (other.classname != "plasma_chain")
1296 if (other.classname != "droppedweapon")
1299 if (other.deadflag && other.iscreature)
1304 pushdeltatime = time - other.lastpushtime;
1305 if (pushdeltatime > 0.15) pushdeltatime = 0;
1306 other.lastpushtime = time;
1307 if(!pushdeltatime) return;
1309 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1311 str = min(self.radius, vlen(self.origin - other.origin));
1313 if(self.falloff == 1)
1314 str = (1 - str / self.radius) * self.strength; // 1 in the inside
1315 else if(self.falloff == 2)
1316 str = (str / self.radius) * self.strength; // 0 in the inside
1318 str = self.strength;
1320 other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
1323 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
1324 -------- KEYS --------
1325 target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
1326 If not, this trigger acts like a damper/accelerator field.
1328 strength : This is how mutch force to add in the direction of .target each second
1329 when .target is set. If not, this is hoe mutch to slow down/accelerate
1330 someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
1332 radius : If set, act as a spherical device rather then a liniar one.
1334 falloff : 0 = none, 1 = liniar, 2 = inverted liniar
1336 -------- NOTES --------
1337 Use a brush textured with common/origin in the trigger entity to determine the origin of the force
1338 in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
1341 void spawnfunc_trigger_impulse()
1346 if(!self.strength) self.strength = 2000;
1347 setorigin(self, self.origin);
1348 setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
1349 self.touch = trigger_impulse_touch3;
1355 if(!self.strength) self.strength = 950;
1356 self.touch = trigger_impulse_touch1;
1360 if(!self.strength) self.strength = 0.9;
1361 self.touch = trigger_impulse_touch2;
1366 /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
1367 "Flip-flop" trigger gate... lets only every second trigger event through
1371 self.state = !self.state;
1376 void spawnfunc_trigger_flipflop()
1378 if(self.spawnflags & 1)
1380 self.use = flipflop_use;
1381 self.reset = spawnfunc_trigger_flipflop; // perfect resetter
1384 /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
1385 "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
1389 self.nextthink = time + self.wait;
1390 self.enemy = activator;
1396 void monoflop_fixed_use()
1400 self.nextthink = time + self.wait;
1402 self.enemy = activator;
1406 void monoflop_think()
1409 activator = self.enemy;
1413 void monoflop_reset()
1419 void spawnfunc_trigger_monoflop()
1423 if(self.spawnflags & 1)
1424 self.use = monoflop_fixed_use;
1426 self.use = monoflop_use;
1427 self.think = monoflop_think;
1429 self.reset = monoflop_reset;
1432 void multivibrator_send()
1437 cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
1439 newstate = (time < cyclestart + self.wait);
1442 if(self.state != newstate)
1444 self.state = newstate;
1447 self.nextthink = cyclestart + self.wait + 0.01;
1449 self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
1452 void multivibrator_toggle()
1454 if(self.nextthink == 0)
1456 multivibrator_send();
1469 void multivibrator_reset()
1471 if(!(self.spawnflags & 1))
1472 self.nextthink = 0; // wait for a trigger event
1474 self.nextthink = max(1, time);
1477 /*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
1478 "Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
1479 -------- KEYS --------
1480 target: trigger all entities with this targetname when it goes off
1481 targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
1482 phase: offset of the timing
1483 wait: "on" cycle time (default: 1)
1484 respawntime: "off" cycle time (default: same as wait)
1485 -------- SPAWNFLAGS --------
1486 START_ON: assume it is already turned on (when targeted)
1488 void spawnfunc_trigger_multivibrator()
1492 if(!self.respawntime)
1493 self.respawntime = self.wait;
1496 self.use = multivibrator_toggle;
1497 self.think = multivibrator_send;
1498 self.nextthink = time;
1501 multivibrator_reset();
1508 src = find(world, targetname, self.killtarget);
1509 dst = find(world, targetname, self.target);
1513 objerror("follow: could not find target/killtarget");
1517 if(self.spawnflags & 1)
1520 if(self.spawnflags & 2)
1522 setattachment(dst, src, self.message);
1526 attach_sameorigin(dst, src, self.message);
1531 if(self.spawnflags & 2)
1533 dst.movetype = MOVETYPE_FOLLOW;
1535 // dst.punchangle = '0 0 0'; // keep unchanged
1536 dst.view_ofs = dst.origin;
1537 dst.v_angle = dst.angles;
1541 follow_sameorigin(dst, src);
1548 void spawnfunc_misc_follow()
1550 InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
1555 void gamestart_use() {
1561 void spawnfunc_trigger_gamestart() {
1562 self.use = gamestart_use;
1563 self.reset2 = spawnfunc_trigger_gamestart;
1567 self.think = self.use;
1568 self.nextthink = game_starttime + self.wait;
1571 InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
1577 .entity voicescript; // attached voice script
1578 .float voicescript_index; // index of next voice, or -1 to use the randomized ones
1579 .float voicescript_nextthink; // time to play next voice
1580 .float voicescript_voiceend; // time when this voice ends
1582 void target_voicescript_clear(entity pl)
1584 pl.voicescript = world;
1587 void target_voicescript_use()
1589 if(activator.voicescript != self)
1591 activator.voicescript = self;
1592 activator.voicescript_index = 0;
1593 activator.voicescript_nextthink = time + self.delay;
1597 void target_voicescript_next(entity pl)
1602 vs = pl.voicescript;
1605 if(vs.message == "")
1607 if(pl.classname != "player")
1612 if(time >= pl.voicescript_voiceend)
1614 if(time >= pl.voicescript_nextthink)
1616 // get the next voice...
1617 n = tokenize_sane(vs.message);
1619 if(pl.voicescript_index < vs.cnt)
1620 i = pl.voicescript_index * 2;
1621 else if(n > vs.cnt * 2)
1622 i = mod(pl.voicescript_index - vs.cnt, (n - vs.cnt * 2 - 1) / 2) * 2 + vs.cnt * 2 + 1;
1628 play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
1629 pl.voicescript_voiceend = time + stof(argv(i + 1));
1632 pl.voicescript = world;
1634 pl.voicescript_index += 1;
1635 pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
1640 void spawnfunc_target_voicescript()
1642 // netname: directory of the sound files
1643 // message: list of "sound file" duration "sound file" duration, a *, and again a list
1644 // foo1 4.1 foo2 4.0 foo3 3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
1645 // wait: average time between messages
1646 // delay: initial delay before the first message
1649 self.use = target_voicescript_use;
1651 n = tokenize_sane(self.message);
1653 for(i = 0; i+1 < n; i += 2)
1660 precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
1666 void trigger_relay_teamcheck_use()
1670 if(self.spawnflags & 2)
1672 if(activator.team != self.team)
1677 if(activator.team == self.team)
1683 if(self.spawnflags & 1)
1688 void trigger_relay_teamcheck_reset()
1690 self.team = self.team_saved;
1693 void spawnfunc_trigger_relay_teamcheck()
1695 self.team_saved = self.team;
1696 self.use = trigger_relay_teamcheck_use;
1697 self.reset = trigger_relay_teamcheck_reset;
1702 void trigger_disablerelay_use()
1709 for(e = world; (e = find(e, targetname, self.target)); )
1711 if(e.use == SUB_UseTargets)
1713 e.use = SUB_DontUseTargets;
1716 else if(e.use == SUB_DontUseTargets)
1718 e.use = SUB_UseTargets;
1724 print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
1727 void spawnfunc_trigger_disablerelay()
1729 self.use = trigger_disablerelay_use;