/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include "g_local.h"


void InitTrigger( edict_t *self )
{
	// Vic
	if ( !VectorCompare (self->pos2, vec3_origin) ) {
		VectorCopy ( self->pos2, self->movedir );
		self->speed = 0.1f;
	} else if ( !VectorCompare (self->s.angles, vec3_origin) ) {
		G_SetMovedir ( self->s.angles, self->movedir );
	}

	self->r.solid = SOLID_TRIGGER;
	self->movetype = MOVETYPE_NONE;
	trap_SetBrushModel (self, self->model);
	self->r.svflags = SVF_NOCLIENT;
}


// the wait time has passed, so set back up for another activation
void multi_wait (edict_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 (edict_t *ent)
{
	if (ent->nextthink)
		return;		// already been triggered

	G_UseTargets (ent, ent->activator);

	if (ent->wait > 0)	
	{
		ent->think = multi_wait;
		ent->nextthink = level.timemsec + 1000 * ent->wait;
	}
	else
	{	// we can't just remove (self) here, because this is a touch function
		// called while looping through area links...
		ent->touch = NULL;
		ent->nextthink = level.timemsec + game.framemsec;
		ent->think = G_FreeEdict;
	}
}

void Use_Multi( edict_t *ent, edict_t *other, edict_t *activator )
{
	ent->activator = activator;
	multi_trigger (ent);
}

void Touch_Multi( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	if( other->r.client )
	{
		if( self->spawnflags & 2 )
			return;
	}
	else if( other->r.svflags & SVF_MONSTER )
	{
		if( !(self->spawnflags & 1) )
			return;
	}
	else
		return;

	if( self->s.team && self->s.team != other->s.team )
		return;

	if( !VectorCompare(self->movedir, vec3_origin) )
	{
		vec3_t	forward;

		AngleVectors( other->s.angles, forward, NULL, NULL );
		if( DotProduct(forward, self->movedir) < 0 )
			return;
	}

	self->activator = other;
	multi_trigger( self );
}


/*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
Variable size repeatable trigger. It will fire the entities it targets when touched by player. Can be made to operate like a trigger_once entity by setting the "wait" key to -1. It can also be activated by another trigger that targets it.
-------- KEYS --------
target : this points to the entity to activate.
targetname : activating trigger points to this.
noise : play this noise when triggered
message : centerprint this text string when triggered
wait : time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once).
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
MONSTER : &1 monsters won't activate this trigger unless this flag is set
NOT_PLAYER : &2 players can't trigger this one (for those triggered by other triggers)
TRIGGERED : &4 spawns as triggered and must wait for the "wait" key to pass to be re-triggered
-------- NOTES --------
message is untested*/

void trigger_enable( edict_t *self, edict_t *other, edict_t *activator )
{
	self->r.solid = SOLID_TRIGGER;
	self->use = Use_Multi;
	trap_LinkEntity( self );
}

void SP_trigger_multiple( edict_t *ent )
{
	/*if (ent->sounds && ent->sounds[0])
	{
		if (ent->sounds[0] == '1')
			ent->noise_index = trap_SoundIndex (S_WORLD_SECRET);
		else if (ent->sounds[0] == '2')
			ent->noise_index = trap_SoundIndex (S_WORLD_MESSAGE);
		else if (ent->sounds[0] == '3')
			ent->noise_index = trap_SoundIndex ("sounds/misc/trigger1.wav");
	}*/
	if( st.noise )
		ent->noise_index = trap_SoundIndex( st.noise );

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		ent->s.team = st.gameteam;
	} else {
		ent->s.team = TEAM_SPECTATOR;
	}

	if( !ent->wait )
		ent->wait = 0.2f;

	ent->touch = Touch_Multi;
	ent->movetype = MOVETYPE_NONE;
	ent->r.svflags |= SVF_NOCLIENT;

	if( ent->spawnflags & 4 )
	{
		ent->r.solid = SOLID_NOT;
		ent->use = trigger_enable;
	}
	else
	{
		ent->r.solid = SOLID_TRIGGER;
		ent->use = Use_Multi;
	}

	if ( !VectorCompare(ent->s.angles, vec3_origin) )
		G_SetMovedir( ent->s.angles, ent->movedir );

	trap_SetBrushModel( ent, ent->model );
	trap_LinkEntity( ent );
}


