//=============================================================================
// Pickup items.
//
// Pickup is the base class of actors that when touched by an appropriate pawn,
// will create and place an Inventory actor in that pawn's inventory chain.  Each
// pickup class has an associated inventory class (its InventoryType).  Pickups are
// placed by level designers.  Pickups can only interact with pawns when in their
// default Pickup state.  Pickups verify that they can give inventory to a pawn by
// calling the GameInfo's PickupQuery() function.  After a pickup spawns an inventory
// item for a pawn, it then queries the GameInfo by calling the GameInfo's
// ShouldRespawn() function about whether it should remain active, enter its Sleep
// state and later become active again, or destroy itself.
//
// When navigation paths are built, each pickup has an InventorySpot (a subclass
// of NavigationPoint) placed on it and associated with it
// (the Pickup's MyMarker== the InventorySpot,
// and the InventorySpot's markedItem == the pickup).
//
//=============================================================================
class Pickup extends Actor
	abstract
	placeable
	native
	nativereplication;

//-----------------------------------------------------------------------------
// AI related info.

var  float		  MaxDesireability;		// Maximum desireability this item will ever have.
var	  InventorySpot MyMarker;
var	  NavigationPoint PickupCache;		// used for dropped pickups
var() class<Inventory> InventoryType;
var() bool		bInstantRespawn;	  // Can be tagged so this item respawns instantly.
var	  bool		bOnlyReplicateHidden;	// only replicate changes in bHidden (optimization for level pickups)
var(Display) bool bAmbientGlow;		  // Whether to glow or not.
var		bool	bDropped;
var		bool	bPredictRespawns;	  // high skill bots may predict respawns for this item
var() float     RespawnTime;          // Respawn after this time, 0 for instant.
var	 float RespawnEffectTime;

var() localized string PickupMessage; // Human readable description when picked up.
var() sound PickupSound;
var() string PickupForce;  // jdf
var() xPickUpBase PickUpBase;          // Pick-up base which spawned this pickup.

var Controller TeamOwner[4];		// AI controller currently going after this pickup (for team coordination)

// #ifdef _KF_
var() bool bExcludeFromNavigation;  // If true, this pickup won't become part of the navigation network.
// #endif

native final function AddToNavigation();			// cache dropped inventory in navigation network
native final function RemoveFromNavigation();

static function StaticPrecache(LevelInfo L);

function Destroyed()
{
	if (MyMarker != None )
		MyMarker.markedItem = None;
	if (Inventory != None )
		Inventory.Destroy();
}

simulated function String GetHumanReadableName()
{
	if ( InventoryType == None )
		return GetItemName(string(class));
	if ( InventoryType.Default.ItemName == "" )
		return GetItemName(string(InventoryType));

	return InventoryType.Default.ItemName;
}

function bool IsSuperItem()
{
	return (PickupBase != None) && PickupBase.bDelayedSpawn;
}

simulated static function UpdateHUD(HUD H)
{
	H.LastPickupTime = H.Level.TimeSeconds;
}

/* Reset()
reset actor to initial state - used when restarting level without reloading.
*/
function Reset()
{
	if ( Inventory != None )
		destroy();
	else
	{
		if (myMarker != None && myMarker.bSuperPickup)
			GotoState('Sleeping', 'DelayedSpawn');
		else
			GotoState('Pickup');
		Super.Reset();
	}
}

function RespawnEffect();

// Turns the pickup into a different type of pickup - specificly used by the WildcardCharger
function Pickup Transmogrify(class<Pickup> NewClass) // de
{
	local Pickup NewPickup;

	NewPickup = Spawn(NewClass);
	NewPickup.PickupBase = PickupBase;
	NewPickup.RespawnTime = RespawnTime;

	if (MyMarker != None )
	{
		MyMarker.markedItem = NewPickup;
		NewPickup.MyMarker = MyMarker;
		MyMarker = None;
	}
	Destroy();

	return NewPickup;
}

/* DetourWeight()
value of this path to take a quick detour (usually 0, used when on route to distant objective, but want to grab inventory for example)
*/
function float DetourWeight(Pawn Other,float PathWeight)
{
	return 0;
}

