------------------------------------------------------------------------------
-- class 'PlayFieldBase'
-- Базовый клас 'локации', предоставляет интерфейс для создания/регистрации 
-- элементов игровой логики (зум зоны, активная область, нахождение тулзы, 
-- хо таски) и доступа к игровым обьектам.
------------------------------------------------------------------------------

class 'PlayFieldBase' (Node)

-------------------------------------------------------------------------------
function PlayFieldBase:__init(game_scene, name)
	Node.__init(self, game_scene:getMainEntity(), 'PlayField', -1)
	WriteInfo('Init PlayFieldBase: '..name)
	
	-- Необходимы для инициализации анимаций и зум-зон
	self.name = name
	self.path_lua = levels['l_'..name].path
	self.game_scene = game_scene
	self.game_scene.playfield = self
	
	--
	self.is_collectors_edition   = levels['l_'..name].collectors_edition
	self.mode                    = levels['l_'..name].mode
	self.parent_level            = levels['l_'..name].parent_level_name
	self.bDestroyed = false
	
	self.active_objects = {}
	self.tool_find_object_names = {}
	self.tool_apply_object_names = {}
	self.linked_scenes = {}
	self.wrong_apply_dialogs = {}
	self.wrong_apply_actions = {}

	self.dragManager = DragObjectManager(self)
	self.hint		 = Hint(self)
	self.glint		 = Glint(self)

	self.collect_manager = CollectObjectsManager(self)    
	self.music_list = nil
	self.is_outdoor_scene = false
	self.sounds = parseLevelSounds(name)
	
	self.active_zones       = CreateInheritedClass(LuaStack):New()
	self.zones              = CreateInheritedClass(LuaStack):New()
	
	self.hidden_zoom_zones		 = {}
	self.hidden_zoom_zones.zones = LuaStack:New()
	self.hidden_zoom_zones.count = LuaStack:New()
	
	self.toolDenyStringsCount	= 4
	self.mouseBlocker			= MouseBlocker:new( self:getMainEntity() )
	
	self.hightlight_object = {}
	self.mark_link_with_particles = false		-- Если false, то для зон перехода в HiddenObjects и HiddenPictures будет создаваться раскадравка, иначе - партиклы.
	
	-- Создаём таймер
	self.timer = Timer()

	-- Содаем объект для fade ZoomZone
	self:createZoomFade()
	
	-- Тут будет загрузка ресурсов
	if self.CreateLoaderTable then
		local table_resource = self:CreateLoaderTable()
		LoadScene( self.main_entity, table_resource )
--[[
		self.lastResourceLoadedNotification = getLastResourceLoadedNotification()

		if self.lastResourceLoadedNotification then
			self.lastResourceLoadedNotification:add(function ()
				self.lastResourceLoadedNotification = nil
			end)
		end
]]
	end
	
	self:hideBlackBar()

	-- Заставляем вызываться OnUpdate по сигналу
	self:getMainEntity().onUpdate:add(bind(self.onDefaultUpdate, self))
	self:getMainEntity():setHandlingChildEvents(true)

	if TEMP_USE_OLD_INVENTORY then
		-- Обработчик для сброса тулзы
		self:getMainEntity().onClickBegin:add(function()
													local tool_in_hand = self:getInventory():getToolInHand()
													if tool_in_hand then
														self:tryToolApply(tool_in_hand, nil)
													else
														self:hideBlackBar()
													end
											  end)
	else
		self:getMainEntity().onClickBegin:add(	function()
													self:hideBlackBar()
												end )
	end
	
	if TEMP_USE_OLD_INVENTORY then
		self:getInventory():checkCellsOpacity()
		self:getInventory():checkSlideBtnsEnable()
	end
	
	-- Проверка и установка состояний о посещении сцены
	do
		if not isSceneVisited(self.name) then
			self.visited_first_time = true
			AddGameState(SCENE_VISITED, self.name)
		end
		
		if HasGameState(LAST_VISITED_SCENE) and GetGameState(LAST_VISITED_SCENE):getSize() > 0 then
			self.lastVisitedScene = GetGameState(LAST_VISITED_SCENE):get(0)
		end
		if not self:isLastVisitedSceneUpdateDisabled() then
			SetGameState(LAST_VISITED_SCENE, self.name)
			SetGameState(LAST_VISITED_SCENE, self.name, EGameStateTag.GENERAL)
		end
	end
	
	-- Устанавливаем обработчик для посдветки объектов
	if isCheatMode() then
		game_scene:getWindow():getHotkeySignal(ERSK_TAB):removeAll()
		game_scene:getWindow():getHotkeySignal(ERSK_TAB):add( bind(self.toggleHighlightObjects, self) )
		game_scene:getWindow():getHotkeySignal(ERSK_S):removeAll()
		game_scene:getWindow():getHotkeySignal(ERSK_S):add(function() 
																local mode = self:getGameScene():getPanel():getMode()
																if mode == EPanelMode.MINIGAME then
																	self:getGameScene():getPanel():getControlsPanel():getHintAnimationLogic():completeFilling()
																end
															end)
		game_scene:getWindow():getHotkeySignal(ERSK_H):removeAll()
		game_scene:getWindow():getHotkeySignal(ERSK_H):add(function()
																local mode = self:getGameScene():getPanel():getMode()
																if mode == EPanelMode.QUEST or mode == EPanelMode.HIDDENOBJECT or mode == EPanelMode.HIDDENPICTURE then
																	self:getHint():hint()
																end
															end)
		if self.game_scene.is_objects_highlighted then
			self.timer:add(	bind(self.highlightObject, self, self.main_entity), 0)
		end
	end
	
	if isBuildForCustomerMode() == false then
		--F5 - savestate
		--F9 - restorestate
		--F10 - reload current level
		self:createDebugShortcuts()
	end
	
	-- self.game_scene:getPanel():setMode(EPanelMode.NONE)
	-- self:getGameScene():getPanel():disableCutSceneMode()
	
	if InteractiveHelp and InteractiveHelp.tutorialScreen then
		InteractiveHelp.tutorialScreen:onScreenDestroy()
	end

	self:updatePanel()
	self:setupAlternativeHO()
	self:getInventory():clearToolsEvents() -- Очищаем функции-колбеки, чтобы избежать их вызовов с чужой сцены

	do
		local size = Vector2f(WSCR_W, WSCR_H)
		self:getMainEntity():setSize( size )
		self:getMainEntity():setHotSpot( (size.x - WORK_W) / 2, (size.y - WORK_H) / 2)
		self:getMainEntity():setColor( Colorf(0, 0, 0, 1) )
	end

	self:getMainEntity().onDestroy:add(bind(self.save, self))
end
-------------------------------------------------------------------------------

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:onSceneStart()
		local inv = self:getInventory()

		local function createFoundButNotShownTool(tool_name)
			local tool_info = inv.tools[tool_name]
		
			if  IsToolFound(tool_name)
				and not IsToolApplied(tool_name)
				and not HasGameState(TOOL_INFO_SHOWN, tool_name)
				and not tool_info.notInInventoryYet -- фикс во избежания появления двух окон тулфайнд
			then            
				inv:addTool(tool_info)
				inv:lockTool(tool_info)

				self:createToolWindow(tool_info)
			end
		end
		
		for tool_name, _ in pairs(inv.tools) do
			createFoundButNotShownTool(tool_name)
		end

		self.timer:add(function() self:glintActiveObjects() end, 5, true)
		self:getGameScene():getPanel():getControlsPanel():getHintAnimationLogic():onSceneStart()
		if self:getGameScene():getIngameEditor() then
            self:getGameScene():getIngameEditor():on_scene_start()
        end
	end
else
	function PlayFieldBase:onSceneStart()
		self:getInventory():checkFoundButNotShownTools()

		self.timer:add( bind(self.glintActiveObjects, self), 5, true)
		self:getGameScene():getPanel():getControlsPanel():getHintAnimationLogic():onSceneStart()
		
		if self:getGameScene():getIngameEditor() then
            self:getGameScene():getIngameEditor():on_scene_start()
        end
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:__finalize()
end
-------------------------------------------------------------------------------

function PlayFieldBase:destroy()
	self.main_entity:destroy()
	for action, _ in pairs(self.timer:getActions()) do
		action:kill()
	end
	self.timer:clear()
	
	if self.ho_logic then
		self.ho_logic:destroy()
	end

	self.bDestroyed = true
end
-------------------------------------------------------------------------------
function PlayFieldBase:save()
	path_manager:saveToGameState()
	ProfilesManager:save()

	GetScoreManager():submit()
end
-------------------------------------------------------------------------------

function PlayFieldBase:isDestroyed()
	return self.bDestroyed
end
-------------------------------------------------------------------------------

function PlayFieldBase:close()
	if TEMP_USE_OLD_INVENTORY then
		self:getDragManager():dropAll(false, true)
		self:getInventory():dropTool()
	else
		abortDrags()
		cancelScreenFocuses()
	end

	if getPrimaryGameStateTag() == EGameStateTag.EXTRA then
		PopUpScreensMgr:destroyAll()
		destroyCurrentScene()
		
		local extra = createExtraScreen()
		setPrimaryGameStateTag( nil )

		local lastReplayedScene = ExtraContentHelpers.getLastReplayedScene()
		
		if lastReplayedScene then
			if string.find(lastReplayedScene, '_HO') or string.find(lastReplayedScene, '_HP') then
				extra:onHiddenObjectsPress()
			else
				extra:onMiniGamesPress()
			end
		end
	else
		if self.parent_level then
			startLevel('l_'..self.parent_level)
		end
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:createZoomFade()
	local entity = CreateSpriteEntity('')
	entity:setName	( 'fade_in_zoom_zone' )
	entity:attachTo	( self:getMainEntity() )
	entity:setSize	( WSCR_W, WSCR_H )
	entity:setXY	( WORK_W / 2, WORK_H / 2)
	entity:setOrder	( 5 )
	entity:setColor	( Colorf(0, 0, 0, 0.7) )
	entity:hide()

	entity.onClickBegin:add(function()
								local activeZone = self:getActiveZones():GetTop()

								if activeZone and activeZone:isCloseOnClickFade() then
									activeZone:close()
								end
							end)
		
	self.zoom_fade = entity
end
-------------------------------------------------------------------------------

function PlayFieldBase:getZoomZonePath(name)
	local pathDir   = GetParentFolderPath(self:getPathLua())
	local nameZoom  = self:getName()..'_Z_'..name
	local pathZoomLua = pathDir..'/'..nameZoom..'.lua'
	return pathZoomLua, nameZoom
end
-------------------------------------------------------------------------------

function PlayFieldBase:createZoomZone(name, mode)
	local path, nameZoom = self:getZoomZonePath(name)
	dofile(path)
	local zoom = Helpers.CallFunctionByName('l_'..nameZoom, name, mode, self)
	return zoom
end
-------------------------------------------------------------------------------

function PlayFieldBase:createComics(name)
	return Comics(self, name)
end
-------------------------------------------------------------------------------

function PlayFieldBase:isComicsShown(name, level_name)
	local nameComics = (level_name and level_name or self:getName() ) ..'_'..name
	return HasGameState(COMICS_SHOWN, nameComics)
end
-------------------------------------------------------------------------------

function PlayFieldBase:onDefaultUpdate(entity, dt)
	self.timer:update(dt)
	
	if self.ho_logic then
		self.ho_logic:update(dt)
	end
	
	if self.onUpdate then
		self:onUpdate(dt)
	end
	
	self.ignore_tool_apply_clicks = false
end
-------------------------------------------------------------------------------

function PlayFieldBase:onDefaultSkipMiniGame()
	self.game_scene:getAchievmentsManager():handleEvent('on_skip_pressed', {['scene_name'] = self:getName(), ['round'] = self:getCurrentMinigameRound()})
	self:getDragManager():dropAll(false, true)

	if self.SkipMiniGame then
		self:SkipMiniGame()
	end
	self:callForZones('onDefaultSkipMiniGame')
	
	if isHiddenPictureLevel(self:getName()) then
		self:getPanel():setMode(EPanelMode.HIDDENPICTURE)
	else
		self:getPanel():setMode(EPanelMode.QUEST)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:callForZones(funcName, ...)
	local active_zone = self:getActiveZones():GetTop()
	if active_zone then
		local ZoneObject = active_zone
		if ZoneObject and ZoneObject[funcName] then
			ZoneObject[funcName](ZoneObject, ...)
		end
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:getObjectClickedState(objectName, levelName)
	levelName = levelName or self:getName()
	return levelName..'('..objectName..')'
end
-------------------------------------------------------------------------------

function PlayFieldBase:isObjectClicked(objectName, levelName)
	local state = self:getObjectClickedState(objectName, levelName)
	return HasGameState(OBJECT_CLICKED, state)
end
-------------------------------------------------------------------------------

