g_target.c (20523B)
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 /*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8) 23 Fire an origin based temp entity event to the clients. 24 "style" type byte 25 */ 26 void Use_Target_Tent (edict_t *ent, edict_t *other, edict_t *activator) 27 { 28 gi.WriteByte (svc_temp_entity); 29 gi.WriteByte (ent->style); 30 gi.WritePosition (ent->s.origin); 31 gi.multicast (ent->s.origin, MULTICAST_PVS); 32 } 33 34 void SP_target_temp_entity (edict_t *ent) 35 { 36 ent->use = Use_Target_Tent; 37 } 38 39 40 //========================================================== 41 42 //========================================================== 43 44 /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable 45 "noise" wav file to play 46 "attenuation" 47 -1 = none, send to whole level 48 1 = normal fighting sounds 49 2 = idle sound level 50 3 = ambient sound level 51 "volume" 0.0 to 1.0 52 53 Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers. 54 55 Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off. 56 Multiple identical looping sounds will just increase volume without any speed cost. 57 */ 58 void Use_Target_Speaker (edict_t *ent, edict_t *other, edict_t *activator) 59 { 60 int chan; 61 62 if (ent->spawnflags & 3) 63 { // looping sound toggles 64 if (ent->s.sound) 65 ent->s.sound = 0; // turn it off 66 else 67 ent->s.sound = ent->noise_index; // start it 68 } 69 else 70 { // normal sound 71 if (ent->spawnflags & 4) 72 chan = CHAN_VOICE|CHAN_RELIABLE; 73 else 74 chan = CHAN_VOICE; 75 // use a positioned_sound, because this entity won't normally be 76 // sent to any clients because it is invisible 77 gi.positioned_sound (ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0); 78 } 79 } 80 81 void SP_target_speaker (edict_t *ent) 82 { 83 char buffer[MAX_QPATH]; 84 85 if(!st.noise) 86 { 87 gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin)); 88 return; 89 } 90 if (!strstr (st.noise, ".wav")) 91 Com_sprintf (buffer, sizeof(buffer), "%s.wav", st.noise); 92 else 93 strncpy (buffer, st.noise, sizeof(buffer)); 94 ent->noise_index = gi.soundindex (buffer); 95 96 if (!ent->volume) 97 ent->volume = 1.0; 98 99 if (!ent->attenuation) 100 ent->attenuation = 1.0; 101 else if (ent->attenuation == -1) // use -1 so 0 defaults to 1 102 ent->attenuation = 0; 103 104 // check for prestarted looping sound 105 if (ent->spawnflags & 1) 106 ent->s.sound = ent->noise_index; 107 108 ent->use = Use_Target_Speaker; 109 110 // must link the entity so we get areas and clusters so 111 // the server can determine who to send updates to 112 gi.linkentity (ent); 113 } 114 115 116 //========================================================== 117 118 void Use_Target_Help (edict_t *ent, edict_t *other, edict_t *activator) 119 { 120 if (ent->spawnflags & 1) 121 strncpy (game.helpmessage1, ent->message, sizeof(game.helpmessage2)-1); 122 else 123 strncpy (game.helpmessage2, ent->message, sizeof(game.helpmessage1)-1); 124 125 game.helpchanged++; 126 } 127 128 /*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1 129 When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars. 130 */ 131 void SP_target_help(edict_t *ent) 132 { 133 if (deathmatch->value) 134 { // auto-remove for deathmatch 135 G_FreeEdict (ent); 136 return; 137 } 138 139 if (!ent->message) 140 { 141 gi.dprintf ("%s with no message at %s\n", ent->classname, vtos(ent->s.origin)); 142 G_FreeEdict (ent); 143 return; 144 } 145 ent->use = Use_Target_Help; 146 } 147 148 //========================================================== 149 150 /*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8) 151 Counts a secret found. 152 These are single use targets. 153 */ 154 void use_target_secret (edict_t *ent, edict_t *other, edict_t *activator) 155 { 156 gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0); 157 158 level.found_secrets++; 159 160 G_UseTargets (ent, activator); 161 G_FreeEdict (ent); 162 } 163 164 void SP_target_secret (edict_t *ent) 165 { 166 if (deathmatch->value) 167 { // auto-remove for deathmatch 168 G_FreeEdict (ent); 169 return; 170 } 171 172 ent->use = use_target_secret; 173 if (!st.noise) 174 st.noise = "misc/secret.wav"; 175 ent->noise_index = gi.soundindex (st.noise); 176 ent->svflags = SVF_NOCLIENT; 177 level.total_secrets++; 178 // map bug hack 179 if (!Q_stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624) 180 ent->message = "You have found a secret area."; 181 } 182 183 //========================================================== 184 185 /*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8) 186 Counts a goal completed. 187 These are single use targets. 188 */ 189 void use_target_goal (edict_t *ent, edict_t *other, edict_t *activator) 190 { 191 gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0); 192 193 level.found_goals++; 194 195 if (level.found_goals == level.total_goals) 196 gi.configstring (CS_CDTRACK, "0"); 197 198 G_UseTargets (ent, activator); 199 G_FreeEdict (ent); 200 } 201 202 void SP_target_goal (edict_t *ent) 203 { 204 if (deathmatch->value) 205 { // auto-remove for deathmatch 206 G_FreeEdict (ent); 207 return; 208 } 209 210 ent->use = use_target_goal; 211 if (!st.noise) 212 st.noise = "misc/secret.wav"; 213 ent->noise_index = gi.soundindex (st.noise); 214 ent->svflags = SVF_NOCLIENT; 215 level.total_goals++; 216 } 217 218 //========================================================== 219 220 221 /*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8) 222 Spawns an explosion temporary entity when used. 223 224 "delay" wait this long before going off 225 "dmg" how much radius damage should be done, defaults to 0 226 */ 227 void target_explosion_explode (edict_t *self) 228 { 229 float save; 230 231 gi.WriteByte (svc_temp_entity); 232 gi.WriteByte (TE_EXPLOSION1); 233 gi.WritePosition (self->s.origin); 234 gi.multicast (self->s.origin, MULTICAST_PHS); 235 236 T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); 237 238 save = self->delay; 239 self->delay = 0; 240 G_UseTargets (self, self->activator); 241 self->delay = save; 242 } 243 244 void use_target_explosion (edict_t *self, edict_t *other, edict_t *activator) 245 { 246 self->activator = activator; 247 248 if (!self->delay) 249 { 250 target_explosion_explode (self); 251 return; 252 } 253 254 self->think = target_explosion_explode; 255 self->nextthink = level.time + self->delay; 256 } 257 258 void SP_target_explosion (edict_t *ent) 259 { 260 ent->use = use_target_explosion; 261 ent->svflags = SVF_NOCLIENT; 262 } 263 264 265 //========================================================== 266 267 /*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8) 268 Changes level to "map" when fired 269 */ 270 void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator) 271 { 272 if (level.intermissiontime) 273 return; // already activated 274 275 if (!deathmatch->value && !coop->value) 276 { 277 if (g_edicts[1].health <= 0) 278 return; 279 } 280 281 // if noexit, do a ton of damage to other 282 if (deathmatch->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world) 283 { 284 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT); 285 return; 286 } 287 288 // if multiplayer, let everyone know who hit the exit 289 if (deathmatch->value) 290 { 291 if (activator && activator->client) 292 gi.bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname); 293 } 294 295 // if going to a new unit, clear cross triggers 296 if (strstr(self->map, "*")) 297 game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK); 298 299 BeginIntermission (self); 300 } 301 302 void SP_target_changelevel (edict_t *ent) 303 { 304 if (!ent->map) 305 { 306 gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin)); 307 G_FreeEdict (ent); 308 return; 309 } 310 311 // ugly hack because *SOMEBODY* screwed up their map 312 if((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0)) 313 ent->map = "fact3$secret1"; 314 315 ent->use = use_target_changelevel; 316 ent->svflags = SVF_NOCLIENT; 317 } 318 319 320 //========================================================== 321 322 /*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8) 323 Creates a particle splash effect when used. 324 325 Set "sounds" to one of the following: 326 1) sparks 327 2) blue water 328 3) brown water 329 4) slime 330 5) lava 331 6) blood 332 333 "count" how many pixels in the splash 334 "dmg" if set, does a radius damage at this location when it splashes 335 useful for lava/sparks 336 */ 337 338 void use_target_splash (edict_t *self, edict_t *other, edict_t *activator) 339 { 340 gi.WriteByte (svc_temp_entity); 341 gi.WriteByte (TE_SPLASH); 342 gi.WriteByte (self->count); 343 gi.WritePosition (self->s.origin); 344 gi.WriteDir (self->movedir); 345 gi.WriteByte (self->sounds); 346 gi.multicast (self->s.origin, MULTICAST_PVS); 347 348 if (self->dmg) 349 T_RadiusDamage (self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH); 350 } 351 352 void SP_target_splash (edict_t *self) 353 { 354 self->use = use_target_splash; 355 G_SetMovedir (self->s.angles, self->movedir); 356 357 if (!self->count) 358 self->count = 32; 359 360 self->svflags = SVF_NOCLIENT; 361 } 362 363 364 //========================================================== 365 366 /*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8) 367 Set target to the type of entity you want spawned. 368 Useful for spawning monsters and gibs in the factory levels. 369 370 For monsters: 371 Set direction to the facing you want it to have. 372 373 For gibs: 374 Set direction if you want it moving and 375 speed how fast it should be moving otherwise it 376 will just be dropped 377 */ 378 void ED_CallSpawn (edict_t *ent); 379 380 void use_target_spawner (edict_t *self, edict_t *other, edict_t *activator) 381 { 382 edict_t *ent; 383 384 ent = G_Spawn(); 385 ent->classname = self->target; 386 VectorCopy (self->s.origin, ent->s.origin); 387 VectorCopy (self->s.angles, ent->s.angles); 388 ED_CallSpawn (ent); 389 gi.unlinkentity (ent); 390 KillBox (ent); 391 gi.linkentity (ent); 392 if (self->speed) 393 VectorCopy (self->movedir, ent->velocity); 394 } 395 396 void SP_target_spawner (edict_t *self) 397 { 398 self->use = use_target_spawner; 399 self->svflags = SVF_NOCLIENT; 400 if (self->speed) 401 { 402 G_SetMovedir (self->s.angles, self->movedir); 403 VectorScale (self->movedir, self->speed, self->movedir); 404 } 405 } 406 407 //========================================================== 408 409 /*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS 410 Fires a blaster bolt in the set direction when triggered. 411 412 dmg default is 15 413 speed default is 1000 414 */ 415 416 void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator) 417 { 418 int effect; 419 420 if (self->spawnflags & 2) 421 effect = 0; 422 else if (self->spawnflags & 1) 423 effect = EF_HYPERBLASTER; 424 else 425 effect = EF_BLASTER; 426 427 fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER); 428 gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0); 429 } 430 431 void SP_target_blaster (edict_t *self) 432 { 433 self->use = use_target_blaster; 434 G_SetMovedir (self->s.angles, self->movedir); 435 self->noise_index = gi.soundindex ("weapons/laser2.wav"); 436 437 if (!self->dmg) 438 self->dmg = 15; 439 if (!self->speed) 440 self->speed = 1000; 441 442 self->svflags = SVF_NOCLIENT; 443 } 444 445 446 //========================================================== 447 448 /*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 449 Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work. 450 */ 451 void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator) 452 { 453 game.serverflags |= self->spawnflags; 454 G_FreeEdict (self); 455 } 456 457 void SP_target_crosslevel_trigger (edict_t *self) 458 { 459 self->svflags = SVF_NOCLIENT; 460 self->use = trigger_crosslevel_trigger_use; 461 } 462 463 /*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 464 Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be true. Delay, target and 465 killtarget also work. 466 467 "delay" delay before using targets if the trigger has been activated (default 1) 468 */ 469 void target_crosslevel_target_think (edict_t *self) 470 { 471 if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags)) 472 { 473 G_UseTargets (self, self); 474 G_FreeEdict (self); 475 } 476 } 477 478 void SP_target_crosslevel_target (edict_t *self) 479 { 480 if (! self->delay) 481 self->delay = 1; 482 self->svflags = SVF_NOCLIENT; 483 484 self->think = target_crosslevel_target_think; 485 self->nextthink = level.time + self->delay; 486 } 487 488 //========================================================== 489 490 /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT 491 When triggered, fires a laser. You can either set a target 492 or a direction. 493 */ 494 495 void target_laser_think (edict_t *self) 496 { 497 edict_t *ignore; 498 vec3_t start; 499 vec3_t end; 500 trace_t tr; 501 vec3_t point; 502 vec3_t last_movedir; 503 int count; 504 505 if (self->spawnflags & 0x80000000) 506 count = 8; 507 else 508 count = 4; 509 510 if (self->enemy) 511 { 512 VectorCopy (self->movedir, last_movedir); 513 VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point); 514 VectorSubtract (point, self->s.origin, self->movedir); 515 VectorNormalize (self->movedir); 516 if (!VectorCompare(self->movedir, last_movedir)) 517 self->spawnflags |= 0x80000000; 518 } 519 520 ignore = self; 521 VectorCopy (self->s.origin, start); 522 VectorMA (start, 2048, self->movedir, end); 523 while(1) 524 { 525 tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); 526 527 if (!tr.ent) 528 break; 529 530 // hurt it if we can 531 if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER)) 532 T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER); 533 534 // if we hit something that's not a monster or player or is immune to lasers, we're done 535 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) 536 { 537 if (self->spawnflags & 0x80000000) 538 { 539 self->spawnflags &= ~0x80000000; 540 gi.WriteByte (svc_temp_entity); 541 gi.WriteByte (TE_LASER_SPARKS); 542 gi.WriteByte (count); 543 gi.WritePosition (tr.endpos); 544 gi.WriteDir (tr.plane.normal); 545 gi.WriteByte (self->s.skinnum); 546 gi.multicast (tr.endpos, MULTICAST_PVS); 547 } 548 break; 549 } 550 551 ignore = tr.ent; 552 VectorCopy (tr.endpos, start); 553 } 554 555 VectorCopy (tr.endpos, self->s.old_origin); 556 557 self->nextthink = level.time + FRAMETIME; 558 } 559 560 void target_laser_on (edict_t *self) 561 { 562 if (!self->activator) 563 self->activator = self; 564 self->spawnflags |= 0x80000001; 565 self->svflags &= ~SVF_NOCLIENT; 566 target_laser_think (self); 567 } 568 569 void target_laser_off (edict_t *self) 570 { 571 self->spawnflags &= ~1; 572 self->svflags |= SVF_NOCLIENT; 573 self->nextthink = 0; 574 } 575 576 void target_laser_use (edict_t *self, edict_t *other, edict_t *activator) 577 { 578 self->activator = activator; 579 if (self->spawnflags & 1) 580 target_laser_off (self); 581 else 582 target_laser_on (self); 583 } 584 585 void target_laser_start (edict_t *self) 586 { 587 edict_t *ent; 588 589 self->movetype = MOVETYPE_NONE; 590 self->solid = SOLID_NOT; 591 self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT; 592 self->s.modelindex = 1; // must be non-zero 593 594 // set the beam diameter 595 if (self->spawnflags & 64) 596 self->s.frame = 16; 597 else 598 self->s.frame = 4; 599 600 // set the color 601 if (self->spawnflags & 2) 602 self->s.skinnum = 0xf2f2f0f0; 603 else if (self->spawnflags & 4) 604 self->s.skinnum = 0xd0d1d2d3; 605 else if (self->spawnflags & 8) 606 self->s.skinnum = 0xf3f3f1f1; 607 else if (self->spawnflags & 16) 608 self->s.skinnum = 0xdcdddedf; 609 else if (self->spawnflags & 32) 610 self->s.skinnum = 0xe0e1e2e3; 611 612 if (!self->enemy) 613 { 614 if (self->target) 615 { 616 ent = G_Find (NULL, FOFS(targetname), self->target); 617 if (!ent) 618 gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target); 619 self->enemy = ent; 620 } 621 else 622 { 623 G_SetMovedir (self->s.angles, self->movedir); 624 } 625 } 626 self->use = target_laser_use; 627 self->think = target_laser_think; 628 629 if (!self->dmg) 630 self->dmg = 1; 631 632 VectorSet (self->mins, -8, -8, -8); 633 VectorSet (self->maxs, 8, 8, 8); 634 gi.linkentity (self); 635 636 if (self->spawnflags & 1) 637 target_laser_on (self); 638 else 639 target_laser_off (self); 640 } 641 642 void SP_target_laser (edict_t *self) 643 { 644 // let everything else get spawned before we start firing 645 self->think = target_laser_start; 646 self->nextthink = level.time + 1; 647 } 648 649 //========================================================== 650 651 /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE 652 speed How many seconds the ramping will take 653 message two letters; starting lightlevel and ending lightlevel 654 */ 655 656 void target_lightramp_think (edict_t *self) 657 { 658 char style[2]; 659 660 style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2]; 661 style[1] = 0; 662 gi.configstring (CS_LIGHTS+self->enemy->style, style); 663 664 if ((level.time - self->timestamp) < self->speed) 665 { 666 self->nextthink = level.time + FRAMETIME; 667 } 668 else if (self->spawnflags & 1) 669 { 670 char temp; 671 672 temp = self->movedir[0]; 673 self->movedir[0] = self->movedir[1]; 674 self->movedir[1] = temp; 675 self->movedir[2] *= -1; 676 } 677 } 678 679 void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator) 680 { 681 if (!self->enemy) 682 { 683 edict_t *e; 684 685 // check all the targets 686 e = NULL; 687 while (1) 688 { 689 e = G_Find (e, FOFS(targetname), self->target); 690 if (!e) 691 break; 692 if (strcmp(e->classname, "light") != 0) 693 { 694 gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin)); 695 gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin)); 696 } 697 else 698 { 699 self->enemy = e; 700 } 701 } 702 703 if (!self->enemy) 704 { 705 gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin)); 706 G_FreeEdict (self); 707 return; 708 } 709 } 710 711 self->timestamp = level.time; 712 target_lightramp_think (self); 713 } 714 715 void SP_target_lightramp (edict_t *self) 716 { 717 if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1]) 718 { 719 gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin)); 720 G_FreeEdict (self); 721 return; 722 } 723 724 if (deathmatch->value) 725 { 726 G_FreeEdict (self); 727 return; 728 } 729 730 if (!self->target) 731 { 732 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); 733 G_FreeEdict (self); 734 return; 735 } 736 737 self->svflags |= SVF_NOCLIENT; 738 self->use = target_lightramp_use; 739 self->think = target_lightramp_think; 740 741 self->movedir[0] = self->message[0] - 'a'; 742 self->movedir[1] = self->message[1] - 'a'; 743 self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME); 744 } 745 746 //========================================================== 747 748 /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) 749 When triggered, this initiates a level-wide earthquake. 750 All players and monsters are affected. 751 "speed" severity of the quake (default:200) 752 "count" duration of the quake (default:5) 753 */ 754 755 void target_earthquake_think (edict_t *self) 756 { 757 int i; 758 edict_t *e; 759 760 if (self->last_move_time < level.time) 761 { 762 gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0); 763 self->last_move_time = level.time + 0.5; 764 } 765 766 for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++) 767 { 768 if (!e->inuse) 769 continue; 770 if (!e->client) 771 continue; 772 if (!e->groundentity) 773 continue; 774 775 e->groundentity = NULL; 776 e->velocity[0] += crandom()* 150; 777 e->velocity[1] += crandom()* 150; 778 e->velocity[2] = self->speed * (100.0 / e->mass); 779 } 780 781 if (level.time < self->timestamp) 782 self->nextthink = level.time + FRAMETIME; 783 } 784 785 void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator) 786 { 787 self->timestamp = level.time + self->count; 788 self->nextthink = level.time + FRAMETIME; 789 self->activator = activator; 790 self->last_move_time = 0; 791 } 792 793 void SP_target_earthquake (edict_t *self) 794 { 795 if (!self->targetname) 796 gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin)); 797 798 if (!self->count) 799 self->count = 5; 800 801 if (!self->speed) 802 self->speed = 200; 803 804 self->svflags |= SVF_NOCLIENT; 805 self->think = target_earthquake_think; 806 self->use = target_earthquake_use; 807 808 self->noise_index = gi.soundindex ("world/quake.wav"); 809 }