2013-04-04 14:52:42 +00:00
|
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
|
|
//
|
|
|
|
#include "g_local.h"
|
|
|
|
|
|
|
|
int gTrigFallSound;
|
|
|
|
|
|
|
|
void InitTrigger( gentity_t *self ) {
|
|
|
|
if (!VectorCompare (self->s.angles, vec3_origin))
|
|
|
|
G_SetMovedir (self->s.angles, self->movedir);
|
|
|
|
|
|
|
|
trap_SetBrushModel( self, self->model );
|
|
|
|
self->r.contents = CONTENTS_TRIGGER; // replaces the -1 from trap_SetBrushModel
|
|
|
|
self->r.svFlags = SVF_NOCLIENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// the wait time has passed, so set back up for another activation
|
|
|
|
void multi_wait( gentity_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( gentity_t *ent, gentity_t *activator ) {
|
|
|
|
gentity_t *rofftarget = NULL, *testent = NULL;
|
|
|
|
gentity_t *te;
|
|
|
|
int i = MAX_CLIENTS;
|
|
|
|
|
|
|
|
if (ent->teamnodmg &&
|
|
|
|
activator && activator->client &&
|
|
|
|
ent->teamnodmg == activator->client->sess.sessionTeam &&
|
|
|
|
!g_ff_objectives.integer)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ent->spawnflags & 1)
|
|
|
|
{
|
|
|
|
if (!activator->client)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(activator->client->pers.cmd.buttons & BUTTON_USE))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ent->activator = activator;
|
|
|
|
if ( ent->nextthink ) {
|
|
|
|
return; // can't retrigger until the wait is over
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( activator->client ) {
|
|
|
|
if ( ( ent->spawnflags & 2 ) &&
|
|
|
|
activator->client->sess.sessionTeam != TEAM_RED ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( ( ent->spawnflags & 4 ) &&
|
|
|
|
activator->client->sess.sessionTeam != TEAM_BLUE ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
G_UseTargets (ent, ent->activator);
|
|
|
|
|
|
|
|
if (ent->roffname && ent->roffid != -1)
|
|
|
|
{
|
|
|
|
if (ent->rofftarget)
|
|
|
|
{
|
|
|
|
while (i < MAX_GENTITIES)
|
|
|
|
{
|
|
|
|
testent = &g_entities[i];
|
|
|
|
|
|
|
|
if (testent && testent->targetname && strcmp(testent->targetname, ent->rofftarget) == 0)
|
|
|
|
{
|
|
|
|
rofftarget = testent;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rofftarget = activator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rofftarget)
|
|
|
|
{
|
|
|
|
trap_ROFF_Play(rofftarget->s.number, ent->roffid, qfalse);
|
|
|
|
|
|
|
|
//Play it at the same time on the client, so that we can catch client-side notetrack events and not have to send
|
|
|
|
//them over from the server (this wouldn't work for things like effects due to lack of ability to precache them
|
|
|
|
//on the server)
|
|
|
|
|
|
|
|
//remember the entity's original position in case of a server-side "loop" notetrack
|
|
|
|
VectorCopy(rofftarget->s.pos.trBase, rofftarget->s.origin2);
|
|
|
|
VectorCopy(rofftarget->s.apos.trBase, rofftarget->s.angles2);
|
|
|
|
|
|
|
|
te = G_TempEntity( rofftarget->s.pos.trBase, EV_PLAY_ROFF );
|
|
|
|
te->s.eventParm = ent->roffid;
|
|
|
|
te->s.weapon = rofftarget->s.number;
|
|
|
|
te->s.trickedentindex = 0;
|
|
|
|
|
|
|
|
//But.. this may not produce desired results for clients who connect while a ROFF is playing.
|
|
|
|
|
|
|
|
rofftarget->roffid = ent->roffid; //let this entity know the most recent ROFF played on him
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ent->wait > 0 ) {
|
|
|
|
ent->think = multi_wait;
|
|
|
|
ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000;
|
|
|
|
} else {
|
|
|
|
// we can't just remove (self) here, because this is a touch function
|
|
|
|
// called while looping through area links...
|
|
|
|
ent->touch = 0;
|
|
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
|
|
ent->think = G_FreeEntity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
|
|
|
|
multi_trigger( ent, activator );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
|
|
if( !other->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
multi_trigger( self, other );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED trigger_multiple (.5 .5 .5) ? USE_BUTTON RED_ONLY BLUE_ONLY
|
|
|
|
USE_BUTTON - Won't fire unless player is in it and pressing use button (in addition to any other conditions)
|
|
|
|
RED_ONLY - Only red team can use
|
|
|
|
BLUE_ONLY - Only blue team can use
|
|
|
|
|
|
|
|
"roffname" If set, will play a roff upon activation
|
|
|
|
"rofftarget" If set with roffname, will activate the roff an entity with
|
|
|
|
this as its targetname. Otherwise uses roff on activating entity.
|
|
|
|
"wait" : Seconds between triggerings, 0.5 default, -1 = one time only.
|
|
|
|
"random" wait variance, default is 0
|
|
|
|
Variable sized repeatable trigger. Must be targeted at one or more entities.
|
|
|
|
so, the basic time between firing is a random time between
|
|
|
|
(wait - random) and (wait + random)
|
|
|
|
*/
|
|
|
|
void SP_trigger_multiple( gentity_t *ent ) {
|
|
|
|
if (ent->roffname)
|
|
|
|
{
|
|
|
|
ent->roffid = trap_ROFF_Cache(ent->roffname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ent->roffid = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_SpawnFloat( "wait", "0.5", &ent->wait );
|
|
|
|
G_SpawnFloat( "random", "0", &ent->random );
|
|
|
|
|
|
|
|
if ( ent->random >= ent->wait && ent->wait >= 0 ) {
|
|
|
|
ent->random = ent->wait - FRAMETIME;
|
|
|
|
G_Printf( "trigger_multiple has random >= wait\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
ent->touch = Touch_Multi;
|
|
|
|
ent->use = Use_Multi;
|
|
|
|
|
|
|
|
InitTrigger( ent );
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
trigger_always
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void trigger_always_think( gentity_t *ent ) {
|
|
|
|
G_UseTargets(ent, ent);
|
|
|
|
G_FreeEntity( ent );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*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 (gentity_t *ent) {
|
|
|
|
// we must have some delay to make sure our use targets are present
|
|
|
|
ent->nextthink = level.time + 300;
|
|
|
|
ent->think = trigger_always_think;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
trigger_push
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
|
|
|
|
|
|
if ( !other->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BG_TouchJumpPad( &other->client->ps, &self->s );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
AimAtTarget
|
|
|
|
|
|
|
|
Calculate origin2 so the target apogee will be hit
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
void AimAtTarget( gentity_t *self ) {
|
|
|
|
gentity_t *ent;
|
|
|
|
vec3_t origin;
|
|
|
|
float height, gravity, time, forward;
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
VectorAdd( self->r.absmin, self->r.absmax, origin );
|
|
|
|
VectorScale ( origin, 0.5, origin );
|
|
|
|
|
|
|
|
ent = G_PickTarget( self->target );
|
|
|
|
if ( !ent ) {
|
|
|
|
G_FreeEntity( self );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
height = ent->s.origin[2] - origin[2];
|
|
|
|
gravity = g_gravity.value;
|
|
|
|
time = sqrt( height / ( .5 * gravity ) );
|
|
|
|
if ( !time ) {
|
|
|
|
G_FreeEntity( self );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set s.origin2 to the push velocity
|
|
|
|
VectorSubtract ( ent->s.origin, origin, self->s.origin2 );
|
|
|
|
self->s.origin2[2] = 0;
|
|
|
|
dist = VectorNormalize( self->s.origin2);
|
|
|
|
|
|
|
|
forward = dist / time;
|
|
|
|
VectorScale( self->s.origin2, forward, self->s.origin2 );
|
|
|
|
|
|
|
|
self->s.origin2[2] = time * gravity;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*QUAKED trigger_push (.5 .5 .5) ?
|
|
|
|
Must point at a target_position, which will be the apex of the leap.
|
|
|
|
This will be client side predicted, unlike target_push
|
|
|
|
*/
|
|
|
|
void SP_trigger_push( gentity_t *self ) {
|
|
|
|
InitTrigger (self);
|
|
|
|
|
|
|
|
// unlike other triggers, we need to send this one to the client
|
|
|
|
self->r.svFlags &= ~SVF_NOCLIENT;
|
|
|
|
|
|
|
|
// make sure the client precaches this sound
|
|
|
|
G_SoundIndex("sound/weapons/force/jump.wav");
|
|
|
|
|
|
|
|
self->s.eType = ET_PUSH_TRIGGER;
|
|
|
|
self->touch = trigger_push_touch;
|
|
|
|
self->think = AimAtTarget;
|
|
|
|
self->nextthink = level.time + FRAMETIME;
|
|
|
|
trap_LinkEntity (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) {
|
|
|
|
if ( !activator->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( activator->client->ps.pm_type != PM_NORMAL && activator->client->ps.pm_type != PM_FLOAT ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy (self->s.origin2, activator->client->ps.velocity);
|
|
|
|
|
|
|
|
// play fly sound every 1.5 seconds
|
|
|
|
if ( activator->fly_sound_debounce_time < level.time ) {
|
|
|
|
activator->fly_sound_debounce_time = level.time + 1500;
|
2013-04-04 21:05:53 +00:00
|
|
|
if (self->noise_index)
|
|
|
|
{
|
|
|
|
G_Sound( activator, CHAN_AUTO, self->noise_index );
|
|
|
|
}
|
2013-04-04 14:52:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad
|
|
|
|
Pushes the activator in the direction.of angle, or towards a target apex.
|
|
|
|
"speed" defaults to 1000
|
2013-04-04 21:05:53 +00:00
|
|
|
if "bouncepad", play bounce noise instead of none
|
2013-04-04 14:52:42 +00:00
|
|
|
*/
|
|
|
|
void SP_target_push( gentity_t *self ) {
|
|
|
|
if (!self->speed) {
|
|
|
|
self->speed = 1000;
|
|
|
|
}
|
|
|
|
G_SetMovedir (self->s.angles, self->s.origin2);
|
|
|
|
VectorScale (self->s.origin2, self->speed, self->s.origin2);
|
|
|
|
|
|
|
|
if ( self->spawnflags & 1 ) {
|
|
|
|
self->noise_index = G_SoundIndex("sound/weapons/force/jump.wav");
|
|
|
|
} else {
|
|
|
|
self->noise_index = 0; //G_SoundIndex("sound/misc/windfly.wav");
|
|
|
|
}
|
|
|
|
if ( self->target ) {
|
|
|
|
VectorCopy( self->s.origin, self->r.absmin );
|
|
|
|
VectorCopy( self->s.origin, self->r.absmax );
|
|
|
|
self->think = AimAtTarget;
|
|
|
|
self->nextthink = level.time + FRAMETIME;
|
|
|
|
}
|
|
|
|
self->use = Use_target_push;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
trigger_teleport
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
|
|
gentity_t *dest;
|
|
|
|
|
|
|
|
if ( !other->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( other->client->ps.pm_type == PM_DEAD ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Spectators only?
|
|
|
|
if ( ( self->spawnflags & 1 ) &&
|
|
|
|
other->client->sess.sessionTeam != TEAM_SPECTATOR ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dest = G_PickTarget( self->target );
|
|
|
|
if (!dest) {
|
|
|
|
G_Printf ("Couldn't find teleporter destination\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TeleportPlayer( other, dest->s.origin, dest->s.angles );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR
|
|
|
|
Allows client side prediction of teleportation events.
|
|
|
|
Must point at a target_position, which will be the teleport destination.
|
|
|
|
|
|
|
|
If spectator is set, only spectators can use this teleport
|
|
|
|
Spectator teleporters are not normally placed in the editor, but are created
|
|
|
|
automatically near doors to allow spectators to move through them
|
|
|
|
*/
|
|
|
|
void SP_trigger_teleport( gentity_t *self ) {
|
|
|
|
InitTrigger (self);
|
|
|
|
|
|
|
|
// unlike other triggers, we need to send this one to the client
|
|
|
|
// unless is a spectator trigger
|
|
|
|
if ( self->spawnflags & 1 ) {
|
|
|
|
self->r.svFlags |= SVF_NOCLIENT;
|
|
|
|
} else {
|
|
|
|
self->r.svFlags &= ~SVF_NOCLIENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure the client precaches this sound
|
|
|
|
G_SoundIndex("sound/weapons/force/speed.wav");
|
|
|
|
|
|
|
|
self->s.eType = ET_TELEPORT_TRIGGER;
|
|
|
|
self->touch = trigger_teleporter_touch;
|
|
|
|
|
|
|
|
trap_LinkEntity (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
trigger_hurt
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF CAN_TARGET SILENT NO_PROTECTION SLOW
|
|
|
|
Any entity that touches this will be hurt.
|
|
|
|
It does dmg points of damage each server frame
|
|
|
|
Targeting the trigger will toggle its on / off state.
|
|
|
|
|
|
|
|
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)
|
|
|
|
If dmg is set to -1 this brush will use the fade-kill method
|
|
|
|
|
|
|
|
*/
|
|
|
|
void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
|
2013-04-04 18:02:27 +00:00
|
|
|
if (activator && activator->inuse && activator->client)
|
|
|
|
{
|
|
|
|
self->activator = activator;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->activator = NULL;
|
|
|
|
}
|
|
|
|
|
2013-04-04 14:52:42 +00:00
|
|
|
if ( self->r.linked ) {
|
|
|
|
trap_UnlinkEntity( self );
|
|
|
|
} else {
|
|
|
|
trap_LinkEntity( self );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
|
|
int dflags;
|
|
|
|
|
|
|
|
if ( !other->takedamage ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( self->timestamp > level.time ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->damage == -1 && other && other->client && other->health < 1)
|
|
|
|
{
|
|
|
|
other->client->ps.fallingToDeath = 0;
|
|
|
|
respawn(other);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->damage == -1 && other && other->client && other->client->ps.fallingToDeath)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( self->spawnflags & 16 ) {
|
|
|
|
self->timestamp = level.time + 1000;
|
|
|
|
} else {
|
|
|
|
self->timestamp = level.time + FRAMETIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
// play sound
|
2013-04-04 18:24:26 +00:00
|
|
|
if ( !(self->spawnflags & 4) && self->damage != -1 ) {
|
2013-04-04 14:52:42 +00:00
|
|
|
G_Sound( other, CHAN_AUTO, self->noise_index );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->spawnflags & 8)
|
|
|
|
dflags = DAMAGE_NO_PROTECTION;
|
|
|
|
else
|
|
|
|
dflags = 0;
|
|
|
|
|
|
|
|
if (self->damage == -1 && other && other->client)
|
|
|
|
{
|
|
|
|
if (other->client->ps.otherKillerTime > level.time)
|
|
|
|
{ //we're as good as dead, so if someone pushed us into this then remember them
|
|
|
|
other->client->ps.otherKillerTime = level.time + 20000;
|
|
|
|
other->client->ps.otherKillerDebounceTime = level.time + 10000;
|
|
|
|
}
|
|
|
|
other->client->ps.fallingToDeath = level.time;
|
|
|
|
|
|
|
|
self->timestamp = 0; //do not ignore others
|
|
|
|
G_EntitySound(other, CHAN_VOICE, G_SoundIndex("*falling1.wav"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-04-04 18:24:26 +00:00
|
|
|
int dmg = self->damage;
|
|
|
|
|
|
|
|
if (dmg == -1)
|
|
|
|
{ //so fall-to-blackness triggers destroy evertyhing
|
|
|
|
dmg = 99999;
|
|
|
|
self->timestamp = 0;
|
|
|
|
}
|
2013-04-04 18:02:27 +00:00
|
|
|
if (self->activator && self->activator->inuse && self->activator->client)
|
|
|
|
{
|
2013-04-04 18:24:26 +00:00
|
|
|
G_Damage (other, self->activator, self->activator, NULL, NULL, dmg, dflags|DAMAGE_NO_PROTECTION, MOD_TRIGGER_HURT);
|
2013-04-04 18:02:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-04-04 18:24:26 +00:00
|
|
|
G_Damage (other, self, self, NULL, NULL, dmg, dflags|DAMAGE_NO_PROTECTION, MOD_TRIGGER_HURT);
|
2013-04-04 18:02:27 +00:00
|
|
|
}
|
2013-04-04 14:52:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SP_trigger_hurt( gentity_t *self ) {
|
|
|
|
InitTrigger (self);
|
|
|
|
|
|
|
|
gTrigFallSound = G_SoundIndex("*falling1.wav");
|
|
|
|
|
|
|
|
self->noise_index = G_SoundIndex( "sound/weapons/force/speed.wav" );
|
|
|
|
self->touch = hurt_touch;
|
|
|
|
|
|
|
|
if ( !self->damage ) {
|
|
|
|
self->damage = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->r.contents = CONTENTS_TRIGGER;
|
|
|
|
|
|
|
|
if ( self->spawnflags & 2 ) {
|
|
|
|
self->use = hurt_use;
|
|
|
|
}
|
|
|
|
|
|
|
|
// link in to the world if starting active
|
|
|
|
if ( ! (self->spawnflags & 1) ) {
|
|
|
|
trap_LinkEntity (self);
|
|
|
|
}
|
|
|
|
else if (self->r.linked)
|
|
|
|
{
|
|
|
|
trap_UnlinkEntity(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
timer
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
|
|
|
|
This should be renamed trigger_timer...
|
|
|
|
Repeatedly fires its targets.
|
|
|
|
Can be turned on or off by using.
|
|
|
|
|
|
|
|
"wait" base time between triggering all targets, default is 1
|
|
|
|
"random" wait variance, default is 0
|
|
|
|
so, the basic time between firing is a random time between
|
|
|
|
(wait - random) and (wait + random)
|
|
|
|
|
|
|
|
*/
|
|
|
|
void func_timer_think( gentity_t *self ) {
|
|
|
|
G_UseTargets (self, self->activator);
|
|
|
|
// set time before next firing
|
|
|
|
self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random );
|
|
|
|
}
|
|
|
|
|
|
|
|
void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
|
|
|
|
self->activator = activator;
|
|
|
|
|
|
|
|
// if on, turn it off
|
|
|
|
if ( self->nextthink ) {
|
|
|
|
self->nextthink = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// turn it on
|
|
|
|
func_timer_think (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SP_func_timer( gentity_t *self ) {
|
|
|
|
G_SpawnFloat( "random", "1", &self->random);
|
|
|
|
G_SpawnFloat( "wait", "1", &self->wait );
|
|
|
|
|
|
|
|
self->use = func_timer_use;
|
|
|
|
self->think = func_timer_think;
|
|
|
|
|
|
|
|
if ( self->random >= self->wait ) {
|
|
|
|
self->random = self->wait - FRAMETIME;
|
|
|
|
G_Printf( "func_timer at %s has random >= wait\n", vtos( self->s.origin ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( self->spawnflags & 1 ) {
|
|
|
|
self->nextthink = level.time + FRAMETIME;
|
|
|
|
self->activator = self;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->r.svFlags = SVF_NOCLIENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|