1 2entity stemp, otemp, s, old; 3 4 5void() trigger_reactivate = 6{ 7 self.solid = SOLID_TRIGGER; 8}; 9 10//============================================================================= 11 12float SPAWNFLAG_NOMESSAGE = 1; 13float SPAWNFLAG_NOTOUCH = 1; 14 15// the wait time has passed, so set back up for another activation 16void() 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 30void() 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 69void() multi_killed = 70{ 71 self.enemy = damage_attacker; 72 multi_trigger(); 73}; 74 75void() multi_use = 76{ 77 self.enemy = activator; 78 multi_trigger(); 79}; 80 81void() 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 99Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. 100If "delay" is set, the trigger waits some time after activating before firing. 101"wait" : Seconds between triggerings. (.2 default) 102If notouch is set, the trigger is only fired by other entities, not by touching. 103NOTOUCH has been obsoleted by trigger_relay! 104sounds 1051) secret 1062) beep beep 1073) large switch 1084) 109set "message" to text string 110*/ 111void() 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 156Variable 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. 158If notouch is set, the trigger is only fired by other entities, not by touching. 159if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. 160if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. 161sounds 1621) secret 1632) beep beep 1643) large switch 1654) 166set "message" to text string 167*/ 168void() 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) 177This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. 178*/ 179void() trigger_relay = 180{ 181 self.use = SUB_UseTargets; 182}; 183 184 185//============================================================================= 186 187/*QUAKED trigger_secret (.5 .5 .5) ? 188secret counter trigger 189sounds 1901) secret 1912) beep beep 1923) 1934) 194set "message" to text string 195*/ 196void() 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 222void() 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 255Acts as an intermediary for an action that takes multiple inputs. 256 257If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished. 258 259After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. 260*/ 261void() 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 274TELEPORT TRIGGERS 275 276============================================================================== 277*/ 278 279float PLAYER_ONLY = 1; 280float SILENT = 2; 281 282void() 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 303void(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 319void() 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 356void(vector org, entity death_owner) spawn_tdeath = 357{ 358local 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 375void() teleport_touch = 376{ 377local entity t; 378local 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) 436This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field. 437*/ 438void() 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 449void() 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 457Any 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 459If the trigger_teleport has a targetname, it will only teleport entities when it has been fired. 460*/ 461void() 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 483trigger_setskill 484 485============================================================================== 486*/ 487 488/*QUAKED trigger_setskill (.5 .5 .5) ? 489sets skill level to the value of "message". 490Only used on start map. 491*/ 492void() trigger_setskill = 493{ 494 remove (self); 495}; 496 497 498/* 499============================================================================== 500 501ONLY REGISTERED TRIGGERS 502 503============================================================================== 504*/ 505 506void() 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) ? 531Only fires if playing the registered version, otherwise prints the message 532*/ 533void() trigger_onlyregistered = 534{ 535 precache_sound ("misc/talk.wav"); 536 InitTrigger (); 537 self.touch = trigger_onlyregistered_touch; 538}; 539 540//============================================================================ 541 542void() hurt_on = 543{ 544 self.solid = SOLID_TRIGGER; 545 self.nextthink = -1; 546}; 547 548void() 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) ? 562Any object touching this will be hurt 563set dmg to damage amount 564defalt dmg = 5 565*/ 566void() trigger_hurt = 567{ 568 InitTrigger (); 569 self.touch = hurt_touch; 570 if (!self.dmg) 571 self.dmg = 5; 572}; 573 574//============================================================================ 575 576float PUSH_ONCE = 1; 577 578void() 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 600Pushes the player 601*/ 602void() 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 613void() 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) ? 631Walking 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*/ 635void() 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