DIFF

TrapSpawnBase.c

class TrapSpawnBase extends ItemBase
{
	bool 							m_IsFoldable;
	bool							m_CanCatch = false;
	
	//configurable stuff
	bool 							m_NeedInstalation;
	int   							m_InitWaitTimeMin;
	int   							m_InitWaitTimeMax;
	int  							m_UpdateWaitTime; 			//! Catch evaluation interval
	int  							m_SpawnUpdateWaitTime; 		//! Catch spawn and player check interval (expensive-ish)
	int  							m_MaxActiveTime; 			//! Max time of trap activity (seconds)
	float 							m_BaitLossFraction; 		//! Normalized bait qty reduction on unsuccessful catch
	float 							m_DefectRate; 				//! Absolute damage dealt to trap when used
	float 							m_MinimalDistanceFromPlayersToCatch; //! duh
	
	//derived stuff
	private int   					m_InitWaitTime; 			//! After this time after deployment, the trap is activated
	private int 					m_AdjustedMaxActiveTime; 	//! Adjusted by init wait time, when appropriate
	private int  					m_ElapsedTime;
	private int  					m_ActivationTime;
	private int  					m_RollSuccessTime;
	private float 					m_CurrentMinimalDistance;
	private float 					m_CurrentlyUsedDelta;
	private bool 					m_IsCatchSuccessful;
	private int 					m_CatchEnviroMask = 0;
	private int 					m_YieldItemIdxLocal = -1;
	private int 					m_YieldItemIdx = -1;
	private int 					m_CatchParticleEffecterId = -1;
	
	vector 							m_PreyPos; // The position where prey will be spawned -> Will be overriden later
	
	protected bool 					m_IsActive;
	protected bool 					m_IsPastWaitingTime;
	protected bool 					m_IsDeployed;
	
	ref Timer 						m_Timer;
	
	string 							m_AnimationPhaseSet;
	string 							m_AnimationPhaseTriggered;
	string 							m_AnimationPhaseUsed;

	protected ref array<string>		m_PlaceableWaterSurfaceList;
	
	protected ref CatchingContextTrapsBase 	m_CatchingContext;
	
	#ifdef DEVELOPER
	int m_dbgAttemptCount = 0;
	#endif
	
	void TrapSpawnBase()
	{
		InitTrapValues();
		
		RegisterNetSyncVariableBool("m_IsActive");
		RegisterNetSyncVariableBool("m_IsDeployed");
		RegisterNetSyncVariableInt("m_YieldItemIdx");
		
		//DEPRECATED stuff below, legacy reasons only
		m_CatchesPond 							= new multiMap<string, float>; //yields now in WorldData.InitYieldBank
		m_CatchesSea 							= new multiMap<string, float>; //yields now in WorldData.InitYieldBank
		m_CatchesGroundAnimal 					= new multiMap<string, float>; //yields now in WorldData.InitYieldBank
	}
	
	void ~TrapSpawnBase()
	{
		if (m_Timer)
		{
			m_Timer.Stop();
			delete m_Timer;
		}
		
		ClearCatchingComponent();
	}
	
	void InitTrapValues()
	{
		m_DefectRate 							= 10; 			//Added damage after trap activation
		m_BaitLossFraction 						= 0.2;
		m_UpdateWaitTime 						= 60;
		m_SpawnUpdateWaitTime 					= 30;
		m_InitWaitTimeMin 						= 10;
		m_InitWaitTimeMax 						= 10;
		m_MaxActiveTime 						= 1200;
		m_NeedInstalation 						= true;
		m_IsFoldable 							= false;
		m_IsCatchSuccessful 					= false;
		m_MinimalDistanceFromPlayersToCatch 	= 0;
		
		m_AnimationPhaseSet 					= "";
		m_AnimationPhaseTriggered 				= "";
		m_AnimationPhaseUsed 					= "";
		
		m_PlaceableWaterSurfaceList 			= new array<string>();
		m_PlaceableWaterSurfaceList.Insert(UAWaterType.SEA);
		m_PlaceableWaterSurfaceList.Insert(UAWaterType.FRESH);
	}
	