/*QUAKED trigger_once (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching "targetname".
-------- KEYS --------
target : this points to the entity to activate.
targetname : activating trigger points to this.
noise : play this noise when triggered
message : centerprint this text string when triggered
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
MONSTER : &1 monsters won't activate this trigger unless this flag is set
NOT_PLAYER : &2 players can't trigger this one (for those triggered by other triggers)
TRIGGERED : &4 spawns as triggered and must wait for the "wait" key to pass to be re-triggered
-------- NOTES --------
Wait key will be ignored. message is untested*/

void SP_trigger_once(edict_t *ent)
{
/*	// make old maps work because I messed up on flag assignments here
	// triggered was on bit 1 when it should have been on bit 4
	if (ent->spawnflags & 1)
	{
		vec3_t	v;

		VectorMA (ent->r.mins, 0.5, ent->r.size, v);
		ent->spawnflags &= ~1;
		ent->spawnflags |= 4;

		if (developer->integer)
			G_Printf ("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
	}*/
	ent->wait = -1;
	SP_trigger_multiple (ent);
}

/*QUAKED trigger_relay (.5 .5 .5) ? (-8 -8 -8) (8 8 8)
This fixed size trigger cannot be touched, it can only be fired by other events.
-------- KEYS --------
target : this points to the entity to activate.
targetname : activating trigger points to this.
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- NOTES --------
Trigger_relay is a tool for use in entities meccanos. It's of no use by itself, and can only be used as an intermediary between events. Wait key will be ignored.*/
void trigger_relay_use( edict_t *self, edict_t *other, edict_t *activator )
{
	G_UseTargets( self, activator );
}

void SP_trigger_relay( edict_t *self )
{
	self->use = trigger_relay_use;
}

/*
==============================================================================

trigger_counter

==============================================================================
*/

/*QUAKED trigger_counter (.5 .5 .5) ? NOMESSAGE NOSOUNDS
Acts as an intermediary for an action that takes multiple inputs. Example: a sequence of several buttons to activate a event
-------- KEYS --------
target : this points to the entity to activate.
targetname : activating trigger points to this.
count : number of actions to count (default 2)
noise_start : sound to play each time a event happens
noise_stop : sound to play at the last event in the count
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
NOMESSAGE : &1 if not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished.
NOSOUNDS : &2 if not set, it will try to play the noise_start and noise_stop sounds
-------- NOTES --------
Sounds like this one should be a target and not a trigger, but well...*/
void trigger_counter_use( edict_t *self, edict_t *other, edict_t *activator )
{
	if( self->count == 0 )
		return;
	
	self->count--;

	if( self->count )
	{
		if( !(self->spawnflags & 1) )
			G_CenterPrintMsg( activator, "%i more to go...", self->count );
		if( !(self->spawnflags & 2))
			G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_start, 1, ATTN_NORM );
		return;
	}
	
	if( !(self->spawnflags & 1) )
		G_CenterPrintMsg( activator, "Sequence completed!" );
	if( !(self->spawnflags & 2) )
			G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_end, 1, ATTN_NORM );
	self->activator = activator;
	multi_trigger( self );
}

void SP_trigger_counter( edict_t *self )
{
	self->wait = -1;
	if( !self->count )
		self->count = 2;

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		self->s.team = st.gameteam;
	} else {
		self->s.team = TEAM_SPECTATOR;
	}

	G_AssignMoverSounds( self, NULL, NULL, NULL );

	self->use = trigger_counter_use;
}


/*
==============================================================================

trigger_always

==============================================================================
*/


