dofile ('Data/Scripts/Common/Screens/PlayFieldBase.lua')

class 'PlayField' (PlayFieldBase)

function PlayField:__init(game_scene, name)
	PlayFieldBase.__init(self, game_scene, name)

	self.mark_link_with_particles = true
	self.is_outdoor_scene = true
		
	fadeObjectCount = 0

	do
		self:hideInfromScreen()
	end

	self:getMainEntity().onPress:add(function()
		self:hideInfromScreen()
	end)

	
	game_scene:getWindow():getHotkeySignal(ERSK_G):removeAll()
	game_scene:getWindow():getHotkeySignal(ERSK_G):add( function()
															path_manager.is_node_debug_info_shown = not path_manager.is_node_debug_info_shown
														end )

	 ---------------------------------------------------------------------------------------------------
    if isDebugMode() then
        self.ingameEditor = nil
        game_scene:getWindow():getHotkeySignal( Hotkey_Ctrl(ERSK_E) ):add( function()
				if not self.ingameEditor then
			        dofile ('Data/Scripts/InGameEditor/EditorScreen.lua')
			    	self.ingameEditor = Helpers.CallFunctionByName('EditorScreen', self)
			    else
			    	self.ingameEditor:hide()
			    	self.ingameEditor.main_entity:destroy()
			    	self.ingameEditor = nil    	
			    end
		   end )
	end
    ---------------------------------------------------------------------------------------------------

	game_scene:getWindow():getHotkeySignal(Hotkey_Ctrl(ERSK_B)):removeAll()
	game_scene:getWindow():getHotkeySignal(Hotkey_Ctrl(ERSK_B)):add( function()
																		if not self.border_left then
																			self.border_left = CreateSpriteEntity('')
																			self.border_left:attachTo			( game_scene:getMainEntity() )
																			self.border_left:setName			( 'border_left' )
																			self.border_left:setAnimationSize	( Vector2f(171, 768) )
																			self.border_left:setHotSpot			( 0, 0 )
																			self.border_left:setColor			( Colorf(0, 0, 0, 1) )
																			self.border_left:setPosition		( Vector2f(-171, 0) )
																			self.border_left:setOrder			( 13666 )
																			self.border_left:hide()
																		end
																		if not self.border_left:isVisible() then
																			self.border_left:show()
																		else
																			self.border_left:hide()
																		end

																		if not self.border_right then
																			self.border_right = CreateSpriteEntity('')
																			self.border_right:attachTo			( game_scene:getMainEntity() )
																			self.border_right:setName			( 'border_right' )
																			self.border_right:setAnimationSize	( Vector2f(171, 768) )
																			self.border_right:setHotSpot			( 0, 0 )
																			self.border_right:setColor			( Colorf(0, 0, 0, 1) )
																			self.border_right:setPosition		( Vector2f(1024, 0) )
																			self.border_right:setOrder			( 13666 )
																			self.border_right:hide()
																		end
																		if not self.border_right:isVisible() then
																			self.border_right:show()
																		else
																			self.border_right:hide()
																		end
																	end )

	self:removeUniqueBar()
end
--------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------
function PlayField:hideInfromScreen(time)
	time = time and time or 0.3
	local inform_screen = self.game_scene:getInformManager():getInformScreen()
	if inform_screen:isOnScreen() then
		inform_screen:hideWindow(time)
	else
		inform_screen:_focusOut(.0)	
	end
end
---------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------
function PlayField:actionCounter(countN, callback)
	local _countN = countN
	local _callback = callback
	local _ind = 1

	local counter = function ()
		if _ind < _countN then
			_ind = _ind + 1
		else
			_callback()
		end
	end

	return counter
end
---------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------
-- hint in notebook -------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
function PlayField:hasNotebookHint()
	return HasGameState('NOTEBOOK_NEED_HINT', 'true', GENERAL_TAG_NAME)
end
---------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------
function PlayField:setNotebookHint()
	AddUniqueGameState('NOTEBOOK_NEED_HINT', 'true', GENERAL_TAG_NAME)
end
---------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------
function PlayField:removeNotebookHint()		
	if HasGameState('NOTEBOOK_NEED_HINT', 'true', GENERAL_TAG_NAME) then
		RemoveGameState('NOTEBOOK_NEED_HINT', 'true', GENERAL_TAG_NAME)
	end