	override void OnStoreSave( ParamsWriteContext ctx )
	{
		super.OnStoreSave( ctx );
		
		ctx.Write( m_IsActive );
		
		ctx.Write( m_IsDeployed );
		
		ctx.Write(m_CatchEnviroMask);
	}
	
	override bool OnStoreLoad( ParamsReadContext ctx, int version )
	{   
		if ( !super.OnStoreLoad(ctx, version) )
			return false;
		
		m_IsStoreLoad = true;
		
		bool b_is_active = false;
		if ( !ctx.Read( b_is_active ) )
			b_is_active = false;
		
		bool b_is_in_progress = false;
		if (version < 139)
		{
			if ( !ctx.Read( b_is_in_progress ) )
				b_is_in_progress = false;
		}
		
		bool b_is_deployed = false;
		if ( !ctx.Read( b_is_deployed ) )
			b_is_deployed = false;
		
		if (version >= 139)
		{
			int enviroMask;
			if (ctx.Read(enviroMask))
				m_CatchEnviroMask = enviroMask;
		}
		
		if (b_is_active)
		{
			InitCatchingComponent();
			SetActive();
		}
		
		SetDeployed( b_is_deployed );
		
		m_IsStoreLoad = false;
		return true;
	}

	//! this event is called all variables are synchronized on client
    override void OnVariablesSynchronized()
    {
        super.OnVariablesSynchronized();
				
		if (m_YieldItemIdx != m_YieldItemIdxLocal)
		{
			m_YieldItemIdxLocal = m_YieldItemIdx;
			if (m_YieldItemIdxLocal != -1)
				OnCatchSpawnClient();
		}
	}
	
	bool IsActive()
	{
		return m_IsActive;
	}
	
	bool IsDeployed()
	{
		return m_IsDeployed;
	}
	
	void SetDeployed( bool newState )
	{
		m_IsDeployed = newState;
		
		if ( newState == true )
		{
			if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
			{
				SetAnimationPhase( m_AnimationPhaseSet, 1 );
				SetAnimationPhase( m_AnimationPhaseTriggered, 0 );
				SetAnimationPhase( m_AnimationPhaseUsed, 1 );
			}
		}
		else
		{
			if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
			{
				SetAnimationPhase( m_AnimationPhaseSet, 0 );
				SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
				SetAnimationPhase( m_AnimationPhaseUsed, 1 );
			}
		}
		
		SetSynchDirty();
	}

	override bool IsTakeable()
	{
		return true;
	}

	bool IsPlaceableAtPosition( vector position )
	{
		string surface_type;
		g_Game.SurfaceGetType3D( position[0], position[1], position[2], surface_type);

		// check surface
		return g_Game.IsSurfaceDigable(surface_type);
	}

	void SetupTrap()
	{ 
		if ( g_Game.IsServer() )
		{
			if ( GetHierarchyRootPlayer() && GetHierarchyRootPlayer().CanDropEntity( this ) )
			{
				SetupTrapPlayer( PlayerBase.Cast( GetHierarchyRootPlayer() ) );
			}
		}
	}

	void SetupTrapPlayer( PlayerBase player, bool set_position = true )
	{ 
		if ( g_Game.IsServer() )
		{
			if ( set_position )
			{
				vector trapPos = player.GetPosition() + ( player.GetDirection() * 0.5 );
				trapPos[1] = g_Game.SurfaceRoadY( trapPos[0], trapPos[2] );
				SetPosition( trapPos );
			}
			
			SetDeployed( true );
		}
	}
	
	void UpdatePreyPos()
	{
		m_PreyPos = GetPosition();
		if (MemoryPointExists("Prey_Position"))
			m_PreyPos = ModelToWorld(GetMemoryPointPos("Prey_Position"));
	}
	
	void Fold()
	{
		if ( g_Game.IsServer() && m_IsFoldable == true )
		{
			SetInactive();
		}
	}

	// Deal damage to trap on specific events
	void AddDefect()
	{
		if ( g_Game.IsServer() )
		{
			DecreaseHealth( "", "", m_DefectRate );
		}
	}
	