/* Pickups have an AI interface to allow AIControllers, such as bots, to assess the
 desireability of acquiring that pickup.  The BotDesireability() method returns a
 float typically between 0 and 1 describing how valuable the pickup is to the
 AIController.  This method is called when an AIController uses the
 FindPathToBestInventory() navigation intrinsic.
*/
function float BotDesireability( pawn Bot )
{
	local Inventory AlreadyHas;
	local float desire;

	desire = MaxDesireability;

	if ( RespawnTime < 10 )
	{
		AlreadyHas = Bot.FindInventoryType(InventoryType);
		if ( AlreadyHas != None )
		{
			if ( Inventory != None )
			{
				if( Inventory.Charge <= AlreadyHas.Charge )
					return -1;
			}
			else if ( InventoryType.Default.Charge <= AlreadyHas.Charge )
				return -1;
		}
	}
	return desire;
}

// Either give this inventory to player Other, or spawn a copy
// and give it to the player Other, setting up original to be respawned.
//
function inventory SpawnCopy( pawn Other )
{
	local inventory Copy;

	if ( Inventory != None )
	{
		Copy = Inventory;
		Inventory = None;
	}
	else
		Copy = spawn(InventoryType,Other,,,rot(0,0,0));

	Copy.GiveTo( Other, self );

	return Copy;
}

function StartSleeping()
{
    if (bDropped)
        Destroy();
    else
	    GotoState('Sleeping');
}

function AnnouncePickup( Pawn Receiver )
{
	Receiver.HandlePickup(self);
	PlaySound( PickupSound,SLOT_Interact );
}

//
// Set up respawn waiting if desired.
//
function SetRespawn()
{
	if( Level.Game.ShouldRespawn(self) )
		StartSleeping();
	else
		Destroy();
}

// HUD Messages

static function string GetLocalString(
	optional int Switch,
	optional PlayerReplicationInfo RelatedPRI_1,
	optional PlayerReplicationInfo RelatedPRI_2
	)
{
	return Default.PickupMessage;
}

function InitDroppedPickupFor(Inventory Inv)
{
	SetPhysics(PHYS_Falling);
	GotoState('FallingPickup');
	Inventory = Inv;
	bAlwaysRelevant = false;
	bOnlyReplicateHidden = false;
	bUpdateSimulatedPosition = true;
    bDropped = true;
    LifeSpan = 16;
	bIgnoreEncroachers = false; // handles case of dropping stuff on lifts etc
	NetUpdateFrequency = 8;
}

function bool ReadyToPickup(float MaxWait)
{
	return false;
}

event Landed(Vector HitNormal)
{
    GotoState('Pickup','Begin');
}

//=============================================================================
// Pickup state: this inventory item is sitting on the ground.

auto state Pickup
{
	function bool ReadyToPickup(float MaxWait)
	{
		return true;
	}

	/* ValidTouch()
	 Validate touch (if valid return true to let other pick me up and trigger event).
	*/
	function bool ValidTouch( actor Other )
	{
		// make sure its a live player
		if ( (Pawn(Other) == None) || !Pawn(Other).bCanPickupInventory || (Pawn(Other).DrivenVehicle == None && Pawn(Other).Controller == None) )
			return false;

		// make sure not touching through wall
		if ( !FastTrace(Other.Location, Location) )
			return false;

		// make sure game will let player pick me up
		if( Level.Game.PickupQuery(Pawn(Other), self) )
		{
			TriggerEvent(Event, self, Pawn(Other));
			return true;
		}
		return false;
	}

	// When touched by an actor.
	function Touch( actor Other )
	{
		local Inventory Copy;

		// If touched by a player pawn, let him pick this up.
		if( ValidTouch(Other) )
		{
			Copy = SpawnCopy(Pawn(Other));
			AnnouncePickup(Pawn(Other));
            SetRespawn();
            if ( Copy != None )
				Copy.PickupFunction(Pawn(Other));
		}
	}

	// Make sure no pawn already touching (while touch was disabled in sleep).
	function CheckTouching()
	{
		local Pawn P;

		ForEach TouchingActors(class'Pawn', P)
			Touch(P);
	}

	function Timer()
	{
		if ( bDropped )
			GotoState('FadeOut');
	}

	function BeginState()
	{
		UntriggerEvent(Event, self, None);
		if ( bDropped )
        {
			AddToNavigation();
		    SetTimer(8, false);
        }
	}

	function EndState()
	{
		if ( bDropped )
			RemoveFromNavigation();
	}

Begin:
	CheckTouching();
}

