importonly abstract class StatusEffectEvent extends Event
{
	import var staticData : StatusEffect_Record;
	import var stackCount : Uint32;
}

importonly class ApplyStatusEffectEvent extends StatusEffectEvent
{
	import var isNewApplication : Bool;
	import var isAppliedOnSpawn : Bool;
	import var instigatorEntityID : EntityID;
	import var proxyEntityID : EntityID;
}

importonly class RemoveStatusEffect extends StatusEffectEvent
{
	import var isFinalRemoval : Bool;
}

importonly abstract class IStatusEffectListener extends IScriptable
{
}

import class ScriptStatusEffectListener extends IStatusEffectListener
{
}

importonly abstract class IStatusEffectSystem extends IGameSystem
{
}

importonly final class StatusEffectSystem extends IStatusEffectSystem
{
	public import function ApplyStatusEffect( objID : EntityID, statusEffectID : TweakDBID, optional instigatorID : TweakDBID, optional instigatorEntityID : EntityID, optional applyCount : Uint32, optional direction : Vector4, optional isSavable : Bool, optional proxyEntityID : EntityID ) : Bool;
	public import function RemoveStatusEffect( objID : EntityID, statusEffectID : TweakDBID, optional removeCount : Uint32 ) : Bool;
	public import function RemoveAllStatusEffectOfType( objID : EntityID, statusEffectType : gamedataStatusEffectType ) : Bool;
	public import function HasStatusEffect( objID : EntityID, statusEffectID : TweakDBID ) : Bool;
	public import function HasStatusEffectOfType( objID : EntityID, statusEffectType : gamedataStatusEffectType ) : Bool;
	public import function HasStatusEffectOfTypeName( objID : EntityID, statusEffectTypeName : String ) : Bool;
	public import function HasStatusEffectWithTag( objID : EntityID, tag : CName ) : Bool;
	public import const function GetAppliedEffects( objID : EntityID, out appliedEffects : array< StatusEffect > );
	public import const function GetAppliedEffectsWithID( objID : EntityID, statusEffectID : TweakDBID, out appliedEffects : array< StatusEffect > );
	public import const function GetAppliedEffectsOfType( objID : EntityID, statusEffectType : gamedataStatusEffectType, out appliedEffects : array< StatusEffect > );
	public import const function GetAppliedEffectsOfTypeName( objID : EntityID, statusEffectTypeName : String, outappliedEffects : array< StatusEffect > );
	public import const function GetAppliedEffectsWithTag( objID : EntityID, tag : CName, out appliedEffects : array< StatusEffect > );
	public import function SetStatusEffectRemainingDuration( objID : EntityID, statusEffectID : TweakDBID, duration : Float ) : Bool;
	public import function RegisterListener( objID : EntityID, listener : IStatusEffectListener );
	public import static function ObjectHasStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID ) : Bool;
	public import static function ObjectHasStatusEffectOfType( target : weak< GameObject >, statusEffectType : gamedataStatusEffectType ) : Bool;
	public import static function ObjectHasStatusEffectOfTypeName( target : weak< GameObject >, statusEffectTypeName : String ) : Bool;
	public import static function ObjectHasStatusEffectWithTag( target : weak< GameObject >, tag : CName ) : Bool;
	public import static function ObjectHasStatusEffectWithTags( target : weak< GameObject >, tag : ref< array< CName > > ) : Bool;
}

enum EKnockdownStates
{
	Invalid = 0,
	Start = 1,
	FallLoop = 2,
	Land = 3,
	Recovery = 4,
	AirRecovery = 5,
}

class StatusEffectHelper
{