	void StartActivate( PlayerBase player )		{ }
	
	// IsTakeable is used to hide tooltips as well, so we use a custom method instead
	// Used to prevent players from taking traps which should be set for catching
	bool CanBeTaken()
	{
		if ( !IsDeployed() || ( GetInventory().AttachmentCount() == 0 && IsDeployed() ) )
		{
			return true;
		}
		
		return false;
	}

	override bool CanPutInCargo( EntityAI parent )
	{
		super.CanPutInCargo( parent );
		return CanBeTaken();
	}
	
	override bool CanPutIntoHands( EntityAI parent )
	{
		super.CanPutIntoHands( parent );
		return CanBeTaken();
	}
	
	void ResetActiveProgress()
	{
		m_ActivationTime = g_Game.GetTickTime();
		m_ElapsedTime = 0;
		m_CurrentMinimalDistance = m_MinimalDistanceFromPlayersToCatch;
		
		#ifdef DEVELOPER
		m_dbgAttemptCount = 0;
		#endif
	}
	
	void ResetRunningTimerProgress()
	{
		if (m_IsPastWaitingTime)
		{
			m_AdjustedMaxActiveTime = m_MaxActiveTime;
			RunTrappingTimer(m_UpdateWaitTime,"EvaluateCatch");
		}
		else
		{
			m_AdjustedMaxActiveTime = m_MaxActiveTime + m_InitWaitTime;
			RunTrappingTimer(m_InitWaitTime + m_UpdateWaitTime,"EvaluateCatch");
		}
	}
	
	//! generic trapping launcher for traps, use this to store delta info
	void RunTrappingTimer(float duration, string fnName)
	{
		if (!m_Timer)
			m_Timer = new Timer(CALL_CATEGORY_SYSTEM);
		else
			m_Timer.Stop();
		
		#ifdef DEVELOPER
		if (IsCLIParam("trapsQuick"))
		{
			m_CurrentlyUsedDelta = 1;
			m_Timer.Run(1, this, fnName);
		}
		else
		#endif
		{
			m_CurrentlyUsedDelta = duration;
			m_Timer.Run(duration, this, fnName);
		}
	}
	
	// Set animation phases according to state
	void SetActive()
	{
		UpdatePreyPos();
		
		if ( g_Game.IsServer() && !IsActive() )
		{
			SetCatchSuccessful(false);
			m_IsActive = true;
			m_IsPastWaitingTime = false;
			m_YieldItemIdx = -1;
			
			if (m_CatchParticleEffecterId >= 0)
				SEffectManager.DestroyEffecterParticleServer(m_CatchParticleEffecterId);
			m_CatchParticleEffecterId = -1;
			
			SetSynchDirty();
			
			if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
			{
				SetAnimationPhase( m_AnimationPhaseSet, 1 );
				SetAnimationPhase( m_AnimationPhaseTriggered, 0 );
				SetAnimationPhase( m_AnimationPhaseUsed, 1 );
			}
			
			ResetActiveProgress();
			m_InitWaitTime = Math.RandomFloatInclusive(m_InitWaitTimeMin,m_InitWaitTimeMax);
			if (!m_IsStoreLoad) //presumably activated by the player, store load initializes component separately
			{
				InitCatchingComponent();
				UpdateTrapEnviroMask();
				
				RunTrappingTimer(m_InitWaitTime + m_UpdateWaitTime,"EvaluateCatch");
				m_AdjustedMaxActiveTime = m_MaxActiveTime + m_InitWaitTime;
			}
			else //presumed store load
			{
				SetTrapEnviroMask(m_CatchEnviroMask);
				
				RunTrappingTimer(m_UpdateWaitTime,"EvaluateCatch");
				m_AdjustedMaxActiveTime = m_MaxActiveTime;
			}
		}
	}

	void SetInactive()
	{
		if ( g_Game.IsServer() )
		{
			// We stop timers as the trap is no longer active, then update visuals
			m_IsActive = false;
			
			ClearCatchingComponent();
			
			if ( m_Timer )
			{
				m_Timer.Stop();
			}
			
			m_IsPastWaitingTime = false;
			
			SetDeployed( false );
			
			SetSynchDirty();
		}
	}