state FallingPickup extends Pickup
{
	function CheckTouching()
	{
	}

	function Timer()
	{
		GotoState('FadeOut');
	}

	function BeginState()
	{
	    SetTimer(8, false);
	}
}

State FadeOut extends Pickup
{
	function Tick(float DeltaTime)
	{
		SetDrawScale(FMax(0.01, DrawScale - Default.DrawScale * DeltaTime));
	}

	function BeginState()
	{
		RotationRate.Yaw=60000;
		SetPhysics(PHYS_Rotating);
		LifeSpan = 1.0;
	}

	function EndState()
	{
		LifeSpan = 0.0;
		SetDrawScale(Default.DrawScale);
		if ( Physics == PHYS_Rotating )
			SetPhysics(PHYS_None);
	}
}

//=============================================================================
// Sleeping state: Sitting hidden waiting to respawn.
function float GetRespawnTime()
{
	return RespawnTime;
}

State WaitingForMatch
{
	ignores Touch;

	function MatchStarting()
	{
		GotoState( 'Sleeping', 'DelayedSpawn' );
	}

	function BeginState()
	{
		NetUpdateTime = Level.TimeSeconds - 1;
		bHidden = true;
	}
}

State Sleeping
{
	ignores Touch;

	function bool ReadyToPickup(float MaxWait)
	{
		return ( bPredictRespawns && (LatentFloat < MaxWait) );
	}

	function StartSleeping() {}

	function BeginState()
	{
		local int i;

		NetUpdateTime = Level.TimeSeconds - 1;
		bHidden = true;
		SetCollision(false, false);
		for ( i=0; i<4; i++ )
			TeamOwner[i] = None;
	}

	function EndState()
	{
		NetUpdateTime = Level.TimeSeconds - 1;
		bHidden = false;
		SetCollision(default.bCollideActors, default.bBlockActors );
	}

DelayedSpawn:
	if ( Level.NetMode == NM_Standalone )
		Sleep(FMin(30, Level.Game.GameDifficulty * 8));
	else
		Sleep(30);
	Goto('Respawn');
Begin:
	Sleep( GetReSpawnTime() - RespawnEffectTime );
Respawn:
	RespawnEffect();
	Sleep(RespawnEffectTime);
    if (PickUpBase != None)
		PickUpBase.TurnOn();
    GotoState('Pickup');
}

State Disabled
{
	function bool ReadyToPickup(float MaxWait)
	{
		return false;
	}

	function Reset() {}
	function StartSleeping() {}

	simulated function BeginState()
	{
		MaxDesireability = 0;
		bHidden = true;
		ResetStaticFilterState();
		SetCollision(false,false,false);
	}
}

defaultproperties
{
	RespawnEffectTime=+0.5
	bOnlyDirtyReplication=true
    NetUpdateFrequency=0.1
    PickupMessage="Snagged an item."
    bAlwaysRelevant=true
    RemoteRole=ROLE_DumbProxy
    bHidden=false
    NetPriority=+1.4
    bCollideActors=true
    bCollideWorld=true
    bAmbientGlow=false
    bFixedRotationDir=True
    RotationRate=(Yaw=5000)
    DesiredRotation=(Yaw=30000)
    Texture=Texture'Engine.S_Inventory'
    MaxDesireability=0.1000
    DrawType=DT_Mesh
    bOnlyReplicateHidden=true
    bOrientOnSlope=true
    bUseCylinderCollision=true
    bShouldBaseAtStartup=true
	bIgnoreEncroachers=true
	bIgnoreVehicles=true
	CullDistance=+8000.0
}