-- 'Регистр' функции ----------------------------------------------------------
-------------------------------------------------------------------------------

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:registerActiveObject(obj_name, on_click, multi_use, cursor_name)
		assert(obj_name)

		if not (multi_use or self:isObjectClicked(obj_name)) then
			node = path_manager:addClickNode(obj_name)
		end

		cursor_name = cursor_name or 'CursorActive'
		
		local bObjectClicked = self:isObjectClicked(obj_name)
		
		local function on_click_check(save)
			if not self:getInventory():getToolInHand() or self.tool_apply_object_names[obj_name] then
				local event  = {}
				event.save = false
				if save == true then
					event.save = true
				end
				event.target = obj_name
				
				if not event.save then
					local state_value = self:getName()..'('..obj_name..')'
					AddUniqueGameState(OBJECT_CLICKED, state_value)
					
					if not multi_use then
						local node = path_manager:getConnectedNode(NodeType.CLICK, obj_name)
						assert(node and 'No node for object click')
						node:trigger()
					end
				end
				
				if on_click then
					on_click(event)
				end
				
				if not multi_use then
					--self:getEntityNamed(obj_name).onClickBegin:removeAll()
					if self.active_objects[obj_name] and self.active_objects[obj_name].connection then
						self.active_objects[obj_name].connection:remove()
						self.active_objects[obj_name].connection = nil
					end
					self:getEntityNamed(obj_name):setCursorOverName('')

				end
			elseif self:getInventory():getToolInHand() then
				--self:getInventory():dropTool() - we also need registerWrongApplyDialog working, so we are calling tryToolApply in place of simple dropTool()
				self:tryToolApply(self:getInventory():getToolInHand(), self:getEntityNamed(obj_name))
			end

			-- Чтоб не посылалось родителям сообщения, для НЕ закрытие BlackBar-a
			--if getHandledCursor() then 
				--self:getEntityNamed(obj_name):discardEvent()
			--end
		end
		
		if multi_use or not bObjectClicked then
			self.active_objects[obj_name] = {}
			self.active_objects[obj_name].on_click  = on_click
			self.active_objects[obj_name].multi_use = multi_use
			
			self:getEntityNamed(obj_name):setCursorOverName(cursor_name)
			local connection = self:getEntityNamed(obj_name).onClickBegin:add(on_click_check)
			self:getEntityNamed(obj_name):setSendEventsToParent(false)
			self.active_objects[obj_name].connection = connection
		end
		
		if bObjectClicked then
			on_click_check(true)
		end
	end
	-------------------------------------------------------------------------------
else
	function PlayFieldBase:registerActiveObject(objectName, onClick, bMultiUse, cursorName)
		assert(objectName)
		local bObjectClicked = self:isObjectClicked(objectName)
		local object		 = self:getEntityNamed(objectName)

		if not (bMultiUse or bObjectClicked) then
			node = path_manager:addClickNode(objectName)
		end
		
		local function on_click_check(save)
			save = (save == true)
			
			if not save then
				local clickedState = self:getObjectClickedState(objectName)
				AddUniqueGameState(OBJECT_CLICKED, clickedState)
				
				if not bMultiUse then
					local node = path_manager:getConnectedNode(NodeType.CLICK, objectName)
					assert(node and 'No node for object click')
					node:trigger()
				end
			end
			
			if onClick then
				local event	 = {}
				event.save	 = save
				event.target = objectName

				onClick(event)
			end
			
			if not bMultiUse then
				if self.active_objects[objectName] and self.active_objects[objectName].connection then
					self.active_objects[objectName].connection:remove()
					self.active_objects[objectName].connection = nil
				end
				object:setCursorOverName('')
			end
		end
		
		if bMultiUse or not bObjectClicked then
			local connection = object.onClickBegin:add(on_click_check)

			object:setSendEventsToParent(false)
			object:setCursorOverName	(cursorName or 'CursorActive')

			self.active_objects[objectName] = {}
			self.active_objects[objectName].on_click	= onClick
			self.active_objects[objectName].multi_use	= bMultiUse
			self.active_objects[objectName].connection	= connection
		end
		
		if bObjectClicked then
			on_click_check(true)
		end
	end
	-------------------------------------------------------------------------------
end

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:registerToolFindInfo(tool_name, trigger_name, on_find, do_not_show_window, on_tool_window_close)
		local info  = self:getInventory().tools[tool_name]
		
		assert(info, "registerToolFindInfo: "..tool_name.." probably not exists.")
		
		info.trigger_name = trigger_name
		info.on_find = on_find
		info.find_scene = self.name
		info.do_not_show_window = do_not_show_window
		info.on_tool_window_close = on_tool_window_close

		node = path_manager:addToolFinding(tool_name, trigger_name)

		local function on_tool_area_click(event)
			if not event.save then
				if not self:getInventory():getToolInHand() then
					self:findTool(tool_name, info.do_not_show_window, info.on_tool_window_close)
				else
					self:getInventory():dropTool()
				end
			else
				if info.on_find then
					local loc_event     = {}
					loc_event.save      = event.save
					loc_event.target    = trigger_name
					loc_event.tool_name = tool_name
				
					info.on_find(loc_event)
				end
			end
				   
			if info.trigger_name then
				self:getEntityNamed(info.trigger_name):hide()
			end
		end
		
		self:registerActiveObject(trigger_name, on_tool_area_click, true, 'CursorActive')
		self.tool_find_object_names[trigger_name] = tool_name
		
		return info
	end
	-------------------------------------------------------------------------------
else
	function PlayFieldBase:registerToolFindInfo(toolName, findTargetName, onFind, bDontShowWindow, onToolWindowClose)
		local toolInfo	= self:getInventory():getToolInfo(toolName)
		assert(toolInfo, string.format("registerToolFindInfo: %s probably not exists.", toolName))
		
		toolInfo.trigger_name			= findTargetName
		toolInfo.find_scene				= self:getName()
		toolInfo.on_find				= onFind
		toolInfo.on_tool_window_close	= onToolWindowClose
		toolInfo.do_not_show_window		= bDontShowWindow

		node = path_manager:addToolFinding(toolName, findTargetName)

		local function on_click(e)
			if e.save then
				if toolInfo.on_find then
					local event     = {}
					event.save      = e.save
					event.target    = findTargetName
					event.tool_name = toolName
				
					toolInfo.on_find(event)
				end
			else
				self:findTool(toolName, toolInfo.do_not_show_window, toolInfo.on_tool_window_close)
			end
				   
			if toolInfo.trigger_name then
				self:getEntityNamed(toolInfo.trigger_name):hide()
			end
		end
		
		self:registerActiveObject(findTargetName, on_click, true, 'CursorActive')
		self.tool_find_object_names[findTargetName] = toolName
		
		return toolInfo
	end
	-------------------------------------------------------------------------------
end