	void SetUsed()
	{
		if ( g_Game.IsServer() )
		{
			// We updated state, visuals and stop timers
			m_IsActive = false;
			m_IsDeployed = false;
			
			// Deal damage to trap
			AddDefect();
			HandleBaitLoss();
			DetachAllAttachments();
			
			ClearCatchingComponent();
			
			if ( m_Timer )
			{
				m_Timer.Stop();
			}
			
			m_IsPastWaitingTime = false;
			
			if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
			{
				SetAnimationPhase( m_AnimationPhaseSet, 1 );
				SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
				SetAnimationPhase( m_AnimationPhaseUsed, 0 );
			}
			
			m_CatchingContext = null;
			
			SetSynchDirty();
		}
	}
	
	void IncreaseElapsedTime()
	{
		m_ElapsedTime += m_CurrentlyUsedDelta;
		
		#ifdef DEVELOPER
		if (IsCLIParam("catchingLogs"))
		{
			Print("dbgTrapz | delta: " + m_CurrentlyUsedDelta);
			Print("dbgTrapz | m_ElapsedTime: " + m_ElapsedTime);
			Print("dbgTrapz | m_AdjustedMaxActiveTime: " + m_AdjustedMaxActiveTime);
		}
		#endif
	}
	
	void AdjustDetectionRange()
	{
		if (m_CurrentMinimalDistance > 0)
		{
			float time = m_ElapsedTime - m_RollSuccessTime;
			float timeLimit = m_AdjustedMaxActiveTime - m_RollSuccessTime;
			time = Math.InverseLerp(0,timeLimit,time);
			time = Easing.EaseInQuad(time);
			m_CurrentMinimalDistance = Math.Lerp(m_MinimalDistanceFromPlayersToCatch,0,time);
			m_CurrentMinimalDistance = Math.Clamp(m_CurrentMinimalDistance,0,m_MinimalDistanceFromPlayersToCatch);
			
			#ifdef DEVELOPER
			if (IsCLIParam("catchingLogs"))
			{
				Print("dbgTrapz | adjusted distance: " + m_CurrentMinimalDistance + "/" + m_MinimalDistanceFromPlayersToCatch + " | LERP progress: " + time);
			}
			#endif
		}
	}
	
	void EvaluateCatch()
	{
		#ifdef DEVELOPER
		m_dbgAttemptCount++;
		#endif
		
		m_IsPastWaitingTime = true;
		IncreaseElapsedTime();
		
		#ifdef DEVELOPER
		if (IsCLIParam("catchingLogs"))
		{
			Print("dbgTrapz | m_dbgAttemptCount: " + m_dbgAttemptCount + "/" + (m_MaxActiveTime/m_UpdateWaitTime));
		}
		#endif
		
		bool success = false;
		m_CanCatch = SetCanCatch(m_Bait);
		
		if (m_CanCatch)
		{
			if (m_CatchingContext.RollCatch())
			{
				success = true;
				
				#ifdef DEVELOPER
				if (IsCLIParam("catchingLogs"))
				{
					Print("dbgTrapz | success!!!");
					Print("---------------------");
				}
				#endif
			}
		}
		
		#ifdef DEVELOPER
		string dbgSuccessOverride;
		if (GetCLIParam("trapsSuccessOverride",dbgSuccessOverride))
		{
			if (dbgSuccessOverride == "true" || dbgSuccessOverride.ToInt() == 1)
				success = true;
			else if (dbgSuccessOverride == "false" || dbgSuccessOverride.ToInt() == 0)
				success = false;
		}
		#endif
		
		m_Timer.Stop();
		
		#ifdef DEVELOPER
		if (m_ElapsedTime >= m_AdjustedMaxActiveTime || (IsCLIParam("trapsQuick") && !success))
		#else
		if (m_ElapsedTime >= m_AdjustedMaxActiveTime)
		#endif
		{
			SetUsed();
			return;
		}
		
		if (success)
		{
			m_RollSuccessTime = m_ElapsedTime;
			m_CurrentlyUsedDelta = 0;
			TrySpawnCatch();
		}
		else
		{
			RunTrappingTimer(m_UpdateWaitTime,"EvaluateCatch");
		}
	}
	
