Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

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