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_BROADCAST, SVC_TEMPENTITY); 311 WriteByte (MSG_BROADCAST, TE_TELEPORT); 312 WriteCoord (MSG_BROADCAST, org_x); 313 WriteCoord (MSG_BROADCAST, org_y); 314 WriteCoord (MSG_BROADCAST, org_z); 315}; 316 317 318void() tdeath_touch = 319{ 320 if (other == self.owner) 321 return; 322 323// frag anyone who teleports in on top of an invincible player 324 if (other.classname == "player") 325 { 326 if (other.invincible_finished > time) 327 self.classname = "teledeath2"; 328 if (self.owner.classname != "player") 329 { // other monsters explode themselves 330 T_Damage (self.owner, self, self, 50000); 331 return; 332 } 333 334 } 335 336 if (other.health) 337 { 338 T_Damage (other, self, self, 50000); 339 } 340}; 341 342 343void(vector org, entity death_owner) spawn_tdeath = 344{ 345local entity death; 346 347 death = spawn(); 348 death.classname = "teledeath"; 349 death.movetype = MOVETYPE_NONE; 350 death.solid = SOLID_TRIGGER; 351 death.angles = '0 0 0'; 352 setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1'); 353 setorigin (death, org); 354 death.touch = tdeath_touch; 355 death.nextthink = time + 0.2; 356 death.think = SUB_Remove; 357 death.owner = death_owner; 358 359 force_retouch = 2; // make sure even still objects get hit 360}; 361 362void() teleport_touch = 363{ 364local entity t; 365local vector org; 366 367 if (self.targetname) 368 { 369 if (self.nextthink < time) 370 { 371 return; // not fired yet 372 } 373 } 374 375 if (self.spawnflags & PLAYER_ONLY) 376 { 377 if (other.classname != "player") 378 return; 379 } 380 381// only teleport living creatures 382 if (other.health <= 0 || other.solid != SOLID_SLIDEBOX) 383 return; 384 385 SUB_UseTargets (); 386 387// put a tfog where the player was 388 spawn_tfog (other.origin); 389 390 t = find (world, targetname, self.target); 391 if (!t) 392 objerror ("couldn't find target"); 393 394// spawn a tfog flash in front of the destination 395 makevectors (t.mangle); 396 org = t.origin + 32 * v_forward; 397 398 spawn_tfog (org); 399 spawn_tdeath(t.origin, other); 400 401// move the player and lock him down for a little while 402 if (!other.health) 403 { 404 other.origin = t.origin; 405 other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y); 406 return; 407 } 408 409 setorigin (other, t.origin); 410 other.angles = t.mangle; 411 if (other.classname == "player") 412 { 413 other.fixangle = 1; // turn this way immediately 414 other.teleport_time = time + 0.7; 415 if (other.flags & FL_ONGROUND) 416 other.flags = other.flags - FL_ONGROUND; 417 other.velocity = v_forward * 300; 418 } 419 other.flags = other.flags - other.flags & FL_ONGROUND; 420}; 421 422/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32) 423This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field. 424*/ 425void() info_teleport_destination = 426{ 427// this does nothing, just serves as a target spot 428 self.mangle = self.angles; 429 self.angles = '0 0 0'; 430 self.model = ""; 431 self.origin = self.origin + '0 0 27'; 432 if (!self.targetname) 433 objerror ("no targetname"); 434}; 435 436void() teleport_use = 437{ 438 self.nextthink = time + 0.2; 439 force_retouch = 2; // make sure even still objects get hit 440 self.think = SUB_Null; 441}; 442 443/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT 444Any 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. 445 446If the trigger_teleport has a targetname, it will only teleport entities when it has been fired. 447*/ 448void() trigger_teleport = 449{ 450 local vector o; 451 452 InitTrigger (); 453 self.touch = teleport_touch; 454 // find the destination 455 if (!self.target) 456 objerror ("no target"); 457 self.use = teleport_use; 458 459 if (!(self.spawnflags & SILENT)) 460 { 461 precache_sound ("ambience/hum1.wav"); 462 o = (self.mins + self.maxs)*0.5; 463 ambientsound (o, "ambience/hum1.wav",0.5 , ATTN_STATIC); 464 } 465}; 466 467/* 468============================================================================== 469 470trigger_setskill 471 472============================================================================== 473*/ 474 475void() trigger_skill_touch = 476{ 477 if (other.classname != "player") 478 return; 479 480 cvar_set ("skill", self.message); 481}; 482 483/*QUAKED trigger_setskill (.5 .5 .5) ? 484sets skill level to the value of "message". 485Only used on start map. 486*/ 487void() trigger_setskill = 488{ 489 InitTrigger (); 490 self.touch = trigger_skill_touch; 491}; 492 493 494/* 495============================================================================== 496 497ONLY REGISTERED TRIGGERS 498 499============================================================================== 500*/ 501 502void() trigger_onlyregistered_touch = 503{ 504 if (other.classname != "player") 505 return; 506 if (self.attack_finished > time) 507 return; 508 509 self.attack_finished = time + 2; 510 if (cvar("registered")) 511 { 512 self.message = ""; 513 SUB_UseTargets (); 514 remove (self); 515 } 516 else 517 { 518 if (self.message != "") 519 { 520 centerprint (other, self.message); 521 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); 522 } 523 } 524}; 525 526/*QUAKED trigger_onlyregistered (.5 .5 .5) ? 527Only fires if playing the registered version, otherwise prints the message 528*/ 529void() trigger_onlyregistered = 530{ 531 precache_sound ("misc/talk.wav"); 532 InitTrigger (); 533 self.touch = trigger_onlyregistered_touch; 534}; 535 536//============================================================================ 537 538void() hurt_on = 539{ 540 self.solid = SOLID_TRIGGER; 541 self.nextthink = -1; 542}; 543 544void() hurt_touch = 545{ 546 if (other.takedamage) 547 { 548 self.solid = SOLID_NOT; 549 T_Damage (other, self, self, self.dmg); 550 self.think = hurt_on; 551 self.nextthink = time + 1; 552 } 553 554 return; 555}; 556 557/*QUAKED trigger_hurt (.5 .5 .5) ? 558Any object touching this will be hurt 559set dmg to damage amount 560defalt dmg = 5 561*/ 562void() trigger_hurt = 563{ 564 InitTrigger (); 565 self.touch = hurt_touch; 566 if (!self.dmg) 567 self.dmg = 5; 568}; 569 570//============================================================================ 571 572float PUSH_ONCE = 1; 573 574void() trigger_push_touch = 575{ 576 if (other.classname == "grenade") 577 other.velocity = self.speed * self.movedir * 10; 578 else if (other.health > 0) 579 { 580 other.velocity = self.speed * self.movedir * 10; 581 if (other.classname == "player") 582 { 583 if (other.fly_sound < time) 584 { 585 other.fly_sound = time + 1.5; 586 sound (other, CHAN_AUTO, "ambience/windfly.wav", 1, ATTN_NORM); 587 } 588 } 589 } 590 if (self.spawnflags & PUSH_ONCE) 591 remove(self); 592}; 593 594 595/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE 596Pushes the player 597*/ 598void() trigger_push = 599{ 600 InitTrigger (); 601 precache_sound ("ambience/windfly.wav"); 602 self.touch = trigger_push_touch; 603 if (!self.speed) 604 self.speed = 1000; 605}; 606 607//============================================================================ 608 609void() trigger_monsterjump_touch = 610{ 611 if ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER ) 612 return; 613 614// set XY even if not on ground, so the jump will clear lips 615 other.velocity_x = self.movedir_x * self.speed; 616 other.velocity_y = self.movedir_y * self.speed; 617 618 if ( !(other.flags & FL_ONGROUND) ) 619 return; 620 621 other.flags = other.flags - FL_ONGROUND; 622 623 other.velocity_z = self.height; 624}; 625 626/*QUAKED trigger_monsterjump (.5 .5 .5) ? 627Walking monsters that touch this will jump in the direction of the trigger's angle 628"speed" default to 200, the speed thrown forward 629"height" default to 200, the speed thrown upwards 630*/ 631void() trigger_monsterjump = 632{ 633 if (!self.speed) 634 self.speed = 200; 635 if (!self.height) 636 self.height = 200; 637 if (self.angles == '0 0 0') 638 self.angles = '0 360 0'; 639 InitTrigger (); 640 self.touch = trigger_monsterjump_touch; 641}; 642 643