	bool IsPlayerInVicinity()
	{
		if (!GetCEApi())
		{
			Debug.Log("CE not enabled, player avoidance not available!");
			return false;
		}
		
		return !GetCEApi().AvoidPlayer(GetPosition(), m_CurrentMinimalDistance);
	}
	
	void TrySpawnCatch()
	{
		IncreaseElapsedTime();
		AdjustDetectionRange();
		
		#ifdef DEVELOPER
		if (IsCLIParam("trapsQuick") || m_CurrentMinimalDistance <= 0 || !IsPlayerInVicinity())
		#else
		if (m_CurrentMinimalDistance <= 0 || !IsPlayerInVicinity())
		#endif
		{
			SpawnCatch();
		}
		else if (m_ElapsedTime < m_AdjustedMaxActiveTime)
		{
			RunTrappingTimer(m_SpawnUpdateWaitTime,"TrySpawnCatch");
		}
	}
	
	// Actually spawns the prey
	void SpawnCatch()
	{
		// Only server side, let's make sure
		if (g_Game.IsMultiplayer() && g_Game.IsClient())
			return;
		
		UpdatePreyPos();
				
		ItemBase catch;
		if (m_CanCatch)
		{
			catch = ItemBase.Cast(m_CatchingContext.SpawnAndSetupCatch(m_YieldItemIdx,m_PreyPos));
			
			OnCatchSpawnServer();
			SetCatchSuccessful(catch != null);
			// We change the trap state and visuals
			SetUsed();
		}
		
		SetSynchDirty();
	}
	
	void SetCatchSuccessful(bool successful)
	{
		m_IsCatchSuccessful = successful;
	}
	
	void OnCatchSpawnServer()
	{
		PlayCatchEffectsServer();
	}
	
	void OnCatchSpawnClient()
	{
		UpdatePreyPos(); //previously set on server only
		
		PlayCatchEffectsClient();
	}
	
	protected void PlayCatchEffectsServer()
	{
		if (m_YieldItemIdx == -1)
			return;
		
		YieldItemBase yItem = g_Game.GetMission().GetWorldData().GetCatchYieldBank().GetYieldItemByIdx(m_YieldItemIdx);
		
		PlayCatchNoise(yItem);
		PlayCatchParticleSynced(yItem);
	}
	
	protected void PlayCatchEffectsClient()
	{
		if (m_YieldItemIdx == -1)
			return;
		
		YieldItemBase yItem = g_Game.GetMission().GetWorldData().GetCatchYieldBank().GetYieldItemByIdx(m_YieldItemIdx);
		PlayCatchSound(yItem);
	}
	
	protected void PlayCatchSound(YieldItemBase yItem)
	{
		if (yItem.GetCatchDeathSoundset() != "")
			SEffectManager.PlaySoundEnviroment(yItem.GetCatchDeathSoundset(),m_PreyPos);
	}
	
	protected void PlayCatchNoise(YieldItemBase yItem)
	{
		string noiseType = yItem.GetCatchAINoise();
		if (noiseType == "")
			return;
		
		NoiseParams m_NoisePar = new NoiseParams();
		m_NoisePar.Load(noiseType);
		float noiseMultiplier = yItem.GetCatchAINoiseBaseStrength();
		noiseMultiplier *= NoiseAIEvaluate.GetNoiseReduction(g_Game.GetWeather());
		g_Game.GetNoiseSystem().AddNoiseTarget(m_PreyPos, 5, m_NoisePar, noiseMultiplier);
	}
	
