123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /*
- Copyright (C) 1997-2001 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "g_local.h"
- void InitTrigger (edict_t *self)
- {
- if (!VectorCompare (self->s.angles, vec3_origin))
- G_SetMovedir (self->s.angles, self->movedir);
- self->solid = SOLID_TRIGGER;
- self->movetype = MOVETYPE_NONE;
- gi.setmodel (self, self->model);
- self->svflags = SVF_NOCLIENT;
- }
- // the wait time has passed, so set back up for another activation
- void multi_wait (edict_t *ent)
- {
- ent->nextthink = 0;
- }
- // the trigger was just activated
- // ent->activator should be set to the activator so it can be held through a delay
- // so wait for the delay time before firing
- void multi_trigger (edict_t *ent)
- {
- if (ent->nextthink)
- return; // already been triggered
- G_UseTargets (ent, ent->activator);
- if (ent->wait > 0)
- {
- ent->think = multi_wait;
- ent->nextthink = level.time + ent->wait;
- }
- else
- { // we can't just remove (self) here, because this is a touch function
- // called while looping through area links...
- ent->touch = NULL;
- ent->nextthink = level.time + FRAMETIME;
- ent->think = G_FreeEdict;
- }
- }
- void Use_Multi (edict_t *ent, edict_t *other, edict_t *activator)
- {
- ent->activator = activator;
- multi_trigger (ent);
- }
- void Touch_Multi (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- if(other->client)
- {
- if (self->spawnflags & 2)
- return;
- }
- else if (other->svflags & SVF_MONSTER)
- {
- if (!(self->spawnflags & 1))
- return;
- }
- else
- return;
- if (!VectorCompare(self->movedir, vec3_origin))
- {
- vec3_t forward;
- AngleVectors(other->s.angles, forward, NULL, NULL);
- if (_DotProduct(forward, self->movedir) < 0)
- return;
- }
- self->activator = other;
- multi_trigger (self);
- }
- /*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
- Variable sized repeatable trigger. Must be targeted at one or more entities.
- If "delay" is set, the trigger waits some time after activating before firing.
- "wait" : Seconds between triggerings. (.2 default)
- sounds
- 1) secret
- 2) beep beep
- 3) large switch
- 4)
- set "message" to text string
- */
- void trigger_enable (edict_t *self, edict_t *other, edict_t *activator)
- {
- self->solid = SOLID_TRIGGER;
- self->use = Use_Multi;
- gi.linkentity (self);
- }
- void SP_trigger_multiple (edict_t *ent)
- {
- if (ent->sounds == 1)
- ent->noise_index = gi.soundindex ("misc/secret.wav");
- else if (ent->sounds == 2)
- ent->noise_index = gi.soundindex ("misc/talk.wav");
- else if (ent->sounds == 3)
- ent->noise_index = gi.soundindex ("misc/trigger1.wav");
-
- if (!ent->wait)
- ent->wait = 0.2;
- ent->touch = Touch_Multi;
- ent->movetype = MOVETYPE_NONE;
- ent->svflags |= SVF_NOCLIENT;
- if (ent->spawnflags & 4)
- {
- ent->solid = SOLID_NOT;
- ent->use = trigger_enable;
- }
- else
- {
- ent->solid = SOLID_TRIGGER;
- ent->use = Use_Multi;
- }
- if (!VectorCompare(ent->s.angles, vec3_origin))
- G_SetMovedir (ent->s.angles, ent->movedir);
- gi.setmodel (ent, ent->model);
- gi.linkentity (ent);
- }
- /*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
- Triggers once, then removes itself.
- You must set the key "target" to the name of another object in the level that has a matching "targetname".
- If TRIGGERED, this trigger must be triggered before it is live.
- sounds
- 1) secret
- 2) beep beep
- 3) large switch
- 4)
- "message" string to be displayed when triggered
- */
- void SP_trigger_once(edict_t *ent)
- {
- // make old maps work because I messed up on flag assignments here
- // triggered was on bit 1 when it should have been on bit 4
- if (ent->spawnflags & 1)
- {
- vec3_t v;
- VectorMA (ent->mins, 0.5, ent->size, v);
- ent->spawnflags &= ~1;
- ent->spawnflags |= 4;
- gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
- }
- ent->wait = -1;
- SP_trigger_multiple (ent);
- }
- /*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
- This fixed size trigger cannot be touched, it can only be fired by other events.
- */
- void trigger_relay_use (edict_t *self, edict_t *other, edict_t *activator)
- {
- G_UseTargets (self, activator);
- }
- void SP_trigger_relay (edict_t *self)
- {
- self->use = trigger_relay_use;
- }
- /*
- ==============================================================================
- trigger_key
- ==============================================================================
- */
- /*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
- A relay trigger that only fires it's targets if player has the proper key.
- Use "item" to specify the required key, for example "key_data_cd"
- */
- void trigger_key_use (edict_t *self, edict_t *other, edict_t *activator)
- {
- int index;
- if (!self->item)
- return;
- if (!activator->client)
- return;
- index = ITEM_INDEX(self->item);
- if (!activator->client->pers.inventory[index])
- {
- if (level.time < self->touch_debounce_time)
- return;
- self->touch_debounce_time = level.time + 5.0;
- gi.centerprintf (activator, "You need the %s", self->item->pickup_name);
- gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0);
- return;
- }
- gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
- if (coop->value)
- {
- int player;
- edict_t *ent;
- if (strcmp(self->item->classname, "key_power_cube") == 0)
- {
- int cube;
- for (cube = 0; cube < 8; cube++)
- if (activator->client->pers.power_cubes & (1 << cube))
- break;
- for (player = 1; player <= game.maxclients; player++)
- {
- ent = &g_edicts[player];
- if (!ent->inuse)
- continue;
- if (!ent->client)
- continue;
- if (ent->client->pers.power_cubes & (1 << cube))
- {
- ent->client->pers.inventory[index]--;
- ent->client->pers.power_cubes &= ~(1 << cube);
- }
- }
- }
- else
- {
- for (player = 1; player <= game.maxclients; player++)
- {
- ent = &g_edicts[player];
- if (!ent->inuse)
- continue;
- if (!ent->client)
- continue;
- ent->client->pers.inventory[index] = 0;
- }
- }
- }
- else
- {
- activator->client->pers.inventory[index]--;
- }
- G_UseTargets (self, activator);
- self->use = NULL;
- }
- void SP_trigger_key (edict_t *self)
- {
- if (!st.item)
- {
- gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
- return;
- }
- self->item = FindItemByClassname (st.item);
- if (!self->item)
- {
- gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
- return;
- }
- if (!self->target)
- {
- gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
- return;
- }
- gi.soundindex ("misc/keytry.wav");
- gi.soundindex ("misc/keyuse.wav");
- self->use = trigger_key_use;
- }
- /*
- ==============================================================================
- trigger_counter
- ==============================================================================
- */
- /*QUAKED trigger_counter (.5 .5 .5) ? nomessage
- Acts as an intermediary for an action that takes multiple inputs.
- If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
- After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
- */
- void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator)
- {
- if (self->count == 0)
- return;
-
- self->count--;
- if (self->count)
- {
- if (! (self->spawnflags & 1))
- {
- gi.centerprintf(activator, "%i more to go...", self->count);
- gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
- }
- return;
- }
-
- if (! (self->spawnflags & 1))
- {
- gi.centerprintf(activator, "Sequence completed!");
- gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
- }
- self->activator = activator;
- multi_trigger (self);
- }
- void SP_trigger_counter (edict_t *self)
- {
- self->wait = -1;
- if (!self->count)
- self->count = 2;
- self->use = trigger_counter_use;
- }
- /*
- ==============================================================================
- trigger_always
- ==============================================================================
- */
- /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
- This trigger will always fire. It is activated by the world.
- */
- void SP_trigger_always (edict_t *ent)
- {
- // we must have some delay to make sure our use targets are present
- if (ent->delay < 0.2)
- ent->delay = 0.2;
- G_UseTargets(ent, ent);
- }
- /*
- ==============================================================================
- trigger_push
- ==============================================================================
- */
- #define PUSH_ONCE 1
- static int windsound;
- void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- if (strcmp(other->classname, "grenade") == 0)
- {
- VectorScale (self->movedir, self->speed * 10, other->velocity);
- }
- else if (other->health > 0)
- {
- VectorScale (self->movedir, self->speed * 10, other->velocity);
- if (other->client)
- {
- // don't take falling damage immediately from this
- VectorCopy (other->velocity, other->client->oldvelocity);
- if (other->fly_sound_debounce_time < level.time)
- {
- other->fly_sound_debounce_time = level.time + 1.5;
- gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
- }
- }
- }
- if (self->spawnflags & PUSH_ONCE)
- G_FreeEdict (self);
- }
- /*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
- Pushes the player
- "speed" defaults to 1000
- */
- void SP_trigger_push (edict_t *self)
- {
- InitTrigger (self);
- windsound = gi.soundindex ("misc/windfly.wav");
- self->touch = trigger_push_touch;
- if (!self->speed)
- self->speed = 1000;
- gi.linkentity (self);
- }
- /*
- ==============================================================================
- trigger_hurt
- ==============================================================================
- */
- /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
- Any entity that touches this will be hurt.
- It does dmg points of damage each server frame
- SILENT supresses playing the sound
- SLOW changes the damage rate to once per second
- NO_PROTECTION *nothing* stops the damage
- "dmg" default 5 (whole numbers only)
- */
- void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
- {
- if (self->solid == SOLID_NOT)
- self->solid = SOLID_TRIGGER;
- else
- self->solid = SOLID_NOT;
- gi.linkentity (self);
- if (!(self->spawnflags & 2))
- self->use = NULL;
- }
- void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- int dflags;
- if (!other->takedamage)
- return;
- if (self->timestamp > level.time)
- return;
- if (self->spawnflags & 16)
- self->timestamp = level.time + 1;
- else
- self->timestamp = level.time + FRAMETIME;
- if (!(self->spawnflags & 4))
- {
- if ((level.framenum % 10) == 0)
- gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
- }
- if (self->spawnflags & 8)
- dflags = DAMAGE_NO_PROTECTION;
- else
- dflags = 0;
- T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
- }
- void SP_trigger_hurt (edict_t *self)
- {
- InitTrigger (self);
- self->noise_index = gi.soundindex ("world/electro.wav");
- self->touch = hurt_touch;
- if (!self->dmg)
- self->dmg = 5;
- if (self->spawnflags & 1)
- self->solid = SOLID_NOT;
- else
- self->solid = SOLID_TRIGGER;
- if (self->spawnflags & 2)
- self->use = hurt_use;
- gi.linkentity (self);
- }
- /*
- ==============================================================================
- trigger_gravity
- ==============================================================================
- */
- /*QUAKED trigger_gravity (.5 .5 .5) ?
- Changes the touching entites gravity to
- the value of "gravity". 1.0 is standard
- gravity for the level.
- */
- void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- other->gravity = self->gravity;
- }
- void SP_trigger_gravity (edict_t *self)
- {
- if (st.gravity == 0)
- {
- gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
- G_FreeEdict (self);
- return;
- }
- InitTrigger (self);
- self->gravity = atoi(st.gravity);
- self->touch = trigger_gravity_touch;
- }
- /*
- ==============================================================================
- trigger_monsterjump
- ==============================================================================
- */
- /*QUAKED trigger_monsterjump (.5 .5 .5) ?
- Walking monsters that touch this will jump in the direction of the trigger's angle
- "speed" default to 200, the speed thrown forward
- "height" default to 200, the speed thrown upwards
- */
- void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- if (other->flags & (FL_FLY | FL_SWIM) )
- return;
- if (other->svflags & SVF_DEADMONSTER)
- return;
- if ( !(other->svflags & SVF_MONSTER))
- return;
- // set XY even if not on ground, so the jump will clear lips
- other->velocity[0] = self->movedir[0] * self->speed;
- other->velocity[1] = self->movedir[1] * self->speed;
-
- if (!other->groundentity)
- return;
-
- other->groundentity = NULL;
- other->velocity[2] = self->movedir[2];
- }
- void SP_trigger_monsterjump (edict_t *self)
- {
- if (!self->speed)
- self->speed = 200;
- if (!st.height)
- st.height = 200;
- if (self->s.angles[YAW] == 0)
- self->s.angles[YAW] = 360;
- InitTrigger (self);
- self->touch = trigger_monsterjump_touch;
- self->movedir[2] = st.height;
- }
|