	public static function ApplyStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID, optional delay : Float ) : Bool
	{
		var applyStatusEffectEvent : ApplyNewStatusEffectEvent;
		if( delay <= 0.0 )
		{
			return GameInstance.GetStatusEffectSystem( target.GetGame() ).ApplyStatusEffect( target.GetEntityID(), statusEffectID );
		}
		else
		{
			applyStatusEffectEvent = new ApplyNewStatusEffectEvent;
			applyStatusEffectEvent.effectID = statusEffectID;
			GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, applyStatusEffectEvent, delay, true );
			return true;
		}
	}

	public static function ApplyStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID, instigatorEntityID : EntityID ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( target.GetGame() ).ApplyStatusEffect( target.GetEntityID(), statusEffectID, GameObject.GetTDBID( ( ( GameObject )( GameInstance.FindEntityByID( target.GetGame(), instigatorEntityID ) ) ) ), instigatorEntityID );
	}

	public static function ApplyStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID, instigatorEntityID : EntityID, proxyEntityID : EntityID ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( target.GetGame() ).ApplyStatusEffect( target.GetEntityID(), statusEffectID, GameObject.GetTDBID( ( ( GameObject )( GameInstance.FindEntityByID( target.GetGame(), instigatorEntityID ) ) ) ), instigatorEntityID, 1, Vector4( 0.0, 0.0, 0.0, 0.0 ), true, proxyEntityID );
	}

	public static function ApplyStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID, instigatorID : TweakDBID ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( target.GetGame() ).ApplyStatusEffect( target.GetEntityID(), statusEffectID, instigatorID );
	}

	public static function ApplyStatusEffectOnSelf( gameInstance : GameInstance, statusEffectID : TweakDBID, entityID : EntityID ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( gameInstance ).ApplyStatusEffect( entityID, statusEffectID, GameObject.GetTDBID( ( ( GameObject )( GameInstance.FindEntityByID( gameInstance, entityID ) ) ) ), entityID );
	}

	public static function ApplyStatusEffectForTimeWindow( target : weak< GameObject >, statusEffectID : TweakDBID, instigatorEntityID : EntityID, delay : Float, duration : Float )
	{
		var applyStatusEffectEvent : ApplyNewStatusEffectEvent;
		var removeStatusEffectEvent : RemoveStatusEffectEvent;
		removeStatusEffectEvent = new RemoveStatusEffectEvent;
		removeStatusEffectEvent.effectID = statusEffectID;
		removeStatusEffectEvent.removeCount = 1;
		GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, removeStatusEffectEvent, duration + delay, true );
		if( delay > 0.0 )
		{
			applyStatusEffectEvent = new ApplyNewStatusEffectEvent;
			applyStatusEffectEvent.effectID = statusEffectID;
			applyStatusEffectEvent.instigatorID = GameObject.GetTDBID( ( ( GameObject )( GameInstance.FindEntityByID( target.GetGame(), instigatorEntityID ) ) ) );
			GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, applyStatusEffectEvent, delay, true );
		}
		else
		{
			ApplyStatusEffect( target, statusEffectID, instigatorEntityID );
		}
	}

	public static function RemoveStatusEffect( target : weak< GameObject >, statusEffectID : TweakDBID, optional removeCount : Uint32 ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( target.GetGame() ).RemoveStatusEffect( target.GetEntityID(), statusEffectID, removeCount );
	}

	public static function RemoveStatusEffect( target : weak< GameObject >, statusEffect : StatusEffect, optional removeCount : Uint32 ) : Bool
	{
		return GameInstance.GetStatusEffectSystem( target.GetGame() ).RemoveStatusEffect( target.GetEntityID(), statusEffect.GetRecord().GetID(), removeCount );
	}

	public static function RemoveStatusEffectsByInstigatorID( target : weak< GameObject >, instigatorID : TweakDBID )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			if( appliedEffects[ i ].GetInstigatorStaticDataID() == instigatorID )
			{
				StatusEffectHelper.RemoveStatusEffect( target, appliedEffects[ i ].GetRecord().GetID() );
			}
		}
	}

	public static function RemoveAllStatusEffects( target : weak< GameObject > )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			StatusEffectHelper.RemoveStatusEffect( target, appliedEffects[ i ].GetRecord().GetID() );
		}
	}

	public static function RemoveAllStatusEffectsByType( gameInstance : GameInstance, target : EntityID, type : gamedataStatusEffectType )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		var effectType : gamedataStatusEffectType;
		var effectSystem : StatusEffectSystem;
		effectSystem = GameInstance.GetStatusEffectSystem( gameInstance );
		effectSystem.GetAppliedEffects( target, appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			effectType = appliedEffects[ i ].GetRecord().StatusEffectType().Type();
			if( effectType == type )
			{
				effectSystem.RemoveStatusEffect( target, appliedEffects[ i ].GetRecord().GetID() );
			}
		}
	}

	public static function RemoveAllStatusEffectsByType( target : weak< GameObject >, type : gamedataStatusEffectType )
	{
		RemoveAllStatusEffectsByType( target.GetGame(), target.GetEntityID(), type );
	}

	public static function RemoveStatusEffectsWithTag( target : weak< GameObject >, gameplayTag : CName, optional delay : Float )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		var effectSystem : StatusEffectSystem;
		var removeStatusEffectEvent : RemoveStatusEffectEvent;
		effectSystem = GameInstance.GetStatusEffectSystem( target.GetGame() );
		effectSystem.GetAppliedEffects( target.GetEntityID(), appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			gameplayTags = appliedEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( gameplayTag ) )
			{
				if( delay <= 0.0 )
				{
					effectSystem.RemoveStatusEffect( target.GetEntityID(), appliedEffects[ i ].GetRecord().GetID() );
				}
				else
				{
					removeStatusEffectEvent = new RemoveStatusEffectEvent;
					removeStatusEffectEvent.effectID = appliedEffects[ i ].GetRecord().GetID();
					GameInstance.GetDelaySystem( target.GetGame() ).DelayEvent( target, removeStatusEffectEvent, delay, true );
				}
			}
		}
	}

	public static function RemoveStatusEffectsWithTag( gameInstance : GameInstance, target : EntityID, gameplayTag : CName )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		var effectSystem : StatusEffectSystem;
		effectSystem = GameInstance.GetStatusEffectSystem( gameInstance );
		effectSystem.GetAppliedEffects( target, appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			gameplayTags = appliedEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( gameplayTag ) )
			{
				effectSystem.RemoveStatusEffect( target, appliedEffects[ i ].GetRecord().GetID() );
			}
		}
	}

	public static function RemoveAllStatusEffectsWithTagBeside( target : weak< GameObject >, gameplayTag : CName, beside : TweakDBID )
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		var effectSystem : StatusEffectSystem;
		effectSystem = GameInstance.GetStatusEffectSystem( target.GetGame() );
		effectSystem.GetAppliedEffects( target.GetEntityID(), appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			if( appliedEffects[ i ].GetRecord().GetID() == beside )
			{
				continue;
			}
			gameplayTags = appliedEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( gameplayTag ) )
			{
				effectSystem.RemoveStatusEffect( target.GetEntityID(), appliedEffects[ i ].GetRecord().GetID() );
			}
		}
	}

	public static function GetStatusEffectWithTag( target : weak< GameObject >, gameplayTag : CName ) : StatusEffect
	{
		var appliedStatusEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedStatusEffects );
		for( i = 0; i < appliedStatusEffects.Size(); i += 1 )
		{
			gameplayTags = appliedStatusEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( gameplayTag ) )
			{
				return appliedStatusEffects[ i ];
			}
			return NULL;
		}
	}

	public static function HasStatusEffectWithTagConst( target : weak< GameObject >, gameplayTag : CName ) : Bool
	{
		var appliedStatusEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedStatusEffects );
		for( i = 0; i < appliedStatusEffects.Size(); i += 1 )
		{
			gameplayTags = appliedStatusEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( gameplayTag ) )
			{
				return true;
			}
		}
		return false;
	}

	public static function HasTag( record : weak< StatusEffect_Record >, tag : CName ) : Bool
	{
		var gameplayTags : array< CName >;
		gameplayTags = record.GameplayTags();
		return gameplayTags.Contains( tag );
	}

	public static function GetAppliedEffects( target : weak< GameObject > ) : array< StatusEffect >
	{
		var effects : array< StatusEffect >;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), effects );
		return effects;
	}

	public static function GetAppliedEffectsWithTag( target : weak< GameObject >, tag : CName, statusEffects : ref< array< StatusEffect > >, optional instigatorID : EntityID ) : Bool
	{
		var appliedStatusEffects : array< StatusEffect >;
		var i : Int32;
		var gameplayTags : array< CName >;
		if( !( target ) || !( IsNameValid( tag ) ) )
		{
			return false;
		}
		statusEffects.Clear();
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedStatusEffects );
		for( i = 0; i < appliedStatusEffects.Size(); i += 1 )
		{
			gameplayTags = appliedStatusEffects[ i ].GetRecord().GameplayTags();
			if( gameplayTags.Contains( tag ) )
			{
				if( !( EntityID.IsDefined( instigatorID ) ) || ( appliedStatusEffects[ i ].GetInstigatorEntityID() == instigatorID ) )
				{
					statusEffects.PushBack( appliedStatusEffects[ i ] );
				}
			}
		}
		return statusEffects.Size() > 0;
	}

	public static function HasStatusEffectFromInstigator( target : weak< GameObject >, statusEffectID : TweakDBID, instigator : EntityID ) : Bool
	{
		var statusEffect : StatusEffect;
		statusEffect = GetStatusEffectByID( target, statusEffectID );
		if( !( statusEffect ) )
		{
			return false;
		}
		else if( statusEffect.GetInstigatorEntityID() == instigator )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	public static function HasStatusEffectAttack( statusEffect : StatusEffect_Record, out statusEffectAttack : Attack_Record ) : Bool
	{
		var package : GameplayLogicPackage_Record;
		var effector : Effector_Record;
		var packageCount : Int32;
		var effectorCount : Int32;
		var attackTDBID : TweakDBID;
		var nextAttackTDBID : TweakDBID;
		var i, j : Int32;
		packageCount = statusEffect.GetPackagesCount();
		for( i = 0; i < packageCount; i += 1 )
		{
			package = statusEffect.GetPackagesItem( i );
			effectorCount = package.GetEffectorsCount();
			for( j = 0; j < effectorCount; j += 1 )
			{
				effector = package.GetEffectorsItem( j );
				nextAttackTDBID = TweakDBInterface.GetContinuousAttackEffectorRecord( effector.GetID() ).AttackRecord().GetID();
				if( TDBID.IsValid( nextAttackTDBID ) )
				{
					attackTDBID = nextAttackTDBID;
					if( TDBID.IsValid( attackTDBID ) )
					{
						statusEffectAttack = TweakDBInterface.GetAttackRecord( attackTDBID );
						return true;
					}
				}
			}
		}
		return false;
	}

	public static function GetTopPriorityEffect( target : weak< GameObject > ) : StatusEffect
	{
		var topPriorityEffect : StatusEffect;
		var appliedEffects : array< StatusEffect >;
		var topPriority, currentPriority : Float;
		var i : Int32;
		var entityID : EntityID;
		var statusEffectAIData : StatusEffectAIData_Record;
		entityID = target.GetEntityID();
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( entityID, appliedEffects );
		topPriorityEffect = NULL;
		topPriority = -9999999.0;
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			statusEffectAIData = appliedEffects[ i ].GetRecord().AIData();
			if( statusEffectAIData == NULL )
			{
				currentPriority = 0.0;
			}
			else
			{
				currentPriority = statusEffectAIData.Priority();
			}
			if( ( currentPriority > topPriority ) || ( ( currentPriority == topPriority ) && ( appliedEffects[ i ].GetLastApplicationSimTimestamp() > topPriorityEffect.GetLastApplicationSimTimestamp() ) ) )
			{
				topPriority = currentPriority;
				topPriorityEffect = appliedEffects[ i ];
			}
		}
		return topPriorityEffect;
	}

	public static function GetTopPriorityEffect( target : weak< GameObject >, statusEffectType : gamedataStatusEffectType, optional discardStatusEffect : Bool ) : StatusEffect
	{
		var topPriorityEffect : StatusEffect;
		var appliedEffects : array< StatusEffect >;
		var topPriority, currentPriority : Float;
		var i : Int32;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedEffects );
		topPriorityEffect = NULL;
		topPriority = -9999999.0;
		if( !( discardStatusEffect ) )
		{
			for( i = 0; i < appliedEffects.Size(); i += 1 )
			{
				if( appliedEffects[ i ].GetRecord().StatusEffectType().Type() == statusEffectType )
				{
					currentPriority = appliedEffects[ i ].GetRecord().AIData().Priority();
					if( currentPriority >= topPriority )
					{
						topPriority = currentPriority;
						topPriorityEffect = appliedEffects[ i ];
					}
				}
			}
		}
		else
		{
			for( i = 0; i < appliedEffects.Size(); i += 1 )
			{
				if( appliedEffects[ i ].GetRecord().StatusEffectType().Type() != statusEffectType )
				{
					if( appliedEffects[ i ].GetRecord().AIData() )
					{
						currentPriority = appliedEffects[ i ].GetRecord().AIData().Priority();
						if( currentPriority >= topPriority )
						{
							topPriority = currentPriority;
							topPriorityEffect = appliedEffects[ i ];
						}
					}
				}
			}
		}
		return topPriorityEffect;
	}

	public static function GetStatusEffectByID( target : weak< GameObject >, statusEffectID : TweakDBID ) : StatusEffect
	{
		var appliedEffects : array< StatusEffect >;
		var i : Int32;
		GameInstance.GetStatusEffectSystem( target.GetGame() ).GetAppliedEffects( target.GetEntityID(), appliedEffects );
		for( i = 0; i < appliedEffects.Size(); i += 1 )
		{
			if( appliedEffects[ i ].GetRecord().GetID() == statusEffectID )
			{
				return appliedEffects[ i ];
			}
		}
		return NULL;
	}

	public static function CheckStatusEffectBehaviorPrereqs( target : weak< GameObject >, statusEffectRecord : weak< StatusEffect_Record > ) : Bool
	{
		var i : Int32;
		var count : Int32;
		var record : IPrereq_Record;
		if( statusEffectRecord && statusEffectRecord.AIData() )
		{
			count = statusEffectRecord.AIData().GetActivationPrereqsCount();
		}
		if( count == 0 )
		{
			return true;
		}
		for( i = 0; i < count; i += 1 )
		{
			record = statusEffectRecord.AIData().GetActivationPrereqsItem( i );
			if( !( IPrereq.CreatePrereq( record.GetID() ).IsFulfilled( target.GetGame(), target ) ) )
			{
				return false;
			}
		}
		return true;
	}

	public static function PopulateStatusEffectAnimData( owner : GameObject, statusEffectRecord : weak< StatusEffect_Record >, state : EKnockdownStates, hitDirection : Vector4, out animData : AnimFeature_StatusEffect )
	{
		var playerData : weak< StatusEffectPlayerData_Record >;
		if( Vector4.IsZero( hitDirection ) )
		{
			animData.direction = 0;
		}
		else
		{
			animData.direction = GameObject.GetLocalAngleForDirectionInInt( hitDirection, owner );
		}
		if( statusEffectRecord.StatusEffectType().Type() == gamedataStatusEffectType.Stunned )
		{
			animData.stunned = true;
			animData.knockdown = false;
			animData.state = 0;
			return;
		}
		animData.knockdown = true;
		animData.stunned = false;
		playerData = statusEffectRecord.PlayerData();
		animData.state = ( ( Int32 )( state ) );
		switch( state )
		{
			case EKnockdownStates.Start:
				animData.duration = playerData.StartupAnimDuration();
			break;
			case EKnockdownStates.FallLoop:
				animData.duration = -1.0;
			break;
			case EKnockdownStates.Land:
				animData.duration = playerData.LandAnimDuration();
			break;
			case EKnockdownStates.Recovery:
				animData.duration = playerData.RecoveryAnimDuration();
			break;
			case EKnockdownStates.AirRecovery:
				animData.duration = playerData.AirRecoveryAnimDuration();
			break;
			default:
				break;
		}
		animData.variation = ( ( Int32 )( playerData.StatusEffectVariation().Type() ) );
	}

	public constexpr static function GetStateStartTimeKey() : CName
	{
		return 'SatusEffectStateStartTime';
	}

	public constexpr static function GetForceKnockdownKey() : CName
	{
		return 'StatusEffect_ForceKnockdown';
	}

	public constexpr static function GetForcedKnockdownImpulseKey() : CName
	{
		return 'StatusEffect_ForceKnockdownImpulse';
	}

	public constexpr static function GetAppliedStatusEffectKey() : CName
	{
		return 'StatusEffect';
	}

	public constexpr static function GetCanExitKnockdownKey() : CName
	{
		return 'StatusEffect_CanExitKnockdown';
	}

	public constexpr static function TriggerSecondaryKnockdownKey() : CName
	{
		return 'StatusEffect_TriggerSecondaryKnockdown';
	}

}

