home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / g_target.c < prev    next >
C/C++ Source or Header  |  1997-11-26  |  19KB  |  776 lines

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