/*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
Automatic trigger. It will fire the entities it targets as soon as it spawns in the game.
-------- KEYS --------
target : fire entities with this targetname.
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
*/
void SP_trigger_always (edict_t *ent)
{
	// we must have some delay to make sure our use targets are present
	if (ent->delay < 0.2f)
		ent->delay = 0.2f;
	G_UseTargets(ent, ent);
}


/*
==============================================================================

trigger_push

==============================================================================
*/

/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
This is used to create jump pads and launch ramps. It MUST point to a target_position or info_notnull entity to work.
-------- KEYS --------
target : this points to the target_position to which the player will jump.
noise : override default noise ("silent" doesn't make any noise)
wait : time before it can be triggered again.
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
PUSH_ONCE : &1 only push when touched the first time
-------- NOTES --------
To make a jump pad or launch ramp, place the target_position/info_notnull entity at the highest point of the jump and target it with this entity.
*/

void G_JumpPadSound( edict_t *ent )
{
	vec3_t org;

	if( !ent->s.modelindex )
		return;

	if( !ent->moveinfo.sound_start )
		return;
		
	org[0] = ent->s.origin[0] + 0.5 * (ent->r.mins[0] + ent->r.maxs[0]);
	org[1] = ent->s.origin[1] + 0.5 * (ent->r.mins[1] + ent->r.maxs[1]);
	org[2] = ent->s.origin[2] + 0.5 * (ent->r.mins[2] + ent->r.maxs[2]);

	G_PositionedSound( org, ent, CHAN_AUTO, ent->moveinfo.sound_start, 1.0f, ATTN_NORM );
}

#define PUSH_ONCE		1

void trigger_push_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	edict_t *event;
	float time, dist, f;
	vec3_t origin, velocity;

	if( self->timestamp >= level.time )
		return;
	if( !other->r.client )
		return;
	if( other->r.client->ps.pmove.pm_type != PM_NORMAL )
		return;
	if( self->s.team && self->s.team != other->s.team )
		return;

	VectorAdd( self->r.absmin, self->r.absmax, origin );
	VectorScale( origin, 0.5, origin );
	time = sqrt( (self->movetarget->s.origin[2] - origin[2]) / (0.5 * g_gravity->value) );
	if (!time)
		goto remove;

	VectorSubtract( self->movetarget->s.origin, origin, velocity );
	velocity[2] = 0;
	dist = VectorNormalize( velocity );

	f = dist / time;
	VectorScale( velocity, f, velocity );
	velocity[2] = time * g_gravity->value;

	other->r.client->jumppad_time = level.time;
	other->r.client->fall_value = 0.0f;	// jal : remove latched fall damage
	VectorCopy( velocity, other->velocity );

	// don't take falling damage immediately from this
	VectorCopy( other->velocity, other->r.client->oldvelocity );

	G_JumpPadSound( self ); // play jump pad sound

	// add an event
	event = G_SpawnEvent( EV_JUMP_PAD, 0, other->s.origin );
	event->r.svflags = SVF_NOOLDORIGIN;
	event->s.ownerNum = other - game.edicts;
	event->s.targetNum = self - game.edicts;

	// reset walljump counter
	other->r.client->ps.pmove.stats[PM_STAT_WJCOUNT] = 0;

	if( !(self->spawnflags & PUSH_ONCE) )
	{
		self->timestamp = level.time + self->wait;
		return;
	}

remove:
	// we can't just remove (self) here, because this is a touch function
	// called while looping through area links...
	self->touch = NULL;
	self->nextthink = level.timemsec + game.framemsec;
	self->think = G_FreeEdict;
}

void S_trigger_push_think( edict_t *ent )
{
	ent->movetarget = G_PickTarget( ent->target );
	if( !ent->movetarget )
		G_FreeEdict( ent );

}

