//=============================================================================
// MosqueTeleporter
//
// 1. If bForcedYaw is set True, the incoming Pawn will be set to the yaw of the teleporter.
//
// 2. If AutoDisable is set True, the Teleporter will disable itself after a Pawn teleports.
//
// 3. If OneShotURL is defined, when the teleporter is enabled it will arm that URL instead of the standard one.
//    Immediately after one Pawn teleports, the original URL will be restored.
//
// 4. If TriggerOnlyEnable is set True, all triggering events will be considered enabling events, as opposed to
//    the normal toggling scenario.
//
// 5. If AutoDisable is set True AND WindowOfOpportunity is > 0.0, when the teleporter is enabled a timer will be set.
//    If a Pawn has not used the teleporter before that window closes, the teleporter will disable anyway. This feature
//    requires that bStatic is False, and therefore bMovable is False also.
//
// Along with these features, there are three new events the teleporter will transmit, if they are defined.
//
// 1. EnabledEvent - messaged every time the teleporter is enabled.
// 2. DisabledEvent - messaged every time the teleporter is disabled.
// 3. URLRestorationEvent - If OneShotURL is defined, messaged every time the original URL is restored.
//
// Plenty of opportunities here, I can't even enumerate all of them.
//=============================================================================

class MosqueTeleporter extends Teleporter placeable;

var()	Bool	AutoDisable;
var()	Name	EnabledEvent;
var()	Name	DisabledEvent;
var()	Name	URLRestorationEvent;
var()	Bool	bForcedYaw;
var()	Float	WindowOfOpportunity;
var()	String	OneShotURL;
var()	Bool	TriggerOnlyEnables;
var	String	OriginalURL;

simulated function PostBeginPlay()
{
super.PostBeginPlay();
OriginalURL = URL; //make a copy of the original destination URL
}

//=============================================================================
function simulated SelfEnable()
{
local Actor	A;

bEnabled = True;
ForEach TouchingActors(class'Actor',A) PostTouch(A);
if (OneShotURL != "") URL = OneShotURL;
if (WindowOfOpportunity > 0.0) SetTimer(WindowOfOpportunity,False);
if (EnabledEvent != '') TriggerEvent(EnabledEvent,self,None);
}

//=============================================================================
function simulated SelfDisable()
{
bEnabled = False;
if (DisabledEvent != '') TriggerEvent(DisabledEvent,self,None);
}

//=============================================================================
function simulated RestoreOriginalURL()
{
if (URL != OriginalURL)
	{
	URL = OriginalURL;
	if (URLRestorationEvent != '') TriggerEvent(URLRestorationEvent,self,None);
	}
}

//=============================================================================
simulated function PostTouch(actor Other)
{
super.PostTouch(Other); //do what always has been done
RestoreOriginalURL(); //set the URL back to whatever it was originally
if (AutoDisable) SelfDisable();
}

//=============================================================================
simulated function Timer()
{
SelfDisable();
RestoreOriginalURL();
}

//=============================================================================
simulated function Trigger(Actor Other,Pawn EventInstigator)
{
if (TriggerOnlyEnables) //all triggers enable
	SelfEnable();
else //triggers toggle enable/disable
	{
	if (bEnabled)
		SelfDisable();
	else
		SelfEnable();
	}
}

//=============================================================================
//This function is over-ridden for the bForcedYaw feature.
simulated function Bool Accept(Actor Incoming,Actor Source)
{
local Rotator newRot,oldRot;
local Float mag;
local Vector oldDir;
local Controller P;

if (Incoming == None) return(False);
		
// Move the actor here.
Disable('Touch');
newRot = Incoming.Rotation;
if (bChangesYaw || bForcedYaw)
	{
	oldRot = Incoming.Rotation;
	newRot.Yaw = Rotation.Yaw;
	if (!bForcedYaw && Source != None) newRot.Yaw += (32768 + Incoming.Rotation.Yaw - Source.Rotation.Yaw);
	}

if (Pawn(Incoming) != None)
	{
	//tell enemies about teleport
	if (Role == ROLE_Authority)
		{
		For (P=Level.ControllerList; P!=None; P=P.NextController)
			{
			if (P.Enemy == Incoming) P.LineOfSightTo(Incoming);
			}
		}

	if (!Pawn(Incoming).SetLocation(Location))
		{
		log(self$" Teleport failed for "$Incoming);
		return(False);
		}

	if ((Role == ROLE_Authority) || (Level.TimeSeconds - LastFired > 0.5))
		{
		if (bChangesYaw || bForcedYaw)
			{
			newRot.Roll = 0;
			Pawn(Incoming).SetRotation(newRot);
			Pawn(Incoming).SetViewRotation(newRot);
			Pawn(Incoming).ClientSetRotation(newRot);
			}

		LastFired = Level.TimeSeconds;
		}

	if (Pawn(Incoming).Controller != None)
		{
		Pawn(Incoming).Controller.MoveTimer = -1.0;
		Pawn(Incoming).Anchor = self;
		Pawn(Incoming).SetMoveTarget(self);
		}

	Incoming.PlayTeleportEffect(False,True);
	}
else
	{
	if (!Incoming.SetLocation(Location))
		{
		Enable('Touch');
		return(False);
		}

	if (bChangesYaw || bForcedYaw) Incoming.SetRotation(newRot);
	}

Enable('Touch');

if (bChangesVelocity)
	{
	Incoming.Velocity = TargetVelocity;
	}
else
	{
	if (bChangesYaw)
		{
		if (Incoming.Physics == PHYS_Walking) OldRot.Pitch = 0;
		oldDir = vector(OldRot);
		mag = Incoming.Velocity Dot oldDir;		
		Incoming.Velocity = Incoming.Velocity - mag * oldDir + mag * vector(Incoming.Rotation);
		}

	if (bReversesX) Incoming.Velocity.X *= -1.0;
	if (bReversesY) Incoming.Velocity.Y *= -1.0;
	if (bReversesZ) Incoming.Velocity.Z *= -1.0;
	}
	
return(True);
}

defaultproperties
{
bForcedYaw=False;
TriggerOnlyEnables=False;
AutoDisable=False;
WindowOfOpportunity=0.0;
OneShotURL="";
bStatic=False;
bMovable=False;
}