File: game\g_trigger.c

    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 
  599