import struct SAreaMapPinInfo
{
	import var areaType : int;
	import var position : Vector;
	import var worldPath : string;
	import var requiredChunk : name;
	import var localisationName : name;
	import var localisationDescription : name;
};

import struct SEntityMapPinInfo
{
	import var entityName							: name;
	import var entityTags							: TagList;
	import var entityPosition						: Vector;
	import var entityType							: name;
	import var fastTravelGroupName					: name;
	import var fastTravelTeleportWayPointTag		: name;
	import var fastTravelTeleportWayPointPosition	: Vector;
	import var fastTravelTeleportWayPointRotation	: EulerAngles;
}

import struct SCommonMapPinInstance
{
	import var id : int;
	import var tag : name;
	import var customNameId : int;
	import var extraTag : name;
	import var type : name;
	import var visibleType : name;
	import var position : Vector;
	import var radius : float;
	import var visibleRadius : float;
	import var guid : CGUID;
	import var isKnown : bool;
	import var isDiscovered : bool;
	import var isDisabled : bool;
	import var isHighlightable : bool;
	import var isHighlighted : bool;
	import var canBePointedByArrow : bool;
}

import struct SMapPathInstance
{
	import var id			: int;
	import var position		: Vector;
	import var splinePoints : array< Vector >;
	import var color		: int;
	import var lineWidth	: float;
}

import struct SCustomMapPinDefinition
{
	import var tag : name;
	import var type : name;
}

struct SAvailableFastTravelMapPin
{
	var tag	: name;
	var type : name;
	var area : EAreaName;
}

