Home | History | Annotate | Download | only in progs
      1 
      2 entity stemp, otemp, s, old;
      3 
      4 
      5 void() trigger_reactivate =
      6 {
      7 	self.solid = SOLID_TRIGGER;
      8 };
      9 
     10 //=============================================================================
     11 
     12 float	SPAWNFLAG_NOMESSAGE = 1;
     13 float	SPAWNFLAG_NOTOUCH = 1;
     14 
     15 // the wait time has passed, so set back up for another activation
     16 void() multi_wait =
     17 {
     18 	if (self.max_health)
     19 	{
     20 		self.health = self.max_health;
     21 		self.takedamage = DAMAGE_YES;
     22 		self.solid = SOLID_BBOX;
     23 	}
     24 };
     25 
     26 
     27 // the trigger was just touched/killed/used
     28 // self.enemy should be set to the activator so it can be held through a delay
     29 // so wait for the delay time before firing
     30 void() multi_trigger =
     31 {
     32 	if (self.nextthink > time)
     33 	{
     34 		return;		// allready been triggered
     35 	}
     36 
     37 	if (self.classname == "trigger_secret")
     38 	{
     39 		if (self.enemy.classname != "player")
     40 			return;
     41 		found_secrets = found_secrets + 1;
     42 		WriteByte (MSG_ALL, SVC_FOUNDSECRET);
     43 	}
     44 
     45 	if (self.noise)
     46 		sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
     47 
     48 // don't trigger again until reset
     49 	self.takedamage = DAMAGE_NO;
     50 
     51 	activator = self.enemy;
     52 	
     53 	SUB_UseTargets();
     54 
     55 	if (self.wait > 0)	
     56 	{
     57 		self.think = multi_wait;
     58 		self.nextthink = time + self.wait;
     59 	}
     60 	else
     61 	{	// we can't just remove (self) here, because this is a touch function
     62 		// called wheil C code is looping through area links...
     63 		self.touch = SUB_Null;
     64 		self.nextthink = time + 0.1;
     65 		self.think = SUB_Remove;
     66 	}
     67 };
     68 
     69 void() multi_killed =
     70 {
     71 	self.enemy = damage_attacker;
     72 	multi_trigger();
     73 };
     74 
     75 void() multi_use =
     76 {
     77 	self.enemy = activator;
     78 	multi_trigger();
     79 };
     80 
     81 void() multi_touch =
     82 {
     83 	if (other.classname != "player")
     84 		return;
     85 	
     86 // if the trigger has an angles field, check player's facing direction
     87 	if (self.movedir != '0 0 0')
     88 	{
     89 		makevectors (other.angles);
     90 		if (v_forward * self.movedir < 0)
     91 			return;		// not facing the right way
     92 	}
     93 	
     94 	self.enemy = other;
     95 	multi_trigger ();
     96 };
     97 
     98 /*QUAKED trigger_multiple (.5 .5 .5) ? notouch
     99 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.
    100 If "delay" is set, the trigger waits some time after activating before firing.
    101 "wait" : Seconds between triggerings. (.2 default)
    102 If notouch is set, the trigger is only fired by other entities, not by touching.
    103 NOTOUCH has been obsoleted by trigger_relay!
    104 sounds
    105 1)	secret
    106 2)	beep beep
    107 3)	large switch
    108 4)
    109 set "message" to text string
    110 */
    111 void() trigger_multiple =
    112 {
    113 	if (self.sounds == 1)
    114 	{
    115 		precache_sound ("misc/secret.wav");
    116 		self.noise = "misc/secret.wav";
    117 	}
    118 	else if (self.sounds == 2)
    119 	{
    120 		precache_sound ("misc/talk.wav");
    121 		self.noise = "misc/talk.wav";
    122 	}
    123 	else if (self.sounds == 3)
    124 	{
    125 		precache_sound ("misc/trigger1.wav");
    126 		self.noise = "misc/trigger1.wav";
    127 	}
    128 	
    129 	if (!self.wait)
    130 		self.wait = 0.2;
    131 	self.use = multi_use;
    132 
    133 	InitTrigger ();
    134 
    135 	if (self.health)
    136 	{
    137 		if (self.spawnflags & SPAWNFLAG_NOTOUCH)
    138 			objerror ("health and notouch don't make sense\n");
    139 		self.max_health = self.health;
    140 		self.th_die = multi_killed;
    141 		self.takedamage = DAMAGE_YES;
    142 		self.solid = SOLID_BBOX;
    143 		setorigin (self, self.origin);	// make sure it links into the world
    144 	}
    145 	else
    146 	{
    147 		if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
    148 		{
    149 			self.touch = multi_touch;
    150 		}
    151 	}
    152 };
    153 
    154 
    155 /*QUAKED trigger_once (.5 .5 .5) ? notouch
    156 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
    157 "targetname".  If "health" is set, the trigger must be killed to activate.
    158 If notouch is set, the trigger is only fired by other entities, not by touching.
    159 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
    160 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.
    161 sounds
    162 1)	secret
    163 2)	beep beep
    164 3)	large switch
    165 4)
    166 set "message" to text string
    167 */
    168 void() trigger_once =
    169 {
    170 	self.wait = -1;
    171 	trigger_multiple();
    172 };
    173 
    174 //=============================================================================
    175 
    176 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
    177 This fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.
    178 */
    179 void() trigger_relay =
    180 {
    181 	self.use = SUB_UseTargets;
    182 };
    183 
    184 
    185 //=============================================================================
    186 
    187 /*QUAKED trigger_secret (.5 .5 .5) ?
    188 secret counter trigger
    189 sounds
    190 1)	secret
    191 2)	beep beep
    192 3)
    193 4)
    194 set "message" to text string
    195 */
    196 void() trigger_secret =
    197 {
    198 	total_secrets = total_secrets + 1;
    199 	self.wait = -1;
    200 	if (!self.message)
    201 		self.message = "You found a secret area!";
    202 	if (!self.sounds)
    203 		self.sounds = 1;
    204 	
    205 	if (self.sounds == 1)
    206 	{
    207 		precache_sound ("misc/secret.wav");
    208 		self.noise = "misc/secret.wav";
    209 	}
    210 	else if (self.sounds == 2)
    211 	{
    212 		precache_sound ("misc/talk.wav");
    213 		self.noise = "misc/talk.wav";
    214 	}
    215 
    216 	trigger_multiple ();
    217 };
    218 
    219 //=============================================================================
    220 
    221 
    222 void() counter_use =
    223 {
    224 	local string junk;
    225 
    226 	self.count = self.count - 1;
    227 	if (self.count < 0)
    228 		return;
    229 	
    230 	if (self.count != 0)
    231 	{
    232 		if (activator.classname == "player"
    233 		&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
    234 		{
    235 			if (self.count >= 4)
    236 				centerprint (activator, "There are more to go...");
    237 			else if (self.count == 3)
    238 				centerprint (activator, "Only 3 more to go...");
    239 			else if (self.count == 2)
    240 				centerprint (activator, "Only 2 more to go...");
    241 			else
    242 				centerprint (activator, "Only 1 more to go...");
    243 		}
    244 		return;
    245 	}
    246 	
    247 	if (activator.classname == "player"
    248 	&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
    249 		centerprint(activator, "Sequence completed!");
    250 	self.enemy = activator;
    251 	multi_trigger ();
    252 };
    253 
    254 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
    255 Acts as an intermediary for an action that takes multiple inputs.
    256 
    257 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
    258 
    259 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
    260 */
    261 void() trigger_counter =
    262 {
    263 	self.wait = -1;
    264 	if (!self.count)
    265 		self.count = 2;
    266 
    267 	self.use = counter_use;
    268 };
    269 
    270 
    271 /*
    272 ==============================================================================
    273 
    274 TELEPORT TRIGGERS
    275 
    276 ==============================================================================
    277 */
    278 
    279 float	PLAYER_ONLY	= 1;
    280 float	SILENT = 2;
    281 
    282 void() play_teleport =
    283 {
    284 	local	float v;
    285 	local	string tmpstr;
    286 
    287 	v = random() * 5;
    288 	if (v < 1)
    289 		tmpstr = "misc/r_tele1.wav";
    290 	else if (v < 2)
    291 		tmpstr = "misc/r_tele2.wav";
    292 	else if (v < 3)
    293 		tmpstr = "misc/r_tele3.wav";
    294 	else if (v < 4)
    295 		tmpstr = "misc/r_tele4.wav";
    296 	else
    297 		tmpstr = "misc/r_tele5.wav";
    298 
    299 	sound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM);
    300 	remove (self);
    301 };
    302 
    303 void(vector org) spawn_tfog =
    304 {
    305 	s = spawn ();
    306 	s.origin = org;
    307 	s.nextthink = time + 0.2;
    308 	s.think = play_teleport;
    309 
    310 	WriteByte (MSG_MULTICAST, SVC_TEMPENTITY);
    311 	WriteByte (MSG_MULTICAST, TE_TELEPORT);
    312 	WriteCoord (MSG_MULTICAST, org_x);
    313 	WriteCoord (MSG_MULTICAST, org_y);
    314 	WriteCoord (MSG_MULTICAST, org_z);
    315 	multicast (org, MULTICAST_PHS);
    316 };
    317 
    318 
    319 void() tdeath_touch =
    320 {
    321 	local entity other2;
    322 
    323 	if (other == self.owner)
    324 		return;
    325 
    326 // frag anyone who teleports in on top of an invincible player
    327 	if (other.classname == "player")
    328 	{
    329 		if (other.invincible_finished > time &&
    330 			self.owner.invincible_finished > time) {
    331 			self.classname = "teledeath3";
    332 			other.invincible_finished = 0;
    333 			self.owner.invincible_finished = 0;
    334 			T_Damage (other, self, self, 50000);
    335 			other2 = self.owner;
    336 			self.owner = other;
    337 			T_Damage (other2, self, self, 50000);
    338 		}
    339 			
    340 		if (other.invincible_finished > time)
    341 		{
    342 			self.classname = "teledeath2";
    343 			T_Damage (self.owner, self, self, 50000);
    344 			return;
    345 		}
    346 		
    347 	}
    348 
    349 	if (other.health)
    350 	{
    351 		T_Damage (other, self, self, 50000);
    352 	}
    353 };
    354 
    355 
    356 void(vector org, entity death_owner) spawn_tdeath =
    357 {
    358 local entity	death;
    359 
    360 	death = spawn();
    361 	death.classname = "teledeath";
    362 	death.movetype = MOVETYPE_NONE;
    363 	death.solid = SOLID_TRIGGER;
    364 	death.angles = '0 0 0';
    365 	setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');
    366 	setorigin (death, org);
    367 	death.touch = tdeath_touch;
    368 	death.nextthink = time + 0.2;
    369 	death.think = SUB_Remove;
    370 	death.owner = death_owner;
    371 	
    372 	force_retouch = 2;		// make sure even still objects get hit
    373 };
    374 
    375 void() teleport_touch =
    376 {
    377 local entity	t;
    378 local vector	org;
    379 
    380 	if (self.targetname)
    381 	{
    382 		if (self.nextthink < time)
    383 		{
    384 			return;		// not fired yet
    385 		}
    386 	}
    387 
    388 	if (self.spawnflags & PLAYER_ONLY)
    389 	{
    390 		if (other.classname != "player")
    391 			return;
    392 	}
    393 
    394 // only teleport living creatures
    395 	if (other.health <= 0 || other.solid != SOLID_SLIDEBOX)
    396 		return;
    397 
    398 	SUB_UseTargets ();
    399 
    400 // put a tfog where the player was
    401 	spawn_tfog (other.origin);
    402 
    403 	t = find (world, targetname, self.target);
    404 	if (!t)
    405 		objerror ("couldn't find target");
    406 		
    407 // spawn a tfog flash in front of the destination
    408 	makevectors (t.mangle);
    409 	org = t.origin + 32 * v_forward;
    410 
    411 	spawn_tfog (org);
    412 	spawn_tdeath(t.origin, other);
    413 
    414 // move the player and lock him down for a little while
    415 	if (!other.health)
    416 	{
    417 		other.origin = t.origin;
    418 		other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);
    419 		return;
    420 	}
    421 
    422 	setorigin (other, t.origin);
    423 	other.angles = t.mangle;
    424 	if (other.classname == "player")
    425 	{
    426 		other.fixangle = 1;		// turn this way immediately
    427 		other.teleport_time = time + 0.7;
    428 		if (other.flags & FL_ONGROUND)
    429 			other.flags = other.flags - FL_ONGROUND;
    430 		other.velocity = v_forward * 300;
    431 	}
    432 	other.flags = other.flags - other.flags & FL_ONGROUND;
    433 };
    434 
    435 /*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32)
    436 This is the destination marker for a teleporter.  It should have a "targetname" field with the same value as a teleporter's "target" field.
    437 */
    438 void() info_teleport_destination =
    439 {
    440 // this does nothing, just serves as a target spot
    441 	self.mangle = self.angles;
    442 	self.angles = '0 0 0';
    443 	self.model = "";
    444 	self.origin = self.origin + '0 0 27';
    445 	if (!self.targetname)
    446 		objerror ("no targetname");
    447 };
    448 
    449 void() teleport_use =
    450 {
    451 	self.nextthink = time + 0.2;
    452 	force_retouch = 2;		// make sure even still objects get hit
    453 	self.think = SUB_Null;
    454 };
    455 
    456 /*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT
    457 Any object touching this will be transported to the corresponding info_teleport_destination entity. You must set the "target" field, and create an object with a "targetname" field that matches.
    458 
    459 If the trigger_teleport has a targetname, it will only teleport entities when it has been fired.
    460 */
    461 void() trigger_teleport =
    462 {
    463 	local vector o;
    464 
    465 	InitTrigger ();
    466 	self.touch = teleport_touch;
    467 	// find the destination 
    468 	if (!self.target)
    469 		objerror ("no target");
    470 	self.use = teleport_use;
    471 
    472 	if (!(self.spawnflags & SILENT))
    473 	{
    474 		precache_sound ("ambience/hum1.wav");
    475 		o = (self.mins + self.maxs)*0.5;
    476 		ambientsound (o, "ambience/hum1.wav",0.5 , ATTN_STATIC);
    477 	}
    478 };
    479 
    480 /*
    481 ==============================================================================
    482 
    483 trigger_setskill
    484 
    485 ==============================================================================
    486 */
    487 
    488 /*QUAKED trigger_setskill (.5 .5 .5) ?
    489 sets skill level to the value of "message".
    490 Only used on start map.
    491 */
    492 void() trigger_setskill =
    493 {
    494 	remove (self);
    495 };
    496 
    497 
    498 /*
    499 ==============================================================================
    500 
    501 ONLY REGISTERED TRIGGERS
    502 
    503 ==============================================================================
    504 */
    505 
    506 void() trigger_onlyregistered_touch =
    507 {
    508 	if (other.classname != "player")
    509 		return;
    510 	if (self.attack_finished > time)
    511 		return;
    512 
    513 	self.attack_finished = time + 2;
    514 	if (cvar("registered"))
    515 	{
    516 		self.message = "";
    517 		SUB_UseTargets ();
    518 		remove (self);
    519 	}
    520 	else
    521 	{
    522 		if (self.message != "")
    523 		{
    524 			centerprint (other, self.message);
    525 			sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
    526 		}
    527 	}
    528 };
    529 
    530 /*QUAKED trigger_onlyregistered (.5 .5 .5) ?
    531 Only fires if playing the registered version, otherwise prints the message
    532 */
    533 void() trigger_onlyregistered =
    534 {
    535 	precache_sound ("misc/talk.wav");
    536 	InitTrigger ();
    537 	self.touch = trigger_onlyregistered_touch;
    538 };
    539 
    540 //============================================================================
    541 
    542 void() hurt_on =
    543 {
    544 	self.solid = SOLID_TRIGGER;
    545 	self.nextthink = -1;
    546 };
    547 
    548 void() hurt_touch =
    549 {
    550 	if (other.takedamage)
    551 	{
    552 		self.solid = SOLID_NOT;
    553 		T_Damage (other, self, self, self.dmg);
    554 		self.think = hurt_on;
    555 		self.nextthink = time + 1;
    556 	}
    557 
    558 	return;
    559 };
    560 
    561 /*QUAKED trigger_hurt (.5 .5 .5) ?
    562 Any object touching this will be hurt
    563 set dmg to damage amount
    564 defalt dmg = 5
    565 */
    566 void() trigger_hurt =
    567 {
    568 	InitTrigger ();
    569 	self.touch = hurt_touch;
    570 	if (!self.dmg)
    571 		self.dmg = 5;
    572 };
    573 
    574 //============================================================================
    575 
    576 float PUSH_ONCE = 1;
    577 
    578 void() trigger_push_touch =
    579 {
    580 	if (other.classname == "grenade")
    581 		other.velocity = self.speed * self.movedir * 10;
    582 	else if (other.health > 0)
    583 	{
    584 		other.velocity = self.speed * self.movedir * 10;
    585 		if (other.classname == "player")
    586 		{
    587 			if (other.fly_sound < time)
    588 			{
    589 				other.fly_sound = time + 1.5;
    590 				sound (other, CHAN_AUTO, "ambience/windfly.wav", 1, ATTN_NORM);
    591 			}
    592 		}
    593 	}
    594 	if (self.spawnflags & PUSH_ONCE)
    595 		remove(self);
    596 };
    597 
    598 
    599 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
    600 Pushes the player
    601 */
    602 void() trigger_push =
    603 {
    604 	InitTrigger ();
    605 	precache_sound ("ambience/windfly.wav");
    606 	self.touch = trigger_push_touch;
    607 	if (!self.speed)
    608 		self.speed = 1000;
    609 };
    610 
    611 //============================================================================
    612 
    613 void() trigger_monsterjump_touch =
    614 {
    615 	if ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )
    616 		return;
    617 
    618 // set XY even if not on ground, so the jump will clear lips
    619 	other.velocity_x = self.movedir_x * self.speed;
    620 	other.velocity_y = self.movedir_y * self.speed;
    621 	
    622 	if ( !(other.flags & FL_ONGROUND) )
    623 		return;
    624 	
    625 	other.flags = other.flags - FL_ONGROUND;
    626 
    627 	other.velocity_z = self.height;
    628 };
    629 
    630 /*QUAKED trigger_monsterjump (.5 .5 .5) ?
    631 Walking monsters that touch this will jump in the direction of the trigger's angle
    632 "speed" default to 200, the speed thrown forward
    633 "height" default to 200, the speed thrown upwards
    634 */
    635 void() trigger_monsterjump =
    636 {
    637 	if (!self.speed)
    638 		self.speed = 200;
    639 	if (!self.height)
    640 		self.height = 200;
    641 	if (self.angles == '0 0 0')
    642 		self.angles = '0 360 0';
    643 	InitTrigger ();
    644 	self.touch = trigger_monsterjump_touch;
    645 };
    646 
    647