void SP_trigger_push( edict_t *self )
{
	InitTrigger( self );

	if( st.noise && Q_stricmp( st.noise, "default") ) {
		if( Q_stricmp( st.noise, "silent") )
			self->moveinfo.sound_start = trap_SoundIndex( st.noise );
	} else
		self->moveinfo.sound_start = trap_SoundIndex( S_JUMPPAD );

	self->touch = trigger_push_touch;
	self->think = S_trigger_push_think;
	self->nextthink = level.timemsec + game.framemsec;
	self->r.svflags &= ~SVF_NOCLIENT;
	self->s.type = ET_PUSH_TRIGGER;

	self->timestamp = level.time;
	if( !self->wait )
		self->wait = 2 * FRAMETIME;

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		self->s.team = st.gameteam;
	} else {
		self->s.team = TEAM_SPECTATOR;
	}

	trap_LinkEntity( self );
}


/*
==============================================================================

trigger_hurt

==============================================================================
*/

/*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW KILL FALL
Any player that touches this will be hurt by "dmg" points of damage
-------- KEYS --------
dmg : number of points of damage inflicted to player per "wait" time lapse (default 5 - integer values only).
wait : wait time before hurting again (in seconds. Default 0.1)
noise : sound to be played when inflicting damage
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
START_OFF : needs to be triggered (toggle) for damage
TOGGLE : toogle
SILENT : supresses the sizzling sound while player is being hurt.
NO_PROTECTION : player will be hurt regardless of protection (see Notes).
SLOW : changes the damage rate to once per second.
KILL : player will die instantly.
FALL : player will die the next time he touches the ground.
-------- NOTES --------
The invulnerability power-up (item_enviro) does not protect the player from damage caused by this entity regardless of whether the NO_PROTECTION spawnflag is set or not. Triggering a trigger_hurt will have no effect if the START_OFF spawnflag is not set. A trigger_hurt always starts on in the game.*/

void hurt_use( edict_t *self, edict_t *other, edict_t *activator )
{
	if( self->r.solid == SOLID_NOT )
		self->r.solid = SOLID_TRIGGER;
	else
		self->r.solid = SOLID_NOT;
	trap_LinkEntity( self );

	if( !(self->spawnflags & 2) )
		self->use = NULL;
}


void hurt_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	int		dflags;
	int		damage;

	if( !other->takedamage )
		return;
	if( other->deadflag )
		return;
	if( self->timestamp > level.time )
		return;

	if( self->spawnflags & 16 )
		self->timestamp = level.time + 1;
	else
		self->timestamp = level.time + self->wait;

	if( self->s.team && self->s.team != other->s.team )
		return;

	if( self->spawnflags & 64 ) // FALL
	{
		if( !other->r.client->fall_fatal )
		{
			other->r.client->fall_fatal = qtrue;
			// Don't allow falling player to walljump and thus die from the collision to the wall
			other->r.client->ps.pmove.stats[PM_STAT_WJCOUNT] = GS_GameType_MaxWallJumps( game.gametype );
			if( self->noise_index )
				G_Sound( other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM );
		}

		return;
	}

	damage = self->dmg;

	if( self->spawnflags & 8)
		dflags = DAMAGE_NO_PROTECTION;
	else
		dflags = 0;

	if( self->spawnflags & 32 ) // KILL
	{
		dflags = DAMAGE_NO_PROTECTION;
		damage = ceil(other->health) - GIB_HEALTH + 1;
		if( self->noise_index )
			G_Sound( other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM );
	} 
	else if( !(self->spawnflags & 4) && self->noise_index )
	{
		if( (level.framenum % 10) == 0) //jalfixme: we should not use framenum, but I don't know very well what sound is this one
			G_Sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM );
	}

	T_Damage( other, self, world, vec3_origin, other->s.origin, vec3_origin, damage, self->dmg, dflags, MOD_TRIGGER_HURT );
}

