home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_trigger.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  11.3 KB  |  448 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. #include "g_local.h"
  4.  
  5.  
  6. void InitTrigger( gentity_t *self ) {
  7.     if (!VectorCompare (self->s.angles, vec3_origin))
  8.         G_SetMovedir (self->s.angles, self->movedir);
  9.  
  10.     trap_SetBrushModel( self, self->model );
  11.     self->r.contents = CONTENTS_TRIGGER;        // replaces the -1 from trap_SetBrushModel
  12.     self->r.svFlags = SVF_NOCLIENT;
  13. }
  14.  
  15.  
  16. // the wait time has passed, so set back up for another activation
  17. void multi_wait( gentity_t *ent ) {
  18.     ent->nextthink = 0;
  19. }
  20.  
  21.  
  22. // the trigger was just activated
  23. // ent->activator should be set to the activator so it can be held through a delay
  24. // so wait for the delay time before firing
  25. void multi_trigger( gentity_t *ent, gentity_t *activator ) {
  26.     ent->activator = activator;
  27.     if ( ent->nextthink ) {
  28.         return;        // can't retrigger until the wait is over
  29.     }
  30.  
  31.     G_UseTargets (ent, ent->activator);
  32.  
  33.     if ( ent->wait > 0 ) {
  34.         ent->think = multi_wait;
  35.         ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
  36.     } else {
  37.         // we can't just remove (self) here, because this is a touch function
  38.         // called while looping through area links...
  39.         ent->touch = 0;
  40.         ent->nextthink = level.time + FRAMETIME;
  41.         ent->think = G_FreeEntity;
  42.     }
  43. }
  44.  
  45. void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
  46.     multi_trigger( ent, activator );
  47. }
  48.  
  49. void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) {
  50.     if( !other->client ) {
  51.         return;
  52.     }
  53.     multi_trigger( self, other );
  54. }
  55.  
  56. /*QUAKED trigger_multiple (.5 .5 .5) ?
  57. "wait" : Seconds between triggerings, 0.5 default, -1 = one time only.
  58. "random"    wait variance, default is 0
  59. Variable sized repeatable trigger.  Must be targeted at one or more entities.
  60. so, the basic time between firing is a random time between
  61. (wait - random) and (wait + random)
  62. */
  63. void SP_trigger_multiple( gentity_t *ent ) {
  64.     G_SpawnFloat( "wait", "0.5", &ent->wait );
  65.     G_SpawnFloat( "random", "0", &ent->random );
  66.  
  67.     if ( ent->random >= ent->wait && ent->wait >= 0 ) {
  68.         ent->random = ent->wait - FRAMETIME;
  69.         G_Printf( "trigger_multiple has random >= wait\n" );
  70.     }
  71.  
  72.     ent->touch = Touch_Multi;
  73.     ent->use = Use_Multi;
  74.  
  75.     InitTrigger( ent );
  76.     trap_LinkEntity (ent);
  77. }
  78.  
  79.  
  80.  
  81. /*
  82. ==============================================================================
  83.  
  84. trigger_always
  85.  
  86. ==============================================================================
  87. */
  88.  
  89. void trigger_always_think( gentity_t *ent ) {
  90.     G_UseTargets(ent, ent);
  91.     G_FreeEntity( ent );
  92. }
  93.  
  94. /*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
  95. This trigger will always fire.  It is activated by the world.
  96. */
  97. void SP_trigger_always (gentity_t *ent) {
  98.     // we must have some delay to make sure our use targets are present
  99.     ent->nextthink = level.time + 300;
  100.     ent->think = trigger_always_think;
  101. }
  102.  
  103.  
  104. /*
  105. ==============================================================================
  106.  
  107. trigger_push
  108.  
  109. ==============================================================================
  110. */
  111.  
  112. void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
  113.     if ( !other->client ) {
  114.         return;
  115.     }
  116.  
  117.     if ( other->client->ps.pm_type != PM_NORMAL ) {
  118.         return;
  119.     }
  120.     if ( other->client->ps.powerups[PW_FLIGHT] ) {
  121.         return;
  122.     }
  123.  
  124.     if (other->client && other->client->hook)
  125.         Weapon_HookFree(other->client->hook);
  126.  
  127.     if ( other->client->ps.velocity[2] < 100 ) {
  128.         // don't play the event sound again if we are in a fat trigger
  129.         G_AddPredictableEvent( other, EV_JUMP_PAD, 0 );
  130.     }
  131.     VectorCopy (self->s.origin2, other->client->ps.velocity);
  132. }
  133.  
  134.  
  135. /*
  136. =================
  137. AimAtTarget
  138.  
  139. Calculate origin2 so the target apogee will be hit
  140. =================
  141. */
  142. void AimAtTarget( gentity_t *self ) {
  143.     gentity_t    *ent;
  144.     vec3_t        origin;
  145.     float        height, gravity, time, forward;
  146.     float        dist;
  147.  
  148.     VectorAdd( self->r.absmin, self->r.absmax, origin );
  149.     VectorScale ( origin, 0.5, origin );
  150.  
  151.     ent = G_PickTarget( self->target );
  152.     if ( !ent ) {
  153.         G_FreeEntity( self );
  154.         return;
  155.     }
  156.  
  157.     height = ent->s.origin[2] - origin[2];
  158.     gravity = g_gravity.value;
  159.     time = sqrt( height / ( .5 * gravity ) );
  160.     if ( !time ) {
  161.         G_FreeEntity( self );
  162.         return;
  163.     }
  164.  
  165.     // set s.origin2 to the push velocity
  166.     VectorSubtract ( ent->s.origin, origin, self->s.origin2 );
  167.     self->s.origin2[2] = 0;
  168.     dist = VectorNormalize( self->s.origin2);
  169.  
  170.     forward = dist / time;
  171.     VectorScale( self->s.origin2, forward, self->s.origin2 );
  172.  
  173.     self->s.origin2[2] = time * gravity;
  174. }
  175.  
  176.  
  177. /*QUAKED trigger_push (.5 .5 .5) ?
  178. Must point at a target_position, which will be the apex of the leap.
  179. This will be client side predicted, unlike target_push
  180. */
  181. void SP_trigger_push( gentity_t *self ) {
  182.     InitTrigger (self);
  183.  
  184.     // unlike other triggers, we need to send this one to the client
  185.     self->r.svFlags &= ~SVF_NOCLIENT;
  186.  
  187.     // make sure the client precaches this sound
  188.     G_SoundIndex("sound/world/jumppad.wav");
  189.  
  190.     self->s.eType = ET_PUSH_TRIGGER;
  191.     self->touch = trigger_push_touch;
  192.     self->think = AimAtTarget;
  193.     self->nextthink = level.time + FRAMETIME;
  194.     trap_LinkEntity (self);
  195. }
  196.  
  197.  
  198. void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  199.     if ( !activator->client ) {
  200.         return;
  201.     }
  202.  
  203.     if ( activator->client->ps.pm_type != PM_NORMAL ) {
  204.         return;
  205.     }
  206.     if ( activator->client->ps.powerups[PW_FLIGHT] ) {
  207.         return;
  208.     }
  209.  
  210.     VectorCopy (self->s.origin2, activator->client->ps.velocity);
  211.  
  212.     // play fly sound every 1.5 seconds
  213.     if ( activator->fly_sound_debounce_time < level.time ) {
  214.         activator->fly_sound_debounce_time = level.time + 1500;
  215.         G_Sound( activator, CHAN_AUTO, self->noise_index );
  216.     }
  217. }
  218.  
  219. /*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad
  220. Pushes the activator in the direction.of angle, or towards a target apex.
  221. "speed"        defaults to 1000
  222. if "bouncepad", play bounce noise instead of windfly
  223. */
  224. void SP_target_push( gentity_t *self ) {
  225.     if (!self->speed) {
  226.         self->speed = 1000;
  227.     }
  228.     G_SetMovedir (self->s.angles, self->s.origin2);
  229.     VectorScale (self->s.origin2, self->speed, self->s.origin2);
  230.  
  231.     if ( self->spawnflags & 1 ) {
  232.         self->noise_index = G_SoundIndex("sound/world/jumppad.wav");
  233.     } else {
  234.         self->noise_index = G_SoundIndex("sound/misc/windfly.wav");
  235.     }
  236.     if ( self->target ) {
  237.         VectorCopy( self->s.origin, self->r.absmin );
  238.         VectorCopy( self->s.origin, self->r.absmax );
  239.         self->think = AimAtTarget;
  240.         self->nextthink = level.time + FRAMETIME;
  241.     }
  242.     self->use = Use_target_push;
  243. }
  244.  
  245. /*
  246. ==============================================================================
  247.  
  248. trigger_teleport
  249.  
  250. ==============================================================================
  251. */
  252.  
  253. void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
  254.     gentity_t    *dest;
  255.  
  256.     if ( !other->client ) {
  257.         return;
  258.     }
  259.     if ( other->client->ps.pm_type == PM_DEAD ) {
  260.         return;
  261.     }
  262.     // Spectators only?
  263.     if ( ( self->spawnflags & 1 ) && 
  264.         other->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  265.         return;
  266.     }
  267.  
  268.  
  269.     dest =     G_PickTarget( self->target );
  270.     if (!dest) {
  271.         G_Printf ("Couldn't find teleporter destination\n");
  272.         return;
  273.     }
  274.  
  275.     TeleportPlayer( other, dest->s.origin, dest->s.angles );
  276. }
  277.  
  278.  
  279. /*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR
  280. Allows client side prediction of teleportation events.
  281. Must point at a target_position, which will be the teleport destination.
  282.  
  283. If spectator is set, only spectators can use this teleport
  284. Spectator teleporters are not normally placed in the editor, but are created
  285. automatically near doors to allow spectators to move through them
  286. */
  287. void SP_trigger_teleport( gentity_t *self ) {
  288.     InitTrigger (self);
  289.  
  290.     // unlike other triggers, we need to send this one to the client
  291.     // unless is a spectator trigger
  292.     if ( self->spawnflags & 1 ) {
  293.         self->r.svFlags |= SVF_NOCLIENT;
  294.     } else {
  295.         self->r.svFlags &= ~SVF_NOCLIENT;
  296.     }
  297.  
  298.     // make sure the client precaches this sound
  299.     G_SoundIndex("sound/world/jumppad.wav");
  300.  
  301.     self->s.eType = ET_TELEPORT_TRIGGER;
  302.     self->touch = trigger_teleporter_touch;
  303.  
  304.     trap_LinkEntity (self);
  305. }
  306.  
  307.  
  308. /*
  309. ==============================================================================
  310.  
  311. trigger_hurt
  312.  
  313. ==============================================================================
  314. */
  315.  
  316. /*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF - SILENT NO_PROTECTION SLOW
  317. Any entity that touches this will be hurt.
  318. It does dmg points of damage each server frame
  319. Targeting the trigger will toggle its on / off state.
  320.  
  321. SILENT            supresses playing the sound
  322. SLOW            changes the damage rate to once per second
  323. NO_PROTECTION    *nothing* stops the damage
  324.  
  325. "dmg"            default 5 (whole numbers only)
  326.  
  327. */
  328. void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  329.     if ( self->r.linked ) {
  330.         trap_UnlinkEntity( self );
  331.     } else {
  332.         trap_LinkEntity( self );
  333.     }
  334. }
  335.  
  336. void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
  337.     int        dflags;
  338.  
  339.     if ( !other->takedamage ) {
  340.         return;
  341.     }
  342.  
  343.     if ( self->timestamp > level.time ) {
  344.         return;
  345.     }
  346.  
  347.     if ( self->spawnflags & 16 ) {
  348.         self->timestamp = level.time + 1000;
  349.     } else {
  350.         self->timestamp = level.time + FRAMETIME;
  351.     }
  352.  
  353.     // play sound
  354.     if ( !(self->spawnflags & 4) ) {
  355.         G_Sound( other, CHAN_AUTO, self->noise_index );
  356.     }
  357.  
  358.     if (self->spawnflags & 8)
  359.         dflags = DAMAGE_NO_PROTECTION;
  360.     else
  361.         dflags = 0;
  362.     G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT);
  363. }
  364.  
  365. void SP_trigger_hurt( gentity_t *self ) {
  366.     InitTrigger (self);
  367.  
  368.     self->noise_index = G_SoundIndex( "sound/world/electro.wav" );
  369.     self->touch = hurt_touch;
  370.  
  371.     if ( !self->damage ) {
  372.         self->damage = 5;
  373.     }
  374.  
  375.     self->r.contents = CONTENTS_TRIGGER;
  376.  
  377.     if ( self->spawnflags & 2 ) {
  378.         self->use = hurt_use;
  379.     }
  380.  
  381.     // link in to the world if starting active
  382.     if ( ! (self->spawnflags & 1) ) {
  383.         trap_LinkEntity (self);
  384.     }
  385. }
  386.  
  387.  
  388. /*
  389. ==============================================================================
  390.  
  391. timer
  392.  
  393. ==============================================================================
  394. */
  395.  
  396.  
  397. /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
  398. This should be renamed trigger_timer...
  399. Repeatedly fires its targets.
  400. Can be turned on or off by using.
  401.  
  402. "wait"            base time between triggering all targets, default is 1
  403. "random"        wait variance, default is 0
  404. so, the basic time between firing is a random time between
  405. (wait - random) and (wait + random)
  406.  
  407. */
  408. void func_timer_think( gentity_t *self ) {
  409.     G_UseTargets (self, self->activator);
  410.     // set time before next firing
  411.     self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random );
  412. }
  413.  
  414. void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
  415.     self->activator = activator;
  416.  
  417.     // if on, turn it off
  418.     if ( self->nextthink ) {
  419.         self->nextthink = 0;
  420.         return;
  421.     }
  422.  
  423.     // turn it on
  424.     func_timer_think (self);
  425. }
  426.  
  427. void SP_func_timer( gentity_t *self ) {
  428.     G_SpawnFloat( "random", "1", &self->random);
  429.     G_SpawnFloat( "wait", "1", &self->wait );
  430.  
  431.     self->use = func_timer_use;
  432.     self->think = func_timer_think;
  433.  
  434.     if ( self->random >= self->wait ) {
  435.         self->random = self->wait - FRAMETIME;
  436.         G_Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) );
  437.     }
  438.  
  439.     if ( self->spawnflags & 1 ) {
  440.         self->nextthink = level.time + FRAMETIME;
  441.         self->activator = self;
  442.     }
  443.  
  444.     self->r.svFlags = SVF_NOCLIENT;
  445. }
  446.  
  447.  
  448.