Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

g_trigger.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 #include "g_local.h"
00024 
00025 
00026 void InitTrigger( gentity_t *self ) {
00027     if (!VectorCompare (self->s.angles, vec3_origin))
00028         G_SetMovedir (self->s.angles, self->movedir);
00029 
00030     trap_SetBrushModel( self, self->model );
00031     self->r.contents = CONTENTS_TRIGGER;        // replaces the -1 from trap_SetBrushModel
00032     self->r.svFlags = SVF_NOCLIENT;
00033 }
00034 
00035 
00036 // the wait time has passed, so set back up for another activation
00037 void multi_wait( gentity_t *ent ) {
00038     ent->nextthink = 0;
00039 }
00040 
00041 
00042 // the trigger was just activated
00043 // ent->activator should be set to the activator so it can be held through a delay
00044 // so wait for the delay time before firing
00045 void multi_trigger( gentity_t *ent, gentity_t *activator ) {
00046     ent->activator = activator;
00047     if ( ent->nextthink ) {
00048         return;     // can't retrigger until the wait is over
00049     }
00050 
00051     if ( activator->client ) {
00052         if ( ( ent->spawnflags & 1 ) &&
00053             activator->client->sess.sessionTeam != TEAM_RED ) {
00054             return;
00055         }
00056         if ( ( ent->spawnflags & 2 ) &&
00057             activator->client->sess.sessionTeam != TEAM_BLUE ) {
00058             return;
00059         }
00060     }
00061 
00062     G_UseTargets (ent, ent->activator);
00063 
00064     if ( ent->wait > 0 ) {
00065         ent->think = multi_wait;
00066         ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
00067     } else {
00068         // we can't just remove (self) here, because this is a touch function
00069         // called while looping through area links...
00070         ent->touch = 0;
00071         ent->nextthink = level.time + FRAMETIME;
00072         ent->think = G_FreeEntity;
00073     }
00074 }
00075 
00076 void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
00077     multi_trigger( ent, activator );
00078 }
00079 
00080 void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) {
00081     if( !other->client ) {
00082         return;
00083     }
00084     multi_trigger( self, other );
00085 }
00086 
00087 /*QUAKED trigger_multiple (.5 .5 .5) ?
00088 "wait" : Seconds between triggerings, 0.5 default, -1 = one time only.
00089 "random"    wait variance, default is 0
00090 Variable sized repeatable trigger.  Must be targeted at one or more entities.
00091 so, the basic time between firing is a random time between
00092 (wait - random) and (wait + random)
00093 */
00094 void SP_trigger_multiple( gentity_t *ent ) {
00095     G_SpawnFloat( "wait", "0.5", &ent->wait );
00096     G_SpawnFloat( "random", "0", &ent->random );
00097 
00098     if ( ent->random >= ent->wait && ent->wait >= 0 ) {
00099         ent->random = ent->wait - FRAMETIME;
00100         G_Printf( "trigger_multiple has random >= wait\n" );
00101     }
00102 
00103     ent->touch = Touch_Multi;
00104     ent->use = Use_Multi;
00105 
00106     InitTrigger( ent );
00107     trap_LinkEntity (ent);
00108 }
00109 
00110 
00111 
00112 /*
00113 ==============================================================================
00114 
00115 trigger_always
00116 
00117 ==============================================================================
00118 */
00119 
00120 void trigger_always_think( gentity_t *ent ) {
00121     G_UseTargets(ent, ent);
00122     G_FreeEntity( ent );
00123 }
00124 
00125 /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
00126 This trigger will always fire.  It is activated by the world.
00127 */
00128 void SP_trigger_always (gentity_t *ent) {
00129     // we must have some delay to make sure our use targets are present
00130     ent->nextthink = level.time + 300;
00131     ent->think = trigger_always_think;
00132 }
00133 
00134 
00135 /*
00136 ==============================================================================
00137 
00138 trigger_push
00139 
00140 ==============================================================================
00141 */
00142 
00143 void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
00144 
00145     if ( !other->client ) {
00146         return;
00147     }
00148 
00149     BG_TouchJumpPad( &other->client->ps, &self->s );
00150 }
00151 
00152 
00153 /*
00154 =================
00155 AimAtTarget
00156 
00157 Calculate origin2 so the target apogee will be hit
00158 =================
00159 */
00160 void AimAtTarget( gentity_t *self ) {
00161     gentity_t   *ent;
00162     vec3_t      origin;
00163     float       height, gravity, time, forward;
00164     float       dist;
00165 
00166     VectorAdd( self->r.absmin, self->r.absmax, origin );
00167     VectorScale ( origin, 0.5, origin );
00168 
00169     ent = G_PickTarget( self->target );
00170     if ( !ent ) {
00171         G_FreeEntity( self );
00172         return;
00173     }
00174 
00175     height = ent->s.origin[2] - origin[2];
00176     gravity = g_gravity.value;
00177     time = sqrt( height / ( .5 * gravity ) );
00178     if ( !time ) {
00179         G_FreeEntity( self );
00180         return;
00181     }
00182 
00183     // set s.origin2 to the push velocity
00184     VectorSubtract ( ent->s.origin, origin, self->s.origin2 );
00185     self->s.origin2[2] = 0;
00186     dist = VectorNormalize( self->s.origin2);
00187 
00188     forward = dist / time;
00189     VectorScale( self->s.origin2, forward, self->s.origin2 );
00190 
00191     self->s.origin2[2] = time * gravity;
00192 }
00193 
00194 
00195 /*QUAKED trigger_push (.5 .5 .5) ?
00196 Must point at a target_position, which will be the apex of the leap.
00197 This will be client side predicted, unlike target_push
00198 */
00199 void SP_trigger_push( gentity_t *self ) {
00200     InitTrigger (self);
00201 
00202     // unlike other triggers, we need to send this one to the client
00203     self->r.svFlags &= ~SVF_NOCLIENT;
00204 
00205     // make sure the client precaches this sound
00206     G_SoundIndex("sound/world/jumppad.wav");
00207 
00208     self->s.eType = ET_PUSH_TRIGGER;
00209     self->touch = trigger_push_touch;
00210     self->think = AimAtTarget;
00211     self->nextthink = level.time + FRAMETIME;
00212     trap_LinkEntity (self);
00213 }
00214 
00215 
00216 void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) {
00217     if ( !activator->client ) {
00218         return;
00219     }
00220 
00221     if ( activator->client->ps.pm_type != PM_NORMAL ) {
00222         return;
00223     }
00224     if ( activator->client->ps.powerups[PW_FLIGHT] ) {
00225         return;
00226     }
00227 
00228     VectorCopy (self->s.origin2, activator->client->ps.velocity);
00229 
00230     // play fly sound every 1.5 seconds
00231     if ( activator->fly_sound_debounce_time < level.time ) {
00232         activator->fly_sound_debounce_time = level.time + 1500;
00233         G_Sound( activator, CHAN_AUTO, self->noise_index );
00234     }
00235 }
00236 
00237 /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad
00238 Pushes the activator in the direction.of angle, or towards a target apex.
00239 "speed"     defaults to 1000
00240 if "bouncepad", play bounce noise instead of windfly
00241 */
00242 void SP_target_push( gentity_t *self ) {
00243     if (!self->speed) {
00244         self->speed = 1000;
00245     }
00246     G_SetMovedir (self->s.angles, self->s.origin2);
00247     VectorScale (self->s.origin2, self->speed, self->s.origin2);
00248 
00249     if ( self->spawnflags & 1 ) {
00250         self->noise_index = G_SoundIndex("sound/world/jumppad.wav");
00251     } else {
00252         self->noise_index = G_SoundIndex("sound/misc/windfly.wav");
00253     }
00254     if ( self->target ) {
00255         VectorCopy( self->s.origin, self->r.absmin );
00256         VectorCopy( self->s.origin, self->r.absmax );
00257         self->think = AimAtTarget;
00258         self->nextthink = level.time + FRAMETIME;
00259     }
00260     self->use = Use_target_push;
00261 }
00262 
00263 /*
00264 ==============================================================================
00265 
00266 trigger_teleport
00267 
00268 ==============================================================================
00269 */
00270 
00271 void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
00272     gentity_t   *dest;
00273 
00274     if ( !other->client ) {
00275         return;
00276     }
00277     if ( other->client->ps.pm_type == PM_DEAD ) {
00278         return;
00279     }
00280     // Spectators only?
00281     if ( ( self->spawnflags & 1 ) && 
00282         other->client->sess.sessionTeam != TEAM_SPECTATOR ) {
00283         return;
00284     }
00285 
00286 
00287     dest =  G_PickTarget( self->target );
00288     if (!dest) {
00289         G_Printf ("Couldn't find teleporter destination\n");
00290         return;
00291     }
00292 
00293     TeleportPlayer( other, dest->s.origin, dest->s.angles );
00294 }
00295 
00296 
00297 /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR
00298 Allows client side prediction of teleportation events.
00299 Must point at a target_position, which will be the teleport destination.
00300 
00301 If spectator is set, only spectators can use this teleport
00302 Spectator teleporters are not normally placed in the editor, but are created
00303 automatically near doors to allow spectators to move through them
00304 */
00305 void SP_trigger_teleport( gentity_t *self ) {
00306     InitTrigger (self);
00307 
00308     // unlike other triggers, we need to send this one to the client
00309     // unless is a spectator trigger
00310     if ( self->spawnflags & 1 ) {
00311         self->r.svFlags |= SVF_NOCLIENT;
00312     } else {
00313         self->r.svFlags &= ~SVF_NOCLIENT;
00314     }
00315 
00316     // make sure the client precaches this sound
00317     G_SoundIndex("sound/world/jumppad.wav");
00318 
00319     self->s.eType = ET_TELEPORT_TRIGGER;
00320     self->touch = trigger_teleporter_touch;
00321 
00322     trap_LinkEntity (self);
00323 }
00324 
00325 
00326 /*
00327 ==============================================================================
00328 
00329 trigger_hurt
00330 
00331 ==============================================================================
00332 */
00333 
00334 /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF - SILENT NO_PROTECTION SLOW
00335 Any entity that touches this will be hurt.
00336 It does dmg points of damage each server frame
00337 Targeting the trigger will toggle its on / off state.
00338 
00339 SILENT          supresses playing the sound
00340 SLOW            changes the damage rate to once per second
00341 NO_PROTECTION   *nothing* stops the damage
00342 
00343 "dmg"           default 5 (whole numbers only)
00344 
00345 */
00346 void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
00347     if ( self->r.linked ) {
00348         trap_UnlinkEntity( self );
00349     } else {
00350         trap_LinkEntity( self );
00351     }
00352 }
00353 
00354 void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
00355     int     dflags;
00356 
00357     if ( !other->takedamage ) {
00358         return;
00359     }
00360 
00361     if ( self->timestamp > level.time ) {
00362         return;
00363     }
00364 
00365     if ( self->spawnflags & 16 ) {
00366         self->timestamp = level.time + 1000;
00367     } else {
00368         self->timestamp = level.time + FRAMETIME;
00369     }
00370 
00371     // play sound
00372     if ( !(self->spawnflags & 4) ) {
00373         G_Sound( other, CHAN_AUTO, self->noise_index );
00374     }
00375 
00376     if (self->spawnflags & 8)
00377         dflags = DAMAGE_NO_PROTECTION;
00378     else
00379         dflags = 0;
00380     G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT);
00381 }
00382 
00383 void SP_trigger_hurt( gentity_t *self ) {
00384     InitTrigger (self);
00385 
00386     self->noise_index = G_SoundIndex( "sound/world/electro.wav" );
00387     self->touch = hurt_touch;
00388 
00389     if ( !self->damage ) {
00390         self->damage = 5;
00391     }
00392 
00393     self->r.contents = CONTENTS_TRIGGER;
00394 
00395     if ( self->spawnflags & 2 ) {
00396         self->use = hurt_use;
00397     }
00398 
00399     // link in to the world if starting active
00400     if ( ! (self->spawnflags & 1) ) {
00401         trap_LinkEntity (self);
00402     }
00403 }
00404 
00405 
00406 /*
00407 ==============================================================================
00408 
00409 timer
00410 
00411 ==============================================================================
00412 */
00413 
00414 
00415 /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
00416 This should be renamed trigger_timer...
00417 Repeatedly fires its targets.
00418 Can be turned on or off by using.
00419 
00420 "wait"          base time between triggering all targets, default is 1
00421 "random"        wait variance, default is 0
00422 so, the basic time between firing is a random time between
00423 (wait - random) and (wait + random)
00424 
00425 */
00426 void func_timer_think( gentity_t *self ) {
00427     G_UseTargets (self, self->activator);
00428     // set time before next firing
00429     self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random );
00430 }
00431 
00432 void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
00433     self->activator = activator;
00434 
00435     // if on, turn it off
00436     if ( self->nextthink ) {
00437         self->nextthink = 0;
00438         return;
00439     }
00440 
00441     // turn it on
00442     func_timer_think (self);
00443 }
00444 
00445 void SP_func_timer( gentity_t *self ) {
00446     G_SpawnFloat( "random", "1", &self->random);
00447     G_SpawnFloat( "wait", "1", &self->wait );
00448 
00449     self->use = func_timer_use;
00450     self->think = func_timer_think;
00451 
00452     if ( self->random >= self->wait ) {
00453         self->random = self->wait - FRAMETIME;
00454         G_Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) );
00455     }
00456 
00457     if ( self->spawnflags & 1 ) {
00458         self->nextthink = level.time + FRAMETIME;
00459         self->activator = self;
00460     }
00461 
00462     self->r.svFlags = SVF_NOCLIENT;
00463 }
00464 
00465 

Generated on Thu Aug 25 12:37:33 2005 for Quake III Arena by  doxygen 1.3.9.1