void SP_trigger_hurt( edict_t *self )
{
	InitTrigger( self );

	if( self->spawnflags & 4 ) // SILENT
		self->noise_index = 0;
	else if( st.noise )
		self->noise_index = trap_SoundIndex( st.noise );
	else if( self->spawnflags & 32 || self->spawnflags & 64 ) // KILL or FALL
		self->noise_index = trap_SoundIndex( S_PLAYER_FALLDEATH );
	else
		self->noise_index = 0;
	
	self->touch = hurt_touch;

	if( !self->dmg )
		self->dmg = 5;

	if( !self->wait )
		self->wait = 0.1f;

	if( self->spawnflags & 1 )
		self->r.solid = SOLID_NOT;
	else
		self->r.solid = SOLID_TRIGGER;

	if( self->spawnflags & 2 )
		self->use = hurt_use;

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		self->s.team = st.gameteam;
	} else {
		self->s.team = TEAM_SPECTATOR;
	}

	trap_LinkEntity( self );
}


/*
==============================================================================

trigger_gravity

==============================================================================
*/

/*QUAKED trigger_gravity (.5 .5 .5) ? 
Any player that touches this will change his gravity fraction. 1.0 is standard gravity
-------- KEYS --------
gravity : fraction of gravity to use. (Default 1.0)
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- NOTES --------
Changes the touching entites gravity to the value of "gravity".  1.0 is standard gravity for the level.*/
void trigger_gravity_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	if( self->s.team && self->s.team != other->s.team )
		return;

	other->gravity = self->gravity;
}

void SP_trigger_gravity( edict_t *self )
{
	if( st.gravity == 0 )
	{
		if( developer->integer )
			G_Printf( "trigger_gravity without gravity set at %s\n", vtos(self->s.origin) );
		G_FreeEdict( self );
		return;
	}

	InitTrigger( self );
	self->gravity = atof( st.gravity );
	self->touch = trigger_gravity_touch;

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		self->s.team = st.gameteam;
	} else {
		self->s.team = TEAM_SPECTATOR;
	}
}


/*
==============================================================================

trigger_monsterjump

==============================================================================
*/

/*QUAKED trigger_monsterjump (.5 .5 .5) ?
Walking monsters that touch this will jump in the direction of the trigger's angle
"speed" default to 200, the speed thrown forward
"height" default to 200, the speed thrown upwards
*/

void trigger_monsterjump_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	if( other->flags & (FL_FLY | FL_SWIM) )
		return;
	if( other->r.svflags & SVF_CORPSE )
		return;
	if( !(other->r.svflags & SVF_MONSTER) )
		return;

	if( self->s.team && self->s.team != other->s.team )
		return;

// set XY even if not on ground, so the jump will clear lips
	other->velocity[0] = self->movedir[0] * self->speed;
	other->velocity[1] = self->movedir[1] * self->speed;
	
	if( !other->groundentity )
		return;
	
	other->groundentity = NULL;
	other->velocity[2] = self->movedir[2];
}

void SP_trigger_monsterjump( edict_t *self )
{
	if( !self->speed )
		self->speed = 200;
	if( !st.height )
		st.height = 200;
	if( self->s.angles[YAW] == 0 )
		self->s.angles[YAW] = 360;
	InitTrigger( self );
	self->touch = trigger_monsterjump_touch;
	self->movedir[2] = st.height;
}

/*--------------------------------------------------------------------------
 * just here to help old map conversions
 *--------------------------------------------------------------------------*/

/*QUAKED trigger_teleport (.5 .5 .5) ? SPECTATOR
Players touching this will be teleported. Target it to a misc_teleporter_dest.
-------- KEYS --------
target : this points to the entity to activate.
targetname : activating trigger points to this.
noise : play this noise when triggered
wait : time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once).
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
-------- SPAWNFLAGS --------
SPECTATOR : &1 only teleport players moving in spectator mode
-------- NOTES --------
Target it to a misc_teleporter_dest.*/