class PlayerGameplayRestrictions
{

	public static function RemoveAllGameplayRestrictions( target : weak< GameObject > )
	{
		StatusEffectHelper.RemoveStatusEffectsWithTag( target, 'GameplayRestriction' );
	}

	public static function OnGameplayRestrictionAdded( player : weak< PlayerPuppet >, record : StatusEffect_Record, const gameplayTags : ref< array< CName > > )
	{
		var restrictionName : CName;
		if( !( player ) )
		{
			return;
		}
		if( gameplayTags.Contains( 'InfiniteAmmo' ) )
		{
			GameInstance.GetInventoryManager( player.GetGame() ).AddEquipmentStateFlag( gameEEquipmentManagerState.InfiniteAmmo );
		}
		if( gameplayTags.Contains( 'Fists' ) )
		{
			PlayerGameplayRestrictions.RequestFists( player );
		}
		if( gameplayTags.Contains( 'Melee' ) )
		{
			PlayerGameplayRestrictions.RequestMeleeWeapon( player );
		}
		if( gameplayTags.Contains( 'NoArmsCW' ) )
		{
			PlayerGameplayRestrictions.RequestFists( player );
		}
		if( gameplayTags.Contains( 'BlockAllHubMenu' ) )
		{
			SendBlockMenuRequest( player, true );
		}
		if( gameplayTags.Contains( 'BlockFastTravel' ) )
		{
			restrictionName = TweakDBInterface.GetCName( record.GetID() + T".restrictionName", 'MISSING - NEEDS TO BE FIXED' );
			ChangeFastTravelSystemState( false, restrictionName, player, record.GetID() );
		}
		if( gameplayTags.Contains( 'PhoneCall' ) )
		{
			StatusEffectHelper.ApplyStatusEffect( player, T"GameplayRestriction.PhoneCallDeviceActionRestrictions" );
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( gameplayTags.Contains( 'NoCombat' ) )
		{
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( ( ( gameplayTags.Contains( 'VehicleFPP' ) || gameplayTags.Contains( 'VehicleScene' ) ) || gameplayTags.Contains( 'VehicleCombat' ) ) || gameplayTags.Contains( 'VehicleBlockRadioInput' ) )
		{
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( gameplayTags.Contains( 'NoEncumbrance' ) )
		{
			player.EvaluateEncumbrance();
		}
	}

	public static function OnGameplayRestrictionRemoved( player : weak< PlayerPuppet >, evt : RemoveStatusEffect, const gameplayTags : ref< array< CName > > )
	{
		var restrictionName : CName;
		if( !( player ) )
		{
			return;
		}
		if( gameplayTags.Contains( 'InfiniteAmmo' ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( player, 'InfiniteAmmo' ) ) )
		{
			GameInstance.GetInventoryManager( player.GetGame() ).RemoveEquipmentStateFlag( gameEEquipmentManagerState.InfiniteAmmo );
		}
		if( ( gameplayTags.Contains( 'BlockAllHubMenu' ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( player, 'BlockAllHubMenu' ) ) ) && !( player.IsJohnnyReplacer() ) )
		{
			SendBlockMenuRequest( player, false );
		}
		if( gameplayTags.Contains( 'BlockFastTravel' ) )
		{
			restrictionName = TweakDBInterface.GetCName( evt.staticData.GetID() + T".restrictionName", 'MISSING - NEEDS TO BE FIXED' );
			ChangeFastTravelSystemState( true, restrictionName, player, evt.staticData.GetID() );
		}
		if( gameplayTags.Contains( 'PhoneCall' ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( player, 'PhoneCall' ) ) )
		{
			StatusEffectHelper.RemoveStatusEffect( player, T"GameplayRestriction.PhoneCallDeviceActionRestrictions" );
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( gameplayTags.Contains( 'NoCombat' ) )
		{
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( ( ( gameplayTags.Contains( 'VehicleFPP' ) || gameplayTags.Contains( 'VehicleScene' ) ) || gameplayTags.Contains( 'VehicleCombatBlockExit' ) ) || gameplayTags.Contains( 'VehicleBlockRadioInput' ) )
		{
			PushForceRefreshInputHintsEventToPSM( player );
		}
		if( gameplayTags.Contains( 'NoEncumbrance' ) && !( StatusEffectSystem.ObjectHasStatusEffectWithTag( player, 'NoEncumbrance' ) ) )
		{
			player.EvaluateEncumbrance();
		}
	}

	public static function PushForceRefreshInputHintsEventToPSM( requester : GameObject )
	{
		var psmForceRefreshInputHintsEvent : PSMPostponedParameterBool;
		psmForceRefreshInputHintsEvent = new PSMPostponedParameterBool;
		psmForceRefreshInputHintsEvent.id = 'ForceRefreshInputHints';
		psmForceRefreshInputHintsEvent.value = true;
		requester.QueueEvent( psmForceRefreshInputHintsEvent );
	}

	public static function SendBlockMenuRequest( player : weak< PlayerPuppet >, blockMenu : Bool )
	{
		var menuEvent : inkMenuInstance_SpawnAddressedEvent;
		menuEvent = new inkMenuInstance_SpawnAddressedEvent;
		if( player )
		{
			if( blockMenu )
			{
				menuEvent.Init( 'MenuScenario_Idle', 'OnBlockHub' );
			}
			else
			{
				menuEvent.Init( 'MenuScenario_Idle', 'OnUnlockHub' );
			}
			GameInstance.GetUISystem( player.GetGame() ).QueueEvent( menuEvent );
		}
	}

	public static function RequestFists( player : weak< PlayerPuppet >, optional animType : gameEquipAnimationType )
	{
		var request : EquipmentSystemWeaponManipulationRequest;
		var eqs : EquipmentSystem;
		if( !( player ) )
		{
			return;
		}
		eqs = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( player.GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( !( eqs ) )
		{
			return;
		}
		request = new EquipmentSystemWeaponManipulationRequest;
		request.owner = player;
		request.requestType = EquipmentManipulationAction.RequestFists;
		request.equipAnimType = animType;
		eqs.QueueRequest( request );
	}

	public static function RequestMeleeWeapon( player : weak< PlayerPuppet >, optional animType : gameEquipAnimationType )
	{
		var request : EquipmentSystemWeaponManipulationRequest;
		var eqs : EquipmentSystem;
		if( !( player ) )
		{
			return;
		}
		eqs = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( player.GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( !( eqs ) )
		{
			return;
		}
		request = new EquipmentSystemWeaponManipulationRequest;
		request.owner = player;
		request.requestType = EquipmentManipulationAction.RequestLastUsedOrFirstAvailableMeleeWeapon;
		request.equipAnimType = animType;
		eqs.QueueRequest( request );
	}

	public static function RequestLastUsedWeapon( player : weak< PlayerPuppet >, optional animType : gameEquipAnimationType )
	{
		var request : EquipmentSystemWeaponManipulationRequest;
		var eqs : EquipmentSystem;
		if( !( player ) )
		{
			return;
		}
		eqs = ( ( EquipmentSystem )( GameInstance.GetScriptableSystemsContainer( player.GetGame() ).Get( 'EquipmentSystem' ) ) );
		if( !( eqs ) )
		{
			return;
		}
		request = new EquipmentSystemWeaponManipulationRequest;
		request.owner = player;
		request.requestType = EquipmentManipulationAction.RequestLastUsedWeapon;
		request.equipAnimType = animType;
		eqs.QueueRequest( request );
	}

	private static function ChangeFastTravelSystemState( enable : Bool, reason : CName, player : weak< PlayerPuppet >, statusEffectID : TweakDBID )
	{
		var request : EnableFastTravelRequest;
		var ftSystem : FastTravelSystem;
		request = new EnableFastTravelRequest;
		request.isEnabled = enable;
		request.reason = reason;
		request.linkedStatusEffectID = statusEffectID;
		ftSystem = ( ( FastTravelSystem )( GameInstance.GetScriptableSystemsContainer( player.GetGame() ).Get( 'FastTravelSystem' ) ) );
		ftSystem.QueueRequest( request );
	}

	public static function IsHotkeyRestricted( game : GameInstance, hotkey : EHotkey ) : Bool
	{
		var tags : array< CName >;
		if( AcquireHotkeyRestrictionTags( hotkey, tags ) )
		{
			return StatusEffectSystem.ObjectHasStatusEffectWithTags( GameInstance.GetPlayerSystem( game ).GetLocalPlayerControlledGameObject(), tags );
		}
		return true;
	}

	public static function AcquireHotkeyRestrictionTags( hotkey : EHotkey, hotkeyTags : ref< array< CName > > ) : Bool
	{
		var recordKey : String;
		var record : DPadUIData_Record;
		var restrictionTags : array< CName >;
		var i : Int32;
		recordKey = "DPadUIData." + EnumValueToString( "EHotkey", ( ( Int64 )( hotkey ) ) );
		record = TweakDBInterface.GetDPadUIDataRecord( TDBID.Create( recordKey ) );
		if( !( record ) )
		{
			return false;
		}
		restrictionTags = record.RestrictionTags();
		for( i = 0; i < restrictionTags.Size(); i += 1 )
		{
			hotkeyTags.PushBack( restrictionTags[ i ] );
		}
		return true;
	}

}

struct SPlayerCooldown
{
	var effectID : TweakDBID;
	var instigatorID : TweakDBID;
}