import abstract class CCommonMapManager extends IGameSystem
{
	private var m_destinationPinTag			: name;
	private var m_debugTeleportWaypointTag	: name;

	private var m_noSaveLock				: int;

	private var m_dbgShowKnownPins			: bool;
	private var m_dbgShowPins				: bool;
	private var m_dbgShowAllFT				: bool;
	private var m_dbgAllowFT				: bool;
	
	private var m_borderTeleportPosition : Vector;
	private var m_borderTeleportRotation : EulerAngles;
	
	private var m_lastGlobalFastTravelArea : int;
	private var m_lastGlobalFastTravelPosition : Vector;
	
	import final function InitializeMinimapManager( minimapModule : CR4HudModule );
	import final function SetHintWaypointParameters( maxRemovalDistance : float, minPlacingDistance : float, refreshInterval : float, pathfindingTolerance : float, maxCount : int );
	import final function OnChangedMinimapRadius( radius : float, zoom : float );
	import final function IsFastTravellingEnabled() : bool;
	import final function EnableFastTravelling( enable : bool );
	import final function IsEntityMapPinKnown( tag : name ) : bool;
	import final function SetEntityMapPinKnown( tag : name, optional set : bool );
	import final function IsEntityMapPinDiscovered( tag : name ) : bool;
	import final function SetEntityMapPinDiscovered( tag : name, optional set : bool );
	import final function IsEntityMapPinDisabled( tag : name ) : bool;
	import final function SetEntityMapPinDisabled( tag : name, optional set : bool );
	import final function IsQuestType( type : name ) : bool;
	import final function ShowKnownEntities( show : bool );
	import final function CanShowKnownEntities() : bool;
	import final function ShowFocusClues( optional show : bool );
	import final function ShowHintWaypoints( optional show : bool );
	import final function AddQuestLootContainer( container : CEntity );
	import final function DeleteQuestLootContainer( container : CEntity );
	import final function CacheMapPins();
	import final function GetMapPinInstances( worldPath : string ) : array< SCommonMapPinInstance >;
	import final function TogglePathsInfo( optional toggle : bool );
	import final function ToggleQuestAgentsInfo( optional toggle : bool );
	import final function ToggleShopkeepersInfo( optional toggle : bool );
	import final function ToggleInteriorInfo( optional toggle : bool );
	import final function TogglePinsInfo( optional flags : int );
	import final function ExportGlobalMapPins();
	import final function ExportEntityMapPins();
	import final function GetAreaMapPins() : array< SAreaMapPinInfo >;
	import final function GetEntityMapPins( worldPath : string ) : array< SEntityMapPinInfo >;
	import final function UseMapPin( pinTag : name, onStart : bool ) : bool;
	import final function UseInteriorsForQuestMapPins( use : bool );
	import final function EnableShopkeeper( tag : name, enable : bool );
	import final function EnableMapPath( tag : name, enable : bool, lineWidth : float, segmentLength : float, color : Color );
	import final function EnableDynamicMappin( tag : name, enable : bool, type : name, optional useAgents : bool );
	import final function InvalidateStaticMapPin( entityName : name );
	import final function ToggleUserMapPin( area : EAreaName, position : Vector ) : bool;
	import final function GetUserMapPin( out area : int, out mapPinX : float, out mapPinY : float );
	import final function GetCurrentArea() : int;
	import final function NotifyPlayerEnteredBorder( interval : float, position : Vector, rotation : EulerAngles ) : int;
	import final function NotifyPlayerExitedBorder() : int;
	import final function IsWorldAvailable( area : int ) : bool;
	import final function GetWorldContentTag( area : int ) : name;
	import final function GetWorldPercentCompleted( area : int ) : int;
	import final function SetPinFilterVisible( pinID : name, visible : bool );
	import final function GetPinFilterVisible( pinID : name ) : bool;

	// called from c++
	event OnMapPinChanged()
	{
		theGame.GetGlobalEventsManager().OnScriptedEvent( SEC_OnMapPinChanged );
	}

	public function SetEntityMapPinDiscoveredScript(isFastTravelPoint : bool, tag : name, optional set : bool )
	{
		var previouslyDiscovered : bool;
		var mapPinType : name;
		var m_guiManager 	  					: CR4GuiManager;
		
		if ( !IsNameValid( tag ) )
		{
			return;
		}

		previouslyDiscovered = IsEntityMapPinDiscovered( tag );

		SetEntityMapPinDiscovered(tag, set);
		
		//achievement for discovering 100 fast travel points in the entire game
		if( !previouslyDiscovered && isFastTravelPoint && set)
		{
			CheckExplorerAchievement();
		}
		
		if ( !previouslyDiscovered && set  )
		{
			mapPinType = GetMappinType( tag );
			if ( mapPinType == 'NoticeBoard' || mapPinType == 'NoticeBoardFull' )
			{
				UpdateHud( 'noticeboard' );
				m_guiManager = theGame.GetGuiManager();
				m_guiManager.RegisterNewMappinEntry('noticeboard','noticeboard');
			}
			else if (	mapPinType == 'MonsterNest' ||
						mapPinType == 'PlaceOfPower' ||
						mapPinType == 'TreasureHuntMappin' ||
						mapPinType == 'SpoilsOfWar' ||
						mapPinType == 'BanditCamp' ||
						mapPinType == 'BanditCampfire' ||
						mapPinType == 'BossAndTreasure' ||
						mapPinType == 'Contraband' ||
						mapPinType == 'ContrabandShip' ||
						mapPinType == 'RescuingTown' ||
						mapPinType == 'DungeonCrawl' )
			{
				UpdateHud( mapPinType );
				m_guiManager = theGame.GetGuiManager();
				m_guiManager.RegisterNewMappinEntry( mapPinType, mapPinType );
			}
			else if ( mapPinType == 'Entrance' )
			{
				UpdateHud( 'entrance' );
				m_guiManager = theGame.GetGuiManager();
				m_guiManager.RegisterNewMappinEntry('entrance','entrance');
			}
			else
			{
				if( ShouldDisplayHudUpdateByType( mapPinType ))
				{
					UpdateHud( tag );
					m_guiManager = theGame.GetGuiManager();
					m_guiManager.RegisterNewMappinEntry(tag,mapPinType);
				}
			}
		}
		
		//saw first tutorial but didn't see the second one
		if(!ShouldProcessTutorial('TutorialPOIAppeared') && ShouldProcessTutorial('TutorialPOIUncovered'))
		{
			FactsAdd("tut_uncovered_POI");
		}
	}
	
	public function CheckExplorerAchievement()
	{
		var arr1 : array< SAvailableFastTravelMapPin >;	
		arr1 = GetFastTravelPoints(true, false, false, true, true);
		
		if( arr1.Size() >= 100 )
		{			
			theGame.GetGamerProfile().AddAchievement(EA_Explorer);
		}	
	}
	
	public function GetMappinType( tag : name ) : name // #B refactor it Paweł :)
	{
		var i : int;
		var mappinArray : array< SAvailableFastTravelMapPin >;			
		mappinArray = GetMappins(false, false);
		for( i = 0; i < mappinArray.Size(); i += 1 )
		{
			if( mappinArray[i].tag == tag )
			{
				return mappinArray[i].type;
			}
		}
		return '';
	}
	
		
	function GetMappins( onlyDiscovered : bool, onlyEnabled : bool ) : array< SAvailableFastTravelMapPin >  // #B refactor it Paweł :)
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var pin : SAvailableFastTravelMapPin;
		var pins : array< SAvailableFastTravelMapPin >;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
			for ( j = 0; j < entityMapPins.Size(); j += 1 )
			{
				if ( onlyDiscovered )
				{
					if ( !IsEntityMapPinDiscovered( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				if ( onlyEnabled )
				{
					if ( IsEntityMapPinDisabled( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				pin.tag  = entityMapPins[ j ].entityName;
				pin.type = entityMapPins[ j ].entityType;
				pin.area = areaMapPins[ i ].areaType;
				pins.PushBack( pin );
			}
		}
		return pins;
	}
	
	public function UpdateHud( mappinTag : name )
	{
		var hud : CR4ScriptedHud;
		
		hud = (CR4ScriptedHud)theGame.GetHud();
		hud.OnMapPinUpdate( mappinTag );
	}
	
	function OnGameStarted()
	{
		if ( m_destinationPinTag != '' )
		{
			UseMapPin( m_destinationPinTag, false );
			m_destinationPinTag = '';
		}
		else if ( m_debugTeleportWaypointTag != '' )
		{
			DebugTeleport();
		}

		theGame.GetLootManager().OnAreaChanged( GetCurrentArea() );
		
		DBG_UpdateShownFT();
		DBG_UpdateShownKnownPins();
		DBG_UpdateShownPins();
	}

	function /* C++ */ GetCurrentJournalArea() : int
	{
		return GetCurrentJournalAreaByPosition( thePlayer.GetWorldPosition() );
	}

	function /* C++ */ GetCurrentJournalAreaByPosition( position : Vector ) : int
	{
		return GetJournalAreaByPosition( GetCurrentArea(), position );
	}

	function GetJournalAreaByPosition( area : int, position : Vector ) : int
	{
		if ( area == AN_NMLandNovigrad  )
		{
			// we need to distinguish Novigrad and Velen
			// checking against these coords seems to be fine
			// (can't use triggers as we need to distinguish them also when the world in not loaded)
			if ( position.X > 970 || position.Y < 1600 )
			{
				return AN_Velen;
			}
		}
		else if ( area == AN_Prologue_Village_Winter  )
		{
			return AN_Prologue_Village;
		}

		return area;
	}

	function /* C++ */ AddMapPathToMinimap( path : SMapPathInstance )
	{
		var minimapModule : CR4HudModuleMinimap2 = GetMinimap2Module();
		if ( minimapModule )
		{
			minimapModule.AddMapPath( path );
		}
	}

	function /* C++ */ DeleteMapPathsFromMinimap( ids : array< int > )
	{
		var minimapModule : CR4HudModuleMinimap2 = GetMinimap2Module();
		if ( minimapModule )
		{
			minimapModule.DeleteMapPaths( ids );
		}
	}
	
	function /* C++ */ NotifyPlayerEnteredInterior( areaPos : Vector, areaYaw : float, texture : string )
	{
		theGame.GetGuiManager().GetHudEventController().RunEvent_MinimapModule_NotifyPlayerEnteredInterior( areaPos, areaYaw, texture );
	}
	
	function /* C++ */ NotifyPlayerExitedInterior()
	{
		theGame.GetGuiManager().GetHudEventController().RunEvent_MinimapModule_NotifyPlayerExitedInterior();
	}
	
	function /* C++ */ NotifyPlayerMountedBoat()
	{
		theGame.GetGuiManager().GetHudEventController().RunEvent_MinimapModule_NotifyPlayerMountedBoat();
	}
	
	function /* C++ */ NotifyPlayerDismountedBoat()
	{
		theGame.GetGuiManager().GetHudEventController().RunEvent_MinimapModule_NotifyPlayerDismountedBoat();
	}
	
	function GetCustomMapPinDefinition( out definitions : array< SCustomMapPinDefinition > )
	{
		var definition : SCustomMapPinDefinition;
		
		definitions.Clear();
	
		// dynamic map pins (entity tag, mappin type)

		// for testing only
		//definition.tag	= 'monster_nekker';
		//definition.type	= 'Quest';
		//definitions.PushBack( definition );
		//definition.tag	= 'Quest';
		//definition.type	= 'Quest';
		//definitions.PushBack( definition );
	}

	function GetKnowableMapPinTypes( out types : array< name > )
	{
		types.Clear();
	
		types.PushBack( 'Entrance' );
		types.PushBack( 'MonsterNest' );
		types.PushBack( 'PlaceOfPower' );
		types.PushBack( 'TreasureHuntMappin' );
		types.PushBack( 'SpoilsOfWar' );
		types.PushBack( 'BanditCamp' );
		types.PushBack( 'BanditCampfire' );
		types.PushBack( 'BossAndTreasure' );
		types.PushBack( 'Contraband' );
		types.PushBack( 'ContrabandShip' );
		types.PushBack( 'RescuingTown' );
		types.PushBack( 'DungeonCrawl' );
	}
	
	function GetDiscoverableMapPinTypes( out types : array< name > )
	{
		types.Clear();
		
		types.PushBack( 'RoadSign' );
		types.PushBack( 'Harbor' );
		types.PushBack( 'MonsterNest' );
		types.PushBack( 'PlaceOfPower' );
		types.PushBack( 'MagicLamp' );
		types.PushBack( 'TreasureHuntMappin' );
		types.PushBack( 'PointOfInterestMappin' );
		//types.PushBack( 'NoticeBoard' ); // TTP 98991
		//types.PushBack( 'PlayerStash' ); // visible from begining
		types.PushBack( 'Rift' );
		types.PushBack( 'Teleport' );
		types.PushBack( 'Whetstone' );
		types.PushBack( 'ArmorRepairTable' );
		types.PushBack( 'Entrance' );
		types.PushBack( 'SpoilsOfWar' );
		types.PushBack( 'BanditCamp' );
		types.PushBack( 'BanditCampfire' );
		types.PushBack( 'BossAndTreasure' );
		types.PushBack( 'Contraband' );
		types.PushBack( 'ContrabandShip' );
		types.PushBack( 'RescuingTown' );
		types.PushBack( 'DungeonCrawl' );
	}
	
	function GetDisableableMapPinTypes( out regularTypes : array< name >, out disabledTypes : array< name > )
	{
		regularTypes.Clear();
		disabledTypes.Clear();
		
		regularTypes.PushBack(  'MonsterNest' );
		disabledTypes.PushBack( 'MonsterNestDisabled' );
		regularTypes.PushBack(  'PlaceOfPower' );
		disabledTypes.PushBack( 'PlaceOfPowerDisabled' );
		regularTypes.PushBack(  'TreasureHuntMappin' );
		disabledTypes.PushBack( 'TreasureHuntMappinDisabled' );
		regularTypes.PushBack(  'SpoilsOfWar' );
		disabledTypes.PushBack( 'SpoilsOfWarDisabled' );
		regularTypes.PushBack(  'BanditCamp' );
		disabledTypes.PushBack( 'BanditCampDisabled' );
		regularTypes.PushBack(  'BanditCampfire' );
		disabledTypes.PushBack( 'BanditCampfireDisabled' );
		regularTypes.PushBack(  'BossAndTreasure' );
		disabledTypes.PushBack( 'BossAndTreasureDisabled' );
		regularTypes.PushBack(  'Contraband' );
		disabledTypes.PushBack( 'ContrabandDisabled' );
		regularTypes.PushBack(  'ContrabandShip' );
		disabledTypes.PushBack( 'ContrabandShipDisabled' );
		regularTypes.PushBack(  'RescuingTown' );
		disabledTypes.PushBack( 'RescuingTownDisabled' );
		regularTypes.PushBack(  'DungeonCrawl' );
		disabledTypes.PushBack( 'DungeonCrawlDisabled' );
	}
	
	event OnStartTeleportingPlayerToPlayableArea( position : Vector, rotation : EulerAngles )
	{
		m_borderTeleportPosition = position;
		m_borderTeleportRotation = rotation;
		thePlayer.OnStartTeleportingPlayerToPlayableArea();
	}
	
	public function GetBorderTeleportPosition() : Vector
	{
		return m_borderTeleportPosition;
	}

	public function GetBorderTeleportRotation() : EulerAngles
	{
		return m_borderTeleportRotation;
	}
	
	function ShouldDisplayHudUpdateByType( type : name ) : bool
	{
		switch(type)
		{
			case 'MonsterNest' :
			case 'PlaceOfPower' :
			case 'Rift' :
			case 'Teleport' :
			case 'Whetstone' :
			case 'ArmorRepairTable' :
			case 'MagicLamp' :
			case '':
				return false;
		}
		return true;
	}

	private function GetMinimap2Module() : CR4HudModuleMinimap2
	{
		var hud : CR4ScriptedHud;
		var minimapModule : CR4HudModuleMinimap2;

		hud = (CR4ScriptedHud)theGame.GetHud();
		if ( !hud )
		{
			return NULL;
		}
		return (CR4HudModuleMinimap2)hud.GetHudModule( "Minimap2Module" );
	}

	function /* C++ */ GetAreaMappinsFileName( out filePath : string )
	{
		filePath = "game\world.w2am";
	}

	function /* C++ */ GetAreaMappinsData( out mappins : array< SAreaMapPinInfo > )
	{
		var i		: int;

		mappins.Clear();

		// for requiredChunk parameter see r4data\engine\misc\playgo_chunks.csv
		FillAreaMapPinInfo( mappins, AN_NMLandNovigrad,           185,  -190,  "levels\novigrad\novigrad.w2w",                    'content4',  'map_location_novigrad',       'map_description_novigrad' );
		FillAreaMapPinInfo( mappins, AN_Skellige_ArdSkellig,     -567,   665,  "levels\skellige\skellige.w2w",                    'content5',  'map_location_skellige',       'map_description_skellige' );
		FillAreaMapPinInfo( mappins, AN_Kaer_Morhen,              1720, -1098, "levels\kaer_morhen\kaer_morhen.w2w",              'content6',  'map_location_kaer_morhen',    'map_description_kaer_morhen' );
		FillAreaMapPinInfo( mappins, AN_Prologue_Village,         797,   250,  "levels\prolog_village\prolog_village.w2w",        'content2',  'map_location_prolog_village', 'map_description_prolog_village' );
		FillAreaMapPinInfo( mappins, AN_Wyzima,                   519,   252,  "levels\wyzima_castle\wyzima_castle.w2w",          'content3',  'map_location_wyzima_castle',  'map_description_wyzima_castle' );
		FillAreaMapPinInfo( mappins, AN_Island_of_Myst,          -9999, -9999, "levels\island_of_mist\island_of_mist.w2w",        'content7',  'map_location_island_of_myst', 'map_description_island_of_myst' );
		FillAreaMapPinInfo( mappins, AN_Spiral,                  -9999, -9999, "levels\the_spiral\spiral.w2w",   			      'content10', 'map_location_spiral',         'map_description_spiral' );
		FillAreaMapPinInfo( mappins, AN_Prologue_Village_Winter,  797,   250,  "levels\prolog_village_winter\prolog_village.w2w", 'content12', 'map_location_prolog_village', 'map_description_prolog_village' );
		FillAreaMapPinInfo( mappins, AN_Velen,                    176,   91,   "levels\novigrad\novigrad.w2w",                    'content4',  'map_location_no_mans_land',   'map_description_no_mans_land' );

		//
		//FillAreaMapPinInfo( mappins, AN_CombatTestLevel,          -300, 400, "qa\combat_test_level\combat_test_level.w2w",       "COMBAT TEST LEVEL" );
		//
	}
	
	private function FillAreaMapPinInfo( out mappins : array< SAreaMapPinInfo >, areaType : EAreaName, areaPinX : int, areaPinY : int, worldPath : string, requiredChunk : name, localisationName : name, localisationDescription : name )
	{
		var info 	: SAreaMapPinInfo;

		info.areaType = areaType;
		info.position.X = areaPinX;
		info.position.Y = areaPinY;
		info.position.Z = 0;
		info.worldPath = worldPath;
		info.requiredChunk = requiredChunk;
		info.localisationName = localisationName;
		info.localisationDescription = localisationDescription;
		
		mappins.PushBack( info );
	}
	
	public function ForceSettingLoadingScreenVideoForWorld( worldName : string )
	{
		var area : int;
		var manager : CWitcherJournalManager = theGame.GetJournalManager();
		if ( manager )
		{
			if ( m_lastGlobalFastTravelArea != 0 )
			{
				area = GetJournalAreaByPosition( m_lastGlobalFastTravelArea, m_lastGlobalFastTravelPosition );
				m_lastGlobalFastTravelArea = 0;
			}
			else
			{
				area = GetAreaFromWorldPath( worldName );
			}
			manager.ForceSettingLoadingScreenVideoForWorld( area );
			manager.ForceSettingLoadingScreenContextNameForWorld( GetLocalisationNameFromAreaType( area ) );
		}
	}
	
	function PerformLocalFastTravelTeleport( destinationPinTag : name )
	{
		var position : Vector;
		var rotation : EulerAngles;
		var contextName : name;
		
		if ( GetLocalFastTravelPointPosition( destinationPinTag, !thePlayer.IsSailing(), position, rotation ) )
		{
			contextName = theGame.GetCommonMapManager().GetLocalisationNameFromAreaType( GetCurrentJournalAreaByPosition( position ) );
			theGame.SetSingleShotLoadingScreen( contextName );
			
			// Use only Yaw from rotation. We dont want to roll or pitch character.
			rotation.Roll = 0.f;
			rotation.Pitch = 0.f;
			thePlayer.TeleportWithRotation( position, rotation );
			UseMapPin( destinationPinTag, false ); // needed because of used fast travel event
			
			theGame.RequestAutoSave( "fast travel", true );
		}
	}
	
	function PerformGlobalFastTravelTeleport( destinationArea : int, destinationPinTag : name )
	{
		var worldPath : string;
		var position : Vector;
		var rotation : EulerAngles;

		// needed for calling UseMapPin in destination world
		m_destinationPinTag = destinationPinTag;

		worldPath = GetWorldPathFromAreaType( destinationArea );
		if ( StrLen( worldPath ) > 0 )
		{
			if ( GetFastTravelPointPosition( worldPath, destinationPinTag, !thePlayer.IsSailing(), position, rotation ) )
			{
				m_lastGlobalFastTravelArea = destinationArea;
				m_lastGlobalFastTravelPosition = position;
				theGame.ScheduleWorldChangeToPosition( worldPath, position, rotation );
			}
			else
			{
				// this should never happen, but just in case
				theGame.ScheduleWorldChangeToMapPin( worldPath, destinationPinTag );
			}
			
			theGame.RequestAutoSave( "fast travel", true ); 
		}
	}

	public function SetDebugTeleportWaypoint( tag : name )
	{
		m_debugTeleportWaypointTag = tag;
	}

	private function DebugTeleport()
	{
		var entity : CEntity;
		
		if ( m_debugTeleportWaypointTag != '' )
		{
			entity = theGame.GetEntityByTag( m_debugTeleportWaypointTag );
			if ( entity )
			{
				thePlayer.TeleportWithRotation( entity.GetWorldPosition(), entity.GetWorldRotation() );
			}
			else
			{
				LogAssert( false, "Waypoint [" + m_debugTeleportWaypointTag + "] not found" );
			}
			m_debugTeleportWaypointTag = '';
		}
	}

	////
	
	function GetAreaFromWorldPath( worldPath : string, optional noWinterPrologVillage : bool ) : int
	{
		var i : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var area : int;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].worldPath == worldPath )
			{
				area = areaMapPins[ i ].areaType;
				if ( !noWinterPrologVillage )
				{
					if ( area == AN_Prologue_Village_Winter )
					{
						// fix for different w2w that actually mean the same map
						area = AN_Prologue_Village;
					}
				}
				return area;
			}
		}
		return AN_Undefined;
	}


	function GetMapName( areaType : int ) : string
	{
		var i : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var mapName : string;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].areaType == areaType )
			{
				mapName = StrAfterLast( areaMapPins[ i ].worldPath, StrChar( 92 ) ); //#B because "/" can't be inserted here as string StrChar(92) was used
				mapName = StrReplace( mapName, ".w2w", "" );
				return mapName;
			}
		}
		return "";
	}
	
	function GetWorldPathFromAreaType( areaType : int ) : string
	{
		var i : int;
		var areaMapPins : array< SAreaMapPinInfo >;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].areaType == areaType )
			{
				return areaMapPins[ i ].worldPath;
			}
		}
		return "";
	}
	
	function GetLocalisationNameFromAreaType( areaType : int ) : name
	{
		var i : int;
		var areaMapPins : array< SAreaMapPinInfo >;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].areaType == areaType )
			{
				return areaMapPins[ i ].localisationName;
			}
		}
		return '';
	}

	function GetLocalisationDescriptionFromAreaType( areaType : int ) : name
	{
		var i : int;
		var areaMapPins : array< SAreaMapPinInfo >;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].areaType == areaType )
			{
				return areaMapPins[ i ].localisationDescription;
			}
		}
		return '';
	}
	
	event OnManageFastTravelAreas( enable : bool, show : bool, affectedAreas : array< int > )
	{
		var i, j : int;
		var tags : array< name >;

		var area : EAreaName;
		var path : string;

		for ( i = 0; i < affectedAreas.Size(); i += 1 )
		{
			/*
			if ( GetEntityMapPinTags( GetWorldPathFromAreaType( affectedAreas[ i ] ), tags ) )
			{
				for ( j = 0; j < tags.Size(); j += 1 )
				{
					ManageFastTravelPoint( enable, show, tags[ j ] );
				}
			}
			*/
		}
	}

	event OnManageFastTravelPoints( enable : bool, show : bool, affectedFastTravelPoints : array< name > )
	{
		var i : int;
		for ( i = 0; i < affectedFastTravelPoints.Size(); i += 1 )
		{
			ManageFastTravelPoint( enable, show, affectedFastTravelPoints[ i ] );
		}
	}
	
	function ManageFastTravelPoint( enable : bool, show : bool, tag : name )
	{
		SetEntityMapPinDisabled( tag, !enable );
		SetEntityMapPinDiscoveredScript(true, tag, show );
	}
	
	// keep it "compatible" with HasFastTravelPoints points (below)
	function GetFastTravelPoints( onlyDiscovered : bool, onlyEnabled : bool, optional ignoreLand : bool, optional ignoreWater : bool, optional ignoreVelenAndPrologueWinter : bool ) : array< SAvailableFastTravelMapPin >
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var pin : SAvailableFastTravelMapPin;
		var pins : array< SAvailableFastTravelMapPin >;
		var type : name;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if(ignoreVelenAndPrologueWinter && (areaMapPins[i].areaType == AN_Velen || areaMapPins[i].areaType == AN_Prologue_Village_Winter) )
				continue;
				
			entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
			for ( j = 0; j < entityMapPins.Size(); j += 1 )
			{
				type = entityMapPins[ j ].entityType;
				if ( type != 'RoadSign' && type != 'Harbor' )
				{
					continue;
				}
				if ( ignoreLand && type == 'RoadSign' )
				{
					continue;
				}
				if ( ignoreWater && type == 'Harbor' )
				{
					continue;
				}

				if ( onlyDiscovered )
				{
					if ( !IsEntityMapPinDiscovered( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				if ( onlyEnabled )
				{
					if ( IsEntityMapPinDisabled( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				pin.tag  = entityMapPins[ j ].entityName;
				pin.type = entityMapPins[ j ].entityType;
				pin.area = areaMapPins[ i ].areaType;
				pins.PushBack( pin );
			}
		}
		return pins;
	}

	// keep it "compatible" with GetFastTravelPoints points (above)
	function HasFastTravelPoints( onlyDiscovered : bool, onlyEnabled : bool, optional ignoreLand : bool, optional ignoreWater : bool, optional ignoreVelenAndPrologueWinter : bool ) : bool
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var type : name;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if(ignoreVelenAndPrologueWinter && (areaMapPins[i].areaType == AN_Velen || areaMapPins[i].areaType == AN_Prologue_Village_Winter) )
				continue;
				
			entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
			for ( j = 0; j < entityMapPins.Size(); j += 1 )
			{
				type = entityMapPins[ j ].entityType;
				if ( type != 'RoadSign' && type != 'Harbor' )
				{
					continue;
				}
				if ( ignoreLand && type == 'RoadSign' )
				{
					continue;
				}
				if ( ignoreWater && type == 'Harbor' )
				{
					continue;
				}

				if ( onlyDiscovered )
				{
					if ( !IsEntityMapPinDiscovered( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				if ( onlyEnabled )
				{
					if ( IsEntityMapPinDisabled( entityMapPins[ j ].entityName ) )
					{
						continue;
					}
				}
				
				return true;
			}
		}
		return false;
	}	
	
	function GetKnownableEntityNames() : array< name >
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var names : array< name >;
		var type : name;
		var knowableMapPinTypes : array< name >;
	
		GetKnowableMapPinTypes( knowableMapPinTypes);
	
		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
			for ( j = 0; j < entityMapPins.Size(); j += 1 )
			{
				if ( knowableMapPinTypes.Contains( entityMapPins[ j ].entityType ) )
				{
					names.PushBack( entityMapPins[ j ].entityName );
				}
			}
		}
		return names;
	}
	
	function GetDiscoverableEntityNames() : array< name >
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var names : array< name >;
		var type : name;
		var discoverableMapPinTypes : array< name >;
	
		GetDiscoverableMapPinTypes( discoverableMapPinTypes );
	
		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
			for ( j = 0; j < entityMapPins.Size(); j += 1 )
			{
				if ( discoverableMapPinTypes.Contains( entityMapPins[ j ].entityType ) )
				{
					names.PushBack( entityMapPins[ j ].entityName );
				}
			}
		}
		return names;
	}
	
	function GetLocalFastTravelPointPosition( entityName : name, landOnly : bool, out position : Vector, out rotation : EulerAngles ) : bool
	{
		return GetFastTravelPointPosition( theGame.GetWorld().GetDepotPath(), entityName, landOnly, position, rotation );
	}
	
	function GetFastTravelPointPosition( worldPath : string, entityName : name, landOnly : bool, out position : Vector, out rotation : EulerAngles ) : bool
	{
		var i, j : int;
		var areaMapPins : array< SAreaMapPinInfo >;
		var entityMapPins : array< SEntityMapPinInfo >;
		var type : name;

		areaMapPins = GetAreaMapPins();
	    for ( i = 0; i < areaMapPins.Size(); i += 1 )
	    {
			if ( areaMapPins[ i ].worldPath == worldPath )
			{
				entityMapPins = GetEntityMapPins( areaMapPins[ i ].worldPath );
				for ( j = 0; j < entityMapPins.Size(); j += 1 )
				{
					if ( entityMapPins[ j ].entityName == entityName )
					{
						if ( landOnly && entityMapPins[ j ].entityType == 'RoadSign' )
						{
							position = entityMapPins[ j ].fastTravelTeleportWayPointPosition;
							rotation = entityMapPins[ j ].fastTravelTeleportWayPointRotation;
							return true;
						}
						else if ( !landOnly && entityMapPins[ j ].entityType == 'Harbor' )
						{
							position = entityMapPins[ j ].fastTravelTeleportWayPointPosition;
							rotation = entityMapPins[ j ].fastTravelTeleportWayPointRotation;
							return true;
						}
					}
				}
				break;
			}
		}
		return false;
	}
	
	function AllowSaving( allow : bool )
	{
		if ( allow )
		{
			theGame.ReleaseNoSaveLock( m_noSaveLock );
		}
		else
		{
			m_noSaveLock = 12345;
			theGame.CreateNoSaveLock( "EndOfTheWorld", m_noSaveLock, false, false );
		}
	}

	function DBG_ShowKnownPins( show : bool )
	{
		m_dbgShowKnownPins = show;
		DBG_UpdateShownKnownPins();
	}
	
	function DBG_UpdateShownKnownPins()
	{
		var i : int;
		var arr : array< name >;

		if ( m_dbgShowKnownPins )
		{
			arr = GetKnownableEntityNames();
			for ( i = 0; i < arr.Size(); i += 1 )
			{
				SetEntityMapPinKnown( arr[ i ], true );
			}
		}
	}
	
	function DBG_ShowPins( show : bool )
	{
		m_dbgShowKnownPins = show;
		DBG_UpdateShownKnownPins();
		
		m_dbgShowPins = show;
		DBG_UpdateShownPins();
	}
	
	function DBG_UpdateShownPins()
	{
		var i : int;
		var arr : array< name >;

		if ( m_dbgShowPins )
		{
			arr = GetDiscoverableEntityNames();

			for ( i = 0; i < arr.Size(); i += 1 )
			{
				SetEntityMapPinKnown( arr[ i ], true );
				SetEntityMapPinDiscovered( arr[ i ], true );
			}
		}
	}

	function DBG_ShowAllFT( show : bool )
	{
		m_dbgShowAllFT = show;
		DBG_UpdateShownFT();
	}
	
	function DBG_UpdateShownFT()
	{
		var i : int;
		var arr : array< SAvailableFastTravelMapPin >;

		if ( m_dbgShowAllFT )
		{
			arr = GetFastTravelPoints( false, false );
			for ( i = 0; i < arr.Size(); i += 1 )
			{
				SetEntityMapPinDiscovered( arr[ i ].tag, true );
			}
		}
	}
	
	function DBG_AllowFT( allow : bool )
	{
		m_dbgAllowFT = allow;
	}
	
	function DBG_IsAllowedFT() : bool
	{
		return m_dbgAllowFT;
	}
}

