2 entity damage_attacker; // ADD FUNCTION FOR
\r
4 void() SUB_UseTargets;
\r
8 activator = self.enemy;
\r
14 ==============================
\r
17 the global "activator" should be set to the entity that initiated the firing.
\r
19 If self.delay is set, a DelayedUse entity will be created that will actually
\r
20 do the SUB_UseTargets after that many seconds have passed.
\r
22 Centerprints any self.message to the activator.
\r
24 Removes all entities with a targetname that match self.killtarget,
\r
25 and removes them, so some events can remove other triggers.
\r
27 Search for (string)targetname in all entities that
\r
28 match (string)self.target and call their .use function
\r
30 ==============================
\r
32 /*void() SUB_UseTargets =
\r
34 local entity t, stemp, otemp, act;
\r
37 // check for a delay
\r
41 // create a temp object to fire at a later time
\r
43 t.classname = "DelayedUse";
\r
44 t.nextthink = time + self.delay;
\r
45 t.think = DelayThink;
\r
46 t.enemy = activator;
\r
47 t.message = self.message;
\r
48 t.killtarget = self.killtarget;
\r
49 t.target = self.target;
\r
55 // print the message
\r
57 if (activator.classname == "player" && self.message != "")
\r
59 centerprint (activator, self.message);
\r
61 sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
\r
65 // kill the killtagets
\r
67 if (self.killtarget)
\r
72 t = find (t, targetname, self.killtarget);
\r
88 t = find (t, targetname, self.target);
\r
110 void() trigger_reactivate =
\r
112 self.solid = SOLID_TRIGGER;
\r
115 //=============================================================================
\r
117 float SPAWNFLAG_NOMESSAGE = 1;
\r
118 float SPAWNFLAG_NOTOUCH = 1;
\r
120 // the wait time has passed, so set back up for another activation
\r
121 void() multi_wait =
\r
123 if (self.max_health)
\r
125 self.health = self.max_health;
\r
126 self.takedamage = DAMAGE_YES;
\r
127 self.solid = SOLID_BBOX;
\r
132 // the trigger was just touched/killed/used
\r
133 // self.enemy should be set to the activator so it can be held through a delay
\r
134 // so wait for the delay time before firing
\r
135 /*void() multi_trigger =
\r
137 if (self.nextthink > time)
\r
139 return; // allready been triggered
\r
142 if (self.classname == "trigger_secret")
\r
144 if (self.enemy.classname != "player")
\r
146 found_secrets = found_secrets + 1;
\r
147 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
\r
151 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
\r
153 // don't trigger again until reset
\r
154 self.takedamage = DAMAGE_NO;
\r
156 activator = self.enemy;
\r
162 self.think = multi_wait;
\r
163 self.nextthink = time + self.wait;
\r
166 { // we can't just remove (self) here, because this is a touch function
\r
167 // called wheil C code is looping through area links...
\r
168 self.touch = SUB_Null;
\r
170 self.nextthink = time + 0.1;
\r
171 self.think = SUB_Remove;
\r
175 void() multi_trigger =
\r
177 if (self.nextthink > time)
\r
181 if (self.classname == "trigger_secret")
\r
183 if (self.enemy.classname != "player")
\r
187 found_secrets = found_secrets + TF_FLARE_OFF;
\r
192 sound(self, 2, self.noise, TF_FLARE_OFF, TF_FLARE_OFF);
\r
194 self.takedamage = TF_FLARE_LIT;
\r
195 activator = self.enemy;
\r
197 if (self.wait > TF_FLARE_LIT)
\r
199 self.think = multi_wait;
\r
200 self.nextthink = time + self.wait;
\r
204 self.touch = SUB_Null;
\r
205 self.nextthink = time + 0.1;
\r
206 self.think = SUB_Remove;
\r
210 void() multi_killed =
\r
212 self.enemy = damage_attacker;
\r
218 self.enemy = activator;
\r
222 void() multi_touch =
\r
225 if (other.classname != "player")
\r
229 if (other.is_dead == 1)
\r
231 if (!Activated(self, other))
\r
233 if (self.else_goal != TF_FLARE_LIT)
\r
235 te = Findgoal(self.else_goal);
\r
238 DoResults(te, other, self.goal_result & 2);
\r
243 // if the trigger has an angles field, check player's facing direction
\r
244 if (self.movedir != '0 0 0')
\r
246 makevectors (other.angles);
\r
247 if (v_forward * self.movedir < 0)
\r
248 return; // not facing the right way
\r
251 self.enemy = other;
\r
255 void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype)
\r
257 if (!self.takedamage)
\r
259 self.health = self.health - damage;
\r
260 if (self.health <= 0)
\r
262 self.enemy = attacker;
\r
267 /*QUAKED trigger_multiple (.5 .5 .5) ? notouch
\r
268 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.
\r
269 If "delay" is set, the trigger waits some time after activating before firing.
\r
270 "wait" : Seconds between triggerings. (.2 default)
\r
271 If notouch is set, the trigger is only fired by other entities, not by touching.
\r
272 NOTOUCH has been obsoleted by trigger_relay!
\r
278 set "message" to text string
\r
280 void (float tno) ConvertToGoal;
\r
281 void () CheckIfQ3FTrigger;
\r
282 void() trigger_multiple =
\r
284 /* if (CheckIfQ3FTrigger() == 1)
\r
289 if (CheckIfQ3FTrigger() == 2)
\r
294 CheckIfQ3FTrigger ();
\r
296 if (self.sounds == 1)
\r
298 precache_sound ("misc/secret.wav");
\r
299 self.noise = "misc/secret.wav";
\r
301 else if (self.sounds == 2)
\r
303 precache_sound ("misc/talk.wav");
\r
304 self.noise = "misc/talk.wav";
\r
306 else if (self.sounds == 3)
\r
308 precache_sound ("misc/trigger1.wav");
\r
309 self.noise = "misc/trigger1.wav";
\r
314 self.use = multi_use;
\r
320 if (self.spawnflags & SPAWNFLAG_NOTOUCH)
\r
321 objerror ("health and notouch don't make sense\n");
\r
322 self.max_health = self.health;
\r
323 self.th_die = multi_killed; // added by xavior
\r
324 self.event_damage = multi_eventdamage;
\r
325 self.takedamage = DAMAGE_YES;
\r
326 self.solid = SOLID_BBOX;
\r
327 setorigin (self, self.origin); // make sure it links into the world
\r
331 if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
\r
333 self.touch = multi_touch;
\r
339 /*QUAKED trigger_once (.5 .5 .5) ? notouch
\r
340 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
\r
341 "targetname". If "health" is set, the trigger must be killed to activate.
\r
342 If notouch is set, the trigger is only fired by other entities, not by touching.
\r
343 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
\r
344 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.
\r
350 set "message" to text string
\r
352 /*void() trigger_once =
\r
355 trigger_multiple();
\r
358 //=============================================================================
\r
360 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
\r
361 This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages.
\r
363 void() trigger_relay =
\r
365 self.use = SUB_UseTargets;
\r
369 //=============================================================================
\r
372 void() counter_use =
\r
374 self.count = self.count - 1;
\r
375 if (self.count < 0)
\r
378 if (self.count != 0)
\r
380 if (activator.classname == "player"
\r
381 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
\r
383 if (self.count >= 4)
\r
384 centerprint (activator, "There are more to go...");
\r
385 else if (self.count == 3)
\r
386 centerprint (activator, "Only 3 more to go...");
\r
387 else if (self.count == 2)
\r
388 centerprint (activator, "Only 2 more to go...");
\r
390 centerprint (activator, "Only 1 more to go...");
\r
395 if (activator.classname == "player"
\r
396 && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
\r
397 centerprint(activator, "Sequence completed!");
\r
398 self.enemy = activator;
\r
402 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
\r
403 Acts as an intermediary for an action that takes multiple inputs.
\r
405 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
\r
407 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
\r
409 void() trigger_counter =
\r
415 self.use = counter_use;
\r
418 /*.float triggerhurttime;
\r
419 void() hurt_touch =
\r
421 if (other.takedamage)
\r
422 if (other.triggerhurttime < time)
\r
424 other.triggerhurttime = time + 1;
\r
425 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
\r
431 /*QUAKED trigger_hurt (.5 .5 .5) ?
\r
432 Any object touching this will be hurt
\r
433 set dmg to damage amount
\r
436 /*void() trigger_hurt =
\r
439 self.touch = hurt_touch;
\r
443 self.message = "was in the wrong place.";
\r
446 //void() target_speaker_use = {sound(self, CHAN_VOICE, self.noise, 1, 1);}
\r
447 //void() target_speaker = {self.use = target_speaker_use;}
\r
449 void() target_speaker =
\r
452 precache_sound (self.noise);
\r
453 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
\r
460 void() sparksthink =
\r
462 self.nextthink = time + 0.1;
\r
464 if(random() < self.wait) {
\r
465 te_spark(self.origin,'0 0 -1',self.cnt);
\r
470 void() func_sparks =
\r
472 self.think = sparksthink;
\r
473 self.nextthink = time + 0.2;
\r
475 // self.cnt is the amount of sparks that one burst will spawn
\r
477 self.cnt = 25.0; // nice default value
\r
480 // self.wait is the probability that a sparkthink will spawn a spark shower
\r
481 // range: 0 - 1, but 0 makes little sense, so...
\r
482 if(self.wait < 0.05) {
\r
483 self.wait = 0.25; // nice default value
\r
488 precache_sound (self.noise);
\r
489 ambientsound (self.origin, self.noise, 1, ATTN_STATIC);
\r
496 =============================================================================
\r
500 =============================================================================
\r
502 // Doors are in TFDOORS.C
\r
503 /*void() fd_secret_move1;
\r
504 void() fd_secret_move2;
\r
505 void() fd_secret_move3;
\r
506 void() fd_secret_move4;
\r
507 void() fd_secret_move5;
\r
508 void() fd_secret_move6;
\r
509 void() fd_secret_done;
\r
511 float SECRET_OPEN_ONCE = 1; // stays open
\r
512 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
\r
513 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
\r
514 float SECRET_NO_SHOOT = 8; // only opened by trigger
\r
515 float SECRET_YES_SHOOT = 16; // shootable even if targeted
\r
518 void () fd_secret_use =
\r
522 self.health = 10000;
\r
523 //self.havocattack = TRUE;
\r
525 // exit if still moving around...
\r
526 if (self.origin != self.oldorigin)
\r
529 self.message = ""; // no more message
\r
531 SUB_UseTargets(); // fire all targets / killtargets
\r
533 self.velocity = '0 0 0';
\r
535 // Make a sound, wait a little...
\r
537 if (self.noise1 != "")
\r
538 sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
\r
539 self.nextthink = self.ltime + 0.1;
\r
541 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
\r
542 makevectors(self.mangle);
\r
546 if (self.spawnflags & SECRET_1ST_DOWN)
\r
547 self.t_width = fabs(v_up * self.size);
\r
549 self.t_width = fabs(v_right * self.size);
\r
552 if (!self.t_length)
\r
553 self.t_length = fabs(v_forward * self.size);
\r
555 if (self.spawnflags & SECRET_1ST_DOWN)
\r
556 self.dest1 = self.origin - v_up * self.t_width;
\r
558 self.dest1 = self.origin + v_right * (self.t_width * temp);
\r
560 self.dest2 = self.dest1 + v_forward * self.t_length;
\r
561 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
\r
562 if (self.noise2 != "")
\r
563 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
\r
566 // Wait after first movement...
\r
567 void () fd_secret_move1 =
\r
569 self.nextthink = self.ltime + 1.0;
\r
570 self.think = fd_secret_move2;
\r
571 if (self.noise3 != "")
\r
572 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
\r
575 // Start moving sideways w/sound...
\r
576 void () fd_secret_move2 =
\r
578 if (self.noise2 != "")
\r
579 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
\r
580 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
\r
583 // Wait here until time to go back...
\r
584 void () fd_secret_move3 =
\r
586 if (self.noise3 != "")
\r
587 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
\r
588 if (!(self.spawnflags & SECRET_OPEN_ONCE))
\r
590 self.nextthink = self.ltime + self.wait;
\r
591 self.think = fd_secret_move4;
\r
595 // Move backward...
\r
596 void () fd_secret_move4 =
\r
598 if (self.noise2 != "")
\r
599 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
\r
600 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
\r
603 // Wait 1 second...
\r
604 void () fd_secret_move5 =
\r
606 self.nextthink = self.ltime + 1.0;
\r
607 self.think = fd_secret_move6;
\r
608 if (self.noise3 != "")
\r
609 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
\r
612 void () fd_secret_move6 =
\r
614 if (self.noise2 != "")
\r
615 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
\r
616 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
\r
619 void () fd_secret_done =
\r
621 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
\r
623 self.health = 10000;
\r
624 self.takedamage = DAMAGE_YES;
\r
625 //self.th_pain = fd_secret_use;
\r
627 if (self.noise3 != "")
\r
628 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
\r
631 void () secret_blocked =
\r
633 if (time < self.attack_finished)
\r
635 self.attack_finished = time + 0.5;
\r
636 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
\r
645 */ // now in TFDOORS.C
\r
646 /*void() secret_touch =
\r
648 if (activator.classname != "player")
\r
650 if (self.attack_finished > time)
\r
653 self.attack_finished = time + 2;
\r
657 if (other.flags & FL_CLIENT)
\r
658 centerprint (other, self.message);
\r
659 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
\r
664 /*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
\r
665 Basic secret door. Slides back, then to the side. Angle determines direction.
\r
666 wait = # of seconds before coming back
\r
667 1st_left = 1st move is left of arrow
\r
668 1st_down = 1st move is down from arrow
\r
669 always_shoot = even if targeted, keep shootable
\r
670 t_width = override WIDTH to move back (or height if going down)
\r
671 t_length = override LENGTH to move sideways
\r
672 "dmg" damage to inflict when blocked (2 default)
\r
674 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
\r
681 /*void () func_door_secret =
\r
683 /*if (!self.deathtype) // map makers can override this
\r
684 self.deathtype = " got in the way";*/
\r
689 // Magic formula...
\r
690 self.mangle = self.angles;
\r
691 self.angles = '0 0 0';
\r
692 self.solid = SOLID_BSP;
\r
693 self.movetype = MOVETYPE_PUSH;
\r
694 self.classname = "door";
\r
695 setmodel (self, self.model);
\r
696 setorigin (self, self.origin);
\r
698 self.touch = secret_touch;
\r
699 self.blocked = secret_blocked;
\r
701 self.use = fd_secret_use;
\r
702 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
\r
704 self.health = 10000;
\r
705 self.takedamage = DAMAGE_YES;
\r
706 self.event_damage = fd_secret_use;
\r
708 self.oldorigin = self.origin;
\r
710 self.wait = 5; // 5 seconds before closing
\r
714 // TF DOORS // MOVED THIS TO SEPERATE TF DOORS FILE
\r
716 // MOVE TO TFTRIGGERS
\r
717 void() trigger_once =
\r
719 if (CheckExistence() == TF_FLARE_LIT)
\r
725 trigger_multiple();
\r
730 self.solid = TF_FLARE_OFF;
\r
731 self.nextthink = -1;
\r
734 void() hurt_touch =
\r
737 if (other.is_dead != 0) // added by xavior to stop killing of dead bodies
\r
739 if (other.takedamage)
\r
741 if (!Activated(self, other))
\r
743 if (self.else_goal != TF_FLARE_LIT)
\r
745 te = Findgoal(self.else_goal);
\r
748 DoResults(te, other, self.goal_result & 2);
\r
753 self.solid = TF_FLARE_LIT;
\r
755 TF_T_Damage(other, self, self, self.dmg, TF_FLARE_OFF, TF_FLARE_LIT);
\r
756 self.think = hurt_on;
\r
757 self.nextthink = time + TF_FLARE_OFF;
\r
761 void() trigger_hurt =
\r
763 if (self.allowteams == "red") // Converted to work with q3f/ETF entities
\r
768 if (self.allowteams == "blue")
\r
774 if (CheckExistence() == TF_FLARE_LIT)
\r
780 self.touch = hurt_touch;
\r
789 void () SUB_UseTargets =
\r
792 local entity stemp;
\r
793 local entity otemp;
\r
796 if (self.dont_do_triggerwork)
\r
798 self.dont_do_triggerwork = 0;
\r
804 t.classname = "DelayedUse";
\r
805 t.nextthink = (time + self.delay);
\r
806 t.think = DelayThink;
\r
807 t.enemy = activator;
\r
808 t.message = self.message;
\r
809 t.killtarget = self.killtarget;
\r
810 t.target = self.target;
\r
813 if (((activator.classname == "player") && (self.message != "")))
\r
815 CenterPrint (activator, self.message);
\r
818 sound (activator, 2, "misc/talk.wav", 1, 1);
\r
821 if ((activator.classname == "player"))
\r
823 DoGroupWork (self, activator);
\r
824 DoGoalWork (self, activator);
\r
826 if (self.killtarget)
\r
831 t = find (t, targetname, self.killtarget);
\r
846 t = find (t, targetname, self.target);
\r
855 if ((self.use != SUB_Null))
\r