	protected void PlayCatchParticleSynced(YieldItemBase yItem)
	{
		int particleId = yItem.GetCatchParticleID();
		if (particleId == ParticleList.INVALID)
			return;
		
		if (m_CatchParticleEffecterId < 0)
		{
			m_CatchParticleEffecterId = SEffectManager.CreateParticleServer(m_PreyPos, new ParticleEffecterParameters("ParticleEffecter", 5, particleId));
		}
		else
		{
			SEffectManager.ReinitParticleServer(m_CatchParticleEffecterId, new ParticleEffecterParameters("ParticleEffecter", 5, particleId)); //reinit here, since particleId might differ
			SEffectManager.ReactivateParticleServer(m_CatchParticleEffecterId);
		}
	}
	
	//Pre-roll validation, bait compatibility handled between YieldItems and bait type
	bool SetCanCatch( out EntityAI bait )
	{
		return m_CatchingContext.IsValid();
	}
	
	override void OnItemLocationChanged( EntityAI old_owner, EntityAI new_owner ) 
	{
		super.OnItemLocationChanged( old_owner, new_owner );
		
		if ( g_Game.IsServer() )
		{
			// throw trap from vicinity if the trap does not need installation ( action required )
			if ( new_owner == NULL && m_NeedInstalation == false )
			{
				SetActive();
			}
			else if ( old_owner == NULL && new_owner != NULL )
			{
				if ( m_IsFoldable )
				{
					Fold();
				}
				else
				{
					SetInactive();
				}
			}
			
			if (m_YieldItemIdx != -1) //resets sound effect idx
			{
				m_YieldItemIdx = -1;
				SetSynchDirty();
			}
		}
	}
	
	// Generic water check, no real distinction between pond or sea
	bool IsSurfaceWater(vector position)
	{
		string surfaceType;
		g_Game.SurfaceGetType3D(position[0], position[1], position[2], surfaceType);
		
		return Surface.AllowedWaterSurface(position[1] + 0.1, surfaceType, m_PlaceableWaterSurfaceList);
	}
	
	// Can only receive attachment if deployed
	override bool CanDisplayAttachmentSlot( int slot_id )
	{
		super.CanDisplayAttachmentSlot( slot_id );
		return IsDeployed();
	}
	
	override bool CanReceiveAttachment( EntityAI attachment, int slotId )
	{
		super.CanReceiveAttachment( attachment, slotId );
		return IsDeployed();
	}
	
	override void EEItemAttached( EntityAI item, string slot_name )
	{
		super.EEItemAttached( item, slot_name );
		
		if (IsActive() && g_Game.IsServer())
		{
			ResetActiveProgress();
			m_CatchingContext.UpdateDataAndMasks();
			m_CatchingContext.GenerateResult();
			ResetRunningTimerProgress();
		}
	}
	
	override void EEItemDetached(EntityAI item, string slot_name)
	{
		super.EEItemDetached( item, slot_name );
		
		if (IsActive() && g_Game.IsServer())
		{
			ResetActiveProgress();
			m_CatchingContext.UpdateDataAndMasks();
			m_CatchingContext.GenerateResult();
			ResetRunningTimerProgress();
		}
	}
	
	void InitCatchingComponent();
	
	void ClearCatchingComponent()
	{
		if (m_CatchingContext)
			delete m_CatchingContext;
	}
	
	void UpdateTrapEnviroMask()
	{
		m_CatchEnviroMask = m_CatchingContext.UpdateTrapEnviroMask();
	}
	
	void SetTrapEnviroMask(int value)
	{
		m_CatchingContext.SetTrapEnviroMask(value);
	}
	
	void HandleBaitLoss()
	{
		if (m_CatchingContext)
		{
			if (m_IsCatchSuccessful)
				m_CatchingContext.RemoveBait();
			else
				m_CatchingContext.ReduceBaitQty(m_BaitLossFraction);
		}
	}
	
	//! detaches everything on catching end (some slots may not be accessible when folded)
	void DetachAllAttachments()
	{
		int count = GetInventory().AttachmentCount();
		if (count > 0)
		{
			EntityAI att;
			for (int i = 0; i < count; i++)
			{
				att = GetInventory().GetAttachmentFromIndex(i);
				GetInventory().DropEntity(InventoryMode.SERVER,this,att);
			}
		}
	}
	
