mirror of
https://github.com/UberGames/rpgxEF.git
synced 2024-11-15 01:11:25 +00:00
1184 lines
No EOL
28 KiB
C
1184 lines
No EOL
28 KiB
C
/*
|
|
* Copyright (C) 1999-2000 Id Software, Inc.
|
|
*/
|
|
|
|
#include "g_local.h"
|
|
|
|
/*these look weired... I'd rather replace them with streight numbers.
|
|
#define SF_SPECTATOR (1<<0)
|
|
#define SF_RANDOM (1<<1)*/
|
|
|
|
/**
|
|
* \brief Inits a trigger entity.
|
|
*
|
|
* Initializes trigger entities.
|
|
*
|
|
* @param self the trigger entity
|
|
*/
|
|
void InitTrigger( gentity_t *self ) {
|
|
if (!VectorCompare (self->s.angles, vec3_origin))
|
|
G_SetMovedir (self->s.angles, self->movedir);
|
|
|
|
if(!self->tmpEntity) /* for spawnTent command */
|
|
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
|
|
/**
|
|
* \brief Reactivate a trigger_multiple after the wait time has passed.
|
|
*
|
|
* Reactivates the trigger_multiple after the wait time has passed.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void multi_wait( gentity_t *ent ) {
|
|
ent->nextthink = 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Function that gets called when a trigger_multiple has been ... umm well triggered ;)
|
|
*
|
|
* The trigger_multiple was just activated. ent->activator should be set to the activator
|
|
* so it can be held through a delay. May wait for the delay time until firing.
|
|
*
|
|
* @param ent the trigger
|
|
* @param activator the activator
|
|
*/
|
|
void multi_trigger( gentity_t *ent, gentity_t *activator ) {
|
|
ent->activator = activator;
|
|
if ( ent->nextthink ) {
|
|
return; /* can't retrigger until the wait is over */
|
|
}
|
|
|
|
if ( activator->client && ((ent->spawnflags&4)||(ent->spawnflags&2)||(ent->spawnflags&1)) )
|
|
{/* see if it's usable by this team */
|
|
switch( activator->client->sess.sessionTeam )
|
|
{
|
|
case TEAM_RED:
|
|
if ( !(ent->spawnflags&1) ) {
|
|
return;/* red is not allowed */
|
|
}
|
|
break;
|
|
case TEAM_BLUE:
|
|
if ( !(ent->spawnflags&2) ) {
|
|
return;/* blue is not allowed */
|
|
}
|
|
break;
|
|
default:
|
|
if ( (ent->spawnflags&4) ) {
|
|
return;/* must be on a team */
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
G_UseTargets (ent, ent->activator);
|
|
|
|
#ifdef G_LUA
|
|
if(ent->luaTrigger && !(ent->nextthink))
|
|
{
|
|
if(activator)
|
|
{
|
|
LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, activator->s.number);
|
|
}
|
|
else
|
|
{
|
|
LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, ENTITYNUM_WORLD);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Use function for trigger_multiple.
|
|
*
|
|
* Use function for trigger_multiple.
|
|
*
|
|
* @param ent the trigger
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
void Use_Multi( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
|
|
multi_trigger( ent, activator );
|
|
}
|
|
|
|
/**
|
|
* \brief Touch function for trigger_multiple.
|
|
*
|
|
* Touch function for trigger_multiple.
|
|
*
|
|
* @param self the trigger
|
|
* @param other touching entity
|
|
* @param trace a trace
|
|
*/
|
|
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) ? RED_OK BLUE_OK TEAM_ONLY
|
|
-----DESCRIPTION-----
|
|
Variable sized repeatable trigger. Must be targeted at one or more entities.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: RED_OK - People on the red team can fire this trigger
|
|
2: BLUE_OK - People on the blue team can fire this trigger
|
|
4: TEAM_ONLY - Only people on red or blue can fire this trigger (not TEAM_FREE like in straight holomatch or spectators)
|
|
|
|
-----KEYS-----
|
|
"wait" - Seconds between triggerings, 0.5 default, -1 = one time only.
|
|
"random" - wait variance, default is 0
|
|
so, the basic time between firing is a random time between
|
|
(wait - random) and (wait + random)
|
|
*/
|
|
/**
|
|
* \brief Spawn function of trigger_multiple.
|
|
*
|
|
* Spawn function of trigger_multiple.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void SP_trigger_multiple( gentity_t *ent ) {
|
|
G_SpawnFloat( "wait", "0.5", &ent->wait );
|
|
G_SpawnFloat( "random", "0", &ent->random );
|
|
|
|
G_SetOrigin(ent, ent->s.origin);
|
|
|
|
if ( ent->random >= ent->wait && ent->wait >= 0 ) {
|
|
ent->random = ent->wait - FRAMETIME;
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Warning] trigger_multiple has random >= wait\n" ););
|
|
}
|
|
|
|
ent->touch = Touch_Multi;
|
|
ent->use = Use_Multi;
|
|
|
|
InitTrigger( ent );
|
|
|
|
VectorCopy(ent->r.maxs, ent->s.origin2);
|
|
VectorCopy(ent->r.mins, ent->s.angles2);
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
trigger_always
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/**
|
|
* \brief Think function of trigger_always.
|
|
*
|
|
* Think function of trigger_always.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_always_think( gentity_t *ent ) {
|
|
G_UseTargets(ent, ent);
|
|
|
|
#ifdef G_LUA
|
|
if(ent->luaTrigger)
|
|
{
|
|
LuaHook_G_EntityTrigger(ent->luaTrigger, ent->s.number, ent->s.number);
|
|
}
|
|
#endif
|
|
|
|
G_FreeEntity( ent );
|
|
}
|
|
|
|
/*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
|
-----DESCRIPTION-----
|
|
This trigger will always fire. It is activated by the world.
|
|
Actually this is going to fire once 0.3 secs after spawn, so it's more a trigger_init.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - targets to fire
|
|
*/
|
|
/**
|
|
* \brief Spawn function of trigger_multiple.
|
|
*
|
|
* Spawn function of trigger_multiple.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
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
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/**
|
|
* \brief Touch function of trigger_push.
|
|
*
|
|
* Touch function of trigger_push.
|
|
*
|
|
* @param self the trigger
|
|
* @param other the touching entity
|
|
* @param trace a trace
|
|
*/
|
|
void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
if ( !other->client ) {
|
|
return;
|
|
}
|
|
/*
|
|
if ( other->client->ps.pm_type != PM_NORMAL ) {
|
|
return;
|
|
}
|
|
if ( other->client->ps.powerups[PW_FLIGHT] ) {
|
|
return;
|
|
}
|
|
*/
|
|
if ( other->client->ps.velocity[2] < 100 ) {
|
|
/* don't play the event sound again if we are in a fat trigger */
|
|
G_AddPredictableEvent( other, EV_JUMP_PAD, 0 );
|
|
}
|
|
VectorCopy (self->s.origin2, other->client->ps.velocity);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Calculate origin2 so the target apogee will be hit.
|
|
*
|
|
* Calculate origin2 so the target apogee will be hit.
|
|
*
|
|
* @param the trigger
|
|
*/
|
|
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) ?
|
|
-----DESCRIPTION-----
|
|
Jumpfield/Booster Effect predicted on client side. This is activated by touch function.
|
|
|
|
-----SPAWNFLAGS-----
|
|
None
|
|
|
|
-----KEYS-----
|
|
"target" - apex of the leap. Must be a target_position or info_notnull.
|
|
*/
|
|
/**
|
|
* \brief Spawn function of trigger_push.
|
|
*
|
|
* Spawn function of trigger_push.
|
|
*
|
|
* @param self the trigger
|
|
*/
|
|
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;
|
|
|
|
self->s.eType = ET_PUSH_TRIGGER;
|
|
self->touch = trigger_push_touch;
|
|
self->think = AimAtTarget;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
trap_LinkEntity (self);
|
|
|
|
VectorCopy(self->r.maxs, self->s.apos.trBase);
|
|
VectorCopy(self->r.mins, self->s.pos.trBase);
|
|
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
/**
|
|
* \brief Use function of target_push.
|
|
*
|
|
* Use function of target_push.
|
|
*
|
|
* @param self the entity
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
void Use_target_push( gentity_t *self, gentity_t *other, gentity_t *activator ) {
|
|
if ( !activator->client ) {
|
|
return;
|
|
}
|
|
|
|
/* RPG-X: J2J noclip use */
|
|
if ( (activator->client->ps.pm_type != PM_NORMAL) || (activator->client->ps.pm_type != PM_NOCLIP)) {
|
|
return;
|
|
}
|
|
|
|
if ( activator->client->ps.powerups[PW_FLIGHT] ) {
|
|
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;
|
|
G_Sound( activator, self->noise_index );
|
|
}
|
|
}
|
|
|
|
/*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) ENERGYNOISE
|
|
-----DESCRIPTION-----
|
|
Pushes the activator in the direction of angle, or towards a target apex.
|
|
This is predicted on the serverside and is triggered by use-function.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: ENERGYNOISE - play energy noise instead of windfly
|
|
|
|
-----KEYS-----
|
|
"speed" - defaults to 1000
|
|
"target" - apex of the leap. Must be a target_position or info_notnull.
|
|
*/
|
|
/**
|
|
* \brief Spawn function of target_push.
|
|
*
|
|
* Spawn function of target_push.
|
|
*
|
|
* @param self the entity
|
|
*/
|
|
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/ambience/forge/antigrav.wav");
|
|
} else {
|
|
self->noise_index = G_SoundIndex("sound/misc/windfly.wav"); /* fixme need sound! */
|
|
}
|
|
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
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#define MAX_TRANSPORTER_POINTS 16
|
|
|
|
gentity_t *SelectRandomSpawnPoint( void )
|
|
{
|
|
gentity_t *spot = NULL;
|
|
int count = 0;
|
|
int selection = 0;
|
|
gentity_t *spots[MAX_TRANSPORTER_POINTS];
|
|
char *classname;
|
|
|
|
classname = "info_player_deathmatch";
|
|
|
|
while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL)
|
|
{
|
|
spots[ count++ ] = spot;
|
|
if (count == MAX_TRANSPORTER_POINTS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !count )
|
|
{ /* no spawn points !!??! */
|
|
return NULL;
|
|
}
|
|
|
|
selection = rand() % count;
|
|
return spots[ selection ];
|
|
}
|
|
|
|
/**
|
|
* \brief Touch function of trigger_teleport.
|
|
*
|
|
* Touch function of trigger_teleport.
|
|
*
|
|
* @param self the trigger
|
|
* @param other the touching entity
|
|
* @param trace a trace
|
|
*/
|
|
void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace )
|
|
{
|
|
gentity_t *dest;
|
|
vec3_t destPoint;
|
|
|
|
trace_t tr;
|
|
vec3_t tracePoint;
|
|
|
|
int clientNum;
|
|
|
|
if(self->flags & FL_LOCKED)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
clientNum = other->client->ps.clientNum;
|
|
|
|
/* BOOKMARK J2J */
|
|
if (self->spawnflags & 2)
|
|
{
|
|
/* find a random spawn point */
|
|
dest = SelectRandomSpawnPoint();
|
|
}
|
|
else
|
|
{
|
|
dest = G_PickTarget( self->target );
|
|
}
|
|
if (!dest)
|
|
{
|
|
DEVELOPER(G_Printf (S_COLOR_YELLOW "[Entity-Error] Couldn't find teleporter destination\n"););
|
|
return;
|
|
}
|
|
|
|
/* suspended */
|
|
if ( self->spawnflags & 8 )
|
|
{
|
|
/* put the bottom of the player's bbox at the bottom of the target's bbox */
|
|
VectorCopy(dest->s.origin, destPoint);
|
|
destPoint[2] += dest->r.mins[2];
|
|
destPoint[2] -= other->r.mins[2];
|
|
/* fudge it upwards just a bit */
|
|
destPoint[2] += 1;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( dest->s.origin, tracePoint );
|
|
tracePoint[2] -= 4096.0f;
|
|
|
|
trap_Trace( &tr, dest->s.origin, dest->r.mins, dest->r.maxs, tracePoint, dest->s.number, MASK_PLAYERSOLID );
|
|
VectorCopy( tr.endpos, destPoint );
|
|
|
|
/* offset the player's bounding box. */
|
|
destPoint[2] += dest->r.mins[2];
|
|
destPoint[2] -= other->r.mins[2];
|
|
|
|
/* add 1 to ensure non-direct collision */
|
|
destPoint[2] += 1;
|
|
}
|
|
|
|
if (self->health)
|
|
{
|
|
/*
|
|
* TiM - Transporter VFX
|
|
* Only if no spitting though. Having spitting transporter with these VFX would look freaky weird
|
|
*/
|
|
if ( self->health == -1 && ( self->spawnflags & 4 ) ) {
|
|
if ( TransDat[clientNum].beamTime == 0 ) {
|
|
G_InitTransport( clientNum, destPoint, dest->s.angles );
|
|
}
|
|
}
|
|
else {
|
|
TransportPlayer(other, destPoint, dest->s.angles, (self->health == -1)?0:self->health);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Transporter VFX */
|
|
if ( self->spawnflags & 4 ) {
|
|
if ( TransDat[clientNum].beamTime == 0 ) {
|
|
G_InitTransport( clientNum, destPoint, dest->s.angles );
|
|
}
|
|
}
|
|
else {
|
|
TeleportPlayer( other, destPoint, dest->s.angles, TP_NORMAL );
|
|
}
|
|
}
|
|
|
|
if(self->sound1to2 && self->wait && !(self->flags & FL_CLAMPED)) {
|
|
G_AddEvent(self, EV_GENERAL_SOUND, self->sound1to2);
|
|
self->flags ^= FL_CLAMPED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Think function of trigger_teleport.
|
|
*
|
|
* Think function of trigger_teleport.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_teleport_think(gentity_t *ent) {
|
|
ent->nextthink = -1;
|
|
if(!(ent->flags & FL_LOCKED))
|
|
ent->flags ^= FL_LOCKED;
|
|
if(ent->wait && (ent->flags & FL_CLAMPED))
|
|
ent->flags ^= FL_CLAMPED;
|
|
}
|
|
|
|
/**
|
|
* \brief Use function of trigger_teleport.
|
|
*
|
|
* Use function of trigger_teleport.
|
|
*
|
|
* @param ent the trigger
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
void trigger_teleport_use(gentity_t *ent, gentity_t *other, gentity_t *activator) {
|
|
if(!Q_stricmp(ent->swapname, activator->target))
|
|
if(ent->flags & FL_LOCKED)
|
|
ent->nextthink = level.time + (ent->wait * 1000);
|
|
ent->flags ^= FL_LOCKED;
|
|
}
|
|
|
|
|
|
/*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR RANDOM VISUAL_FX SUSPENDED DEACTIVATED
|
|
-----DESCRIPTION-----
|
|
Allows client side prediction of teleportation events.
|
|
Must point at a target_position or info_notnull, which will be the teleport destination.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: SPECTATOR: If 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.
|
|
2: RANDOM: send player to random info_player_deathmatch spawn point
|
|
4: VISUAL_FX: plays the Star Trek transporter FX and beams the player out slowly
|
|
8: SUSPENDED: player appears with the bounding box aligned to the bottom of the target
|
|
If this isn't set, the player materializes at the first solid surface under it
|
|
16: DEACTIVATED: Spawns deactivated
|
|
|
|
-----KEYS-----
|
|
"swapname" - ACTIVATE/DEACTIVATE (Using entity needs SELF/NOACTIVATOR)
|
|
"wait" - time before trigger deactivates itself automatically
|
|
"soundstart" - sound to play if triggered
|
|
"health" - default is original behavior (speed of 400), any other value will be the
|
|
speed at which the player is spewed forth from the tranpsorter destination. -1
|
|
if you want no speed. The transporter VISUAL_FX flag will only work if the health
|
|
is set to 0 or -1 as it cannot support 'spewing'.
|
|
*/
|
|
/**
|
|
* \brief Spawn function of trigger_teleport.
|
|
*
|
|
* Spawn function of trigger_teleport.
|
|
*
|
|
* @param self the trigger
|
|
*/
|
|
void SP_trigger_teleport( gentity_t *self )
|
|
{
|
|
char *temp;
|
|
|
|
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;
|
|
}
|
|
|
|
self->s.eType = ET_TELEPORT_TRIGGER;
|
|
self->touch = trigger_teleporter_touch;
|
|
|
|
if(self->spawnflags & 16)
|
|
self->flags ^= FL_LOCKED;
|
|
|
|
self->use = trigger_teleport_use;
|
|
|
|
if(self->wait) {
|
|
self->nextthink = -1;
|
|
self->think = trigger_teleport_think;
|
|
}
|
|
|
|
if(G_SpawnString("soundstart", "", &temp))
|
|
self->sound1to2 = G_SoundIndex(temp);
|
|
|
|
VectorCopy(self->r.maxs, self->s.origin2);
|
|
VectorCopy(self->r.mins, self->s.angles2);
|
|
|
|
trap_LinkEntity (self);
|
|
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
trigger_hurt
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW EVO_PROTECT NO_ADMIN
|
|
-----DESCRIPTION-----
|
|
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.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: START_OFF - trigger will not be doing damage until toggled on
|
|
2: TOGGLE - can be toggled
|
|
4: SILENT - supresses playing the sound
|
|
8: NO_PROTECTION - *nothing* stops the damage
|
|
16: SLOW - changes the damage rate to once per second
|
|
32: EVO_PROTECT - Evosuit protects the client, even if NO_PROTECTION is set
|
|
64: NO_ADMIN - admins don't get hurt, even if NO_PROTECTION is set
|
|
|
|
-----KEYS-----
|
|
"dmg" - default 5 (whole numbers only)
|
|
*/
|
|
/**
|
|
* \brief Use function of trigger_hurt.
|
|
*
|
|
* Use function of trigger hurt.
|
|
*
|
|
* @param self the trigger
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) {
|
|
self->count = !self->count;
|
|
}
|
|
|
|
/**
|
|
* \brief Touch function of trigger_hurt.
|
|
*
|
|
* Touch function of trigger_hurt.
|
|
*
|
|
* @param self the trigger
|
|
* @param other the touching entity
|
|
* @param trace a trace
|
|
*/
|
|
void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
|
|
int dflags;
|
|
|
|
if(!self->count) return;
|
|
|
|
/*
|
|
* RPG-X | Phenix | 8/8/204
|
|
* (If the guy is wearning an evosuit)
|
|
*/
|
|
if ((self->spawnflags & 32) && (other->flags & FL_EVOSUIT)) {
|
|
return;
|
|
}
|
|
|
|
if(self->spawnflags & 64) {
|
|
if(IsAdmin(other))
|
|
return;
|
|
}
|
|
|
|
if ( !other->takedamage ) {
|
|
return;
|
|
}
|
|
|
|
if ( self->timestamp > level.time ) {
|
|
return;
|
|
}
|
|
|
|
/* RPG-X | Phenix | 9/8/2004 */
|
|
if (other->health <= 1) {
|
|
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, self->noise_index );
|
|
}
|
|
|
|
if (self->spawnflags & 8)
|
|
dflags = DAMAGE_NO_PROTECTION;
|
|
else
|
|
dflags = 0;
|
|
G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT);
|
|
}
|
|
|
|
/**
|
|
* \brief Spawn function of trigger_hurt.
|
|
*
|
|
* Spawn function of trigger_hurt.
|
|
*
|
|
* @param self the trigger
|
|
*/
|
|
void SP_trigger_hurt( gentity_t *self ) {
|
|
InitTrigger (self);
|
|
|
|
/* TiM - gets very annoying after a while */
|
|
self->noise_index = G_SoundIndex( "sound/world/electro.wav" );
|
|
self->touch = hurt_touch;
|
|
|
|
if ( !self->damage ) {
|
|
self->damage = 5;
|
|
}
|
|
|
|
self->r.contents = CONTENTS_TRIGGER;
|
|
|
|
if ( self->spawnflags & 2 ) {
|
|
self->use = hurt_use;
|
|
}
|
|
|
|
self->count = !(self->spawnflags & 1);
|
|
|
|
/* link in to the world if starting active | this won't work */
|
|
/*if ( ! (self->spawnflags & 1) ) {
|
|
trap_LinkEntity (self);
|
|
}*/
|
|
VectorCopy(self->r.maxs, self->s.origin2);
|
|
VectorCopy(self->r.mins, self->s.angles2);
|
|
|
|
trap_LinkEntity(self);
|
|
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
timer
|
|
This should be renamed trigger_timer...
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
|
|
/*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
|
|
-----DESCRIPTION-----
|
|
Fires its targets every "wait" seconds.
|
|
Can be turned on or off by using.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: START_ON - will be on at spawn and setting up for it's first intervall
|
|
|
|
-----KEYS-----
|
|
"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)
|
|
*/
|
|
/**
|
|
* \brief Think function of func_timer.
|
|
*
|
|
* Think function of func_timer.
|
|
*
|
|
* @param self the entity
|
|
*/
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* \brief Use function of func_timer.
|
|
*
|
|
* Use function of func_timer.
|
|
*
|
|
* @param self the entity
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* \brief Spawn function of func_timer.
|
|
*
|
|
* Spawn function of func_timer.
|
|
*
|
|
* @param self the entity
|
|
*/
|
|
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;
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Warning] 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;
|
|
}
|
|
|
|
/*QUAKED trigger_transporter (0.5 0.5 0.5) ?
|
|
-----DESCRIPTION-----
|
|
This is used in combination with ui_transporter.
|
|
Have this be targeted by ui_transporter.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"wait" time to wait before trigger gets deactivated again(in seconds, default 5)
|
|
"soundstart" transport sound;
|
|
*/
|
|
/**
|
|
* \brief Think function of trigger_transporter.
|
|
*
|
|
* Think function of trigger_transporter.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_transporter_think(gentity_t *ent) {
|
|
if(Q_stricmp(ent->classname, "tent")) {
|
|
ent->nextthink = -1;
|
|
ent->flags ^= FL_LOCKED;
|
|
ent->flags ^= FL_CLAMPED;
|
|
} else {
|
|
ent->target_ent->flags ^= FL_LOCKED;
|
|
ent->target_ent->flags ^= FL_CLAMPED;
|
|
G_FreeEntity(ent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Think function of trigger_transporter for server change.
|
|
*
|
|
* Think function of trigger_transporter for server change.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_transporter_serverchange(gentity_t *ent) {
|
|
trap_SendServerCommand(ent->touched-g_entities, va("cg_connect \"%s\"\n", ent->targetname2));
|
|
G_FreeEntity(ent);
|
|
}
|
|
|
|
/**
|
|
* \brief Touch function of trigger_transporter.
|
|
*
|
|
* Touch function of trigger_transporter.
|
|
*
|
|
* @param ent the trigger
|
|
* @param other the touching entity
|
|
* @param trace a trace
|
|
*/
|
|
void trigger_transporter_touch(gentity_t *ent, gentity_t *other, trace_t *trace) {
|
|
char *srv;
|
|
vec3_t offset = { 0, 0, 12 };
|
|
vec3_t target;
|
|
gentity_t *targetEnt;
|
|
gentity_t *tent;
|
|
int clientNum;
|
|
|
|
if((ent->flags & FL_LOCKED) || (!other->client) || (other->flags & FL_CLAMPED)) return;
|
|
|
|
clientNum = other->client->ps.clientNum;
|
|
|
|
if(ent->sound1to2 && !(ent->flags & FL_CLAMPED)) {
|
|
G_AddEvent(other, EV_GENERAL_SOUND, ent->sound1to2);
|
|
ent->flags ^= FL_CLAMPED;
|
|
}
|
|
|
|
other->flags ^= FL_CLAMPED;
|
|
|
|
if(!ent->count)
|
|
if(ent->target_ent->target) {
|
|
targetEnt = G_PickTarget(ent->target_ent->target);
|
|
VectorAdd(targetEnt->s.origin, offset, target);
|
|
G_InitTransport(clientNum, target, targetEnt->s.angles);
|
|
} else {
|
|
G_InitTransport(clientNum, ent->target_ent->s.origin, ent->target_ent->s.angles);
|
|
}
|
|
else {
|
|
|
|
srv = level.srvChangeData.ip[ent->health];
|
|
|
|
tent = G_Spawn();
|
|
tent->think = trigger_transporter_serverchange;
|
|
tent->nextthink = level.time + 3000;
|
|
TransDat[clientNum].beamTime = level.time + 8000;
|
|
other->client->ps.powerups[PW_BEAM_OUT] = level.time + 8000;
|
|
tent->touched = other;
|
|
tent->targetname2 = G_NewString(srv);
|
|
|
|
|
|
tent = G_Spawn();
|
|
tent->classname = G_NewString("tent");
|
|
tent->target_ent = ent;
|
|
tent->think = trigger_transporter_think;
|
|
tent->nextthink = level.time + ent->wait;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Delay think function of trigger_transporter.
|
|
*
|
|
* Delay think function of trigger_transporter.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_transporter_delay(gentity_t *ent) {
|
|
ent->think = trigger_teleport_think;
|
|
ent->nextthink = level.time + ent->wait;
|
|
ent->flags ^= FL_LOCKED;
|
|
}
|
|
|
|
/**
|
|
* \brief Spawn function of trigger_transporter.
|
|
*
|
|
* Spawn function of trigger_transporter.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void SP_trigger_transporter(gentity_t *ent) {
|
|
char *temp;
|
|
InitTrigger(ent);
|
|
|
|
if(!ent->wait) {
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Error] trigger_transporter without wait at %s!\n", vtos(ent->s.origin)););
|
|
G_FreeEntity(ent);
|
|
return;
|
|
}
|
|
|
|
if(G_SpawnString("soundstart", "", &temp))
|
|
ent->sound1to2 = G_SoundIndex(temp);
|
|
|
|
if(!ent->wait)
|
|
ent->wait = 5;
|
|
ent->wait *= 1000;
|
|
|
|
ent->touch = trigger_transporter_touch;
|
|
|
|
ent->flags ^= FL_LOCKED;
|
|
|
|
VectorCopy(ent->r.maxs, ent->s.apos.trBase);
|
|
VectorCopy(ent->r.mins, ent->s.pos.trBase);
|
|
|
|
trap_LinkEntity(ent);
|
|
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
/*QUAKED trigger_radiation (0.5 0.5 0.5) ? START_OFF MAP_WIDE
|
|
-----DESCRIPTION-----
|
|
This can be used in three ways:
|
|
- as radiation volume trigger
|
|
- as mapwide radiation
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: START_OFF - ent is off at spawn
|
|
2: MAP_WIDE - mapwide radiation
|
|
|
|
-----KEYS-----
|
|
The damage the radiation does is calculated from these two values:
|
|
"damage" damage(default 1)
|
|
"wait" wait(seconds, default 10)
|
|
|
|
Forumla is: dps = dmg / wait
|
|
*/
|
|
/**
|
|
* \brief Touch function of trigger_radiation.
|
|
*
|
|
* Touch function of trigger_radiation.
|
|
*
|
|
* @param ent the trigger
|
|
* @param other the touching entity
|
|
* @param trace a trace
|
|
*/
|
|
void trigger_radiation_touch(gentity_t *ent, gentity_t *other, trace_t *trace) {
|
|
if(!other || !other->client || !ent->count) return;
|
|
|
|
if(!(ent->flags & FL_LOCKED)) {
|
|
if(other->health - ent->damage < 0)
|
|
other->health = 0;
|
|
else
|
|
other->health -= ent->damage;
|
|
|
|
other->client->ps.stats[STAT_HEALTH] = other->health;
|
|
|
|
ent->flags ^= FL_LOCKED;
|
|
}
|
|
|
|
/* TODO: display radiation symbol? */
|
|
}
|
|
|
|
/**
|
|
* \brief Think function of trigger_radiation.
|
|
*
|
|
* Think function of trigger_radiation.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void trigger_radiation_think(gentity_t *ent) {
|
|
if(ent->flags & FL_LOCKED)
|
|
ent->flags ^= FL_LOCKED;
|
|
}
|
|
|
|
/**
|
|
* \brief Use function of trigger_radiation.
|
|
*
|
|
* Use function of trigger_radiation.
|
|
*
|
|
* @param ent the trigger
|
|
* @param other another entity
|
|
* @param activator the activator
|
|
*/
|
|
void trigger_radiation_use(gentity_t *ent, gentity_t *other, gentity_t *activator) {
|
|
ent->count = !ent->count;
|
|
}
|
|
|
|
/**
|
|
* \brief Spawn function of trigger_radiation.
|
|
*
|
|
* Spawn function of trigger_radiation.
|
|
*
|
|
* @param ent the trigger
|
|
*/
|
|
void SP_trigger_radiation(gentity_t *ent) {
|
|
|
|
if(!ent->damage)
|
|
ent->damage = 1;
|
|
if(!ent->wait)
|
|
ent->wait = 10000;
|
|
else
|
|
ent->wait *= 1000;
|
|
|
|
InitTrigger(ent);
|
|
|
|
ent->count = !(ent->spawnflags & 1);
|
|
|
|
if(!(ent->spawnflags & 2))
|
|
ent->touch = trigger_radiation_touch;
|
|
|
|
ent->think = trigger_radiation_think;
|
|
ent->nextthink = level.time + ent->wait;
|
|
ent->flags ^= FL_LOCKED;
|
|
ent->use = trigger_radiation_use;
|
|
|
|
VectorCopy(ent->r.maxs, ent->s.apos.trBase);
|
|
VectorCopy(ent->r.mins, ent->s.pos.trBase);
|
|
|
|
trap_LinkEntity(ent);
|
|
|
|
if(!ent->tmpEntity)
|
|
level.numBrushEnts++;
|
|
}
|
|
|
|
/*QUAKED trigger_airlock (0.5 0.5 0.5) ?
|
|
-----DESCRIPTION-----
|
|
This is an entity that manages airlocks.
|
|
It can be used for Maintenance-Locks (Space Walks) internal Airlocks (for example to a quarantene zone)
|
|
or... as a way to abandon ship the hard way ^^ In that last case even an EVA-Suit won't protect you.
|
|
|
|
It is controlled by User Interface.
|
|
|
|
The Entity automatically features door management in a cycle, a check for an EVA-Suit (trigger_hurt)
|
|
and a push to get overboard (trigger_push).
|
|
|
|
It is hardcoded to expect func_doors for entrance on either side and a func_forcefiled for ejecting
|
|
(the forcefield is best placed within the outer door).
|
|
|
|
For Setup please set both sets of doors to wait = -1 and have the inner (if you select spawnflag no. 1
|
|
the outer) door spawn in it's open state.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: START_OUTSIDE - assumes that the outside door is open and set's itself up approopriately to cycle in at first use
|
|
2: NO_VENT - Will not check for push-target and forcefield that are required to vent the airlock.
|
|
4: QUARTANTENE_LOCK - airlock will be considered as an internal airlock to a quarantene zone or similar.
|
|
this will not kill and will not check for ejection-stuff, so NO_VENT is included in this spawnflag.
|
|
|
|
-----KEYS-----
|
|
"wait" time to wait before trigger gets deactivated again(in seconds, default 5)
|
|
"soundstart" transport sound;
|
|
*/ |