mirror of
https://github.com/ReactionQuake3/reaction.git
synced 2024-11-25 21:51:30 +00:00
683 lines
18 KiB
C
683 lines
18 KiB
C
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Log$
|
|
// Revision 1.33 2005/02/15 16:33:39 makro
|
|
// Tons of updates (entity tree attachment system, UI vectors)
|
|
//
|
|
// Revision 1.32 2004/03/13 01:17:32 makro
|
|
// no message
|
|
//
|
|
// Revision 1.31 2004/03/12 15:56:46 makro
|
|
// no message
|
|
//
|
|
// Revision 1.30 2003/09/18 00:05:06 makro
|
|
// Lens flares. Opendoor trigger_multiple fixes
|
|
//
|
|
// Revision 1.29 2003/09/16 23:25:32 makro
|
|
// trigger_multiple - new spawnflag, 3 new keys
|
|
//
|
|
// Revision 1.28 2003/09/08 19:19:20 makro
|
|
// New code for respawning entities in TP
|
|
//
|
|
// Revision 1.27 2003/07/30 16:05:46 makro
|
|
// no message
|
|
//
|
|
// Revision 1.26 2003/04/26 22:33:07 jbravo
|
|
// Wratted all calls to G_FreeEnt() to avoid crashing and provide debugging
|
|
//
|
|
// Revision 1.25 2003/03/22 20:29:26 jbravo
|
|
// wrapping linkent and unlinkent calls
|
|
//
|
|
// Revision 1.24 2002/07/22 06:32:27 niceass
|
|
// cleaned up the powerup code
|
|
//
|
|
// Revision 1.23 2002/06/16 20:06:14 jbravo
|
|
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
|
|
//
|
|
// Revision 1.22 2002/06/06 18:08:01 makro
|
|
// Removed pathtarget code for trigger_pushes for now
|
|
//
|
|
// Revision 1.21 2002/06/04 21:35:40 makro
|
|
// Updated trigger_push code
|
|
//
|
|
// Revision 1.20 2002/06/04 20:53:19 makro
|
|
// Trigger_pushes can now be teamed up
|
|
//
|
|
// Revision 1.19 2002/05/30 21:18:28 makro
|
|
// Bots should reload/bandage when roaming around
|
|
// Added "pathtarget" key to all the entities
|
|
//
|
|
// Revision 1.18 2002/05/29 13:49:26 makro
|
|
// Elevators/doors
|
|
//
|
|
// Revision 1.17 2002/05/23 18:37:50 makro
|
|
// Bots should crouch more often when they attack with a SSG
|
|
// Made this depend on skill. Also, elevator stuff
|
|
//
|
|
// Revision 1.16 2002/05/23 15:55:25 makro
|
|
// Elevators
|
|
//
|
|
// Revision 1.15 2002/05/22 04:19:45 blaze
|
|
// Sound entity changes as per Sze
|
|
//
|
|
// Revision 1.14 2002/05/18 14:52:16 makro
|
|
// Bot stuff. Other stuff. Just... stuff :p
|
|
//
|
|
// Revision 1.13 2002/05/16 06:57:54 makro
|
|
// Doors with health (again !), bot-only trigger_pushes
|
|
//
|
|
// Revision 1.12 2002/05/11 22:01:41 makro
|
|
// Trigger_hurt
|
|
//
|
|
// Revision 1.11 2002/05/11 17:59:49 makro
|
|
// Trigger_hurt update
|
|
//
|
|
// Revision 1.10 2002/05/11 00:38:47 blaze
|
|
// trigger_push and target_push default to no noise when the noise flag is not set.
|
|
//
|
|
// Revision 1.9 2002/05/05 15:18:03 makro
|
|
// Fixed some crash bugs. Bot stuff. Triggerable func_statics.
|
|
// Made flags only spawn in CTF mode
|
|
//
|
|
// Revision 1.8 2002/03/18 06:20:39 blaze
|
|
// noise tag will play sounds for trigger_push and target_push
|
|
//
|
|
// Revision 1.7 2002/01/11 19:48:30 jbravo
|
|
// Formatted the source in non DOS format.
|
|
//
|
|
// Revision 1.6 2001/12/31 16:28:42 jbravo
|
|
// I made a Booboo with the Log tag.
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
#include "g_local.h"
|
|
|
|
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;
|
|
}
|
|
|
|
/*QUAKED trigger_multiple (.5 .5 .5) ? DOOR
|
|
"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)
|
|
*/
|
|
|
|
#define SF_TRIGGER_MULTIPLE_RED 1
|
|
#define SF_TRIGGER_MULTIPLE_BLUE 2
|
|
#define SF_TRIGGER_MULTIPLE_DOOR 4
|
|
|
|
// 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)
|
|
{
|
|
ent->activator = activator;
|
|
if (ent->nextthink) {
|
|
return; // can't retrigger until the wait is over
|
|
}
|
|
//Makro - inactive trigger ?
|
|
if (ent->inactive) {
|
|
//note - since the trigger is not sent to the clients, we cannot send the event
|
|
//either, so we'll just play the sound locally
|
|
if (ent->soundInactive)
|
|
G_AddEvent(ent->activator, EV_GENERAL_SOUND, ent->soundInactive);
|
|
if (ent->targetInactive)
|
|
G_UseEntities(ent->activator, ent->targetInactive, activator);
|
|
ent->think = multi_wait;
|
|
ent->nextthink = level.time + (ent->wait + ent->random * crandom()) * 1000;
|
|
return;
|
|
}
|
|
//Makro - added check; Q3 crashed in archives when playing
|
|
//with .dll's and shooting one of the barrels
|
|
if (activator != NULL) {
|
|
if (activator->client) {
|
|
if ((ent->spawnflags & SF_TRIGGER_MULTIPLE_RED) && activator->client->sess.sessionTeam != TEAM_RED) {
|
|
return;
|
|
}
|
|
if ((ent->spawnflags & SF_TRIGGER_MULTIPLE_BLUE) && activator->client->sess.sessionTeam != TEAM_BLUE) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ent->sound1to2)
|
|
G_AddEvent(ent->activator, EV_GENERAL_SOUND, ent->sound1to2);
|
|
G_UseTargets(ent, ent->activator);
|
|
|
|
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...
|
|
//Makro - we no longer remove the entity, we just make it inactive
|
|
//otherwise, we would only be able to use it during the first
|
|
//round in TP games
|
|
/*
|
|
ent->touch = 0;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
ent->think = G_FreeEntity;
|
|
*/
|
|
//On second thought, now that I've added those soundInactive/targetInactive keys
|
|
//I think just setting touch to 0 will do
|
|
//ent->inactive = 1;
|
|
ent->touch = 0;
|
|
}
|
|
}
|
|
|
|
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 || !other->client) {
|
|
return;
|
|
}
|
|
if (self->spawnflags & SF_TRIGGER_MULTIPLE_DOOR) {
|
|
if (!other->client->openDoor || other->client->openDoorTime <= self->timestamp)
|
|
return;
|
|
self->timestamp = other->client->openDoorTime;
|
|
}
|
|
multi_trigger(self, other);
|
|
}
|
|
|
|
void Reset_Multi(gentity_t *ent)
|
|
{
|
|
ent->inactive = ent->unbreakable;
|
|
ent->think = 0;
|
|
ent->nextthink = 0;
|
|
ent->touch = Touch_Multi;
|
|
}
|
|
|
|
void SP_trigger_multiple(gentity_t * ent)
|
|
{
|
|
char *s;
|
|
|
|
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");
|
|
}
|
|
//Makro - added
|
|
if (G_SpawnString("soundInactive", "", &s))
|
|
ent->soundInactive = G_SoundIndex(s);
|
|
if (G_SpawnString("noise", "", &s))
|
|
ent->sound1to2 = G_SoundIndex(s);
|
|
else if (G_SpawnString("sound", "", &s))
|
|
ent->sound1to2 = G_SoundIndex(s);
|
|
|
|
|
|
ent->touch = Touch_Multi;
|
|
ent->use = Use_Multi;
|
|
ent->unbreakable = ent->inactive;
|
|
ent->reset = Reset_Multi;
|
|
|
|
InitTrigger(ent);
|
|
trap_LinkEntity(ent);
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
trigger_always
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
void trigger_always_think(gentity_t * ent)
|
|
{
|
|
G_UseTargets(ent, ent);
|
|
//Makro - we want to be able to re-use this entity (round-based gametypes)
|
|
//so we're not going to free it
|
|
//G_FreeEntity(ent);
|
|
trap_UnlinkEntity(ent);
|
|
}
|
|
|
|
//Makro - reset function
|
|
void trigger_always_reset(gentity_t *ent)
|
|
{
|
|
trap_LinkEntity(ent);
|
|
ent->nextthink = level.time + 300;
|
|
ent->think = trigger_always_think;
|
|
}
|
|
|
|
/*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;
|
|
//Makro - reset function
|
|
ent->reset = trigger_always_reset;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
trigger_push
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
void trigger_push_touch(gentity_t * self, gentity_t * other, trace_t * trace)
|
|
{
|
|
|
|
if (!other || !self)
|
|
return;
|
|
|
|
if (!other->client) {
|
|
return;
|
|
}
|
|
//Makro - too soon to activate ?
|
|
/*if (level.time < self->s.legsAnim) {
|
|
return;
|
|
} */
|
|
|
|
//Makro - bot only triggers
|
|
if (self->spawnflags & 1) {
|
|
if (!(other->r.svFlags & SVF_BOT)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
BG_TouchJumpPad(&other->client->ps, &self->s);
|
|
//Makro - "team up" trigger_pushes
|
|
//something is broken here :/
|
|
/*
|
|
if (self->pathtarget) {
|
|
if (self->pathtarget[0]) {
|
|
gentity_t *loop = NULL;
|
|
for (loop = G_Find2(NULL, FOFS(classname), self->classname, FOFS(pathtarget), self->pathtarget); loop; G_Find2(loop, FOFS(classname), self->classname, FOFS(pathtarget), self->pathtarget)) {
|
|
//Makro - delay 5 seconds before triggering another trigger_push from the same "team"
|
|
if (self->distance) {
|
|
loop->s.legsAnim = level.time + self->distance * 1000;
|
|
} else {
|
|
loop->s.legsAnim = level.time + 5 * 1000;
|
|
}
|
|
}
|
|
}
|
|
} */
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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) ? BOT_ONLY
|
|
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)
|
|
{
|
|
char *sound;
|
|
|
|
InitTrigger(self);
|
|
|
|
// unlike other triggers, we need to send this one to the client
|
|
// NiceAss: Added for custom push sounds. Default is none. Q3 is "sounds/world/bouncepad.wav"
|
|
//Changed from noise to sound as per Sze
|
|
if (G_SpawnString("sound", "sound/misc/silence.wav", &sound)) {
|
|
self->s.generic1 = G_SoundIndex(sound);
|
|
}
|
|
//Makro - for bot-only triggers
|
|
if (!(self->spawnflags & 1)) {
|
|
self->r.svFlags &= ~SVF_NOCLIENT;
|
|
}
|
|
self->s.powerups = (self->spawnflags & 1);
|
|
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) {
|
|
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;
|
|
//Elder: added to check noise_index
|
|
if (self->noise_index)
|
|
G_Sound(activator, CHAN_AUTO, self->noise_index);
|
|
}
|
|
}
|
|
|
|
/*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
|
|
if "bouncepad", play bounce noise instead of windfly
|
|
*/
|
|
void SP_target_push(gentity_t * self)
|
|
{
|
|
char *sound;
|
|
|
|
if (!self->speed) {
|
|
self->speed = 1000;
|
|
}
|
|
G_SetMovedir(self->s.angles, self->s.origin2);
|
|
VectorScale(self->s.origin2, self->speed, self->s.origin2);
|
|
//Changed from noise to sound as per Sze
|
|
if (G_SpawnString("sound", "sound/misc/silence.wav", &sound)) {
|
|
//Makro - debug message, no longer needed
|
|
//G_Printf("^2Sound was %s\n",sound);
|
|
self->noise_index = G_SoundIndex(sound);
|
|
}
|
|
|
|
if (self->spawnflags & 1) {
|
|
//Elder: edited for now TODO: let mappers use a key pair
|
|
//self->noise_index = G_SoundIndex("sound/world/jumppad.wav");
|
|
} else {
|
|
//Elder: edited for now TODO: let mappers use a key pair
|
|
//self->noise_index = 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/world/jumppad.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 - 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)
|
|
|
|
*/
|
|
//Makro - reset function
|
|
void hurt_reset(gentity_t *ent)
|
|
{
|
|
ent->timestamp = level.time;
|
|
if (!(ent->spawnflags & 1)) {
|
|
trap_LinkEntity(ent);
|
|
} else {
|
|
//Makro - added
|
|
trap_UnlinkEntity(ent);
|
|
}
|
|
}
|
|
|
|
void hurt_use(gentity_t * self, gentity_t * other, gentity_t * activator)
|
|
{
|
|
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->spawnflags & 16) {
|
|
self->timestamp = level.time + 1000;
|
|
} else {
|
|
self->timestamp = level.time + FRAMETIME;
|
|
}
|
|
|
|
// play sound
|
|
if (!(self->spawnflags & 4)) {
|
|
G_Sound(other, CHAN_AUTO, self->noise_index);
|
|
}
|
|
|
|
if (self->spawnflags & 8)
|
|
dflags = DAMAGE_NO_PROTECTION;
|
|
else
|
|
dflags = 0;
|
|
if (self->methodOfDeath)
|
|
{
|
|
G_Damage(other, self, self, NULL, NULL, self->damage, dflags, MOD_CUSTOM+self->methodOfDeath-1);
|
|
} else {
|
|
G_Damage(other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT);
|
|
}
|
|
}
|
|
|
|
void SP_trigger_hurt(gentity_t * self)
|
|
{
|
|
InitTrigger(self);
|
|
|
|
self->noise_index = G_SoundIndex("sound/world/electro.wav");
|
|
self->touch = hurt_touch;
|
|
|
|
if (!self->damage) {
|
|
self->damage = 5;
|
|
}
|
|
|
|
//self->r.contents = CONTENTS_TRIGGER;
|
|
|
|
//Makro - custom death message
|
|
G_InitCustomDeathMessage(self, &self->methodOfDeath);
|
|
|
|
//Makro - removed this check
|
|
//if ( self->spawnflags & 2 ) {
|
|
self->use = hurt_use;
|
|
//}
|
|
//Makro - reset function
|
|
self->reset = hurt_reset;
|
|
|
|
// link in to the world if starting active
|
|
if (!(self->spawnflags & 1)) {
|
|
trap_LinkEntity(self);
|
|
} else {
|
|
//Makro - added
|
|
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;
|
|
}
|