ActiveZonesStackClass = CreateInheritedClass(LuaStack);

function ActiveZonesStackClass:Pop()

    assert(self:GetTop() == game_state.active_zone)
    LuaStack.Pop(self);
end

active_zones = ActiveZonesStackClass:New();

--------------------------------------------------------------------------------
function DefaultOnSceneLoad(path, level_mode, parent_scene)
    game_state = {}
    game_state.custom_cursors = {}
    game_state.locked_objects = {}
    game_state.tool         = nil
    game_state.tool_objects = {}
    game_state.locker       = ReferenceCounter()
    game_state.fly_tools    = ReferenceCounter();
    game_state.state        = GetGameManager():GetCurrPlayer().m_gameState
    game_state.is_hard_mode = HasGameState('HARD_PLAY_MODE');
    game_state.level_mode   = level_mode
    game_state.parent_scene = parent_scene
    game_state.scene_path   = path
    game_state.is_inventory_opened = false
    game_state.comics = nil
    game_state.linked_scenes = {}
    
    game_state.ignore_on_highlight  = {}
    game_state.ignore_get_tool      = {}
    game_state.ignore_on_hinting    = {}
    game_state.blocked_on_full_inventory = {}
    
    game_state.quest_objects = {}
    game_state.cursor_to_restore = 'CursorDefault'
    
    game_state.wrong_apply_dialogs = {}
    
    DragObject    = nil;
    SetCursor('CursorDefault');

	local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom_border_horizontal.png',0,0,674,554,1,0)
	anim:SetHotSpot(335,293)
	anim:SetBlendMode(2)
	anim:SetColor('0x00FFFFFF')
	play_field:CreateObject('zoom_border_across',anim,512,384,205,0,1,1)
	play_field:GetObject('zoom_border_across'):SetScale(1.)
	--play_field:GetObject('zoom_border_across'):SetVisible(false)
	
	--[[
	local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom_border_vertical.png',0,0,516,715,1,0)
	anim:SetHotSpot(257,375)
	anim:SetBlendMode(2)
	anim:SetColor('0x00FFFFFF')
	play_field:CreateObject('zoom_border_upright',anim,512,384,205,0,1,1)
	play_field:GetObject('zoom_border_upright'):SetScale(1.)
	--]]
	
	local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom_border_vertical.png',0,0,464,644,1,0)
	anim:SetHotSpot(232,337)
	anim:SetBlendMode(2)
	anim:SetColor('0x00FFFFFF')
	play_field:CreateObject('zoom_border_upright',anim,512,384,205,0,1,1)
	play_field:GetObject('zoom_border_upright'):SetScale(1.)
	
	local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom_glass_anim.png',0,0,78,78,12,12)
	anim:SetHotSpot(39,39)
	anim:SetBlendMode(2)
	anim:SetColor('0x00FFFFFF')
	play_field:CreateObject('zoom_glass_anim',anim,413,368,206,0,1,1)
	
	--[[
	local anim = CreateAnimation('data/Texture/GUI/Zoom/button_back.png',0,0,81,93,1,0)
	anim:SetHotSpot(40,46)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	play_field:CreateObject('zoom_button_back',anim,-500,-500,499,0,1,1)
	play_field:GetObject('zoom_button_back'):SetMaxOpacity(0.01)
	--]]
	
	local anim = CreateAnimation('data/Texture/Scenes/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	play_field:CreateObject('comics_button_back',anim,920,28,499,0,23,7)
	play_field:GetObject('comics_button_back'):SetVisible(false);
	
	local anim = CreateAnimation('data/Texture/GUI/Square_dialog_back.png',0,0,618,474,1,0)
	anim:SetHotSpot(309, 237)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	play_field:CreateObject('zoom_info_back',anim,512,350,300,0,1,1)
	play_field:GetObject('zoom_info_back'):SetOpacity(0.)
	
	local anim = CreateAnimation('data/Texture/GUI/Buttons/ok.png',0,0,141,55,1,0)
	anim:SetHotSpot(70,27)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	play_field:CreateObject('zoom_info_button',anim,512,570,310,0,1,1)
	play_field:GetObject('zoom_info_button'):SetOpacity(0.)

    local anim = CreateAnimation('data/Texture/Scenes/Common/fade.png',0,0,512,384,1,0)
    anim:SetHotSpot(256,192)
    anim:SetBlendMode(2)
    anim:SetColor('0xFFFFFFFF')
    play_field:CreateObject('zoom_fade',anim,512,384,200,0,2,2)
    play_field:GetObject('zoom_fade'):SetOpacity(0.0)
    play_field:IgnoreOnTestPoint('zoom_fade')
    
    local anim = CreateAnimation('data/Texture/Scenes/Common/fade.png',0,0,512,384,1,0)
	anim:SetHotSpot(256,192)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	play_field:CreateObject('fade_in_on_load',anim,512,384,1000,0,2,2)
	play_field:IgnoreOnTestPoint('fade_in_on_load')
	play_field:GetObject('fade_in_on_load'):ChangeOpacity(1., 0., 0.5)

	local anim = CreateAnimation('data/Texture/Effect/ObjectHighlight.png',0,0,47,47,1,0)
	--local anim = CreateAnimation('data/Texture/Effect/particles.png',0,32,32,32,1,0)
	anim:SetHotSpot(23.5,23.5)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	play_field:CreateObject('object_highlight',anim,512,384,140,0,1,1)
    play_field:GetObject('object_highlight'):SetOpacity(0.)
    play_field:IgnoreOnTestPoint('object_highlight');
    game_state.object_highlight_delay = 2.
    
    if level_mode == "hidden_object" and HasGameState("AllSceneTaskObjectsFound", game_state.scene_path)
    then
        game_state.level_mode = "quest";
        level_mode = "quest";
    end
    
    if level_mode == 'quest' then
        local screen_bak = GetGameManager():GetCurrentScreen()
        local inventory  = play_field:GetLevelScreen():GetInventory()
        
        GetGameManager():SetCurrentScreen(inventory)
        LoadInventoryData(inventory)
        GetGameManager():SetCurrentScreen(screen_bak)
        
        play_field:GetLevelScreen():GetPanel():SetMode(PM_QUEST)
        
    elseif level_mode == 'hidden_object' then
        play_field:GetLevelScreen():GetPanel():SetMode(PM_HIDDENOBJECT)
        local state_name = GetHintTimerState(path)
        local hint_timer = 0
        
        if GameStateExists(state_name) then
            hint_timer = tonumber(GetGameState(state_name):Get(0))
        end
        play_field:GetLevelScreen():GetPanel():SetDelayTimer(hint_timer)
        
    elseif level_mode == 'mini_game' then
        play_field:GetLevelScreen():GetPanel():SetMode(PM_MINIGAME)
        
    elseif level_mode == 'map' then
        play_field:GetLevelScreen():GetPanel():SetMode(PM_MAP)
        
    elseif level_mode == 'clean' then
        play_field:GetLevelScreen():GetPanel():SetMode(PM_CLEAN)
        
    end

    RegisterTools();
    
    panel.notebook.LoadNotes();
    panel.nootebook_disable = PanelIsNotebookDisable();

    --[[
    if GameStateExists('ToolFound') then
        local rec = GetGameState('ToolFound')
        for i = 0, rec:GetSize() - 1 do
            local val = rec:Get(i)
            if not HasGameState('ToolApplied', val) then
                AddTool(val)
            end
        end
    end
    ]]

    if level_mode == 'quest'
    then    
        ReloadInventoryContent();
    end

    PanelOnSceneStart()

    if not HasGameState('SceneVisited', path) then
        AddGameState('SceneVisited', path)
    end
    if level_mode ~= 'mini_game' then
        if not HasGameState('LastVisitedScene') then
            AddGameState('LastVisitedScene', path)
        else
            SetGameState('LastVisitedScene', path)
        end
    end
    
    PlayRandomAmbientMusic();
    
    if game_state.level_mode == 'map' then
        panel.monolog_ctc_text:SetVisible(false);
        ShowMonolog('MAP_INFO', 'MAP_INFO');
    end
end

function GetHintTimerState(path)
    return "HintTimer"
    --return "HintTimer("..path..")"
end

function HighlightObject(obj_name)
    game_state.object_highlight_delay = 5;

    local obj = play_field:GetObject('object_highlight')
    local target

    if obj_name then
        target = play_field:GetObject(obj_name)
    else
        local candidates = {}
        
        for i, v in pairs(game_state.custom_cursors) do
            if v ~= nil and v ~= '' then
                table.insert(candidates, i)
            end
        end

        local candidates_copy = candidates
        candidates = {}
        for i, v in ipairs(candidates_copy) do
            if play_field:HasObject(v)
                and play_field:GetObject(v):IsVisible()
                and play_field:GetObject(v):GetOpacity() > 0.
                and game_state.ignore_on_highlight[v] ~= true
                and (not game_state.is_hard_mode or game_state.quest_objects[v] ~= true)
                and ((game_state.active_zone and game_state.active_zone.HasObject(v)) or
                    (not game_state.active_zone))
            then
                table.insert(candidates, v)
				--[[if game_state.quest_objects[v] == true then
					table.insert(candidates, v)
					table.insert(candidates, v)
				end
				--]]
            end
        end
        candidates_copy = nil

        if table.getn(candidates) == 0 then
            target = nil
        else
            local target_index = math.random(1, table.getn(candidates))
            target = play_field:GetObject(candidates[target_index])
        end
    end

    if not target then
        obj:Hide()
    else
        obj:Show()
        obj:SetXY(target:GetX(), target:GetY())
        obj:SetZ(target:GetZ() + 10)

        local effect_time = 0.9
        
        if game_state.object_highlight_effector then    
            game_state.object_highlight_effector:Destroy();
            game_state.object_highlight_effector = nil;
        end
        
        local af = CreateAnimationEffector()
        af:AttachTo( obj )
        af:SetMode(_ANIM_NOLOOP)
        af:SetKey(_TRACK_O, effect_time * 0.,   0.)
        af:SetKey(_TRACK_A, effect_time * 0.,   0.)
        af:SetKey(_TRACK_S, effect_time * 0.,   0.3)
        af:SetKey(_TRACK_O, effect_time * 0.05, 1.)
        af:SetKey(_TRACK_S, effect_time * 0.25, 1.)
        af:SetKey(_TRACK_O, effect_time * 0.45, 1.)
        af:SetKey(_TRACK_A, effect_time * 0.45, 35. * M_PI / 180.)
        af:SetKey(_TRACK_S, effect_time * 0.45, 1.4)
        af:SetKey(_TRACK_O, effect_time * 0.70, 1.)
        af:SetKey(_TRACK_A, effect_time * 1.,   50. * M_PI / 180.)
        af:SetKey(_TRACK_S, effect_time * 1.,   1.)
        af:SetKey(_TRACK_O, effect_time * 1.,   0.)
        af:Play()
        
        game_state.object_highlight_effector = af;
    end
end

function DefaultOnSceneFree()
end

function DefaultOnSceneDestroy(screen)
--[[
    if screen:GetLevelScreen():GetPanel():GetMode() == PM_HIDDENOBJECT then
        local state_name = GetNHintsStateName(screen:GetName())
        local nhints = screen:GetLevelScreen():GetPanel():GetHintNumber()
        SetGameState(state_name, tostring(nhints))
    end
    --]]
end

function IsZoomZoneNeedsScreenBottomExitZone(zone)
    return zone.biggest_object:GetAnimation():GetWidth() > 1000.
end

function DefaultOnUpdate(dt)
    local panelMode = play_field:GetLevelScreen():GetPanel():GetMode();
    
    local return_zone_check_needed = (game_state.active_zone and IsZoomZoneNeedsScreenBottomExitZone(game_state.active_zone))
                                        or panelMode == PM_HIDDENOBJECT
                                        or panelMode == PM_MINIGAME
        
    local return_main_check_needed = not game_state.active_zone
                                        and panelMode == PM_QUEST
                                        and game_state.parent_scene ~= nil
        
    if (   (return_zone_check_needed and game_state.disable_default_zoom_exit ~= true)
        or (return_main_check_needed and game_state.disabled_exit_from_main ~= true and play_field:GetTool() == '')  )
        and IsAboveReturnFromZoomZone()
    then
        if currentCursor ~= 'CursorDown' then
            game_state.cursor_to_restore = currentCursor
            SetCursor('CursorDown')
        end
    else
        if game_state.cursor_to_restore then
            SetCursor(game_state.cursor_to_restore)
            game_state.cursor_to_restore = nil
        end
    end
    
    if tutorScreen and (not game_state.forcedObject) then
        return;
    end
    
    if game_state.level_mode ~= 'hidden_object'
       -- and game_state.level_mode ~= 'mini_game'
    then
        game_state.object_highlight_delay = game_state.object_highlight_delay - dt
        if game_state.object_highlight_delay < 0. then
            HighlightObject(game_state.forcedObject)
        end
    end
end

function OnLastItemFound()
    if not HasGameState("AllSceneTaskObjectsFound", game_state.scene_path) then
        AddGameState("AllSceneTaskObjectsFound", game_state.scene_path)
    end
    
    local activeTasksState = (__EXTRAS_SCENE) and EXTRAS_MODE_PREFIX.."TaskIsActive" or "TaskIsActive"
    play_field:GetLevelScreen():RemoveTasksActiveStates(activeTasksState);
    
	if game_state.clear_task_object_found_states_on_finish and
	   game_state.created_task_objects
	then
		for i, v in ipairs(game_state.created_task_objects) do
			local state_value = game_state.scene_path.."("..v..")"
			if HasGameState("TaskObjectFound", state_value) then
				RemoveGameState("TaskObjectFound", state_value)
			end
		end
	end
    
    if game_state.tools then
        for tool_name, tool_info in pairs(game_state.tools) do
            if tool_info.trigger_name == nil and
               tool_info.find_scene == game_state.scene_path
            then
                if game_state.level_mode == 'quest' then
                    AddTool(tool_name)
                end
                if tool_info.on_find ~= nil then
                    tool_info.on_find(tool_name)
                end
                if not HasGameState('ToolFound', tool_name) then
                    AddGameState('ToolFound', tool_name)
                end
            end
        end
    end

    if game_state.level_mode == 'hidden_object' and not game_state.disable_close_scene_on_complete then
        AddGameState('ClosedScene', game_state.scene_path)
    end
    
    if game_state.parent_scene ~= 'main_menu' then
        SetGameState('LastVisitedScene', game_state.parent_scene)
    end
end

function DefaultOnAllItemsFound()
    play_field:GetLevelScreen():GetPanel():ButtonPress('Close')
end

function DefaultOnLose()
end

function DefaultOnExit()
end

function DefaultOnClose()
    if game_state.parent_scene then
        if game_state.parent_scene == 'main_menu' then
            GTimerDoString("ExitToMainMenu(true);", 0.1)
        else
            StartLevel(game_state.parent_scene)
        end
    end
end

function DefaultOnObjectMouseEnter(object)
    if game_state.tool then
        if currentCursor ~= 'CursorHand' then
            SetCursor('CursorHand');
        end
    else
        local objectName = object:GetName();
        
        if game_state.custom_cursors[objectName] ~= nil
            and game_state.custom_cursors[objectName] ~= ''
            and (not game_state.active_zone or game_state.active_zone.HasObject(objectName))
            and (not play_field.mouseDownBlocked or game_state.forcedObject == objectName)
        then
            name = game_state.custom_cursors[object:GetName()]
            if (game_state.forcedObject)
            then
                SetCursor(name, true);
            else
                SetCursor(name)
            end
        end
    end
end

function DefaultOnObjectMouseLeave(object)
    if not game_state.tool then
        SetCursor('CursorDefault', true)
    end
end

function FindTool(tool_name)
    if game_state.level_mode == 'quest' then
        AddTool(tool_name)
    end

    local tool_info = game_state.tools[tool_name]
    
    if tool_info.on_find ~= nil then
        tool_info.on_find(tool_name)
    end

    AddGameState('ToolFound', tool_name)
    
    -- game_state.ignore_add_tool - zapret dobavleniya tools v tekus4ei scene
    if game_state.level_mode == 'quest'
        and not HasGameState('ToolInfoShown', tool_name)
        and not game_state.ignore_add_tool
    then
        AddGameState('ToolInfoShown', tool_name)
        local info = CreateToolWindow(tool_name)
        info.SetVisible(true)
    end
end

function DefaultOnObjectMouseDown(object)
    
    if endDemoScreen then
        return;
    end
    
    local object_found = false
    local obj_name = object:GetName()
    
    if (play_field.mouseDownBlocked and (game_state.forcedObject == nil or game_state.forcedObject ~= obj_name))
    then
        return true;
    end
    
    if obj_name ~= 'comics_button_back' and game_state.level_mode ~= 'map' then
        UpdateComicsRequest()
    end
    
    if play_field:GetLevelScreen():GetInventory():GetToolsCount() >= 7
        and game_state.blocked_on_full_inventory[obj_name]
        and not (game_state.linked_scenes[obj_name] and HasGameState('ClosedScene', game_state.linked_scenes[obj_name].path))
    then
        ShowMonolog('C_INVENTORY_FULL', 'C_INVENTORY_FULL');
        return;
    end
    
    local has_tool = play_field:GetTool() ~= ''

    -- tool finding
    local success      = false
    local trigger_name = nil
    
    if game_state.locked_objects[obj_name] ~= true and
       game_state.tools and
       not has_tool
    then
        for tool_name, tool_info in pairs(game_state.tools) do
            if tool_info.find_scene == game_state.scene_path
            then
                trigger_name = tool_info.trigger_name
                local action_triggered = obj_name == tool_info.trigger_name
                
                if not action_triggered and type(tool_info.trigger_name) == 'table'
                then
                    for i, v in ipairs(tool_info.trigger_name) do
                        if obj_name == v then
                            table.remove(tool_info.trigger_name, i)
                            if table.getn(tool_info.trigger_name) == 0 then
                                trigger_name = v
                                action_triggered = true
                            end
                        end
                    end
                end
                
                if action_triggered and not success then
                    local objects_zone = nil
                    if game_state.zones ~= nil then
                        for i, zone in ipairs(game_state.zones) do
                            if zone.HasObject(trigger_name) then
                                objects_zone = zone
                            end
                        end
                    end

                    if (game_state.locker:GetNReferences() == 0 and objects_zone == nil) or
                       (objects_zone ~= nil and objects_zone.IsVisible())
                    then
                        success = true

                        if objects_zone ~= nil then
                            objects_zone.SetObjectVisible(trigger_name, false)
                            objects_zone.SetVisible(false)
                       --     game_state.custom_cursors[objects_zone.trigger_name] = ''
                       --     game_state.locked_objects[objects_zone.trigger_name] = true
                        end

                        if game_state.active_zone and game_state.active_zone.HasObject(object:GetName()) then
                            game_state.active_zone.RemoveObject(object:GetName())
                        end
                        
                        object:ChangeOpacity(1., 0., 0.1)
                        
                        FindTool(tool_name);
                        object_found = true
                    end
                end
            end
        end
    end
    
    -- click on target tool area
    ---[[
    if game_state.tools ~= nil
        and game_state.locked_objects[obj_name] ~= true
        and (game_state.locker:GetNReferences() == 0 or (game_state.active_zone and game_state.active_zone.HasObject(obj_name)))
        and not has_tool
    then
        for tool_name, info in pairs(game_state.tools) do
            local is_target = false

            if info and info.target_name then
                if type(info.target_name) == 'string' then
                    is_target = (info.target_name == obj_name)
                else
                    for i, v in pairs(info.target_name) do
                        is_target = (v == obj_name)
                        
                        if is_target then
                            break
                        end
                    end
                end
            end
            
            if is_target then
            
                local state_value = tool_name.."("..game_state.scene_path.."\\"..obj_name..")"
                
                if not HasGameState('ToolTargetClicked', state_value)
                then
                    AddGameState('ToolTargetClicked', state_value)
                    game_state.ignore_on_highlight[obj_name] = true;
                end
                
                if info.on_click then
                    info.on_click(tool_name, obj_name)
                end
            end
        end
    end
    --]]

    -- active objects
    local state_value = game_state.scene_path..'('..obj_name..')'
    
    if game_state.active_objects
        and game_state.active_objects[obj_name]
        and game_state.locked_objects[obj_name] ~= true
        and (game_state.locker:GetNReferences() == 0 or (game_state.active_zone and game_state.active_zone.HasObject(obj_name)))
        and not has_tool
        and (not HasGameState('ObjectClicked', state_value) or game_state.active_objects[obj_name].multi_use == true)
    then
        if not HasGameState('ObjectClicked', state_value) then
            AddGameState('ObjectClicked', state_value)
            
            game_state.ignore_on_highlight[obj_name] = true;
        end
        
        local callback = game_state.active_objects[obj_name].on_click;

        if not game_state.active_objects[obj_name].multi_use == true
        then
            game_state.locked_objects[obj_name] = true
            game_state.custom_cursors[obj_name] = ''
            --TODO: delete next line
            play_field:IgnoreOnTestPoint(obj_name)
            game_state.active_objects[obj_name] = nil
        end
        
        callback(obj_name)
        object_found = true
    end
    
    -- task objects
    if not object_found
        and game_state.locked_objects[obj_name] ~= true
        and play_field:GetLevelScreen():IsTaskObject(obj_name)
        and (game_state.locker:GetNReferences() == 0 or game_state.active_zone.HasObject(obj_name))
        and not has_tool
    then
        local is_in_zone =
            game_state.active_zone ~= nil and game_state.active_zone.HasObject(obj_name)
            
        if (is_in_zone and game_state.locker:GetNReferences() == 1) or
           (not is_in_zone and game_state.locker:GetNReferences() == 0)
        then
            play_field:OnObjectFound(object)
            play_field:IgnoreOnTestPoint(obj_name)
            object_found = true
        end

        if game_state.tool_objects[object:GetName()] then
            -- save xml data
        end
    end
    
    -- linked scenes
    local linked_scene_info = game_state.linked_scenes[obj_name]
    
    if not object_found
        and game_state.locked_objects[obj_name] ~= true
        and linked_scene_info
        and (game_state.locker:GetNReferences() == 0 or (game_state.active_zone and game_state.active_zone.HasObject(obj_name)))
        and not has_tool
    then
        local is_closed  = HasGameState('ClosedScene', linked_scene_info.path)
        local is_visited = HasGameState('SceneVisited', linked_scene_info.path)
        
        if linked_scene_info.condition then
            if linked_scene_info.condition() then
                if not is_closed then
                    StartLevel(linked_scene_info.path)
                elseif is_visited and linked_scene_info.on_scene_visited then
                    linked_scene_info.on_scene_visited();
                end
            else
                if linked_scene_info.on_condition_fail then 
                    linked_scene_info.on_condition_fail()
                end
            end
        else
            if not is_closed then
                object_found = true
                StartLevel(linked_scene_info.path)
            elseif is_visited and linked_scene_info.on_scene_visited then
                linked_scene_info.on_scene_visited();
            end
        end
    end

    local zone_opened = false
    if not object_found
        and (game_state.locker:GetNReferences() == 0 or (game_state.active_zone and game_state.active_zone.HasObject(obj_name)))
        and game_state.locked_objects[obj_name] ~= true
        and game_state.zones ~= nil
        and not has_tool
    then
        for i, zone in pairs(game_state.zones) do
            if zone.trigger_name == obj_name then
                zone.SetVisible(true)
                zone_opened = true
				object_found = true
            end
        end
    end

    local panelMode = play_field:GetLevelScreen():GetPanel():GetMode();
    
    -- test for return zone of opened zoom zone click
    if game_state.active_zone
        and not zone_opened
        and IsZoomZoneNeedsScreenBottomExitZone(game_state.active_zone)
        and IsAboveReturnFromZoomZone()
    then
        game_state.active_zone.SetVisible(false)
        SetCursor('CursorDefault')
        object_found = true
    end
    
    if panelMode == PM_MINIGAME and
       IsAboveReturnFromZoomZone()
    then
        SetCursor('CursorDefault')
        play_field:GetLevelScreen():GetPanel():ButtonPress('Close')
        object_found = true
    end
    
    if panelMode == PM_QUEST
        and not game_state.active_zone
        and IsAboveReturnFromZoomZone()
        and game_state.parent_scene ~= nil
        and game_state.disabled_exit_from_main ~= true
    then
        SetCursor('CursorDefault')
        object_found = true
        
        if game_state.parent_scene == 'main_menu' then
            GTimerDoString("ExitToMainMenu(true);", 0.1)
        else
            StartLevel(game_state.parent_scene)
        end
    end
    
    if object_found and game_state.forcedObject and tutorScreen
    then
        tutorScreen:Hide();
    end

    return object_found
end

function DefaultOnObjectMouseDownProcessed(is_wrong_click)
    
    if is_wrong_click and (game_state.level_mode == 'hidden_object' or game_state.force_fast_click_punishment) then
        play_field:ShowWrongObjectEffect();
        PlaySound("Wrong_click");
        
        if (play_field.wrongClicksCount)
        then
            local curTime = APP:GetCurrentTime();
            if (curTime - play_field.lastClickTime <=
                    CLICK_TIME_TO_PUNISH / CLICKS_COUNT_TO_PUNISH)
            then
                play_field.wrongClicksCount = play_field.wrongClicksCount + 1;
                if (play_field.wrongClicksCount >= CLICKS_COUNT_TO_PUNISH)
                then
                    PunishClicking();
                    play_field.wrongClicksCount = 0;
                end
            else
                play_field.wrongClicksCount = 1;
            end
        else
            play_field.wrongClicksCount = 1;
        end
    else
        play_field.wrongClicksCount = 0;
    end
    
    play_field.lastClickTime = APP:GetCurrentTime();
    
    --[[
    if is_wrong_click and game_state.level_mode == 'hidden_object' then
        play_field:ShowWrongObjectEffect();
        PlaySound("Wrong_click");
    end
    --]]
end

function DefaultOnObjectMouseUp(object)
    if game_state.linked_scenes then
        for i, v in pairs(game_state.linked_scenes) do
            local is_closed               = HasGameState('ClosedScene', v.path)
            local is_allObjectsFound      = HasGameState("AllSceneTaskObjectsFound", v.path)
            local is_active_after_visited = HasGameState('SceneVisited', v.path) and v.on_scene_visited;
            
            local mark_zone = (not is_closed or is_active_after_visited) and (not v.condition or v.condition())
            
            if is_allObjectsFound then
                v.mark_with_ps = false
            end
            
            if mark_zone then
                if is_closed and is_active_after_visited then
                    MarkLinkZone(i, v.cursor_name, true, false)
                else
                    MarkLinkZone(i, v.cursor_name, true, v.mark_with_ps)
                end
            else
                if (v.condition ~= nil and not v.condition()) then
                    MarkLinkZone(i, v.cursor_name, true, false)
                else
                    MarkLinkZone(i, v.cursor_name, false, v.mark_with_ps)
                end
            end
        end
    end
end

function DefaultOnObjectFound(object)
    
    local state_value = game_state.scene_path.."("..object:GetName()..")"
    assert(not HasGameState("TaskObjectFound", state_value))
    AddGameState("TaskObjectFound", state_value)
    
    PlaySound("Item_found");

end

function DefaultOnToolFound(tool)

end

function DefaultOnToolApply(tool_name, object)
    --game_state.tool = nil
    local tool_applied = false
    
    if (play_field.mouseDownBlocked and (game_state.forcedObject == nil or game_state.forcedObject ~= obj_name))
    then
        return;
    end

    if object and game_state.tools ~= nil then
        for name, info in pairs(game_state.tools) do
            local is_target = false
            local target_table_index

            if info and info.target_name then
                if type(info.target_name) == 'string' then
                    is_target = info.target_name == object:GetName()
                else
                    for i, v in pairs(info.target_name) do
                        is_target = v == object:GetName()
                        target_table_index = i
                        if is_target then
                            break
                        end
                    end
                end
            end

            if not tool_applied and
               tool_name == name and
               is_target
            then
                game_state.custom_cursors[object:GetName()] = ''
                if info.on_apply and type(info.target_name) ~= 'string' then
                    info.on_apply(tool_name, object:GetName())
                end
                if type(info.target_name) == 'table' then
                    assert(not HasGameState('ToolTargetUsed', object:GetName()))
                    AddGameState('ToolTargetUsed', object:GetName())
                    table.remove(info.target_name, target_table_index)
                end
                if type(info.target_name) == 'string' or table.getn(info.target_name) == 0 then
                    if info.on_apply then
                        info.on_apply(tool_name)
                    end
                    
                    if play_field:GetTool() ~= '' then
                        play_field:SetTool('')
                    end
                    
                    -- Astahov: добавил if, потому что если тулза уже применена через CustomToolApply, то
                    -- в следующем блоке приложение падало, поскольку ее в инвентаре уже нет.
                    if not HasGameState('ToolApplied', tool_name) then
                        InventoryOnToolApplied(tool_name);
                        game_state.tool = nil
                        local inv = play_field:GetLevelScreen():GetInventory()
                        inv:GetToolInfo(tool_name).m_tool:ChangeOpacity(1., 0., 0.2)
                        inv:RemoveTool(tool_name)
--                      assert(not HasGameState('ToolApplied', tool_name)) -- больше не нужна, поскольку находится под соотв. if'ом
                        AddGameState('ToolApplied', tool_name);
                    end
                    --OnInventoryButtonPress()
                end
                tool_applied = true
            end
        end
    end

	if not tool_applied then
	    local obj_name = object:GetName()
		local zoom_opened = false
		if game_state.locker:GetNReferences() == 0 or (game_state.active_zone and game_state.active_zone.HasObject(obj_name))
            and game_state.locked_objects[obj_name] ~= true
            and game_state.zones ~= nil
		then
			if game_state.zones then
				for i, zone in ipairs(game_state.zones) do
					if zone.trigger_name == obj_name then
					    
					     if play_field:GetLevelScreen():GetInventory():GetToolsCount() >= 7
                            and game_state.blocked_on_full_inventory[obj_name]
                        then
                            ShowMonolog('C_INVENTORY_FULL', 'C_INVENTORY_FULL');
                            return;
                        else
                            zone.SetVisible(true)
                            zoom_opened = true
                        end
                        
						break
					end
				end
			end
		end
		if not zoom_opened then
			local is_target_for_some_tool = false
			for i, v in pairs(game_state.tools) do
				if v.target_name then
					if type(v.target_name) == 'string' then
						if v.target_name == obj_name then
							is_target_for_some_tool = true
							break
						end
					else
						for i, v in pairs(v.target_name) do
							if v == obj_name then
								is_target_for_some_tool = true
								break
							end
						end
					end
				end
				if is_target_for_some_tool then
					break
				end
			end

			if object and is_target_for_some_tool then
			    local wrongApplyDialog = GetWrongApplyDialog(tool_name, object:GetName());
			    
			    if wrongApplyDialog then
			        ShowMonolog(wrongApplyDialog, wrongApplyDialog);
			    else
				    ShowMonolog('ToolDenyMonolog', 'C_TOOL_DENY_'..tostring(math.random(1, 2)));
			    end
			end

			DropTool();
			SetCursor('CursorDefault');
		end
	end
	
	if tool_applied and game_state.forcedObject and tutorScreen
    then
        tutorScreen:Hide();
    end
end

function DefaultOnObjectReachPanel(object)
end

function DefaultOnButtonPress(title)
    
    if (play_field.mouseDownBlocked and (game_state.forcedObject == nil or game_state.forcedObject ~= title))
    then
        return;
    end
    
    if game_state.zones ~= nil then
        for i, zone in ipairs(game_state.zones) do
            if zone.close_button_message == title and zone.IsVisible() then
                zone.SetVisible(false);
                PlaySound('Menu_button_click');
            end
        end
    end
end

function DefaultOnMouseMove(previous_pos, current_pos)
end

function DefaultOnSetTool(tool)
end

function DefaultOnMiniGameWin()
    --assert(game_state.mini_game ~= nil)
    if game_state.mini_game ~= nil then
        dofile(game_state.level_path)
        --ReleaseFocus(game_state.mini_game)
        game_state.mini_game:Destroy()
        game_state.mini_game = nil
        play_field:GetLevelScreen():Go()
        play_field:GetLevelScreen():ScreenShow()
        --SetFocusTo(play_field:GetLevelScreen())
    end
end

function DefaultOnIntroFinish()
    if game_state.intro ~= nil then
        --local timer = play_field:GetLevelScreen():GetTimer()
        dofile(game_state.level_path)
        --game_state.intro:SetOpacity(0.7)
        GTimerDoString('game_state.intro:ScreenDestroy()', 0.5)
    end
end

function TimerDo(timer, delay, fun, ...)
    assert(fun)
    local tag = FunctionDispatcher.AddFunction(fun, ...)
    TimerDoString('FunctionDispatcher.Call('..
                  tag..
                  ')',
                  delay,
                  timer)
    return tag
end