end
---------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------
function PlayField:startCutScene(cutscene, callback, time, cutsceneName)
	cutscene = type(cutscene) == 'table' and cutscene or {cutscene}
	time = time and time or 0

	local move_time = 0.25
	local panel = self:getPanel()
	local current_panel_mode = panel:getMode()

	self:getGameScene():volumeTurnDownMusic()

	if cutsceneName then
		self:addCutsceneInGallery(cutsceneName)	
	end
		
	local function on_end_video()	

		self:getGameScene():volumeRestoreMusic()
		panel:setMode(current_panel_mode, move_time)
		self.timer:add(function() self.mouseBlocker:pop() end, move_time)

		for i = 1, #cutscene-1 do
			if cutscene[i] then
				cutscene[i]:destroy()
			end
		end

		if cutscene[#cutscene] then
			local eff = cutscene[#cutscene]:ChangeOpacity(1, 0, CHANGE_OBJECT_OPACITY)
			eff.onFinish:add(function()
								cutscene[#cutscene]:destroy()
							 end)
		end
		
		if callback then
			callback()
		end
	end

	self.mouseBlocker:push()
	panel:setMode(EPanelMode.CUTSCENE, move_time, on_end_video)
	self.timer:add(function()
						self:hideBlackBar(true)
					end,
					0)

	if cutscene[1] then
		for i = 1, #cutscene - 1 do
			cutscene[i].onFinish:add(function()
										cutscene[i]:destroy()
										cutscene[i] = nil
										cutscene[i+1]:play()
									end)
		end

		cutscene[#cutscene].onFinish:add(on_end_video)
		cutscene[1]:play()
	else
		cutscene = {}
		self.timer:add(on_end_video, time)
	end
end
--------------------------------------------------------------------------------

function PlayField:zoomPlayField(scale, zoomPoint, duration)
	assert(scale)

	if zoomPoint.x > SCR_W - SCR_W / 2. / scale.x - (SCR_W - WORK_W) / 2  then
		zoomPoint.x = SCR_W - SCR_W / 2. / scale.x - (SCR_W - WORK_W) / 2
	end
	if zoomPoint.x < SCR_W / 2. / scale.x then
		zoomPoint.x = SCR_W / 2. / scale.x
	end
	if zoomPoint.y > SCR_H - SCR_H / 2. / scale.y then
		zoomPoint.y = SCR_H - SCR_H / 2. / scale.y
	end
	if zoomPoint.y < SCR_H / 2. / scale.y then
		zoomPoint.y = SCR_H / 2. / scale.y
	end

	zoomPoint.x = SCR_W / 2. - zoomPoint.x * scale.x
	zoomPoint.y = SCR_H / 2. - zoomPoint.y * scale.y

	local af = CreateAnimationEffector();
	af:setDestroysOnEnd(true)
	af:setKey(_TRACK_X, 0, self:getMainEntity():getX())
	af:setKey(_TRACK_Y, 0, self:getMainEntity():getY())
	af:setKey(_TRACK_SX, 0, self:getMainEntity():getScale().y)
	af:setKey(_TRACK_SY, 0, self:getMainEntity():getScale().x)
	af:setKey(_TRACK_X, duration, zoomPoint.x)
	af:setKey(_TRACK_Y, duration, zoomPoint.y)
	af:setKey(_TRACK_SX, duration, scale.x)
	af:setKey(_TRACK_SY, duration, scale.y)
	af:attachTo(self:getMainEntity())
	af:play()
end
-------------------------------------------------------------------------------

function PlayField:zoomPlayFieldIn(scale, zoomPoint, duration)
	assert(self:getMainEntity():getScale().x == 1.0 and self:getMainEntity():getScale().y == 1.0);
	self:zoomPlayField(scale, zoomPoint, duration);
end
-------------------------------------------------------------------------------

function PlayField:zoomPlayFieldOut(duration)
	local pScale = self:getMainEntity():getScale();
	self:zoomPlayField(Vector2f(1.0, 1.0), {x = self:getMainEntity():getX()/(1.0 - pScale.x), y = self:getMainEntity():getY()/(1.0 - pScale.y)}, duration);
end
-------------------------------------------------------------------------------

function PlayField:registerEyeObject(obj_name, radius)

	local obj = self:getEntityNamed(obj_name)
	local base_x = obj:getScrX()
	local base_y = obj:getScrY()

	local function onUpdate()
		local dx    = GetCursorEntity():getScrPosition().x - base_x
		local dy    = GetCursorEntity():getScrPosition().y - base_y
		local angle = math.atan2(dy, dx)
	
		local x = base_x + math.cos(angle) * radius
		local y = base_y + math.sin(angle) * radius
	
		obj:setScrXY(x, y)
	end

	obj.onUpdate:add(onUpdate)  
end
-------------------------------------------------------------------------------

function PlayField: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 = self:getInventory():getToolInHand()

		self:hideInfromScreen()
	
		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, 'CursorQuestion')
	self:getEntityNamed(obj_name):setSendEventsToParent(false)
	-- TODO
	--if ignore_highlight_on_click then
 --       GetCurrentLevel().ignore_on_highlight[event.target] = true
 --   end
end
-------------------------------------------------------------------------------

function PlayField:createLinkMarkerParticles(triggerName)
    local trigger = self:getEntityNamed(triggerName)
    local box     = trigger:getBoundingBox()
    local entity  = CreateParticleEntity()

    entity:setParticle('Data/ParticleSystems/LinkMarker.psi', 'Data/Textures/Common/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 PlayField:completeMinigame(time, callback)
	time	 = time or 0
	callback = callback or bind(self.close, self)

	local function trigger_achievement_event()
        self.game_scene:getAchievmentsManager():handleEvent('on_minigame_completed', 
            {['callback'] = callback, ['scene_name'] = self:getName()})
    end
	
	AddUniqueGameState(SCENE_CLOSED, self.name)
	AddUniqueGameState(MG_COMPLETED, self.name)
	SetGameState(LAST_VISITED_SCENE, self.parent_level)
	SetGameState(LAST_VISITED_SCENE, self.parent_level, EGameStateTag.GENERAL)
	
	if self.find_toolMG then
		self:findTool(self.find_toolMG, false, function() self.timer:add(trigger_achievement_event, time) end)
	else
		self.timer:add(trigger_achievement_event, time)
	end
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField: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
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField:isPhotoAdded(id)
	return HasGameState('PhotoGallerySlot', id)
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField:addPhoto(id)	
	if not self:isPhotoAdded(id) then
		AddUniqueGameState('PhotoGallerySlot', id)
		AddUniqueGameState('GalleryType_' .. id, 'photo')
	end
end

function PlayField:addCutsceneInGallery(id)	
	if not self:isPhotoAdded(id) then
		AddUniqueGameState('PhotoGallerySlot', id)
		SetGameState('GalleryType_' .. id, 'video')
	end
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField:registerFindFlyPhoto(area_find_photo, id, onClick, cursor_name)  
	local bObjectClicked = self:isObjectClicked(area_find_photo, self.name)
	local apperaEntity = self:getEntityNamed(area_find_photo)

	if bObjectClicked then
		apperaEntity:hide()
		self:addPhoto(id)
	
		--local num_photo = HasGameState('PHOTO_COLLECTED') and tonumber(GetGameState('PHOTO_COLLECTED'):get(0)) or 0	
		--SetGameState('PHOTO_COLLECTED', tostring(num_photo + 1))
		--self.game_scene:getAchievmentsManager():handleEvent('on_photo_collect')			

		if onClick then
			local event = {}
			event.id   = id
			event.flyingTime = 0
			event.save = true
			
			onClick(event)
		end

		self:getPanel():getControlsPanel():entityShow('bttn_phone')	
	else
		local event = {}
		if onClick then			
			event.id   = id
			event.flyingTime = 0
			event.save = false			
		end

		cursor_name = cursor_name or 'CursorCamera'			
		--local inform_button  = current_scene:getInformManager():getInformScreen():getEntityNamed('inform_button')
		local function createPhotoScreen()
			apperaEntity:hide()	
			self:addPhoto(id)

			self:hideBlackBar()	
			self:hideInfromScreen()	
			
			-- --
			-- inform_button:setActive(false)
			-- --

			local state_value = self.name..'('..area_find_photo..')'
	        AddUniqueGameState(OBJECT_CLICKED, state_value)

			local flyId 		= 'FlyPhoto_'..id
			local filePath 		= FLYPHOTOS_FOLDER..flyId..'.lua'
			if IsLevelCollectorsEdition(current_scene:getPlayField().name) then
			    filePath    	= FLYPHOTOS_FOLDER_CE..flyId..'.lua'
			end		
			assert(resourceExists(filePath))
			dofile(filePath)
			
			local photo = Helpers.CallFunctionByName(flyId, self, flyId)
			photo:loadPage()

			local flyPhotoObj 	= photo:getEntityNamed('sending_photo_anchor')
			local blur_photo 	= photo:getEntityNamed('Layer_' .. id .. '_blur')

			local fadeInfo 	= photo:getEntityNamed('hover_flyPhoto')
			fadeInfo:setOpacity(0)

			photo:getMainEntity():setOrder(1020)
			flyPhotoObj:setRespondable(true)

			local appearPos = Vector2f(flyPhotoObj:getScrX(), flyPhotoObj:getScrY())
			if apperaEntity then
				appearPos = Vector2f(apperaEntity:getScrX(), apperaEntity:getScrY())
			end			
			
			local bttn_phone = self:getPanel():getControlsPanel():getEntityNamed('bttn_phone')
			
			if not save then
				playCommonSound('FlyNote')
			end	

        	self:getPanel():getControlsPanel():entityHide('bttn_phone')

			local function on_oscillation_ended()	
				self:setForcedObject(flyPhotoObj) 

				local effector = CreateAnimationEffector()
				effector:setDestroysOnEnd(true)
			    effector:attachTo(blur_photo)
			    effector:setKey(_TRACK_SX, 0,  1)
			    effector:setKey(_TRACK_SY, 0,  1)
				effector:setKey(_TRACK_SX, 0.4, 1.1)
				effector:setKey(_TRACK_SY, 0.4, 1.1)
				effector:setKey(_TRACK_SX, 0.8,  1)
				effector:setKey(_TRACK_SY, 0.8,  1)
				effector:setKey(_TRACK_SX, 1.0,  1)
				effector:setKey(_TRACK_SY, 1.0,  1)

				effector:setKey(_TRACK_O, 0.8, 1)
				effector:setKey(_TRACK_O, 1.2, 0)

				effector:play()

				self.timer:add( function()	
									local entityFlash = CreateSpriteEntity('')
									entityFlash:setName('FlyPhotoFlash')
									entityFlash:setOrder(1010)
									entityFlash:setAnimationSize(1366, 768)
							   		entityFlash:setHotSpot(683, 384)
							   		entityFlash:setXY(512, 384)
							   		entityFlash:attachTo(self:getMainEntity())
							   		entityFlash:setVisible(true)

									local effector = CreateAnimationEffector()
									effector:setDestroysOnEnd(true)
								    effector:attachTo(entityFlash)
									effector:setKey(_TRACK_O, 0.0, 0)
									effector:setKey(_TRACK_O, 0.1, 1)
									effector:setKey(_TRACK_O, 0.3, 0)
									effector:setKey(_TRACK_O, 0.6, 0)

									playCommonSound('GetPhotoGallery')

									effector:play()
									
									local function flashEnd()

										local time = 0.4
						    			--fadeInfo:ChangeOpacity(0, 1, time)

						    			--self.timer:add( function()
										self:setForcedObject(nil)
										entityFlash:destroy()
							    		local function end_oscilation()	
							    			--	inform_button:setActive(true)
							    				onClick(event)

								    			self:getPanel():getControlsPanel():entityShow('bttn_phone')		
								    			photo:getMainEntity():hide()

								    			self:showUniqueBar('PHOTO_FINDED', 
								    				function()
										    			local num_photo = HasGameState('PHOTO_COLLECTED') and tonumber(GetGameState('PHOTO_COLLECTED'):get(0)) or 0	
														SetGameState('PHOTO_COLLECTED', tostring(num_photo + 1))
														self.game_scene:getAchievmentsManager():handleEvent('on_photo_collect')			
													end)
											
							    		end
							           
							            self:oscillateZoomOut(1.1, -- max scale
									                            0.1, -- min scale
									                            ZOOM_ZONE_APPEARING_TIME/2, -- scale up time
									                            ZOOM_ZONE_APPEARING_TIME/2,  -- scale down time
									                            bttn_phone,
									                            flyPhotoObj
									                        ).onFinish:add(end_oscilation)	 
						            	--end, time + 1.5)
									end

									effector.onFinish:add( function() 														
																flashEnd()
														   end)					           
						    						              
						        end, 2.0)
			end

			self:oscillateZoomIn(ZOOM_ZONE_APPEARING_TIME,   -- open time
				                    0.005,     -- amplitude
				                    10,     -- frequency
				                    0.1,    -- damping time                                
				                    10,      -- number of iterations
									bttn_phone,
									flyPhotoObj
				                ).onFinish:add(on_oscillation_ended)	
		end

		self:getEntityNamed(area_find_photo):setCursorOverName(cursor_name)
		self:getEntityNamed(area_find_photo).onClickBegin:add(createPhotoScreen)
		self:getEntityNamed(area_find_photo):setSendEventsToParent(false)
	end
end
-------------------------------------------------------------------------------

function PlayField:oscillateZoomIn(open_time, amplitude, frequency, damping_time, num_iterations, trigger_object, attachEntity)
	self:getMainEntity():destroyEffectors()
	
	local trigger_x = WORK_W / 2
	local trigger_y = WORK_H / 2
	
	if trigger_object then
	    local pos = self:getMainEntity():screenToLocal(trigger_object:getScrPosition())
		trigger_x = pos.x
		trigger_y = pos.y
	end
	
	local start_scale = 0.3	
	local zoom_in_time = math.min(open_time, 0.1)
	local move_in_time = math.min(open_time, 0.2)
	local oscillation_time = open_time
	local k_max_move_amp = 3		
	-- move from trigger zone to the center
	local eff = CreateAnimationEffector()
	eff:setDestroysOnEnd(true)
    eff:attachTo(attachEntity)
	eff:setKey(_TRACK_X, 0.0, trigger_x)
	eff:setKey(_TRACK_Y, 0.0, trigger_y)
	
	if oscillation_time>0 then
	    eff:setKey(_TRACK_X, move_in_time, WORK_W / 2)
	    eff:setKey(_TRACK_Y, move_in_time, WORK_H / 2)
	
        eff:setKey(_TRACK_SX, 0, start_scale)
        eff:setKey(_TRACK_SY, 0, start_scale)
        
        local dx = (WORK_W / 2) - trigger_x
        local move_amplitude_x = (dx / (WORK_W / 2)) * k_max_move_amp
        
        local dy = (WORK_H / 2) - trigger_y
        local move_amplitude_y = (dy / (WORK_H / 2)) * k_max_move_amp

        for i = 1, num_iterations do
            local time = i*(oscillation_time / num_iterations)
            local damp_coeff = math.sin(time*frequency)*math.exp(-time/damping_time+M_E)
            local scale = amplitude * damp_coeff + 1
            eff:setKey(_TRACK_SX, time+zoom_in_time, scale)
            eff:setKey(_TRACK_SY, time+zoom_in_time, scale)
            
            local x = WORK_W / 2 + (move_amplitude_x * damp_coeff + 1)
            local y = WORK_H / 2 + (move_amplitude_y * damp_coeff + 1)
            
            eff:setKey(_TRACK_X, time+move_in_time, x)
            eff:setKey(_TRACK_Y, time+move_in_time, y)
        end
        
        eff:setKey(_TRACK_SX, oscillation_time+zoom_in_time, 1)
        eff:setKey(_TRACK_SY, oscillation_time+zoom_in_time, 1)
    else
        eff:setKey(_TRACK_X, 0, WORK_W / 2)
	    eff:setKey(_TRACK_Y, 0, WORK_H / 2)
    
        eff:setKey(_TRACK_SX, 0, 1)
        eff:setKey(_TRACK_SY, 0, 1)
	end
	eff:play()
	
	return eff
end

function PlayField:oscillateZoomOut(max_scale, min_scale, scale_up_time, scale_down_time, trigger_object, attachEntity)
	self:getMainEntity():destroyEffectors()
	
	local trigger_x = WORK_W / 2
	local trigger_y = WORK_H / 2
	
	if trigger_object then
		local pos = self:getMainEntity():screenToLocal(trigger_object:getScrPosition())
		trigger_x = pos.x
		trigger_y = pos.y
	end
	
	local eff = CreateAnimationEffector()
	eff:setDestroysOnEnd(true)
	eff:attachTo(attachEntity)
	-- scale ------------------
	eff:setKey(_TRACK_SX, 0, 1)
	eff:setKey(_TRACK_SY, 0, 1)
	eff:setKey(_TRACK_SX, scale_up_time, max_scale)
	eff:setKey(_TRACK_SY, scale_up_time, max_scale)
	eff:setKey(_TRACK_SX, scale_up_time + scale_down_time, min_scale)
	eff:setKey(_TRACK_SY, scale_up_time + scale_down_time, min_scale)
	eff:setKey(_TRACK_SX, scale_up_time + scale_down_time + .1, 1)
	eff:setKey(_TRACK_SY, scale_up_time + scale_down_time + .1, 1)
	---------------------------
	
	-- position ---------------
	eff:setKey(_TRACK_X, scale_up_time, WORK_W / 2)
	eff:setKey(_TRACK_Y, scale_up_time, WORK_H / 2)
	eff:setKey(_TRACK_X, scale_up_time + scale_down_time, trigger_x)
	eff:setKey(_TRACK_Y, scale_up_time + scale_down_time, trigger_y)
	---------------------------
	
	-- opacity ----------------
	eff:setKey(_TRACK_O, scale_up_time, 1)
	eff:setKey(_TRACK_O, scale_up_time + scale_down_time, 0)
	---------------------------
	
	eff:play()
	
	return eff
end
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
function PlayField:addFlyNote(id, save, layerName, doEffect, apperaEntity, destPos, startScale, onClick)    
	if self:isObjectClicked(layerName, self.name) 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)

		local appearPos = Vector2f(flyNoteObj:getScrX(), flyNoteObj:getScrY())
		if apperaEntity then
			appearPos = Vector2f(apperaEntity:getScrX(), apperaEntity: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,   1)
		moverCycle:setKey(_TRACK_S, 2,   1)
		moverCycle:setMode(_ANIM_FWD + _ANIM_LOOP)
		moverCycle:pause()

		if save then
			if doEffect ~= false then
				moverStart:pause()
				moverStart:setToFinish()
				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 'bttn_phone')
				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')
	end
end
-------------------------------------------------------------------------------

function PlayField: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_gray_ent = tool_info.create_entities_functions.create_tool_gray()
	--tool_info.entities.tool_shadow_ent = tool_info.create_entities_functions.create_tool_shadow()
	
	inv:addTool(tool_info)
end
-------------------------------------------------------------------------------

function PlayField:createToolWindow(tool_info)
	local inv = self:getInventory()
	local tool_entity = tool_info.entities.tool_ent
	local tool_gray_entity = tool_info.entities.tool_gray_ent
	local text = tool_info.texts.info_text
	local cell_info = inv:getCellInfoByToolInfo(tool_info)
	local wndIconMaxH = 200

	inv:lockTool(tool_info)
	
	local screenName = 'ToolFound'

	local is_unique = isUniqueTool(tool_info.name)
	if is_unique then
		screenName = 'ToolFoundUnique'
		text = 'ENTER_UNIQUE_TOOL_' .. tool_info.name:upper()
	end

	local zone = Helpers.CallFunctionByName(screenName, screenName)
	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()
		if is_unique then
			if zone:isLocked() then 
				return 
			end
		end

		local c_inf, cellIndex = inv:getCellInfoByToolInfo(tool_info)

		if is_unique then
			local tool_name_new = zone.inputNameTextField:getTextView():getText()
			SetGameState('TOOL_NAME_' .. tool_info.name:upper(), tool_name_new)
			zone:removeAllHanderInHotKey(ERSK_ENTER)
		end

		Helpers.attachInPlace(wndIcon, inv:getMainEntity())
		wndIcon:setOrder(100)
		
		-- 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_gray_entity:setVisible(true)
	--	tool_shadow_entity:setVisible(true)
		tool_entity:setOpacity(0)
		tool_gray_entity:setOpacity(0)
		--tool_shadow_entity:setOpacity(0)
		
		AddGameState(TOOL_INFO_SHOWN, tool_info.name)		
		
		inv.timer:add(function()
							cell_info.notInInventoryYet = nil
							inv:onToolFound(tool_info)
							inv:unlockTool(tool_info)
							inv:updateCollectionText(c_inf)
							
							tool_entity:setOpacity(1)
							tool_gray_entity:setOpacity(1)
							--tool_shadow_entity:setOpacity(1)
							wndIcon:destroy()
							
							local owner = inv:getToolInfoCollectionOwner(tool_info)
							if owner and owner.name ~= tool_info.name then
								tool_entity:setVisible(false)
								tool_gray_entity:setVisible(false)
							--	tool_shadow_entity:setVisible(false)
							end
					   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

		playCommonSound('ObjectFly')
	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)

	if not is_unique then
		zone:getEntityNamed('click_close_tool').onClickBegin:add(on_close_button)
	else		
		zone:addHotKey(ERSK_ENTER, on_close_button)		
	end
	
	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)
		if not is_unique then
			zone:getEntityNamed('click_close_tool').onPress:add(tool_info.on_tool_window_close)
		else
			zone:addHotKey(ERSK_ENTER, tool_info.on_tool_window_close)
		end
	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_gray_entity:setVisible(false)
	--tool_shadow_entity:setVisible(false)
	
	tool_info.notInInventoryYet = true
	cell_info.notInInventoryYet = true

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

-------------------------------------------------------------------------------
function PlayField:createComics(name, type, on_frame_change)
	return Comics(self, name, type, on_frame_change)
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField:getPickPhoneParameters()
    return self.phone_call_name, self.pick_phone_callback
end


function PlayField:setPickPhoneCallback(call_name, callback, entity)
    if callback and type(callback) == 'function' then
        if HasGameState('PHONE_CALL_SHOWN', call_name) then
            callback(true)
            return
        end
    
        self.phone_call_name = call_name
        self.pick_phone_callback = function()
                self.mouseBlocker:pop()
                AddUniqueGameState('PHONE_CALL_SHOWN', call_name)
                callback()
            end
    
        self.mouseBlocker:push()
        self:getPanel():getControlsPanel():setPhoneRinging(true, entity)
    else
        self.phone_call_name = nil
        self.pick_phone_callback = nil
        self:getPanel():getControlsPanel():setPhoneRinging(false, entity)
    end
end
-------------------------------------------------------------------------------

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

	if isQuestLevel(self.name) then
		panel:setMode(EPanelMode.QUEST, 0)
	elseif isHiddenObjectLevel(self.name) then
	--	if not self.eventDispatcher then
		self.eventDispatcher = EventDispatcher(self)
		--end	

		self.ho_logic = HiddenObjectLogicED(self.game_scene)
		panel:setMode(EPanelMode.HIDDENOBJECT, 0)
		panel:getHOPanel():addExit()
		--panel:getControlsPanel().hintAnimationLogic:setMode(EPanelMode.MATCH3)
		
		self.match3 = M3Game(self.name)
        self.ho_logic.eventDispatcher:addEventListener('TaskObjectMouseDown', bind(self.match3.onTaskObjectMouseDown, self.match3))
        
        self.timer:add( function()
                            local m3key = self.name..'_'..self.ho_logic:getDealIndex()
                            self.match3:init(m3key)
                        end, 0)               

	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 PlayField:destroy()
	PlayFieldBase.destroy(self)
	
	if self.match3 then
        self.match3:destroy()
        self.match3 = nil
    end
end
-------------------------------------------------------------------------------
function PlayField:registerDragObject(	obj_name, 
										get_area, 
										target_area, 
										on_get, 
										on_drop, 
										on_drop_target, 
										rect, 
										bLockCenter, 
										bLockTargetArea, 
										bIntersectionMode, 
										bDropOnUp,
										bOnceDrop,
										item_name)
	local drag_object = PlayFieldBase.registerDragObject(self, obj_name, get_area, target_area, on_get, on_drop, on_drop_target, rect, bLockCenter, bLockTargetArea, bIntersectionMode, bDropOnUp, bOnceDrop)

	local bHOInteractive 	= (item_name ~= nil)
	local level  			= current_scene:getPlayField()

	-- привязка интерактива к итему в HiddenObject
    if bHOInteractive then
        assert(level.mode == 'hidden_object')
        
        local bindInfo = {
                            itemName   = item_name,
                            objectName = obj_name,
                            type       = 'dragObject'
                         }
        
        level.ho_logic:addInteractiveItemBind(bindInfo)
    end
    -------------------------
end
-------------------------------------------------------------------------------

function PlayField:removeUniqueBar()
	if current_scene:getPanel():hasEntityNamed('unique_inform_screen') then
		local inform_screen  = current_scene:getInformManager():getInformScreen()
		local inform_screen_title = current_scene:getPanel():getEntityNamed('unique_inform_screen')
		inform_screen_title:destroy()

		inform_screen.active = true
	end	
end

-------------------------------------------------------------------------------
function PlayField:showUniqueBar(string_key, callback)
	function getString(alias)
		return StringManager:getString(alias)
	end

	local inform_screen  = current_scene:getInformManager():getInformScreen()
	local inform_button  = inform_screen:getEntityNamed('inform_button')

	self:hideBlackBar()

	inform_screen.active = false

	self:hideInfromScreen()	

	local textures_path = 'Data/Textures/GUI/Inform/'
	local fadeTime = 0.5
	local inform_screen_title = CreateSpriteEntity(textures_path .. 'inform_title.png')
	inform_screen_title:setName('unique_inform_screen')
	inform_screen_title:setRespondable(false)
	inform_screen_title:setOrder(20)
	inform_screen_title:setXY(512, 22)
	inform_screen_title:attachTo( current_scene:getPanel():getMainEntity() )

	local inform = CreateTextEntity()
    inform:attachTo(inform_screen_title)
    inform:setName('unique_text')
    inform:setFont('Data/Fonts/Options.fnt') 
    inform:setHeight(23)
    inform:setXY(0, -12)
    inform:setOrder(2)
    inform:setText(getString(string_key))
  
	inform_screen_title:ChangeOpacity(0, 1, fadeTime)

	self.timer:add( function()
						inform_screen_title:ChangeOpacity(1, 0, fadeTime, 1.6)
					end, fadeTime)
	
	if callback then
		self.timer:add( function()
							callback()
							inform_screen_title:destroy()	
							--inform_button:setActive(true)	
							inform_screen.active = true					
						end, fadeTime*2 + 1.65)	
	else
		self.timer:add( function()
							inform_screen_title:destroy()	
							--inform_button:setActive(true)
							inform_screen.active = true						
						end, fadeTime*2 + 1.65)	
	end
end
-------------------------------------------------------------------------------

--------------------------------------------------------------------------------
function PlayField:fadeGhostClock(spriteClock, coverClock, areaClick)
    local areaClickEntity   = self:getEntityNamed(areaClick)
    local spriteClockEntity = self:getEntityNamed(spriteClock)
    local coverClockEntity  = self:getEntityNamed(coverClock)

    if not self:isObjectClicked(areaClick) then     
        
        spriteClockEntity:ChangeOpacity(0, 1, 0.8)
        coverClockEntity:ChangeOpacity(1, 0, 0.8) 
        self:getEntityNamed(areaClick):show()
        playCommonSound('MorphingClock')
        self.timer:add( function() 
                            coverClockEntity:ChangeOpacity(0, 1, 0.8)  
                            spriteClockEntity:ChangeOpacity(1, 0, 0.8)
                            self:getEntityNamed(areaClick):hide()
                        end, 20) 
    
        self.timer:add( function() 
                            self:fadeGhostClock(spriteClock, coverClock, areaClick)
                        end, 40)   
    end        
end  

-------------------------------------------------------------------------------
function PlayField:registerFindClockObject(area_obj_name, obj_name, clock_name, on_click, on_click_ended, cursor_name)
	assert(area_obj_name)

	cursor_name = cursor_name or 'CursorActive'

	self:hideInfromScreen()
	
	local bObjectClicked = self:isObjectClicked(area_obj_name)
	local maxDpos = 256
	
	local function on_click_check(save)
		self:hideInfromScreen()

		if not self:getInventory():getToolInHand() or self.tool_apply_object_names[area_obj_name] then
			local event  = {}
			event.save = false
			if save == true then
				event.save = true
			end
			event.target = area_obj_name

			on_click(event)

			if self.active_objects[area_obj_name] and self.active_objects[area_obj_name].connection then
				self.active_objects[area_obj_name].connection:remove()
				self.active_objects[area_obj_name].connection = nil
			end
			self:getEntityNamed(area_obj_name):setCursorOverName('')	
			
			local area_entity = self:getEntityNamed(area_obj_name)
			area_entity:hide()

			if not event.save then
				local state_value 		= self:getName() .. '(' .. area_obj_name .. ')'
				local clock_state_value = CLOCK_ROOM .. '(' .. clock_name .. ')'
				AddUniqueGameState(OBJECT_CLICKED, state_value)
				AddUniqueGameState(CLOCK_CLICKED, clock_state_value, GENERAL_TAG_NAME)				
				
				-- local node = path_manager:getConnectedNode(NodeType.CLICK, area_obj_name)
				-- assert(node and 'No node for clock object click')
				-- node:trigger()				
				-- fly effector ----------------------------------------------------------------------
				do
					--self.mouseBlocker:push()
					local clock = self:getPanel():getControlsPanel():getEntityNamed('bttn_clock')
					local obj_entity = self:getEntityNamed(obj_name)
					
					obj_entity:attachTo(self:getMainEntity())
					obj_entity:setOrder(2001)
					obj_entity:show()

					self:getZoomFade():destroyEffectors()
			        self:getZoomFade():show()
			        self:getZoomFade():setOrder( 2000)
			        self:getZoomFade():ChangeOpacity(0, 1, 0.5)


					local moverEnd = CreateAnimationEffector()
					moverEnd:attachTo(obj_entity)
					moverEnd:setKeysMode(_KEYS_XY_SCR)

					local moveTime = 1.0
					local moveDelay = 1.0

					playCommonSound('FindClock')

					local startPos 		= self:getMainEntity():localToScreen(Vector2f(area_entity:getX(), area_entity:getY()))
					local center_pos 	= self:getMainEntity():localToScreen(Vector2f(WORK_W/2, WORK_H/2))
					local finishPos 	= Vector2f(clock:getScrX(), clock:getScrY())


					moverEnd:setKey(_TRACK_SX, 0,       0.2)
					moverEnd:setKey(_TRACK_SY, 0,       0.2)
					moverEnd:setKey(_TRACK_SX, moveTime, 1.0)
					moverEnd:setKey(_TRACK_SY, moveTime, 1.0)
					
					-- create effector keys table
					local pointCount = 20
					local effectorKeys = HelpersEffect.createSplineBezier(
											startPos,
											startPos + Vector2f(RandomFloat(-maxDpos, maxDpos), RandomFloat(-maxDpos, maxDpos)),
											center_pos,
											center_pos + Vector2f(RandomFloat(-maxDpos, maxDpos), -200),
											pointCount,
											moveTime,
											moveDelay
										 )
					
					-- set keys from created effector keys table
					local timeCenter = 0
					local dTime = 1 / pointCount
					for _, keys in ipairs(effectorKeys) do
						moverEnd:setKey(_TRACK_X, timeCenter * moveTime, keys.x)
						moverEnd:setKey(_TRACK_Y, timeCenter * moveTime, keys.y)

						timeCenter = timeCenter + dTime
					end

					moverEnd:setKey(_TRACK_SX, timeCenter + 0.1,       1.0)
					moverEnd:setKey(_TRACK_SY, timeCenter + 0.1,       1.0)
					moverEnd:setKey(_TRACK_SX, timeCenter + 1,       0.9)
					moverEnd:setKey(_TRACK_SY, timeCenter + 1,       0.9)
					moverEnd:setKey(_TRACK_SX, timeCenter + 2,       1.0)
					moverEnd:setKey(_TRACK_SY, timeCenter + 2,       1.0)

					moverEnd:setKey(_TRACK_SX, timeCenter + 2,       1.0)
					moverEnd:setKey(_TRACK_SY, timeCenter + 2,       1.0)
					moverEnd:setKey(_TRACK_SX, timeCenter + 2 + moveTime, 0.2)
					moverEnd:setKey(_TRACK_SY, timeCenter + 2 + moveTime, 0.2)

					self.timer:add( function()
										playCommonSound('FlyClock')
										self:getZoomFade():destroyEffectors()
								        self:getZoomFade():setRespondable(true)
								        self:getZoomFade():ChangeOpacity(nil, 0, 0.5):setDestroysOnEnd(false)
									end, timeCenter + 2)

					-- create effector keys table
					local pointCount = 20
					local effectorKeys = HelpersEffect.createSplineBezier(
											center_pos,
											center_pos + 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 time = timeCenter + 2
					local dTime = 1 / pointCount
					for _, keys in ipairs(effectorKeys) do
						moverEnd:setKey(_TRACK_X, time * moveTime, keys.x)
						moverEnd:setKey(_TRACK_Y, time * moveTime, keys.y)

						time = time + dTime
					end
										
					moverEnd.onFinish:add( function() 
							if on_click_ended then	
								on_click_ended(event) 
							end
							--self.mouseBlocker:pop()
							obj_entity:destroy()

							-- if (not HasGameState('CLOCK_COLLECTED')) then		
							-- 	SetGameState('CLOCK_COLLECTED', '1')
							-- 	self.game_scene:getAchievmentsManager():handleEvent('on_clock_collect')
							-- end	

							self:getZoomFade():hide()

							self:showUniqueBar('CLOCK_FINDED', nil)
						end)

					moverEnd:play()
				end	
			else
				-- if (not HasGameState('CLOCK_COLLECTED')) then		
				-- 	SetGameState('CLOCK_COLLECTED', '1')
				-- 	self.game_scene:getAchievmentsManager():handleEvent('on_clock_collect')
				-- end					

				if on_click then
					on_click(event)
				end 

				if on_click_ended then
					on_click_ended(event)
				end
			end
			-- end fly effector -------------------------------------------------------------------

		elseif self:getInventory():getToolInHand() then			
			self:tryToolApply(self:getInventory():getToolInHand(), self:getEntityNamed(area_obj_name))
		end	
	end
	
	if not bObjectClicked then	
		self:getEntityNamed(area_obj_name):setCursorOverName(cursor_name)
		self:getEntityNamed(area_obj_name).onClickBegin:add(on_click_check)
		self:getEntityNamed(area_obj_name):setSendEventsToParent(false)	
	end
	
	if bObjectClicked then
		on_click_check(true)
	end
end

-------------------------------------------------------------------------------
function PlayField:registerActiveObject(obj_name, on_click, multi_use, cursor_name, item_name)
	PlayFieldBase.registerActiveObject(self, obj_name, on_click, multi_use, cursor_name)

	self:hideInfromScreen()

	local bHOInteractive 	= (item_name ~= nil)
	local level  			= current_scene:getPlayField()

	 -- привязка интерактива к итему в HiddenObject
    if bHOInteractive then
        assert(level.mode == 'hidden_object')
        
        local bindInfo = {
                            itemName   = item_name,
                            objectName = obj_name,
                            type       = 'activeObject'
                         }
        
        level.ho_logic:addInteractiveItemBind(bindInfo)
    end
    -------------------------
end
-------------------------------------------------------------------------------
function PlayField:checkActiveObjectMouseDown(obj_name, bIgnoreZoneCheck)
    local info = self.active_objects[obj_name]
    
  -- if (not self:hasEntityNamed(obj_name)) then  
	    if info
	        and not self:getEntityNamed(obj_name):isRespondable()
	        and (not current_scene:getPlayField():isObjectClicked(obj_name) or info.multi_use == true)
	        and (bIgnoreZoneCheck or self.locker:GetNReferences() == 0 or IsActiveZoneObject(obj_name) or (self.forcedObject == obj_name))
	    then
	        local state_value = self.name..'('..obj_name..')'
	        AddUniqueGameState(OBJECT_CLICKED, state_value)
	        
	        if not info.multi_use == true then
	            self:getEntityNamed(obj_name):setRespondable(false)
	           -- UnregisterActiveObject(obj_name)
	        end
	        
	        if info.on_click then
	            local event           = {}
	            event.save            = false
	            event.target          = obj_name
	            event.interactiveTime = HiddenObjectLogic.DEFAULT_INTERACTIVE_TIME
	            
	            info.on_click(event)
	            
	            if info.bHOInteractive then
	                current_scene:getPlayField().ho_logic:onInteract(obj_name, event.interactiveTime)
	                return true, event.interactiveTime
	            end
	        end
	        
	        return true
	    end
   -- end
    return false
end
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
function PlayField: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
			self:hideInfromScreen()
			local tool_in_hand = self:getInventory():getToolInHand()
			
			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 == 'function' 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
					
					self:hideInfromScreen()
					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)
			
			-- Дебажный обработчик, для авто-применения тулзов
			if isBuildForCustomerMode() == false then
				local function on_right_click(object)
					self:autoApplyTool(tool_name, object)
				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)
				
				if isBuildForCustomerMode() == false then
					local function on_right_click(object)
						self:autoApplyTool(tool_name, object)
					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 PlayField: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
			self:hideInfromScreen()

			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

-------------------------------------------------------------------------------