	//================================================================
	// ADVANCED PLACEMENT
	//================================================================
	
	override void OnPlacementComplete(Man player, vector position = "0 0 0", vector orientation = "0 0 0")
	{
		super.OnPlacementComplete(player, position, orientation);
		
		if (g_Game.IsServer())
		{
			vector rotation_matrix[3];
			float direction[4];
			Math3D.YawPitchRollMatrix(orientation, rotation_matrix);
			Math3D.MatrixToQuat(rotation_matrix, direction);
			InventoryLocation source = new InventoryLocation;
			InventoryLocation destination = new InventoryLocation;
			
			if (GetInventory().GetCurrentInventoryLocation(source))
			{		
				destination.SetGroundEx(this, position, direction);
				if (g_Game.IsMultiplayer())
				{
					player.ServerTakeToDst(source, destination);
					SetupTrapPlayer(PlayerBase.Cast(player), false);
				}
				else // singleplayer
				{
					PlayerBase.Cast(player).GetDayZPlayerInventory().RedirectToHandEvent(InventoryMode.LOCAL, source, destination);
					g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(SetupTrapPlayer, 100, false, PlayerBase.Cast(player), false);
				}
			}
			
			InitCatchingComponent();
			UpdateTrapEnviroMask();
			SetActive();
		}
	}
	
	
	bool IsPlaceable()
	{
		if ( g_Game.IsServer() )
		{
			InventoryLocation loc = new InventoryLocation();
			GetInventory().GetCurrentInventoryLocation(loc);
			if (loc.GetType() == InventoryLocationType.HANDS)
			{
				PlayerBase player = PlayerBase.Cast(GetHierarchyRootPlayer());
				
				vector player_pos = player.GetPosition();
				vector aim_pos = player.GetAimPosition();
				
				if ( vector.DistanceSq(player_pos, aim_pos) <= ( 1.5 * 1.5 ) )
				{
					return IsPlaceableAtPosition( aim_pos );
				}
			}
		}
		
		return false;
	}
	
	override bool CanBePlaced( Man player, vector position )
	{
		return IsPlaceableAtPosition(position);
	}
	
	// We add the action to deploy a trap laid on ground
	override void SetActions()
	{
		super.SetActions();
		
		AddAction(ActionActivateTrap);
		AddAction(ActionDeployHuntingTrap);
	}
	
	// ===============================================================
	// =====================  DEPRECATED  ============================
	// ===============================================================
	
	const string 					m_PlaceableWaterType //!DEPRECATED
	
	ref Timer 						m_PrevTimer;//!DEPRECATED
	bool 							m_BaitNeeded;//!DEPRECATED
	bool 							m_IsUsable;//!DEPRECATED
	private ItemBase 				m_Catch;//!DEPRECATED, no reason to keep the information as member
	ref Timer 						m_AlignCatchTimer;//!DEPRECATED
	string 							m_InfoSetup;//!DEPRECATED
	protected bool 					m_IsInProgress;//!DEPRECATED
	protected ref EffectSound 		m_DeployLoopSound;//!DEPRECATED
	protected EntityAI				m_Bait;
	float							m_BaitCatchProb; //!DEPRECATED
	float							m_NoBaitCatchProb; //!DEPRECATED
	float							m_FinalCatchProb; //!DEPRECATED
	
	bool m_WaterSurfaceForSetup;	//!DEPRECATED
	ref multiMap<string, float>	m_CatchesPond;	//!DEPRECATED
	ref multiMap<string, float>	m_CatchesSea;	//!DEPRECATED
	ref multiMap<string, float>	m_CatchesGroundAnimal;	//!DEPRECATED
	
	//!DEPRECATED
	bool CanPutInInventory(EntityAI  player);
	//!DEPRECATED
	void AlignCatch(ItemBase obj, string catch_name);
	///!DEPRECATED
	void CatchSetQuant(ItemBase catch);
	void PlayDeployLoopSound();	//!DEPRECATED
	void StopDeployLoopSound();	//!DEPRECATED
	// ===============================================================
}