static void old_teleporter_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	edict_t		*dest;
	edict_t		*event;
	int			i;
	vec3_t		forward;
	float		speed; //jal: mantain speed when teleporting

	if( !other->r.client ) {
		return;
	} 
	if( self->spawnflags & 1 ) {
		if( other->r.client->ps.pmove.pm_type != PM_SPECTATOR )
			return;
	} else {
		if( other->r.client->ps.pmove.pm_type == PM_DEAD )
			return;
	}

	if( self->s.team && self->s.team != other->s.team )
		return;

	// wait delay
	if( self->timestamp > level.time )
		return;
	self->timestamp = level.time + self->wait;

	dest = G_Find( NULL, FOFS(targetname), self->target );

	if( !dest )
	{
		if( developer->integer )
			G_Printf( "Couldn't find destination.\n" );
		return;
	}

	// unlink to make sure it can't possibly interfere with KillBox
	trap_UnlinkEntity( other );

	// draw the teleport splash at source and on the player
	event = G_SpawnEvent( EV_PLAYER_TELEPORT_OUT, 0, other->s.origin );
	event->r.svflags = SVF_NOOLDORIGIN;
	event->s.ownerNum = ENTNUM( other );

	VectorCopy( dest->s.origin, other->s.origin );
	VectorCopy( dest->s.origin, other->s.old_origin );
	G_AddEvent( other, EV_TELEPORT, 0, qtrue );

	//jal: Don't clear the velocity in teleporters, but let the moveflag in
	other->r.client->ps.pmove.pm_time = 1;
	other->velocity[2] = 0; //ignore vertical velocity
	speed = VectorLengthFast( other->velocity );
	other->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;

	event = G_SpawnEvent( EV_PLAYER_TELEPORT_IN, 0, other->s.origin );
	event->r.svflags = SVF_NOOLDORIGIN;
	event->s.ownerNum = ENTNUM( other );

	//play custom sound if any (played from the teleporter entrance)
	if( self->noise_index ) {
		vec3_t org;
		if( self->s.modelindex ) {
			org[0] = self->s.origin[0] + 0.5 * (self->r.mins[0] + self->r.maxs[0]);
			org[1] = self->s.origin[1] + 0.5 * (self->r.mins[1] + self->r.maxs[1]);
			org[2] = self->s.origin[2] + 0.5 * (self->r.mins[2] + self->r.maxs[2]);
		} else
			VectorCopy( self->s.origin, org );
		G_PositionedSound( org, self, CHAN_AUTO, self->noise_index, 1.0f, ATTN_NORM );
	}

	// set angles
	other->s.angles[PITCH] = 0;
	other->s.angles[YAW] = dest->s.angles[YAW];
	other->s.angles[ROLL] = 0;
	VectorCopy( dest->s.angles, other->r.client->ps.viewangles );
	VectorCopy( dest->s.angles, other->r.client->v_angle );

	// set the delta angle
	for( i = 0 ; i < 3 ; i++ )
		other->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT(other->s.angles[i]) - other->r.client->pers.cmd_angles[i];

	// give a little forward velocity
	AngleVectors( other->r.client->v_angle, forward, NULL, NULL );
	VectorScale( forward, speed, other->velocity );//jal: mantain player velocity

	// kill anything at the destination
	if( !KillBox (other) )
	{
	}

	trap_LinkEntity( other );
}

void SP_trigger_teleport( edict_t *ent )
{
	if( !ent->target )
	{
		if( developer->integer )
			G_Printf( "teleporter without a target.\n" );
		G_FreeEdict( ent );
		return;
	}

	if( st.noise )
		ent->noise_index = trap_SoundIndex( st.noise );

	InitTrigger ( ent );
	ent->touch = old_teleporter_touch;

	// gameteam field from editor
	if( st.gameteam >= TEAM_SPECTATOR && st.gameteam < GS_MAX_TEAMS ) {
		ent->s.team = st.gameteam;
	} else {
		ent->s.team = TEAM_SPECTATOR;
	}
}

/*QUAKED info_teleport_destination (0.5 0.5 0.5) (-16 -16 -24) (16 16 32)
You can point trigger_teleports at these.
-------- KEYS --------
targetname : must match the target key of entity that uses this for pointing.
notsingle : when set to 1, entity will not spawn in Single Player mode
notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. 
notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo)
*/
void SP_info_teleport_destination( edict_t *ent )
{
	ent->s.origin[2] += 16;
}


