g_trigger.c (13860B)
1 /* 2 Copyright (C) 1997-2001 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 #include "g_local.h" 21 22 23 void InitTrigger (edict_t *self) 24 { 25 if (!VectorCompare (self->s.angles, vec3_origin)) 26 G_SetMovedir (self->s.angles, self->movedir); 27 28 self->solid = SOLID_TRIGGER; 29 self->movetype = MOVETYPE_NONE; 30 gi.setmodel (self, self->model); 31 self->svflags = SVF_NOCLIENT; 32 } 33 34 35 // the wait time has passed, so set back up for another activation 36 void multi_wait (edict_t *ent) 37 { 38 ent->nextthink = 0; 39 } 40 41 42 // the trigger was just activated 43 // ent->activator should be set to the activator so it can be held through a delay 44 // so wait for the delay time before firing 45 void multi_trigger (edict_t *ent) 46 { 47 if (ent->nextthink) 48 return; // already been triggered 49 50 G_UseTargets (ent, ent->activator); 51 52 if (ent->wait > 0) 53 { 54 ent->think = multi_wait; 55 ent->nextthink = level.time + ent->wait; 56 } 57 else 58 { // we can't just remove (self) here, because this is a touch function 59 // called while looping through area links... 60 ent->touch = NULL; 61 ent->nextthink = level.time + FRAMETIME; 62 ent->think = G_FreeEdict; 63 } 64 } 65 66 void Use_Multi (edict_t *ent, edict_t *other, edict_t *activator) 67 { 68 ent->activator = activator; 69 multi_trigger (ent); 70 } 71 72 void Touch_Multi (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 73 { 74 if(other->client) 75 { 76 if (self->spawnflags & 2) 77 return; 78 } 79 else if (other->svflags & SVF_MONSTER) 80 { 81 if (!(self->spawnflags & 1)) 82 return; 83 } 84 else 85 return; 86 87 if (!VectorCompare(self->movedir, vec3_origin)) 88 { 89 vec3_t forward; 90 91 AngleVectors(other->s.angles, forward, NULL, NULL); 92 if (_DotProduct(forward, self->movedir) < 0) 93 return; 94 } 95 96 self->activator = other; 97 multi_trigger (self); 98 } 99 100 /*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED 101 Variable sized repeatable trigger. Must be targeted at one or more entities. 102 If "delay" is set, the trigger waits some time after activating before firing. 103 "wait" : Seconds between triggerings. (.2 default) 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_enable (edict_t *self, edict_t *other, edict_t *activator) 112 { 113 self->solid = SOLID_TRIGGER; 114 self->use = Use_Multi; 115 gi.linkentity (self); 116 } 117 118 void SP_trigger_multiple (edict_t *ent) 119 { 120 if (ent->sounds == 1) 121 ent->noise_index = gi.soundindex ("misc/secret.wav"); 122 else if (ent->sounds == 2) 123 ent->noise_index = gi.soundindex ("misc/talk.wav"); 124 else if (ent->sounds == 3) 125 ent->noise_index = gi.soundindex ("misc/trigger1.wav"); 126 127 if (!ent->wait) 128 ent->wait = 0.2; 129 ent->touch = Touch_Multi; 130 ent->movetype = MOVETYPE_NONE; 131 ent->svflags |= SVF_NOCLIENT; 132 133 134 if (ent->spawnflags & 4) 135 { 136 ent->solid = SOLID_NOT; 137 ent->use = trigger_enable; 138 } 139 else 140 { 141 ent->solid = SOLID_TRIGGER; 142 ent->use = Use_Multi; 143 } 144 145 if (!VectorCompare(ent->s.angles, vec3_origin)) 146 G_SetMovedir (ent->s.angles, ent->movedir); 147 148 gi.setmodel (ent, ent->model); 149 gi.linkentity (ent); 150 } 151 152 153 /*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED 154 Triggers once, then removes itself. 155 You must set the key "target" to the name of another object in the level that has a matching "targetname". 156 157 If TRIGGERED, this trigger must be triggered before it is live. 158 159 sounds 160 1) secret 161 2) beep beep 162 3) large switch 163 4) 164 165 "message" string to be displayed when triggered 166 */ 167 168 void SP_trigger_once(edict_t *ent) 169 { 170 // make old maps work because I messed up on flag assignments here 171 // triggered was on bit 1 when it should have been on bit 4 172 if (ent->spawnflags & 1) 173 { 174 vec3_t v; 175 176 VectorMA (ent->mins, 0.5, ent->size, v); 177 ent->spawnflags &= ~1; 178 ent->spawnflags |= 4; 179 gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v)); 180 } 181 182 ent->wait = -1; 183 SP_trigger_multiple (ent); 184 } 185 186 /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) 187 This fixed size trigger cannot be touched, it can only be fired by other events. 188 */ 189 void trigger_relay_use (edict_t *self, edict_t *other, edict_t *activator) 190 { 191 G_UseTargets (self, activator); 192 } 193 194 void SP_trigger_relay (edict_t *self) 195 { 196 self->use = trigger_relay_use; 197 } 198 199 200 /* 201 ============================================================================== 202 203 trigger_key 204 205 ============================================================================== 206 */ 207 208 /*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8) 209 A relay trigger that only fires it's targets if player has the proper key. 210 Use "item" to specify the required key, for example "key_data_cd" 211 */ 212 void trigger_key_use (edict_t *self, edict_t *other, edict_t *activator) 213 { 214 int index; 215 216 if (!self->item) 217 return; 218 if (!activator->client) 219 return; 220 221 index = ITEM_INDEX(self->item); 222 if (!activator->client->pers.inventory[index]) 223 { 224 if (level.time < self->touch_debounce_time) 225 return; 226 self->touch_debounce_time = level.time + 5.0; 227 gi.centerprintf (activator, "You need the %s", self->item->pickup_name); 228 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0); 229 return; 230 } 231 232 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0); 233 if (coop->value) 234 { 235 int player; 236 edict_t *ent; 237 238 if (strcmp(self->item->classname, "key_power_cube") == 0) 239 { 240 int cube; 241 242 for (cube = 0; cube < 8; cube++) 243 if (activator->client->pers.power_cubes & (1 << cube)) 244 break; 245 for (player = 1; player <= game.maxclients; player++) 246 { 247 ent = &g_edicts[player]; 248 if (!ent->inuse) 249 continue; 250 if (!ent->client) 251 continue; 252 if (ent->client->pers.power_cubes & (1 << cube)) 253 { 254 ent->client->pers.inventory[index]--; 255 ent->client->pers.power_cubes &= ~(1 << cube); 256 } 257 } 258 } 259 else 260 { 261 for (player = 1; player <= game.maxclients; player++) 262 { 263 ent = &g_edicts[player]; 264 if (!ent->inuse) 265 continue; 266 if (!ent->client) 267 continue; 268 ent->client->pers.inventory[index] = 0; 269 } 270 } 271 } 272 else 273 { 274 activator->client->pers.inventory[index]--; 275 } 276 277 G_UseTargets (self, activator); 278 279 self->use = NULL; 280 } 281 282 void SP_trigger_key (edict_t *self) 283 { 284 if (!st.item) 285 { 286 gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin)); 287 return; 288 } 289 self->item = FindItemByClassname (st.item); 290 291 if (!self->item) 292 { 293 gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin)); 294 return; 295 } 296 297 if (!self->target) 298 { 299 gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin)); 300 return; 301 } 302 303 gi.soundindex ("misc/keytry.wav"); 304 gi.soundindex ("misc/keyuse.wav"); 305 306 self->use = trigger_key_use; 307 } 308 309 310 /* 311 ============================================================================== 312 313 trigger_counter 314 315 ============================================================================== 316 */ 317 318 /*QUAKED trigger_counter (.5 .5 .5) ? nomessage 319 Acts as an intermediary for an action that takes multiple inputs. 320 321 If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished. 322 323 After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. 324 */ 325 326 void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator) 327 { 328 if (self->count == 0) 329 return; 330 331 self->count--; 332 333 if (self->count) 334 { 335 if (! (self->spawnflags & 1)) 336 { 337 gi.centerprintf(activator, "%i more to go...", self->count); 338 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); 339 } 340 return; 341 } 342 343 if (! (self->spawnflags & 1)) 344 { 345 gi.centerprintf(activator, "Sequence completed!"); 346 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); 347 } 348 self->activator = activator; 349 multi_trigger (self); 350 } 351 352 void SP_trigger_counter (edict_t *self) 353 { 354 self->wait = -1; 355 if (!self->count) 356 self->count = 2; 357 358 self->use = trigger_counter_use; 359 } 360 361 362 /* 363 ============================================================================== 364 365 trigger_always 366 367 ============================================================================== 368 */ 369 370 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) 371 This trigger will always fire. It is activated by the world. 372 */ 373 void SP_trigger_always (edict_t *ent) 374 { 375 // we must have some delay to make sure our use targets are present 376 if (ent->delay < 0.2) 377 ent->delay = 0.2; 378 G_UseTargets(ent, ent); 379 } 380 381 382 /* 383 ============================================================================== 384 385 trigger_push 386 387 ============================================================================== 388 */ 389 390 #define PUSH_ONCE 1 391 392 static int windsound; 393 394 void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 395 { 396 if (strcmp(other->classname, "grenade") == 0) 397 { 398 VectorScale (self->movedir, self->speed * 10, other->velocity); 399 } 400 else if (other->health > 0) 401 { 402 VectorScale (self->movedir, self->speed * 10, other->velocity); 403 404 if (other->client) 405 { 406 // don't take falling damage immediately from this 407 VectorCopy (other->velocity, other->client->oldvelocity); 408 if (other->fly_sound_debounce_time < level.time) 409 { 410 other->fly_sound_debounce_time = level.time + 1.5; 411 gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0); 412 } 413 } 414 } 415 if (self->spawnflags & PUSH_ONCE) 416 G_FreeEdict (self); 417 } 418 419 420 /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE 421 Pushes the player 422 "speed" defaults to 1000 423 */ 424 void SP_trigger_push (edict_t *self) 425 { 426 InitTrigger (self); 427 windsound = gi.soundindex ("misc/windfly.wav"); 428 self->touch = trigger_push_touch; 429 if (!self->speed) 430 self->speed = 1000; 431 gi.linkentity (self); 432 } 433 434 435 /* 436 ============================================================================== 437 438 trigger_hurt 439 440 ============================================================================== 441 */ 442 443 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW 444 Any entity that touches this will be hurt. 445 446 It does dmg points of damage each server frame 447 448 SILENT supresses playing the sound 449 SLOW changes the damage rate to once per second 450 NO_PROTECTION *nothing* stops the damage 451 452 "dmg" default 5 (whole numbers only) 453 454 */ 455 void hurt_use (edict_t *self, edict_t *other, edict_t *activator) 456 { 457 if (self->solid == SOLID_NOT) 458 self->solid = SOLID_TRIGGER; 459 else 460 self->solid = SOLID_NOT; 461 gi.linkentity (self); 462 463 if (!(self->spawnflags & 2)) 464 self->use = NULL; 465 } 466 467 468 void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 469 { 470 int dflags; 471 472 if (!other->takedamage) 473 return; 474 475 if (self->timestamp > level.time) 476 return; 477 478 if (self->spawnflags & 16) 479 self->timestamp = level.time + 1; 480 else 481 self->timestamp = level.time + FRAMETIME; 482 483 if (!(self->spawnflags & 4)) 484 { 485 if ((level.framenum % 10) == 0) 486 gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0); 487 } 488 489 if (self->spawnflags & 8) 490 dflags = DAMAGE_NO_PROTECTION; 491 else 492 dflags = 0; 493 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT); 494 } 495 496 void SP_trigger_hurt (edict_t *self) 497 { 498 InitTrigger (self); 499 500 self->noise_index = gi.soundindex ("world/electro.wav"); 501 self->touch = hurt_touch; 502 503 if (!self->dmg) 504 self->dmg = 5; 505 506 if (self->spawnflags & 1) 507 self->solid = SOLID_NOT; 508 else 509 self->solid = SOLID_TRIGGER; 510 511 if (self->spawnflags & 2) 512 self->use = hurt_use; 513 514 gi.linkentity (self); 515 } 516 517 518 /* 519 ============================================================================== 520 521 trigger_gravity 522 523 ============================================================================== 524 */ 525 526 /*QUAKED trigger_gravity (.5 .5 .5) ? 527 Changes the touching entites gravity to 528 the value of "gravity". 1.0 is standard 529 gravity for the level. 530 */ 531 532 void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 533 { 534 other->gravity = self->gravity; 535 } 536 537 void SP_trigger_gravity (edict_t *self) 538 { 539 if (st.gravity == 0) 540 { 541 gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin)); 542 G_FreeEdict (self); 543 return; 544 } 545 546 InitTrigger (self); 547 self->gravity = atoi(st.gravity); 548 self->touch = trigger_gravity_touch; 549 } 550 551 552 /* 553 ============================================================================== 554 555 trigger_monsterjump 556 557 ============================================================================== 558 */ 559 560 /*QUAKED trigger_monsterjump (.5 .5 .5) ? 561 Walking monsters that touch this will jump in the direction of the trigger's angle 562 "speed" default to 200, the speed thrown forward 563 "height" default to 200, the speed thrown upwards 564 */ 565 566 void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 567 { 568 if (other->flags & (FL_FLY | FL_SWIM) ) 569 return; 570 if (other->svflags & SVF_DEADMONSTER) 571 return; 572 if ( !(other->svflags & SVF_MONSTER)) 573 return; 574 575 // set XY even if not on ground, so the jump will clear lips 576 other->velocity[0] = self->movedir[0] * self->speed; 577 other->velocity[1] = self->movedir[1] * self->speed; 578 579 if (!other->groundentity) 580 return; 581 582 other->groundentity = NULL; 583 other->velocity[2] = self->movedir[2]; 584 } 585 586 void SP_trigger_monsterjump (edict_t *self) 587 { 588 if (!self->speed) 589 self->speed = 200; 590 if (!st.height) 591 st.height = 200; 592 if (self->s.angles[YAW] == 0) 593 self->s.angles[YAW] = 360; 594 InitTrigger (self); 595 self->touch = trigger_monsterjump_touch; 596 self->movedir[2] = st.height; 597 } 598