function PlayFieldBase:registerToolApplyInfo(tool_name, target_name, on_apply, text_alias, once_apply)
	local info = self:getInventory().tools[tool_name]
	
	-- Подменяем регистрацию, если тулза входит в коллекцию, но не является самой "главной" в ней
	assert(info or WriteError('Tool '..tool_name..' not found'))
	if info.additional_info.collection and not info.additional_info.collection_owner then
		for _, t_name in ipairs( info.additional_info.collection:get_tools() ) do
			local t_inf = self:getInventory().tools[t_name]
			if t_inf.additional_info.collection_owner then
				tool_name = t_name
				info = self:getInventory().tools[tool_name]
				break
			end
		end
	end
	
	info.target_name      = (target_name ~= nil) and target_name or info.getLevelTargets()
	info.target_scene     = self:getName()
	info.on_apply         = on_apply
	info.on_get           = nil
	info.on_drop          = nil
	info.on_target_click  = nil -- Дополнительное действие при клике на зону применения
	info.apply_text_alias = text_alias
	info.once_apply       = once_apply -- Для айтемов, которые должны быть применены на одну из нескольких зон
	
	assert(info.target_name)
	
	if type(target_name) == 'table' then
		for _, area in ipairs(target_name) do
			node = path_manager:addToolApplying(tool_name, area)
		end
	else
		node = path_manager:addToolApplying(tool_name, target_name)
	end

	info.additional_info.local_targets_count   = (type(info.target_name) == 'table') and #info.target_name or 1
	info.additional_info.local_targets_applied = 0
	
	local targetCursor = 'CursorGlass'
	local bToolApplied = IsToolApplied(tool_name)
	
	-- Обработчик клика тулзой (общий для простой и мультитулзы)
	local function on_target_click(l_event)
		if not l_event.save and not self.ignore_tool_apply_clicks then
			local tool_in_hand = TEMP_USE_OLD_INVENTORY and self:getInventory():getToolInHand() or nil
			
			if tool_in_hand  then
				self:tryToolApply(tool_in_hand, self:getEntityNamed(l_event.target))
				self.ignore_tool_apply_clicks = true --disable other tool apply tries on this update
			elseif  (type(info.target_name) == 'string' and not IsToolApplied(tool_name)) or
					(type(info.target_name) == 'table' and not IsToolTargetUsed(tool_name, l_event.target, self.name))
			then
				local alias = nil
				local text_alias_type = type(info.apply_text_alias)
				
				if text_alias_type == 'string' then
					alias = info.apply_text_alias
				elseif text_alias_type == 'table' then
					local index = math.random(#info.apply_text_alias)
					alias = info.apply_text_alias[index]
				end
				
				if alias and getPlayModeParameters().blackbarEnabled then -- isHardPlayMode() == false
					self:showBlackBar(alias)
				end

				if info.on_target_click then
					local event    = {}
					event.toolName = tool_name
					event.target   = l_event.target
					
					info.on_target_click(event)
				end
			end
		end
	end
	
	-- Обработчик простой тулзы
	if type(info.target_name) == 'string' then
		local state, value = GenerateToolTargedUsed(tool_name, info.target_name, self.name)
		
		-- Если тулзу уже применили
		if HasGameState(state, value) then
			info.additional_info.local_targets_applied = info.additional_info.local_targets_applied + 1
			
			if on_apply then
				local event                  = {}
				event.save                   = true
				event.target                 = info.target_name
				event.toolName               = tool_name
				event.applied                = bToolApplied
				event.allLocalTargetsApplied = (info.additional_info.local_targets_applied == info.additional_info.local_targets_count)
				
				on_apply(event)
			end
			
		-- Если тулзу ещё не применили
		else       
			self:registerActiveObject(info.target_name, on_target_click, true, targetCursor)
			self:getEntityNamed(info.target_name):setSendEventsToParent(false)
			
			local target = self:getEntityNamed(info.target_name)
				if not target.properties then
				target.properties = {}
			end
			target.properties.doTryToolApply = function(toolInfo)
				self:tryToolApply(toolInfo, target)
			end
			
			-- Дебажный обработчик, для авто-применения тулзов
			if isBuildForCustomerMode() == false then
				local function on_right_click(object, screenFocus)
					self:autoApplyTool(tool_name, object, screenFocus)
				end
				self:getEntityNamed(info.target_name).onRightClickBegin:add(on_right_click)
			end
		end

		self:addObjectAsTargetForTool(target_name, tool_name)
		
	-- Обработчик мультитулзы
	else
		if not info.additional_info.global_targets then
			info.additional_info.global_targets = {}
		end
		
		-- Добавить зоны применения в global_targets, если их там нет
		do
			for _, target in ipairs(info.target_name) do
				local not_in_global_targets = true
				for i, v in ipairs(info.additional_info.global_targets) do
					if v.level_name == self.name and 
					   v.target_name == target and
					   v.tool_name == info.name
					then
						not_in_global_targets = false
						break
					end
				end
				if not_in_global_targets then
					table.insert(info.additional_info.global_targets, GlobalToolTarget(self.name, info.name, target))
				end
			end
		end

		--
		local i = 1
		while info.target_name[i] do
			local v = info.target_name[i]
			local state, value = GenerateToolTargedUsed(tool_name, v, self.name)
			
			-- Если тулза уже применена на арею
			if HasGameState(state, value) then
				info.additional_info.local_targets_applied = info.additional_info.local_targets_applied + 1
				
				if on_apply then
					local event                  = {}
					event.save                   = true
					event.target                 = v
					event.toolName               = tool_name
					event.applied                = (bToolApplied and #info.target_name == 1)
					event.allLocalTargetsApplied = (info.additional_info.local_targets_applied == info.additional_info.local_targets_count)
					
					on_apply(event)
				end
				if once_apply then
					info.target_name = nil
					break
				end
				table.remove(info.target_name, i)
				
			-- Если тулза ещё не применена на арею
			else           
				self:registerActiveObject(v, on_target_click, true, targetCursor)
				self:getEntityNamed(v):setSendEventsToParent(false)
				
				local target = self:getEntityNamed(v)
				if not target.properties then
					target.properties = {}
				end
				target.properties.doTryToolApply = function(toolInfo)
					self:tryToolApply(toolInfo, target)
				end
				
				if isBuildForCustomerMode() == false then
					local function on_right_click(object, screenFocus)
						self:autoApplyTool(tool_name, object, screenFocus)
					end
					self:getEntityNamed(info.target_name[i]).onRightClickBegin:add(on_right_click)
				end
				i = i + 1
			end
		end

		for k, v in pairs(target_name) do
			self:addObjectAsTargetForTool(v, tool_name)
		end
	end
	
	-- Этот стейт используется только в хинте, и я считаю его избыточным. Возможно стоит его удалить.
	local state_name = 'RegisterToolApply_'..self.name    
	if not HasGameState(state_name, tool_name) then
		AddGameState(state_name, tool_name)
	end
	
	return info
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerWrongApplyDialog(tool_name, trigger_name, text_alias)
	if self.wrong_apply_dialogs[text_alias] == nil then
		self.wrong_apply_dialogs[text_alias] = {}
	end
	local dialog_table = self.wrong_apply_dialogs[text_alias]
	
	local function add_tool_function(area_table, tool)
		if tool ~= nil then
			if type(tool) == 'table' then
				for j, curr_tool in ipairs(tool) do
					table.insert(area_table, curr_tool)
				end
			else
				table.insert(area_table, tool)
			end
		else
			table.insert(area_table, '_ANY_TOOL_')
		end
	end
	   
	if type(trigger_name) == 'table' then
		for i, area in ipairs(trigger_name) do
			if dialog_table[area] == nil then
				dialog_table[area] = {}
			end
			
			add_tool_function(dialog_table[area], tool_name)
		end
	else
		if dialog_table[trigger_name] == nil then
			dialog_table[trigger_name] = {}
		end
			
		add_tool_function(dialog_table[trigger_name], tool_name)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:getWrongApplyDialog(toolName, targetName)
	for dialog, dialog_table in pairs(self.wrong_apply_dialogs) do
		local area_table = dialog_table[targetName]
		
		if area_table ~= nil then
			local any_tool_key_exist = false
			
			for i, tool in ipairs(area_table) do
				if tool == '_ANY_TOOL_' then
					any_tool_key_exist = true
					
				elseif tool == toolName then
					return dialog
				end
			end
			
			if any_tool_key_exist then
				return dialog
			end
		end
	end
	
	return nil
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerWrongApplyAction(toolName, targetName, action)
	assert(type(targetName) == 'string')
	toolName = toolName or '_ANY_TOOL_'

	local actionsTable = self.wrong_apply_actions[targetName] or {}
	actionsTable[toolName] = action
	self.wrong_apply_actions[targetName] = actionsTable
end
-------------------------------------------------------------------------------

function PlayFieldBase:getWrongApplyAction(toolName, targetName)
	local actionsTable = self.wrong_apply_actions[targetName]

	if actionsTable then
		return actionsTable[toolName] or actionsTable['_ANY_TOOL_']
	end

	return nil
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerBBT(obj_name, text_alias, alias_order, ignore_highlight_on_click, do_not_show_in_hard_mode)
	alias_order = alias_order or EBBTAliasOrder.SIMPLE
		
	local function on_click(event)
		local tool_in_hand = TEMP_USE_OLD_INVENTORY and self:getInventory():getToolInHand() or nil
	
		if not tool_in_hand then
			if not event.save then
				if do_not_show_in_hard_mode and (not getPlayModeParameters().blackbarEnabled) then -- isHardPlayMode()
					return
				end
				local text_alias_type = type(text_alias)
				 
				if text_alias_type == 'table' then
					local aliasCount = #text_alias
					local textKey    = nil
					
					if alias_order == EBBTAliasOrder.RANDOM then
						local index = math.random(aliasCount)
						textKey     = text_alias[index]
						
					elseif alias_order == EBBTAliasOrder.LOOP then
						local index = 0;
						local state = string.upper('MONOLOG_LOOP_STEP_'..self.name..'_'..obj_name)
						
						if HasGameState(state) then
							index = tonumber( GetGameState(state):get(0) )
						end
						
						index   = (index < aliasCount) and (index + 1) or 1
						textKey = text_alias[index]
						
						SetGameState(state, tostring(index))
						
					elseif alias_order == EBBTAliasOrder.SIMPLE then
						for _, _textKey in ipairs(text_alias) do
							textKey = _textKey
							if not HasGameState(BLACK_BAR_SHOWN, textKey) then
								break;
							end
						end
					end
					
					if textKey then
						self:showBlackBar(textKey)
					end
					
				elseif text_alias_type == 'string' then
					self:showBlackBar(text_alias)
				else
					assert(false and 'text_alias must be a string or table')
				end
			end
		else
			self:tryToolApply(tool_in_hand, self:getEntityNamed(obj_name))
		end
	end
						
	self:registerActiveObject(obj_name, on_click, true, 'CursorGlass')
	self:getEntityNamed(obj_name):setSendEventsToParent(false)
	-- TODO
	--if ignore_highlight_on_click then
 --       GetCurrentLevel().ignore_on_highlight[event.target] = true
 --   end
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerBBTWithScore(objectName, ...)
	self:getEntityNamed(objectName).onClickBegin:add(
		function()
			local object = self:getEntityNamed(objectName)
			GetScoreManager():addScoreBBT(object)
		end)

	self:registerBBT(objectName, ...)
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerTasks(keyPrefix, randomObjectsName, actionTasksObjectsName, ...)
	assert(self.ho_logic)
	self.ho_logic:registerTasks(keyPrefix, randomObjectsName, actionTasksObjectsName, ...)
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerDragObject(	obj_name, 
											get_area, 
											target_area, 
											on_get, 
											on_drop, 
											on_drop_target, 
											rect, 
											bLockCenter, 
											bLockTargetArea, 
											bIntersectionMode, 
											bDropOnUp,
											bOnceDrop)
	local dragObject = DragObject(self:getGameScene(), obj_name)
	self.dragManager:add(dragObject)

	dragObject:setTakeTarget( get_area )
	dragObject:setDropTarget( target_area )

	do
		local dropTargets = dragObject:getDropTarget()
		local takeTarget = dragObject:getTakeTarget()
		for i, theTarget in ipairs(dropTargets) do
			local node = path_manager:addDragDropNode(takeTarget:getName(), theTarget:getName())
		end
	end

	dragObject:setCallback('onTake',		on_get)
	dragObject:setCallback('onDrop',		on_drop)
	dragObject:setCallback('onDropTarget',	on_drop_target)

	if bLockCenter then
		dragObject:setDraggingOffset( Vector2f(0,0) )
		--dragObject:setOffsetAnimTime( 0 )
	end

	if bLockTargetArea then
		dragObject:setEnableDropTargetsOnDrag( true )
	end
	
	if bDropOnUp then
		dragObject:setStickyDrag( false )
	end

	if bOnceDrop then
		dragObject:setOnceTargetDrop()
	end
	
	if bIntersectionMode then
	    dragObject:setIntersectionMode(true)
	end

	if rect then
		if type(rect) == 'table' then
			local pos	= Vector2f(rect.x, rect.y)
			local size	= Vector2f(rect.w, rect.h)

			dragObject:setBoundsBySize(pos, size)
		else
			local LTPos = rect:getLeftTop()
			local RBPos = rect:getRightBottom()

			dragObject:setBounds(LTPos, RBPos)
		end
	end

	dragObject:run()
	return dragObject
end
-------------------------------------------------------------------------------

--[[
 * Метод регистрирует зону перехода на другую сцену. Если целевая сцена является HiddenObjects или HiddenPictures,
 * то для зоны перехда создается либо анимация (раскадровка), либо партиклы, которые маркируют ее.
 * @param	triggerName		Имя зоны перехода
 * @param	cursorName		Имя курсора
 * @param	levelName		Имя целевой сцены
 * @param	markerObjects	Имя анимации либо таблица имен анимаций, уже созданых на цсене для подсветки зоны перехода
--]]
function PlayFieldBase:linkScene(triggerName, cursorName, levelName, markerObjects)
	assert(self:getEntityNamed(triggerName) and "Trigger object doesn't exist in this location's playfield!")
	
	local node = path_manager:addLink(levelName, triggerName)

	local link           = {}
	link.level_name      = levelName
	link.cursor_name     = cursorName
	link.markerObjects   = nil
	link.markerParticles = nil

	self.linked_scenes[triggerName] = link

	if (isHiddenObjectLevel( levelName ) or isHiddenPictureLevel( levelName )) and getPlayModeParameters().sparklesHOEnabled then -- isCasualPlayMode()
		self:markLinkTrigger(triggerName, markerObjects)
	end
	
	if isSceneClosed(levelName) then
		self:getEntityNamed(triggerName):hide()
	end

	-- регистрация ActiveObject, при клике по которому будет происходить переход
	local on_click_transition = function(event)
		if TEMP_USE_OLD_INVENTORY then
			if self:getInventory():getToolInHand() then
				self:getInventory():dropTool()
				return
			end
		end

		if not event.save then
			if not isSceneClosed(levelName) then
				self:getMainEntity():setRespondable(false)      -- Убираем возможность открывать зумы, реагировать на активные объекты...
				self:getGameScene():loadPlayField(levelName)
			end
		end
	end
	self:registerActiveObject(triggerName, on_click_transition, true, cursorName)
end
-------------------------------------------------------------------------------

--[[
 * Метот создает необходимый маркер/ы для зоны перехода на другую сцену.
 * Если передается список уже созданых на сцене анимаций (раскадровок), то все они аттачатся к триггеру,
 * иначе - в завичимости от значения  константы self.mark_link_with_particles создается либо
 * анимация по-умолчанию, либо ParticleEntity.
 * @param	triggerName		Имя зоны перехода
 * @param	markerObjects	Имя анимации или таблица имен анимаций, которые уже созданы на сцени
 *						    и нуждаются в привязке к зоне перехода.
--]]
function PlayFieldBase:markLinkTrigger(triggerName, markerObjects)
	local link    = self.linked_scenes[triggerName]
	local trigger = self:getEntityNamed(triggerName)

	if markerObjects then
		if type(markerObjects) == 'string' then
			markerObjects = {markerObjects}
		end

		link.markerObjects = {}
		for i, markerName in ipairs(markerObjects) do
			local marker = self:getEntityNamed(markerName)
			marker:setRespondable(false)

			if not marker:hasParent(trigger) then
				local order = math.max(0, marker:getOrder() - trigger:getOrder())

				Helpers.attachInPlace(trigger, marker)
				marker:setOrder(order)
			end

			table.insert(link.markerObjects, marker)
		end
	else
		if self.mark_link_with_particles then
			link.markerParticles = self:createLinkMarkerParticles( triggerName )
		else
			link.markerObjects = {}

			local marker = self:createDefaultLinkMarkerObject( triggerName )
			table.insert(link.markerObjects, marker)
		end
	end
	
	local bActive = not isSceneClosed(link.level_name)
	self:setLinkMarkerActive(triggerName, bActive)
end
-------------------------------------------------------------------------------

--[[
 * Метод устанавливает активность и видимость для маркеров зоны перехода на другую сцену.
 * @param	triggerName		Имя зоны перехода
 * @param	bActive			Булка активности маркера
--]]
function PlayFieldBase:setLinkMarkerActive(triggerName, bActive)
	local link = self.linked_scenes[triggerName]

	if link.markerParticles then
		if bActive then
			link.markerParticles:fire()
		else
			link.markerParticles:stopParticle(false)
		end
	end

	if link.markerObjects then
		for i, marker in ipairs(link.markerObjects) do
			marker:setVisible(bActive)

			if bActive then
				marker:play()
			else
				marker:stop()
			end
		end
	end
end
-------------------------------------------------------------------------------

--[[
 *  Метод создает раскадровку по-умолчанию, маркирующую зону перехода на сцену HiddenObjects или HiddenPictures
 * @param	triggerName		Имя зоны перехода
--]]
function PlayFieldBase:createDefaultLinkMarkerObject(triggerName)
	local trigger = self:getEntityNamed(triggerName)
	local box     = trigger:getBoundingBox()

	local obj = CreateSpriteEntity('Data/Textures/Common/HO_Highlight.png')
	obj:setName('ho_highlight_'..triggerName)

	obj:attachTo(trigger)
	obj:setRespondable(false)
	obj:setVisible(true)
	obj:setOrder(0)
	
	obj:setAnimationSize(202, 232)
	obj:setHotSpot(101, 116)
	obj:setFramesCount(48)
	obj:setFPS(30)
	obj:setMode(_ANIM_LOOP)
	obj:setXY(0, 0)
	obj:setScale(math.max(box:getWidth()  / obj:getSize().x,
						  box:getHeight() / obj:getSize().y))

	return obj
end
-------------------------------------------------------------------------------

--[[
 *  Метод создает систему частиц, маркирующую зону перехода на сцену HiddenObjects или HiddenPictures
 * @param	triggerName		Имя зоны перехода
--]]
function PlayFieldBase:createLinkMarkerParticles(triggerName)
	local trigger = self:getEntityNamed(triggerName)
	local box     = trigger:getBoundingBox()
	local entity  = CreateParticleEntity()

	entity:setParticle('Data/ParticleSystem/LinkMarker.psi', 'Data/Textures/Effect/particle_10.png')
	entity:attachTo(trigger)
	entity:setOrder(0)
	entity:setXY(0, 0)
	entity:setRespondable(false)
	entity:setGenerateAreaSize(box:getWidth(), box:getHeight())

	return entity
end
-------------------------------------------------------------------------------

function PlayFieldBase:autoApplyTool(inToolName, object, screenFocus)
	if IsToolTargetUsed(inToolName, object:getName()) or self.ignore_tool_apply_clicks then
		return
	end

	if not IsToolFound(inToolName) then
		AddGameState(TOOL_INFO_SHOWN, inToolName)
		self:findTool(inToolName)
	end

	local inv = self:getInventory()
	local tool_info = inv:getToolInfoByName(inToolName)
	
	if tool_info == nil then
		return
	end
	
	if tool_info.additional_info.collection then
		for _, t_name in ipairs(tool_info.additional_info.collection:get_tools()) do
			if not IsToolFound(t_name) then
				AddGameState(TOOL_INFO_SHOWN, t_name)
				self:findTool(t_name)
			end
		end
	end

	if TEMP_USE_OLD_INVENTORY then
		inv:takeTool(tool_info)
	else
		inv:takeTool(tool_info, screenFocus)
	end

	self:tryToolApply(tool_info, object)
	WriteInfo("Applied "..inToolName.." to "..object:getName().." by right mouse button.")
	self.ignore_tool_apply_clicks = true
end
-------------------------------------------------------------------------------

function PlayFieldBase:updatePanel()
	local panel = self.game_scene:getPanel()

	if isQuestLevel(self.name) then
		panel:setMode(EPanelMode.QUEST, 0)
	elseif isHiddenObjectLevel(self.name) then
		self.ho_logic = HiddenObjectLogic(self.game_scene)
		panel:setMode(EPanelMode.HIDDENOBJECT, 0)
		panel:getHOPanel():addExit()
	elseif isMinigameLevel(self.name) then
		panel:setMode(EPanelMode.MINIGAME, 0)
	elseif isHiddenPictureLevel(self.name) then
		self.ho_logic = HiddenPictureLogic(self.game_scene)
		panel:setMode(EPanelMode.HIDDENPICTURE, 0)
		panel:getHPPanel():addExit()
	end
	
	if isMinigameLevelType(self.name) then
		panel:getMGPanel():addExitFromMG()
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:setupAlternativeHO()
	if isHiddenObjectLevel(self.name) and self:isAlternativeHOActive() then
		self.alternativeHOZoom = self:createAlternativeHOZoom()

		local controlsPanel  = self:getPanel():getControlsPanel()
		local openButtonName = 'bttn_alternative_ho'

		if controlsPanel:hasEntityNamed(openButtonName) then
			local button = controlsPanel:getEntityNamed(openButtonName)
			self.alternativeHOZoom:setOpenButton(button)
		end
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:isAlternativeHOActive()
	return false
end
-------------------------------------------------------------------------------

function PlayFieldBase:createAlternativeHOZoom()
	local zoom = AlternativeHOZoomBase(self)
	return zoom
end
-------------------------------------------------------------------------------

function PlayFieldBase:getAlternativeHOZoom()
	return self.alternativeHOZoom
end
-------------------------------------------------------------------------------

function PlayFieldBase:getAreaExit()
	local panel = self.game_scene:getPanel()
	local subpanel = nil

	if isHiddenObjectLevel(self.name) then
		subpanel = panel:getHOPanel()
	elseif isMinigameLevelType(self.name) then
		subpanel = panel:getMGPanel()
	elseif isHiddenPictureLevel(self.name) then
		subpanel = panel:getHPPanel()
	end
	
	if subpanel and subpanel.getAreaExit then
		return subpanel:getAreaExit()
	else
		return nil
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:bindDoorObject(trigger_name, door_object, switch_opacity, open_sound, close_sound)
	local linkInfo = self.linked_scenes[trigger_name]
	assert(linkInfo)
	
	door_object = (type(door_object) == 'string') and self:getEntityNamed(door_object) or door_object
	switch_opacity = switch_opacity or 0

	linkInfo.door_object    = door_object
	linkInfo.open_sound     = open_sound 
	linkInfo.close_sound    = close_sound
	linkInfo.switch_opacity = switch_opacity
	
	door_object:setRespondable(false)
	door_object:setOpacity(switch_opacity)
	self:getEntityNamed(trigger_name).onFocusIn:add(  function()
															door_object:destroyEffectors()
															local eff = door_object:ChangeOpacity(door_object:getOpacity(), switch_opacity and 1 or 0, CHANGE_OBJECT_OPACITY)
															eff:setDestroysOnEnd(false)
															if open_sound then
																self:playSound(open_sound)
															else
																playCommonSound(self.door_open_sound_name or 'DoorOpen')
															end
														end)
								
	self:getEntityNamed(trigger_name).onFocusOut:add(  function()
															door_object:destroyEffectors()
															local eff = door_object:ChangeOpacity(door_object:getOpacity(), switch_opacity and 0 or 1, CHANGE_OBJECT_OPACITY)
															eff:setDestroysOnEnd(false)
															if close_sound then
																self:playSound(close_sound)
															else
																playCommonSound(self.door_open_sound_name or 'DoorClose')
															end
														 end)
end
-------------------------------------------------------------------------------

-- Это обработчик для подсветки объектов
function PlayFieldBase:toggleHighlightObjects()
	if isCheatMode() == false then
		return
	end
	
	if self.game_scene.is_objects_highlighted then
		self:restoreHighlightedObject(self.main_entity)
		self.game_scene.is_objects_highlighted = false
	else
		self:highlightObject(self.main_entity)
		self.game_scene.is_objects_highlighted = true
	end
	
end
-------------------------------------------------------------------------------

--[[
 * Метод возвращает таблицу цветов для подсветки игровых зон при нажатии TAB.
--]]
function PlayFieldBase:getHighlightColors()
	return	{
				['area_link_']   = Colorf(1.0, 1.0,   0, 0.5),
				['area_zoom_']   = Colorf(0.0, 1.0, 1.0, 0.5),
				['area_click_']  = Colorf(1.0, 0.0, 1.0, 0.5),
				['area_find_']   = Colorf(0.0, 1.0, 0.5, 0.5),
				['area_apply_']  = Colorf(0.0, 1.0, 0.0, 0.5),
				['area_comics_'] = Colorf(0.9, 0.8, 0.7, 0.5),
				['area_bbt_']    = Colorf(0.3, 0.8, 0.2, 0.5),
				['area_get_']    = Colorf(0.0, 0.5, 1.0, 0.5),
				['area_drop_']   = Colorf(1.0, 0.5, 0.0, 0.5),
				['ho_item']      = Colorf(0.2, 1.0, 0.2, 1.0)
			}
end
-------------------------------------------------------------------------------

function PlayFieldBase:highlightObject(object)
	local highlight_colors = self:getHighlightColors()
	
	local function saveObjectColor(object)
		local data = {}
		data.name   = object:getName()
		data.r      = object:getColor():getR()
		data.g      = object:getColor():getG()
		data.b      = object:getColor():getB()
		data.a      = object:getColor():getA()
		data.blend  = object:getBlend()
		self.hightlight_object[object:getName()] = data
	end
	
	local str_start, str_end = string.find(object:getName(), 'area_')
	if str_start and str_start == 1 then
		for area_prefix, color in pairs(highlight_colors) do
			if string.find(object:getName(), area_prefix) then	
				saveObjectColor(object)
				object:setColor(color)
				object:setBlend(RenderedObject.EBlendModeColorAdd)
			end
		end
	elseif self.ho_logic then
		if self.ho_logic:isTaskObject(object:getName()) then
			saveObjectColor(object)
			object:setColor(highlight_colors['ho_item'])
			-- object:setBlend(RenderedObject.EBlendModeColorAdd)
		end
	end
	
	local childs = object:getChilds()
	for i = 0, childs:size() - 1 do
		self:highlightObject( childs:get(i) )
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:restoreHighlightedObject(object)
	local function getObjectColor(object)
		return self.hightlight_object[object:getName()]
	end
	
	for object_name, color in pairs (self.hightlight_object) do
		if object_name == object:getName() then
			local object_color = getObjectColor(object)
			object:setColor(object_color.r, object_color.g, object_color.b, object_color.a)
			object:setBlend(object_color.blend)
			self.hightlight_object[object_name] = nil
			break
		end
	end
	
	local childs = object:getChilds()
	for i = 0, childs:size() - 1 do
		self:restoreHighlightedObject( childs:get(i) )
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:completeMinigame(time, callback)
	time	 = time or 0
	callback = callback or bind(self.close, self)
	
	AddUniqueGameState(SCENE_CLOSED, self.name)
	AddUniqueGameState(MG_COMPLETED, self.name)

	if not self:isLastVisitedSceneUpdateDisabled() then
		SetGameState(LAST_VISITED_SCENE, self.parent_level)
		SetGameState(LAST_VISITED_SCENE, self.parent_level, EGameStateTag.GENERAL)
	end
	
	if self.find_toolMG then
		self:findTool(self.find_toolMG, false, function() self.timer:add(callback, time) end)
	else
		self.timer:add(callback, time)
	end

	GetScoreManager():addScoreMGComplete()
end
-------------------------------------------------------------------------------

function PlayFieldBase:setForcedObject(object)
	local name_forced_object = 'Area_forced_object'
	
	local forced_object = nil
	if self:hasEntityNamed(name_forced_object) == true then
		forced_object = self:getEntityNamed(name_forced_object)
	else
		local size = Vector2f(WSCR_W, WSCR_H)
	
		forced_object = CreateSpriteEntity('')
		forced_object:setName			( name_forced_object )
		forced_object:attachTo			( self:getMainEntity() )
		forced_object:setAnimationSize	( size )
		forced_object:setXY				( WORK_W / 2, WORK_H / 2 )
		forced_object:setColor			( Colorf(1, 1, 1, 0.01) )
	end
	forced_object:setMaxOrder()
	
	if object then
		forced_object:show()

		local node = path_manager:findNodeNamed(object:getName())
		if node then
			node:setState(NodeState.ENABLED)
		end

		self.forced_object = forced_object
		if self.forced_object_data == nil then
			self.forced_object_data = {}
		end
		self.forced_object_data.object = object
		if object:getParent():getName() ~= name_forced_object then
			self.forced_object_data.object_parent = object:getParent()
		end
		
		Helpers.attachInPlace(object, forced_object)
		-- forced_object.onUpdate:add( function() 
		-- 								forced_object:setMaxOrder()
		-- 							end)
	else
		if self.forced_object_data then
			Helpers.attachInPlace(self.forced_object_data.object, self.forced_object_data.object_parent)
		end

		forced_object:hide()
		forced_object.onUpdate:removeAll()
		self.forced_object = nil
		self.forced_object_data = nil
	end
	
	return forced_object
end
-------------------------------------------------------------------------------

function PlayFieldBase:focusOnObject(object, dx,dy, scaleFactor, angle, dismiss_on_click, multi_use)
	dx = dx or 0
	dy = dy or 0
	scaleFactor =  scaleFactor or {x = 1.5, y =1.5}
	angle =  angle or 0
	
	if not multi_use and self:isObjectClicked(object:getName()) then
		return
	end
	
	fadeObjectCount = (not fadeObjectCount and 0 or fadeObjectCount + 1)

	local opacityRange = {0.8, 0.0}
	local scaleRange = {12 * scaleFactor.x, 12 * scaleFactor.y}
	
	local name_focus_object = 'fadeObject_'..tostring(fadeObjectCount)
	
	local fadeObj = CreateSpriteEntityInstant('Data/Textures/Common/focus.png')
	fadeObj:attachTo(object)
	fadeObj:setName(name_focus_object)
	fadeObj:setXY(dx, dy)
	fadeObj:setScrScale( Vector2f(scaleRange[1], scaleRange[2]) )
	fadeObj:setRotationRad(angle)
	fadeObj:setRespondable(false)
	
	local fadeDuration = 3.0
	local fadeDelayPercent = 0.4

	local eff = CreateAnimationEffector()
	eff:setDestroysOnEnd(true)
	eff:attachTo(fadeObj)
	eff:setKey(_TRACK_O, 0, 								0.0)
	eff:setKey(_TRACK_O, fadeDelayPercent/5*fadeDuration,   opacityRange[1])
	eff:setKey(_TRACK_O, fadeDelayPercent*fadeDuration,     opacityRange[1])
	eff:setKey(_TRACK_SX,0,								    scaleRange[1])
	eff:setKey(_TRACK_SY,0,								    scaleRange[2])
	eff:setKey(_TRACK_SX,fadeDelayPercent*fadeDuration,     scaleRange[1])
	eff:setKey(_TRACK_SY,fadeDelayPercent*fadeDuration,     scaleRange[2])
	
	if dismiss_on_click then
		do
			self.timer:add (function()
								local eff = CreateAnimationEffector()                    
								eff:setDestroysOnEnd(true)
								eff:attachTo(fadeObj)
								eff:setMode(_ANIM_FWD + _ANIM_LOOP)
								eff:setKey(_TRACK_SX, 0,	scaleRange[1])
								eff:setKey(_TRACK_SY, 0,	scaleRange[2])
								eff:setKey(_TRACK_SX, 1,	scaleRange[1] * 1.2)
								eff:setKey(_TRACK_SY, 1,	scaleRange[2] * 1.2)
								eff:setKey(_TRACK_SX, 2,	scaleRange[1])
								eff:setKey(_TRACK_SY, 2,	scaleRange[2])
								eff:play()
							end, fadeDelayPercent*fadeDuration)
			
			local function on_click(e)
				fadeObj:setVisible(false)
			end
			
			object.onClickBegin:add(on_click)
		end            
	else
		eff:setKey(_TRACK_O, fadeDuration,					opacityRange[2])
		eff:setKey(_TRACK_SX,fadeDuration,					scaleRange[1]*2)
		eff:setKey(_TRACK_SY,fadeDuration,					scaleRange[2]*2)
	end
	
	eff:play()	

	return fadeObj
end
-------------------------------------------------------------------------------

function PlayFieldBase:playAnimationPeriodically(animation, max_delay, sound_name, loopCondition, on_play)  -- Todo не работает, разобраться!
	if on_play then
		on_play()
	end
	
	local time = animation:getDuration()
	
	animation:play()
	animation:setVisible(true)
	
	if sound_name then
		-- TODO: play sound here.
	end
	
	self.timer:add( function()
						animation:setVisible(false)
					end, time)
	
	self.timer:add( function()
						self:playAnimationPeriodically(animation, max_delay, soundName, loopCondition, on_play)
					end, time + (math.random() * max_delay) )
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Переопределяя эту функцию в дочерних класах убедитесь в том, что она 
-- так же возвращяет три значения
function PlayFieldBase:onHintObject(object, is_proper, priority, additional_obj)
	return is_proper, priority, additional_obj
end
-------------------------------------------------------------------------------

function PlayFieldBase:showBlackBar(textKey, once, bTextAsIs)
	if not (once and self:isBlackBarShown(textKey)) then
		self.game_scene:getPanel():getBlackBar():showText(textKey, bTextAsIs)
	end
end
-------------------------------------------------------------------------------
--checks for difficulty (hard) mode
function PlayFieldBase:showBlackBarInNonHardMode(textKey, once, bTextAsIs)
	if getPlayModeParameters().blackbarEnabled then -- not isHardPlayMode()
		self:showBlackBar(textKey, once, bTextAsIs)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:hideBlackBar(bInstantly)
	self.game_scene:getPanel():getBlackBar():hideText(bInstantly)
end
-------------------------------------------------------------------------------

function PlayFieldBase:isBlackBarShown(textKey)
	return HasGameState(BLACK_BAR_SHOWN, textKey)
end
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- Прячем и отображаем открытые зум зоны --------------------------------------
-------------------------------------------------------------------------------
function PlayFieldBase:hideZoomZones()
	local count = 0
	
	while (self.active_zones:GetTop()) do
		local zone = self.active_zones:GetTop()
		
		if zone.mode ~= ZOOM_TYPE_FULLSCREEN and zone:isVisible() then
			self.hidden_zoom_zones.zones:Push(zone)
			
			zone:close(0)
			count = count + 1
		else
			break
		end
	end
	
	self.hidden_zoom_zones.count:Push(count)
end
-------------------------------------------------------------------------------

function PlayFieldBase:showHiddenZoomZones()
	for i = 1, self.hidden_zoom_zones.count:GetTop() do
		local zone = self.hidden_zoom_zones.zones:GetTop()
		
		if not zone:isDisabled() then
			zone:open(0)
		end
		self.hidden_zoom_zones.zones:Pop()
	end
	self.hidden_zoom_zones.count:Pop()
end
-------------------------------------------------------------------------------

function PlayFieldBase:getVideoPath(video_name)
	local videos_folder =
	{
		[EGameStateTag.SE] = 'Data/Video/Scenes/',
		[EGameStateTag.CE] = 'Data/ExtraContent/Videos/Scenes/',
	}
	return videos_folder[getDefaultGameStateTag()]..string.gsub(self.name, 'l_', '')..'/'..video_name..'.webm'
end
-------------------------------------------------------------------------------

function PlayFieldBase:startCutScene(videoPath, soundPath, callback, bHidePlayfield)
	if TEMP_USE_OLD_INVENTORY then
		self:getInventory():dropTool(0)
	else
		abortDrags()
		cancelScreenFocuses()
	end

	local panel = self:getPanel()

	local video = CreateVideoEntity(videoPath, false)
	video:setName		( ExtractFileName(videoPath) )
	video:attachTo		( panel:getMainEntity() --[[ current_scene:getMainEntity()]])
	video:setXY			( WORK_W/2, WORK_H/2 )
	video:setOrder		( self:getMainEntity():getOrder() + 0.1 )
	video:setRespondable( false )

	if soundPath then
		video:setSoundTrack( soundPath )
	end

	video:play()

	self:hideBlackBar()

	if bHidePlayfield then
		self:getMainEntity():setVisible(false)
	end

	local current_panel_mode = panel:getMode()
	local move_time = 0.8
	local playfield = self
	local subtitles = Subtitles(panel)

	self.timer:add(	function()
						subtitles:play()
					end, 0)

	self:getGameScene():volumeTurnDownMusic()
	playfield.mouseBlocker:push()

	local function on_end_video()
		self:getGameScene():volumeRestoreMusic()
		panel:setMode(current_panel_mode, move_time)

		if bHidePlayfield then
			self:getMainEntity():setVisible(true)
		end

		subtitles:destroy()

		video.onFinish:removeAll()
		local eff = video:ChangeOpacity(1, 0, CHANGE_OBJECT_OPACITY)
		eff.onFinish:add(function()
							video:destroy()
						 end)

		--self.timer:add(	function()
		playfield.mouseBlocker:pop()
		--				end, move_time)

		if callback then
			callback()
		end
	end

	panel:setMode(EPanelMode.CUTSCENE, move_time, on_end_video)
	video.onFinish:add(on_end_video)

	return video, subtitles
end
-------------------------------------------------------------------------------

function PlayFieldBase:setCutSceneMode(time, callback)
	assert(time > 0, 'Specify a cutscene\'s time')

	if TEMP_USE_OLD_INVENTORY then
		self:getInventory():dropTool(0)
	else
		abortDrags()
		cancelScreenFocuses()
	end

	local panel = self:getPanel()
	local move_time = 0.8
	local current_panel_mode = panel:getMode()
	local subtitles = Subtitles(panel)

	self.timer:add(	function()
						subtitles:play()
					end, 0)

	self:getGameScene():volumeTurnDownMusic()
	self:getMouseBlocker():push()

	self:hideBlackBar()

	local function on_end()
		self:getGameScene():volumeRestoreMusic()
		panel:setMode(current_panel_mode, move_time)

		subtitles:destroy()

		self:getMouseBlocker():pop()

		if callback then
			callback()
		end
	end

	local action = self:getTimer():add(function()
						on_end()
					end, time)
	
	self:getPanel():setMode(EPanelMode.CUTSCENE, move_time, function() 
																action:kill()
																on_end()
															end
							)
	return subtitles, action
end
-------------------------------------------------------------------------------

-- геттеры и сеттеры ----------------------------------------------------------
-------------------------------------------------------------------------------
function PlayFieldBase:glintActiveObjects()
	if getPlayModeParameters().sparklesEnabled then -- isCasualPlayMode()
		self.glint:hint()
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:getHOLogic()
	return self.ho_logic
end
-------------------------------------------------------------------------------

function PlayFieldBase:getDragManager()
	return self.dragManager
end
-------------------------------------------------------------------------------

function PlayFieldBase:getGameScene()
	return self.game_scene
end
-------------------------------------------------------------------------------

function PlayFieldBase:getInventory()
	return self.game_scene:getPanel():getInventory()
end
-------------------------------------------------------------------------------

function PlayFieldBase:getPanel()
	return self.game_scene:getPanel()
end
-------------------------------------------------------------------------------

function PlayFieldBase:getTimer()
	return self.timer
end
-------------------------------------------------------------------------------

function PlayFieldBase:getName()
	return self.name
end
-------------------------------------------------------------------------------

function PlayFieldBase:getPathLua()
	return self.path_lua
end
-------------------------------------------------------------------------------

function PlayFieldBase:getZoomFade()
	return self.zoom_fade
end
-------------------------------------------------------------------------------

function PlayFieldBase:getActiveZones()
	return self.active_zones
end
-------------------------------------------------------------------------------

function PlayFieldBase:getActiveObjects()
	return self.active_objects
end
-------------------------------------------------------------------------------

function PlayFieldBase:getActiveObject(objectName)
	return self.active_objects[objectName]
end
-------------------------------------------------------------------------------

function PlayFieldBase:getZones()
	return self.zones
end
-------------------------------------------------------------------------------

function PlayFieldBase:getLinkedScenes()
	return self.linked_scenes
end
-------------------------------------------------------------------------------

function PlayFieldBase:getHint()
	return self.hint
end
-------------------------------------------------------------------------------

function PlayFieldBase:getGlint()
	return self.glint
end
-------------------------------------------------------------------------------

function PlayFieldBase:getMouseBlocker()
	return self.mouseBlocker
end
-------------------------------------------------------------------------------

function PlayFieldBase:getForcedObject()
	return self.forced_object_data
end
-------------------------------------------------------------------------------

function PlayFieldBase:getCollectObjectsManager()
	return self.collect_manager
end
-------------------------------------------------------------------------------

function PlayFieldBase:registerToolFindMG(toolName)
	local tool_node = path_manager:addToolFinding(toolName, toolName)
	tool_node.target_is_visible = TargetVisibility.INVISIBLE

	self.find_toolMG = toolName
end
-------------------------------------------------------------------------------

function PlayFieldBase:isFirstVisited()
	return self.visited_first_time
end
-------------------------------------------------------------------------------

function PlayFieldBase:getMaxOrder()
	local childs = self.main_entity:getChilds()
	local max_order = 0
	for i = 0, childs:size() - 1 do
		local child_order = childs:get(i):getOrder()
		if child_order > max_order then
			max_order = child_order
		end
	end
	return max_order
end
-------------------------------------------------------------------------------

function PlayFieldBase:addNote(id, force)
	force = force or false
	
	if not self:isNoteAdded(id) or force then
		self:setNotebookActive(id)
		
		AddUniqueGameState('PageSlot', id)

		local controlsPanel = self:getPanel():getControlsPanel()
		local notesButton	= controlsPanel:hasEntityNamed('bttn_notes') and controlsPanel:getEntityNamed('bttn_notes')
																		  or nil
		GetScoreManager():addScoreNote(id, notesButton)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:removeNote(id)
	if self:isNoteAdded(id) then
		local isSetNewActivePageId = false
		local activePageId = (HasGameState('NotebookActivePage')) and GetGameState('NotebookActivePage'):get(0) or nil
		if activePageId == id then
			isSetNewActivePageId = true
		end

		RemoveGameState('PageSlot', id)
		if isSetNewActivePageId then
			if HasGameState('PageSlot') then
				local size = GetGameState('PageSlot'):getSize()
				if size >= 1 then
					local id = GetGameState('PageSlot'):get(size - 1)
					SetGameState('NotebookActivePage', id)
				end
			end
		end
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:addFlyNote(id, save, layerName, doEffect, appearPos, destPos, startScale, onClick)    
	if self:isObjectClicked(layerName) then
		self:addNote(id)
		
		if onClick then
			local event = {}
			event.id   = id
			event.flyingTime = 0
			event.save = true
			
			onClick(event)
		end
	else
		local flyId = 'Fly_'..id
		local filePath = FLYNOTES_FOLDER..flyId..'.lua'
		if IsLevelCollectorsEdition(current_scene:getPlayField().name) then
		    filePath    =   FLYNOTES_FOLDER_CE..flyId..'.lua'
		end		
		assert(resourceExists(filePath))
		dofile(filePath)
		
		local note = Helpers.CallFunctionByName(flyId, self, flyId)
		note:loadPage()

		local flyNoteObj = note:getMainEntity():getEntityNamed(layerName)

		flyNoteObj:setRespondable(true)

		if not appearPos then
			appearPos = Vector2f(flyNoteObj:getScrX(), flyNoteObj:getScrY())
		end
		
		if not startScale then
			startScale = Vector2f(flyNoteObj:getScale().x, flyNoteObj:getScale().y)
		end

		local moveDelay    = 1.1
		local moveTime     = 0.8
		local timeCenter   = 0.9
		
		if not destPos then
			destPos = self:getMainEntity():localToScreen(Vector2f(WORK_W/2, WORK_H/2))
		end

		local maxDpos = 256
		
		if not save then
			playCommonSound('FlyNote')
		end

		local moverStart = CreateAnimationEffector()
		moverStart:attachTo(flyNoteObj)
		moverStart:setKeysMode(_KEYS_XY_SCR)

		moverStart:setKey(_TRACK_O, 0.0,           flyNoteObj:getOpacity())
		moverStart:setKey(_TRACK_X, 0.0,           appearPos.x)
		moverStart:setKey(_TRACK_Y, 0.0,           appearPos.y)
		moverStart:setKey(_TRACK_A, 0.0,           flyNoteObj:getRotationDeg())
		moverStart:setKey(_TRACK_O, timeCenter,   1)
		moverStart:setKey(_TRACK_X, timeCenter,   destPos.x)
		moverStart:setKey(_TRACK_Y, timeCenter,   destPos.y)
		moverStart:setKey(_TRACK_A, timeCenter,   0)
		moverStart:setKey(_TRACK_SX, 0.0,          startScale.x)
		moverStart:setKey(_TRACK_SY, 0.0,          startScale.y)
		moverStart:setKey(_TRACK_SX, timeCenter,   1)
		moverStart:setKey(_TRACK_SY, timeCenter,   1)
		moverStart:setDestroysOnEnd(true)
		moverStart:play()

		local moverCycle = CreateAnimationEffector()
		moverCycle:attachTo(flyNoteObj)
		moverCycle:setKeysMode(_KEYS_XY_SCR)
		moverCycle:setKey(_TRACK_S, 0,   1)
		moverCycle:setKey(_TRACK_S, 1,   0.98)
		moverCycle:setKey(_TRACK_S, 2,   1)
		moverCycle:setMode(_ANIM_FWD + _ANIM_LOOP)
		moverCycle:pause()

		if save then
			moverStart:pause()
			moverStart:setToFinish()
			
			if doEffect ~= false then
				moverCycle:play()
				local node = path_manager:getConnectedNode(NodeType.CLICK, layerName)
				if node then
					path_manager:connectNode(node, path_manager.current_node)
				end
			end    
		else
			if doEffect ~= false then
				self.timer:add(
					function()
						if moverCycle then
							moverCycle:play()
							local node = path_manager:getConnectedNode(NodeType.CLICK, layerName)
							if node then
								if #self.active_zones > 0 then
									path_manager:connectNode(node, self.active_zones:GetTop().node)
									path_manager:removeNode(node, path_manager:getCurrentNode())
								end
							end
						end
					end,
					timeCenter + 0.1)
			end                                
		end
									
		self:setForcedObject(flyNoteObj)

		local function onNoteClick(e)
			if onClick then
				local event = {}
				event.id   = id
				event.flyingTime = e and (moveTime + moveDelay) or 0
				event.save = e.save
				
				onClick(event)
			end
		
			if not e.save then
				local diary = self:getPanel():getControlsPanel():getEntityNamed(self.flynote_target_object_name or 'plate_left_block')
				local finishPos = diary:getScrPosition()
				
				self.timer:add(
					function ()
						playCommonSound('FlyNoteAdd')
					end,
					moveDelay)

				flyNoteObj:setRespondable(false)

				flyNoteObj:destroyEffectors()
				moverCycle = nil
			
				local moverEnd = CreateAnimationEffector()
				moverEnd:attachTo(flyNoteObj)
				moverEnd:setKeysMode(_KEYS_XY_SCR)

				moverEnd:setKey(_TRACK_SX, 0,       flyNoteObj:getScale().x)
				moverEnd:setKey(_TRACK_SY, 0,       flyNoteObj:getScale().y)
				moverEnd:setKey(_TRACK_SX, 0.2,     1)
				moverEnd:setKey(_TRACK_SY, 0.2,     1)
				moverEnd:setKey(_TRACK_SX, 1,       1)
				moverEnd:setKey(_TRACK_SY, 1,       1)

				moverEnd:setKey(_TRACK_SX, moveTime + moveDelay - 0.2,       0.1)
				moverEnd:setKey(_TRACK_SY, moveTime + moveDelay - 0.2,       0.1)
				moverEnd:setKey(_TRACK_O, moveTime - 0.05,        1)
				moverEnd:setKey(_TRACK_O, moveTime + moveDelay - 0.1,  0)
				
				-- create effector keys table
				local pointCount = 20
				local effectorKeys = HelpersEffect.createSplineBezier(
										destPos,
										destPos + Vector2f(RandomFloat(-maxDpos, maxDpos), RandomFloat(-maxDpos, maxDpos)),
										finishPos,
										finishPos + Vector2f(RandomFloat(-maxDpos, maxDpos), -200),
										pointCount,
										moveTime,
										moveDelay
									 )
				
				-- set keys from created effector keys table
				local t = 1
				local dT = 1 / pointCount
				for _, keys in ipairs(effectorKeys) do
					moverEnd:setKey(_TRACK_X, t * moveTime, keys.x)
					moverEnd:setKey(_TRACK_Y, t * moveTime, keys.y)

					t = t + dT
				end

				moverEnd:play()
				
				local flyId = id and id or ''
				SetGameState('FlyNote', flyId)

				-- self:getPanel():getControlsPanel():getMainEntity():setRespondable(false)
				
				self.timer:add(
					function()
						self:setForcedObject(nil)

						if id then
							self:addNote(id)
						else
							self:getPanel():getControlsPanel():onNoteAdded()
						end
						
						RemoveGameState('FlyNote')
					end,
					moveTime + moveDelay - 0.3)
			else
				self:setForcedObject(nil)
			end
		end
		self:registerActiveObject(layerName, onNoteClick, false, 'CursorActive', true)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:setNotebookPageActive(id)
	SetGameState('NotebookActivePage', id)
end
-------------------------------------------------------------------------------

function PlayFieldBase:setNotebookActive(id)
	self:getPanel():getControlsPanel():onNoteAdded()
	if id then
		self:setNotebookPageActive(id)
	end
	SetGameState('NotebookActive', '')
end
-------------------------------------------------------------------------------

function PlayFieldBase:isNoteAdded(id)
	return HasGameState('PageSlot', id)
end
-------------------------------------------------------------------------------

function PlayFieldBase:isNotebookActive()
	return HasGameState('NotebookActive', '')
end
-------------------------------------------------------------------------------

function PlayFieldBase:setMusic(list)
	self.music_list = list    
end
-------------------------------------------------------------------------------

function PlayFieldBase:getMusic()
	return self.music_list
end
-------------------------------------------------------------------------------

function PlayFieldBase:getToolFindObjectNames()
	return self.tool_find_object_names
end
-------------------------------------------------------------------------------

function PlayFieldBase:playSound(sound_name, repeated)
	assert(self.sounds[sound_name])
	self.sounds[sound_name].repeated = repeated or false
	local sound_entity = self:createSound(sound_name)
	sound_entity:play()



	return sound_entity
end
-------------------------------------------------------------------------------

function PlayFieldBase:stopSound(sound_name)
	assert(self.sounds[sound_name])
	if self.sounds[sound_name].entity then
		self.sounds[sound_name].entity:stop()
	end
end
-------------------------------------------------------------------------------

--BopoH-->If sound is playing restart it or start to play it.
function PlayFieldBase:restartSound(sound_name)
	assert(self.sounds[sound_name])
	if self.sounds[sound_name].entity then
		self.sounds[sound_name].entity:restart()
	else
		self:playSound(sound_name)
	end
end
-------------------------------------------------------------------------------

-- sound_type defined in HelpersSound.lua
function PlayFieldBase:setSoundType(sound_name, sound_type)
	assert(self.sounds[sound_name])
	if self.sounds[sound_name].entity then
		self.sounds[sound_name].entity:destroy()
		self.sounds[sound_name].entity = nil
	end
	self.sounds[sound_name].type = sound_type
end
-------------------------------------------------------------------------------

function PlayFieldBase:createSound(sound_name)
	local sound_creators =
	{
		[sound_types.sound_track] =
		function()
			local sound_entity = CreateSoundTrack(self.sounds[sound_name].path)
			sound_entity:setRepeated(self.sounds[sound_name].repeated)
			sound_entity:setName(sound_name)
			sound_entity:attachTo(self:getMainEntity())
			return sound_entity
		end,
		[sound_types.voice_track] =
		function()
			local sound_entity = CreateVoiceTrack(self.sounds[sound_name].path)
			sound_entity:setRepeated(self.sounds[sound_name].repeated)
			sound_entity:setName(sound_name)
			sound_entity:attachTo(self:getMainEntity())
			return sound_entity
		end,
		[sound_types.sfx_track] = 
		function()
			local sound_entity = CreateSFXTrack(self.sounds[sound_name].path)
			sound_entity:setName(sound_name)
			sound_entity:attachTo(self:getMainEntity())
			return sound_entity
		end,
	}

	assert(self.sounds[sound_name])
	local sound_entity = self.sounds[sound_name].entity
	if not sound_entity then
		sound_entity = sound_creators[self.sounds[sound_name].type](sound_name)
		self.sounds[sound_name].entity = sound_entity
	end

	sound_entity.onChangeActivity:add(	function(isActive)
										if isActive == false then
											if self.sounds[sound_name].entity:isPlaying() then
												self.sounds[sound_name].is_paused = true
												self.sounds[sound_name].entity:pause()
											end
										else
											if self.sounds[sound_name].is_paused then
												self.sounds[sound_name].entity:play()
												self.sounds[sound_name].is_paused = nil
											end
										end
									end)

	return sound_entity 
end
-------------------------------------------------------------------------------

function PlayFieldBase:getSound(sound_name)
	assert(self.sounds[sound_name])
	return self.sounds[sound_name]
end
-------------------------------------------------------------------------------

function PlayFieldBase:blockMouseOnPeriod(period)
	self:getMouseBlocker():push()

	self:getTimer():add(
		function ()
			self:getMouseBlocker():pop()
		end,
		period)
end
-------------------------------------------------------------------------------

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:tryToolApply(tool_info, object)
		local tool_name = tool_info.name
		local inv = self:getInventory()
		
		if tool_name ~= "" then
			self:hideBlackBar()
		end
		
		if not object then
			if inv:getToolInHand() then
				inv:dropTool()
			end
			return
		end
		
		local tool_applied = false
		local is_collect_all_tools = true
		local obj_name = object:getName()

		if object then
			-- FIXME Этот цикл - наследие старых времён и можно обойтись без перебора всех тулзов
			for name, info in pairs(inv.tools) do
				if name == tool_name then
					local target_table_index = nil
					local is_target = false
					local collection = info.additional_info.collection

					local function has_target(info)
						local is_target = false
						local target_table_index = nil
					
						if info and info.target_name then
							if type(info.target_name) == 'string' then
								is_target = info.target_name == obj_name and info.target_scene == self:getName()
							else
								for i, v in pairs(info.target_name) do
									is_target = (v == obj_name) and info.target_scene == self:getName()
									target_table_index = i
									if is_target then
										break
									end
								end
							end
								
							return is_target, target_table_index
						end
					end
					
					if collection then
						local collection_tools = collection:get_tools()

						for _, name_tool in pairs(collection_tools) do
							local info_tool = inv.tools[name_tool]
							is_target, target_table_index = has_target(info_tool)
							if is_target then
								if not info_tool.additional_info.collection_owner then
									local function copy_missing_fields(t1, t2)
										for key, value in pairs(t1) do
											if not t2[key] then
												t2[key] = value
											elseif type(value) == 'table' then
												copy_missing_fields(value, t2[key])
											end
										end
									end
									copy_missing_fields(info_tool, info)
								end
								break
							end
						end
					else
						is_target, target_table_index = has_target(info)
					end

					if is_target then
						if collection then
							is_collect_all_tools = collection:is_collect_all_tools()
							if not is_collect_all_tools then
								break
							end
						end
					
						self:getEntityNamed(obj_name):setCursorOverName('CursorDefault')
						
						local node = nil
						if info.once_apply then
							for _, area in ipairs(info.target_name) do
								local node = path_manager:getConnectedNode(NodeType.TOOL_APPLY, tool_name, area)
								assert(node and 'No node for tool applying')
								node:trigger()
							end
						else
							local node = path_manager:getConnectedNode(NodeType.TOOL_APPLY, tool_name, obj_name)
							assert(node and 'No node for tool applying')
							node:trigger()
						end
						
						if target_table_index ~= nil then
							table.remove(info.target_name, target_table_index)
						end
						
						local state, value = GenerateToolTargedUsed(tool_name, obj_name, self.name)
						assert(not HasGameState(state, value))
						AddGameState(state, value)

						-- if info.additional_info == nil then
						--     info.additional_info = {}
						--     info.additional_info.local_targets_applied = 0
						-- end
						info.additional_info.local_targets_applied = info.additional_info.local_targets_applied + 1
						
						local all_local_targets_used  = (info.additional_info.local_targets_applied == info.additional_info.local_targets_count)
						local all_global_targets_used = IsToolAllGlobalTargetsUsed(tool_name, inv)

						if all_global_targets_used or info.once_apply then
							assert(not IsToolApplied(tool_name))
							
							inv:removeTool(info)
							
							if collection then
								local collection_tools = collection:get_tools()
								for _, tool_name in pairs(collection_tools) do
									AddGameState(TOOL_APPLIED, tool_name)
									local state, value = GenerateToolTargedUsed(tool_name, obj_name, self.name)
									if not HasGameState(state, value) then
										AddGameState(state, value)
									end
								end
							else
								AddGameState(TOOL_APPLIED, tool_name)
							end
													
							if info.once_apply then
								info.target_name = {}
							end
						else
							if all_local_targets_used then
								inv:dropTool(0)
							end
						end
						
						if info.on_apply then
							local event                  = {}
							event.save                   = false
							event.target                 = obj_name
							event.toolName               = tool_name
							event.applied                = IsToolApplied(tool_name)
							event.allLocalTargetsApplied = all_local_targets_used
							event.lockAndRestoreTime     = nil
							
							info.on_apply(event)
							
							if not IsToolApplied(tool_name) then
								if event.lockAndRestoreTime then
									local lockTime    = nil
									local restoreTime = nil
									
									assert(type(event.lockAndRestoreTime) == 'table' or type(event.lockAndRestoreTime) == 'number')
									
									if type(event.lockAndRestoreTime) == 'table' then
										lockTime    = event.lockAndRestoreTime[1]
										restoreTime = event.lockAndRestoreTime[2]
									else
										lockTime    = event.lockAndRestoreTime
										restoreTime = event.lockAndRestoreTime
									end

									-- callbacks -------
									-- in next block all commented strings hides multitarget tool during animation (old code)
									local function callback_unlock()
										--inv:UnlockTool(tool_name)
									end
									
									local function callback_tacktool()
										if not inv:getToolInHand() and not all_local_targets_used then
											--inv:TakeTool(tool_name)             
										end
									end
									--------------------
																	
									if lockTime == restoreTime then
										--inv:LockTool(tool_name)
										
										self.timer:Add( function()
															callback_unlock()
															callback_tacktool()
														end,
														restoreTime)
									else
										if lockTime ~= nil then
											--inv:LockTool(tool_name)
											self.timer:add(callback_unlock, lockTime)
										end
										
										if restoreTime ~= nil then
											self.timer:add(callback_tacktool, restoreTime)
										end
									end
								end
							end
						else
							-- Если регистрация была на другую тулзу
							if collection then
								local collection_tools = inv.tools[tool_name].collection:get_tools()
								for _, tool_name in pairs(collection_tools) do
									local tool_info = inv.tools[tool_name]         
									if tool_info.on_apply then
										local event     = {}
										event.save      = false
										event.applied   = IsToolApplied(tool_name)
										event.target    = obj_name
										event.toolName  = tool_name
							
										tool_info.on_apply(event)
										break
									end
								end
							end
						end
						
						tool_applied = true
						break
					end
				end
			end
		end

		if not tool_applied then
			local function getWrongApplyDialog(tool_name, area_name)
				for dialog, dialog_table in pairs(self.wrong_apply_dialogs) do
					local area_table = dialog_table[area_name]
					
					if area_table ~= nil then
						local any_tool_key_exist = false
						
						for i, tool in ipairs(area_table) do
							if tool == '_ANY_TOOL_' then
								any_tool_key_exist = true
								
							elseif tool == tool_name then
								return dialog
							end
						end
						
						if any_tool_key_exist then
							return dialog
						end
					end
				end
				
				return nil
			end
		
			local targetTool       = self:getToolNameByTargetName(obj_name, true)
			local wrongApplyDialog = getWrongApplyDialog(tool_name, obj_name)
			
			if wrongApplyDialog then
				self:showBlackBar(wrongApplyDialog)
			elseif targetTool ~= nil then
				self:showBlackBar('C_TOOL_DENY_'..tostring(math.random(1, self.toolDenyStringsCount)))
			end

			inv:dropTool()
		end
	end
else
	function PlayFieldBase:tryToolApply(toolInfo, object)
		local inv = self:getInventory()
		local toolName = toolInfo:getName()
		
		if toolName ~= "" then
			self:hideBlackBar()
		end
		
		if not object then
			inv:dropTool(toolInfo)
			return
		end
		
		local bApplied = false
		local is_collect_all_tools = true
		local obj_name = object:getName()

		if object then
			local collection  = toolInfo.additional_info.collection
			local targetIndex = nil
			local bTarget	  = false
			
			if collection then
				local collection_tools = collection:get_tools()

				for _, collectToolName in pairs(collection_tools) do
					local collectToolInfo = inv:getToolInfo(collectToolName)

					bTarget, targetIndex = collectToolInfo:hasTarget(obj_name, self:getName())

					if bTarget then
						if not collectToolInfo:isCollectionOwner() then
							toolInfo:copyApplyData(collectToolInfo)
						end
						break
					end
				end
			else
				bTarget, targetIndex = toolInfo:hasTarget(obj_name, self:getName())
			end
			----------

			if bTarget and (not collection or collection:is_collect_all_tools()) then
				bApplied = true

				GetScoreManager():addScoreToolApply(object)

				self:getEntityNamed(obj_name):setCursorOverName('CursorDefault')
				
				local node = nil
				if toolInfo.once_apply then
					for _, area in ipairs(toolInfo.target_name) do
						local node = path_manager:getConnectedNode(NodeType.TOOL_APPLY, toolName, area)
						assert(node and 'No node for tool applying')
						node:trigger()
					end
				else
					local node = path_manager:getConnectedNode(NodeType.TOOL_APPLY, toolName, obj_name)
					assert(node and 'No node for tool applying')
					node:trigger()
				end
				
				if targetIndex ~= nil then
					table.remove(toolInfo.target_name, targetIndex)
				end
				
				local state, value = GenerateToolTargedUsed(toolName, obj_name, self.name)
				assert(not HasGameState(state, value))
				AddGameState(state, value)

				-- if info.additional_info == nil then
				--     info.additional_info = {}
				--     info.additional_info.local_targets_applied = 0
				-- end
				toolInfo.additional_info.local_targets_applied = toolInfo.additional_info.local_targets_applied + 1
				
				local all_local_targets_used  = (toolInfo.additional_info.local_targets_applied == toolInfo.additional_info.local_targets_count)
				local all_global_targets_used = IsToolAllGlobalTargetsUsed(toolName, inv)

				if all_global_targets_used or toolInfo.once_apply then
					assert(not IsToolApplied(toolName))
					
					inv:dropTool(toolInfo, 0, true)
					inv:removeTool(toolInfo)
					
					if collection then
						local collection_tools = collection:get_tools()
						for _, tool_name in pairs(collection_tools) do
							AddGameState(TOOL_APPLIED, tool_name)
							local state, value = GenerateToolTargedUsed(tool_name, obj_name, self.name)
							if not HasGameState(state, value) then
								AddGameState(state, value)
							end
						end
					else
						AddGameState(TOOL_APPLIED, toolName)
					end
											
					if toolInfo.once_apply then
						toolInfo.target_name = {}
					end
				else
					if all_local_targets_used then
						inv:dropTool(toolInfo, 0)
					end
				end
				
				if toolInfo.on_apply then
					local event                  = {}
					event.save                   = false
					event.target                 = obj_name
					event.toolName               = toolName
					event.applied                = IsToolApplied(toolName)
					event.allLocalTargetsApplied = all_local_targets_used
					
					toolInfo.on_apply(event)
				else
					-- Если регистрация была на другую тулзу
					if collection then
						local collection_tools = collection:get_tools()

						for _, tool_name in pairs(collection_tools) do
							local tool_info = inv.tools[tool_name]         
							if tool_info.on_apply then
								local event     = {}
								event.save      = false
								event.applied   = IsToolApplied(tool_name)
								event.target    = obj_name
								event.toolName  = tool_name
					
								tool_info.on_apply(event)
								break
							end
						end
					end
				end
			end
		end

		if not bApplied then
			local targetTool       = self:getToolNameByTargetName(obj_name, true)
			local wrongApplyAction = self:getWrongApplyAction(toolName, obj_name)
			local bCancelBBT	   = false 

			if wrongApplyAction then
				local e		 = {}
				e.toolName	 = toolName
				e.targetName = obj_name
				e.bCancelBBT = false

				wrongApplyAction(e)

				bCancelBBT = e.bCancelBBT
			end

			if not bCancelBBT then
				local wrongApplyDialog = self:getWrongApplyDialog(toolName, obj_name)

				if wrongApplyDialog then
					self:showBlackBar(wrongApplyDialog)
				elseif targetTool ~= nil then
					self:showBlackBar('C_TOOL_DENY_'..tostring(math.random(1, self.toolDenyStringsCount)))
				end
			end

			inv:dropTool(toolInfo)
		end
	end
end
-------------------------------------------------------------------------------

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:findTool(tool_name, do_not_show_window, on_tool_window_close)
		do_not_show_window = do_not_show_window or false
		local info = self:getInventory().tools[tool_name]
		if not info.on_tool_window_close then
			info.on_tool_window_close = on_tool_window_close
		end

		if IsToolFound(tool_name) then
			if not HasGameState(TOOL_INFO_SHOWN, tool_name) then
				 self:createToolWindow(info)
			end
			return
		end

		local node = path_manager:findNode(NodeType.TOOL_FIND, tool_name)
		if not node then
			node = path_manager:addToolFinding(tool_name, tool_name)
		end
		
		if not info.additional_info.collection then
			node:trigger()
		elseif info.additional_info.collection:is_collect_all_tools() then
			for _, c_tool_name in ipairs(info.additional_info.collection:get_tools()) do
				local node = path_manager:findNode(NodeType.TOOL_FIND, c_tool_name)
				if node then
					node:trigger()
				else
					WriteInfo('node of collection tool: '..c_tool_name .. ' not found')
				end
			end
		else
			node:setState(NodeState.TRIGGERED)
		end

		self:generateTool(tool_name)
		AddGameState(TOOL_FOUND, tool_name)
		if do_not_show_window then
			AddGameState(TOOL_INFO_SHOWN, tool_name)
		end

		local info = self:getInventory().tools[tool_name]

		if not HasGameState(TOOL_INFO_SHOWN, tool_name) then
			self:createToolWindow(info)
		else
			self:getInventory():onToolFound(info)
			local c_inf, cellIndex = self:getInventory():getCellInfoByToolInfo(info)
			self:getInventory():updateCollectionText(c_inf)
		end
		
	    if isBuildForCustomerMode() == false then
			local command = Console:getCommand("findTool")
			if command then
				command:removePossibleParam(tool_name)
			end
		end
		
		if info.on_find then
			local event     = {}
			event.save      = false
			event.target    = info.trigger_name
			event.tool_name = tool_name
			
			info.on_find(event)
		end
	end
else
	function PlayFieldBase:findTool(toolName, bDontShowWindow, onToolWindowClose)
		bDontShowWindow = bDontShowWindow or false

		local inv		= self:getInventory()
		local toolInfo	= inv:getToolInfo(toolName)

		if not toolInfo.on_tool_window_close then
			toolInfo.on_tool_window_close = onToolWindowClose
		end

		if ToolHelpers.isToolFound(toolName) then
			if  not ToolHelpers.isToolInfoShown(toolName) and
				not toolInfo.notInInventoryYet
			then
				-- TODO: проверить выполняется ли когда-нибудь этот код
				inv:addTool(toolInfo)
				inv:showToolFoundScreen(toolInfo)
			end
		else
			-- path manager
			do
				local node = path_manager:findNode(NodeType.TOOL_FIND, toolName)
				if not node then
					node = path_manager:addToolFinding(toolName, toolName)
				end
				
				local collection = toolInfo:getCollection()

				if not collection then
					node:trigger()
				elseif collection:is_collect_all_tools() then
					for _, c_tool_name in ipairs(collection:get_tools()) do
						local node = path_manager:findNode(NodeType.TOOL_FIND, c_tool_name)
						if node then
							node:trigger()
						else
							WriteInfo('node of collection tool: '..c_tool_name .. ' not found')
						end
					end
				else
					node:setState(NodeState.TRIGGERED)
				end
			end
			
			ToolHelpers.setToolFound(toolName)
			inv:addTool(toolInfo)

			if bDontShowWindow then
				ToolHelpers.setToolInfoShown(toolName)
			end

			if not ToolHelpers.isToolInfoShown(toolName) then
				inv:showToolFoundScreen(toolInfo)
			else
				inv:onToolFound(toolInfo)
			end
			
			-- console
			do
			    if isBuildForCustomerMode() == false then
					local command = Console:getCommand("findTool")
					if command then
						command:removePossibleParam(toolName)
					end
				end
			end
			
			-- callback
			if toolInfo.on_find then
				local event     = {}
				event.save      = false
				event.target    = toolInfo.trigger_name
				event.tool_name = toolName
				
				toolInfo.on_find(event)
			end
		end
	end
end
-------------------------------------------------------------------------------

if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:generateTool(tool_name)
		local inv   = self:getInventory()
		local tool_info  = inv.tools[tool_name]

		tool_info.entities.tool_ent = tool_info.create_entities_functions.create_tool()
		tool_info.entities.tool_shadow_ent = tool_info.create_entities_functions.create_tool_shadow()
		
		inv:addTool(tool_info)
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:getToolNameByTargetName(target_name, bNotUsed)
	bNotUsed = bNotUsed or false
	
	for tool_name, info in pairs(self:getInventory().tools) do
		if info.target_name
			and not (bNotUsed and IsToolApplied(tool_name))
		then
			if type(info.target_name) == 'string' then
				if info.target_name == target_name then
					return tool_name, nil
				end
				
			else
				for i, tmp_target_name in pairs(info.target_name) do
					if tmp_target_name == target_name then
						return tool_name, i
					end
				end
			end
		end
	end
	
	return nil
end
-------------------------------------------------------------------------------

function PlayFieldBase:isObjectTargetForToolWeHave(obj_name)
	local result = false
	
	local tool_applying_info = self.tool_apply_object_names[obj_name]
	if type(tool_applying_info) == "string" then --only one tool registered to apply on zone
		result = IsToolCollected(tool_applying_info) and not IsToolTargetUsed(tool_applying_info, obj_name, self:getName())
	elseif type(tool_applying_info) == "table" then --many tools on single area
		for i, v in pairs(tool_applying_info) do
			if IsToolCollected(v) and not IsToolTargetUsed(v, obj_name, self:getName()) then
				result = true
			end
		end
	end

	return result
end
-------------------------------------------------------------------------------

function PlayFieldBase:addObjectAsTargetForTool(obj_name, tool_name)
	if self.tool_apply_object_names[obj_name] == nil then --first tool
		self.tool_apply_object_names[obj_name] = tool_name
	elseif type(self.tool_apply_object_names[obj_name]) == "table" then  --add new tool to existing table
		table.insert(self.tool_apply_object_names[obj_name], tool_name)
	elseif type(self.tool_apply_object_names[obj_name]) == "string" then --add new tool to fist tool
		local tool_table = {}
		table.insert(tool_table, self.tool_apply_object_names[obj_name])
		table.insert(tool_table, tool_name)
		self.tool_apply_object_names[obj_name] = tool_table
	end
end
-------------------------------------------------------------------------------

-- INFO: moved to InventoryBase:showToolFoundScreen
if TEMP_USE_OLD_INVENTORY then
	function PlayFieldBase:createToolWindow(tool_info)
		local inv = self:getInventory()
		local tool_entity = tool_info.entities.tool_ent
		local tool_shadow_entity = tool_info.entities.tool_shadow_ent
		local text = tool_info.texts.info_text
		local cell_info = inv:getCellInfoByToolInfo(tool_info)
		local wndIconMaxH = 200

		inv:lockTool(tool_info)
		
		local zone = Helpers.CallFunctionByName('ToolFound', 'ToolFound')
		self.tool_found_window = zone
		zone:getWindowLayer():attachTo(self:getGameScene():getMainEntity())
		zone:getWindowLayer():setOrder(10)
		-- Временный фикс проблемы с фокусом и клавишей ESC
		local controls_panel = self:getPanel():getControlsPanel()
		zone:getWindow():getHotkeySignal(ERSK_ESCAPE):add( bind(controls_panel.onMenuButtonClick, controls_panel) )
		
		local wndIcon = tool_info.create_entities_functions.create_info_icon()
		wndIcon:attachTo( zone:getEntityNamed('ToolAnchor') )
		wndIcon:setScale(0.8)
		wndIcon:setXY(0, 0)
		wndIcon:setOrder(2)
		
		zone:getEntityNamed('MessageTxt'):setText(StringManager:getString(text))
		
		local function on_close_button()
			local c_inf, cellIndex = inv:getCellInfoByToolInfo(tool_info)
			local currIndex = inv:getIndex()
			
			Helpers.attachInPlace(wndIcon, inv:getMainEntity())
			wndIcon:setOrder(100)
			
			local invY = inv.root:getY()
			if inv:getVisibleCellsCount() < cellIndex then
				inv:setIndex(cellIndex - inv:getVisibleCellsCount())
			end
			inv:slideUp(0)
			
			-- FIXME Добавить партиклы
			--wndIcon:SetZ(TOOL_IN_INVENTORY_Z)
			--wndIcon:AddParticleSystem('Data/ParticleSystem/ObjectMoveTail.psi', 'ZoneHighlightParticle', true)
				
			local tool_fly_time = 1.0
			
			local ef = CreateAnimationEffector()
			ef:setDestroysOnEnd(true)
			ef:attachTo(wndIcon)
			ef:setKeysMode(_KEYS_XY_SCR)
			ef:setKey(_TRACK_X, 0., wndIcon:getScrX())
			ef:setKey(_TRACK_Y, 0., wndIcon:getScrY())
			ef:setKey(_TRACK_S, 0., wndIcon:getScale().x)
			ef:setKey(_TRACK_O, 0.9*tool_fly_time, 1)
			ef:setKey(_TRACK_X, tool_fly_time, tool_entity:getScrX())
			ef:setKey(_TRACK_Y, tool_fly_time, tool_entity:getScrY())
			ef:setKey(_TRACK_S, tool_fly_time, wndIcon:getScale().x * 0.2)
			ef:setKey(_TRACK_O, tool_fly_time, 0.5)
			ef:play()
			
			tool_entity:setVisible(true)
			tool_entity:setOpacity(0)
			
			AddGameState(TOOL_INFO_SHOWN, tool_info.name)
			
			if inv:getVisibleCellsCount() < cellIndex then
				inv:setIndex(currIndex)
			end
			inv.root:setY(invY)
			
			inv:onFocusInInventoryPanel()
			
			if inv:getVisibleCellsCount() < cellIndex then
				inv:slideInventoryTo(cellIndex - inv:getVisibleCellsCount())
			end
			
			inv.timer:add(function()
								cell_info.notInInventoryYet = nil
								inv:onToolFound(tool_info)
								inv:unlockTool(tool_info)
								inv:updateCollectionText(c_inf)
								
								tool_entity:setOpacity(1)
								wndIcon:destroy()
								
								local owner = inv:getToolInfoCollectionOwner(tool_info)
								if owner and owner.name ~= tool_info.name then
									tool_entity:setVisible(false)
								end
											
								inv:onFocusOutInventoryPanel()
						   end, tool_fly_time)
			
			self:showHiddenZoomZones()
			zone:destroyScreen()
			self.tool_found_window = nil
			-- if no active zooms, fade out zoom fade
			if self:getActiveZones():GetTop() == nil then
				self:getZoomFade():destroyEffectors()
				self:getZoomFade():ChangeOpacity(nil, 0, ZOOM_ZONE_APPEARING_TIME):setDestroysOnEnd(false)
			end
			
			self:getMouseBlocker():pop()
			
			if RandomBool() then
				playCommonSound('ObjectFly_1')
			else
				playCommonSound('ObjectFly_2')
			end
		end
		
		zone.on_close_button = on_close_button
		zone:getEntityNamed('OKButton').onPress:add(on_close_button)
		zone:getEntityNamed('OKButton'):setSFXTrackOnPress( getCommonSoundEntity('ButtonClick') )
		zone:getEntityNamed('fade_in_on_load'):setRespondable(true)
		zone:getEntityNamed('fade_in_on_load').onPress:add(on_close_button)
		-- zone:getMainEntity().onClickBegin:add(on_close_button)
		
		if tool_info.on_tool_window_close then
			zone:getEntityNamed('OKButton').onPress:add(tool_info.on_tool_window_close)
			zone:getEntityNamed('fade_in_on_load').onPress:add(tool_info.on_tool_window_close)
			-- zone:getMainEntity().onClickBegin:add(tool_info.on_tool_window_close)
		end
		
		self:hideZoomZones()
		zone:show()
		if self.hidden_zoom_zones.zones:GetTop() then 	-- if there are hidden zooms below
			self:getZoomFade():destroyEffectors()
			self:getZoomFade():setOpacity(1)
			self:getZoomFade():setVisible(true)
			zone:getFadeObject():setVisible(false)
		end
		
		self:getMouseBlocker():push()
		
		tool_entity:setVisible(false)
		tool_shadow_entity:setVisible(false)
		
		tool_info.notInInventoryYet = true
		cell_info.notInInventoryYet = true

		playCommonSound('ToolFound')
		
		return zone
	end
end
-------------------------------------------------------------------------------

function PlayFieldBase:getToolApplyObjectNames()
	return self.tool_apply_object_names
end
-------------------------------------------------------------------------------

function PlayFieldBase:isOutdoorScene()
	return self.is_outdoor_scene
end
-------------------------------------------------------------------------------

function PlayFieldBase:createDebugShortcuts()
		local ERSK_F5 = 0x74
		local ERSK_F9 = 0x78
		local ERSK_F10 = 0x79
		
		local game_scene_windows = self.game_scene:getWindow()

		game_scene_windows:getHotkeySignal(ERSK_F5):removeAll()
		game_scene_windows:getHotkeySignal(ERSK_F5):add(function() 
																WriteInfo("savestate by pressing F5")
																saveState()
														  end)

		game_scene_windows:getHotkeySignal(ERSK_F9):removeAll()
		game_scene_windows:getHotkeySignal(ERSK_F9):add(function()
																WriteInfo("restorestate by pressing F9")
																restoreState()
														  end)
		
		game_scene_windows:getHotkeySignal(ERSK_F10):removeAll()
		game_scene_windows:getHotkeySignal(ERSK_F10):add(function() 
																WriteInfo("Reloading "..self.name.." by pressing F10")
																self:getMainEntity():setRespondable(false)
																self:getGameScene():loadPlayField(self.name)
														  end)

		game_scene_windows:getHotkeySignal(Hotkey_Ctrl(ERSK_F10)):removeAll()
		game_scene_windows:getHotkeySignal(Hotkey_Ctrl(ERSK_F10)):add(function() 
																WriteInfo("Destroying and loading "..self.name.." by pressing Ctrl+F10")
																PopUpScreensMgr:destroyAll()
																destroyCurrentScene()
																
	                                                            startLevel("l_" .. self.name)
														  end)
end
-------------------------------------------------------------------------------

function PlayFieldBase:getLastVisitedScene()
	return self.lastVisitedScene
end
-------------------------------------------------------------------------------

function PlayFieldBase:getResourceFolderPath(name)
	assert(name)
	local prefix = self.is_collectors_edition and "Data/ExtraContent/" or "Data/"
	local suffix = "/Scenes/"
    return prefix .. name .. suffix .. self.name .. "/"
end
-------------------------------------------------------------------------------

function PlayFieldBase:getVideoFolderPath()
	return self:getResourceFolderPath("Video")
end
-------------------------------------------------------------------------------

function PlayFieldBase:getSoundFolderPath()
	return self:getResourceFolderPath("Sounds")
end
-------------------------------------------------------------------------------

function PlayFieldBase:getTexturesFolderPath()
	return self:getResourceFolderPath("Textures")
end
-------------------------------------------------------------------------------

function PlayFieldBase:isLastVisitedSceneUpdateDisabled()
	return false
end
-------------------------------------------------------------------------------

function PlayFieldBase:setCurrentMinigameRound(round)
	self.current_minigame_round = round
end
-------------------------------------------------------------------------------

function PlayFieldBase:getCurrentMinigameRound()
	return self.current_minigame_round
end
-------------------------------------------------------------------------------

function PlayFieldBase:setMinigameRoundCompleted(round)
	round = round or self:getCurrentMinigameRound()
	AddUniqueGameState(MG_ROUND_COMPLETED, self.name .. "_" .. round)
end
-------------------------------------------------------------------------------

function PlayFieldBase:isMinigameRoundCompleted(round)
	round = round or self:getCurrentMinigameRound()
	return HasGameState(MG_ROUND_COMPLETED, self.name .. "_" .. round)
end
-------------------------------------------------------------------------------

function PlayFieldBase:completeMinigameRound(round, time)
	time = time or 0
	round = round or self:getCurrentMinigameRound()
	self:setMinigameRoundCompleted(round)
	if time == 0 then
		self.game_scene:getAchievmentsManager():handleEvent('on_minigame_completed',  {['callback'] = callback, ['scene_name'] = self:getName(), ['round'] = round})
	else
		self.timer:add(
			function()
				self.game_scene:getAchievmentsManager():handleEvent('on_minigame_completed',  {['callback'] = callback, ['scene_name'] = self:getName(), ['round'] = round})
			end,
			time
		)
	end
end
-------------------------------------------------------------------------------