exec function ShowPinsFTInfo()
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.TogglePinsInfo( 1 );
}

exec function ShowPathsInfo( show : bool )
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.TogglePathsInfo( show );
}

exec function ShowQuestAgents( show : bool )
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.ToggleQuestAgentsInfo( show );
}

exec function ShowShopkeepers( show : bool )
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.ToggleShopkeepersInfo( show );
}

exec function ii()
{
	theGame.GetCommonMapManager().ToggleInteriorInfo( true );
}

exec function ShowPinsInfo( value : int )
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.TogglePinsInfo( value );
}

exec function exportglobalmappins()
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.ExportGlobalMapPins();
}

exec function exportentitymappins()
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.ExportEntityMapPins();
}

exec function useinteriors( use : bool )
{
	var commonMapManager : CCommonMapManager;
	
	commonMapManager = theGame.GetCommonMapManager();
	commonMapManager.UseInteriorsForQuestMapPins( use );
}

exec function testFT()
{
	var commonMapManager : CCommonMapManager;
	var pins : array< SAvailableFastTravelMapPin >;
	var i : int;

	commonMapManager = theGame.GetCommonMapManager();
	pins = commonMapManager.GetFastTravelPoints( false, false );
	
	LogChannel( 'OOOOOO', "--------- AVAILABLE FAST TRAVEL MAP PINS: " + pins.Size() );
	for ( i = 0; i < pins.Size(); i += 1 )
	{
		LogChannel( 'OOOOOO', ">>> " + pins[ i ].area + " " + pins[ i ].type + " " + pins[ i ].tag );
	}
	
	pins = commonMapManager.GetFastTravelPoints( true, true );
	
	LogChannel( 'OOOOOO', "--------- AVAILABLE FAST TRAVEL MAP PINS: " + pins.Size() );
	for ( i = 0; i < pins.Size(); i += 1 )
	{
		LogChannel( 'OOOOOO', ">>> " + pins[ i ].area + " " + pins[ i ].type + " " + pins[ i ].tag );
	}
	LogChannel( 'OOOOOO', "-----------------------------------------" );

}

