/*=============================================================================
// LadderVolumes, when touched, cause ladder supporting actors to use Phys_Ladder.
// note that underwater ladders won't be waterzones (no breathing problems)
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
============================================================================= */

class LadderVolume extends PhysicsVolume
	native
	placeable;

var() rotator WallDir;
var vector LookDir;
var vector ClimbDir;	// pawn can move in this direction (or reverse)
var const Ladder LadderList;		// list of Ladder actors associated with this LadderVolume
var() bool	bNoPhysicalLadder;	// if true, won't push into/keep player against geometry in lookdir
var() bool	bAutoPath;			// add top and bottom ladders automatically
var() bool  bAllowLadderStrafing;  // if true, players on ladder can strafe sideways

var Pawn PendingClimber;

/** Editor visual cue for the direction of the wall */
var ArrowComponent WallDirArrow;

cpptext
{
	// Editor modification
#if WITH_EDITOR
	FVector FindTop(FVector V);
	FVector FindCenter();
	virtual INT AddMyMarker(AActor *S);
#endif
	virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent);
}

simulated event PostBeginPlay()
{
	local Ladder L, M;
	local vector Dir;

	Super.PostBeginPlay();
	LookDir = vector(WallDir);
	if ( !bAutoPath && (LookDir.Z != 0) )
	{
		ClimbDir = vect(0,0,1);
		for ( L=LadderList; L!=None; L=L.LadderList )
			for ( M=LadderList; M!=None; M=M.LadderList )
				if ( M != L )
				{
					Dir = Normal(M.Location - L.Location);
					if ( (Dir dot ClimbDir) < 0 )
						Dir *= -1;
					ClimbDir += Dir;
				}

		ClimbDir = Normal(ClimbDir);
		if ( (ClimbDir Dot vect(0,0,1)) < 0 )
			ClimbDir *= -1;
	}
}

function bool InUse(Pawn Ignored)
{
	local Pawn StillClimbing;

	ForEach TouchingActors(class'Pawn',StillClimbing)
	{
		if ( (StillClimbing != Ignored) && StillClimbing.bCollideActors && StillClimbing.bBlockActors )
			return true;
	}

	if ( PendingClimber != None )
	{
		if ( (PendingClimber.Controller == None)
			|| !PendingClimber.bCollideActors || !PendingClimber.bBlockActors
			|| (Ladder(PendingClimber.Controller.MoveTarget) == None)
			|| (Ladder(PendingClimber.Controller.MoveTarget).MyLadder != self) )
				PendingClimber = None;
	}
	return ( (PendingClimber != None) && (PendingClimber != Ignored) );
}

simulated event PawnEnteredVolume(Pawn P)
{
	local rotator PawnRot;

	Super.PawnEnteredVolume(P);
	if ( !P.CanGrabLadder() )
		return;

	PawnRot = P.Rotation;
	PawnRot.Pitch = 0;
	if ( (vector(PawnRot) Dot LookDir > 0.9)
		|| ((AIController(P.Controller) != None) && (Ladder(P.Controller.MoveTarget) != None)) )
		P.ClimbLadder(self);
	else if ( !P.bDeleteMe && (P.Controller != None) )
		spawn(class'PotentialClimbWatcher',P);
}

simulated event PawnLeavingVolume(Pawn P)
{
	local Controller C;

	if ( P.OnLadder != self )
		return;
	Super.PawnLeavingVolume(P);
	P.OnLadder = None;
	P.EndClimbLadder(self);
	if ( P == PendingClimber )
		PendingClimber = None;

	// tell all waiting pawns, if not in use
	if ( !InUse(P) )
	{
		foreach WorldInfo.AllControllers(class'Controller', C)
		{
			if (C.bPreparingMove && Ladder(C.MoveTarget) != None && Ladder(C.MoveTarget).MyLadder == self)
			{
				C.bPreparingMove = false;
				PendingClimber = C.Pawn;
				return;
			}
		}
	}
}

simulated event PhysicsChangedFor(Actor Other)
{
	if ( (Other.Physics == PHYS_Falling) || (Other.Physics == PHYS_Ladder) || Other.bDeleteMe || (Pawn(Other) == None) || (Pawn(Other).Controller == None) )
		return;
	spawn(class'PotentialClimbWatcher',Other);
}

defaultproperties
{
	Begin Object Class=ArrowComponent Name=Arrow
		ArrowColor=(R=150,G=100,B=150)
		ArrowSize=5.0
	End Object
	WallDirArrow = Arrow
	Components.Add(Arrow)

	Begin Object Name=BrushComponent0
		HiddenEditor=false
	End Object

	RemoteRole=ROLE_SimulatedProxy
	ClimbDir=(X=+0.0,Y=+0.0,Z=+1.0)
	bAutoPath=true
	bAllowLadderStrafing=true
}