exec function ShowKnownPins( show : bool )
{
	theGame.GetCommonMapManager().DBG_ShowKnownPins( show );
}

exec function ShowPins( show : bool )
{
	theGame.GetCommonMapManager().DBG_ShowPins( show );
}

exec function ShowAllFT( show : bool )
{
	theGame.GetCommonMapManager().DBG_ShowAllFT( show );
}

exec function AllowFT( allow : bool )
{
	theGame.GetCommonMapManager().DBG_AllowFT( allow );
}

exec function gotoWyzima()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\wyzima_castle\wyzima_castle.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}

exec function gotoNovigrad()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\novigrad\novigrad.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}
exec function gotoSkellige()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\skellige\skellige.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}
exec function gotoKaerMohren()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\kaer_morhen\kaer_morhen.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}
exec function gotoProlog()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\prolog_village\prolog_village.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}

exec function gotoPrologWinter()
{
	theGame.ScheduleWorldChangeToMapPin( "levels\prolog_village_winter\prolog_village.w2w", '' );
	theGame.RequestAutoSave( "fast travel", true );
}

exec function knowMapPin( tag : name )
{
	if ( tag != '' )
	{
		theGame.GetCommonMapManager().SetEntityMapPinKnown( tag, true );
	}
}

exec function discoverMapPin( tag : name )
{
	if ( tag != '' )
	{
		theGame.GetCommonMapManager().SetEntityMapPinDiscovered( tag, true );
	}
}

exec function disableMapPin( tag : name )
{
	if ( tag != '' )
	{
		theGame.GetCommonMapManager().SetEntityMapPinDisabled( tag, true );
	}
}
