function GetCursorZoom()
    return 'CursorZoom'
end

function GetCursorPhoto()
    return 'CursorArrow';--'CursorPhoto';
end

function GetCursorMech()
    return 'CursorArms';--'CursorMech';
end

function GetCursorArrow()
    return 'CursorArrow'
end

function GetCursorArms()
    return 'CursorArms'
end

function CreateEmptyTask(text_singular, text_plural, name_obj)
    local anim = CreateAnimation('data/Texture/Scenes/Common/pixel.jpg',0,0,8,8,1,0)
    anim:SetHotSpot(4,4)
    anim:SetBlendMode(2)
    anim:SetColor('0xFFFFFFFF')
    local obj = play_field:CreateObject(name_obj,anim,512,384,1000,0,1,1)
    obj:SetZ(-1)
    obj:GetAnimation():SetColor('0x00FFFFFF')
    play_field:IgnoreOnTestPoint(name_obj)

    return CreateTask(text_singular, text_plural, name_obj)
end

function CreateTask(text_singular, text_plural, ...)
    local arg = {...}
    local task = nil
    task = Task(text_singular, text_plural)

    for i, v in ipairs(arg) do
        if play_field:HasObject(v) then
            local state_value = game_state.scene_path.."("..v..")"
            
            task:AddObject(v)
            if HasGameState("TaskObjectFound", state_value) then
                play_field:GetObject(v):SetOpacity(0)
                play_field:GetObject(v):SetMaxOpacity(0)
                task:OnObjectFound(v)
                task:OnObjectFindAnimationFinished(v)
            end
        end
    end

    assert(not play_field:GetLevelScreen():HasTask(task))

    return task
end

function LevelScreen:AddTaskSafe(task)
    self:AddTask(task)
end

function AddRandomTasks(number_to_select, ...)
    local arg = {...}
    local level_screen = play_field:GetLevelScreen()
    local AlreadyAdded = false
    
    for i, v in ipairs(arg) do
        local state_value = game_state.scene_path.."("..v:GetTextSingular()..")"
        
        if HasGameState('ActiveRandomTaskName', state_value) then
            level_screen:AddTask(v)
            AlreadyAdded = true
        end
    end
    
    if not AlreadyAdded then
        local random_objects = {}
        
        for i, v in ipairs(arg) do
            if v:GetNumberOfObjects() > 0 and HasGameState("UseItem", v:GetTextSingular()) == false then
                table.insert(random_objects, v)
            end
        end

        if debug_localization then
	        number_to_select = 10000
	    end
	    
	    if GetGameApp():AreCheatsEnabled() then
	        local curr_player = GetGameManager():GetCurrPlayer()
	        if curr_player and curr_player:GetName() == 'ChEaTeR93' then
	            number_to_select = 10000    
	        end
	    end
	    
	    -- Обработка недостачи
	    if table.getn(random_objects) < number_to_select then
	        local lack = number_to_select - table.getn(random_objects)
	        for i, v in ipairs(arg) do
	            if v:GetNumberOfObjects() > 0 and HasGameState("UseItem", v:GetTextSingular()) == true then
                    table.insert(random_objects, v)
                    lack = lack - 1
                    if lack <= 0 then
                        break;
                    end -- if lack <= 0 then
                end
	        end -- for i, v in ipairs(arg) do
	    end -- if table.getn(random_objects) < number_to_select then

        RandomTableSort(random_objects)

        if table.getn(random_objects) < number_to_select then
            number_to_select = table.getn(random_objects);
        end
        
        for i = 1, number_to_select do
            level_screen:AddTask(random_objects[i])
            
            local state_value = game_state.scene_path.."("..random_objects[i]:GetTextSingular()..")"
            AddGameState('ActiveRandomTaskName', state_value)
            
            -- Save use item
            local text_singular = random_objects[i]:GetTextSingular()
            if not HasGameState("UseItem", text_singular) then
                AddGameState("UseItem", text_singular)
            end
        end
    end
end

function CreateTool(tool, photo)
    local tool_data = {}
    tool_data.tool = tool
    tool_data.photo = photo

    photo:SetVisible(false)
end

function SetCursor(cursor_name)
    if cursor_name == GetCursorZoom() then
        local ty = 0
    end        

    ---- immediate cursor change ----
    GetGameApp():SetCustomCursor(cursor_name)
end

function DefaultOnObjectMouseEnter(object)
    local object_name = object:GetName()

	if custom_cursors[object_name] ~= nil and
       custom_cursors[object_name] ~= '' and
       (play_field:GetLevelScreen():IsTaskObject(object:GetName()) or
        custom_cursors[object_name] ~= GetCursorPhoto()
       )
    then
	    name = custom_cursors[object:GetName()]
	    SetCursor(name)
	end
end

function DefaultOnObjectMouseLeave(object)
    SetCursor('CursorArrow')
end

function IsPhotoObjects(object)
    if game_state.photo_objects ~= nil then
        for i, photo_info in ipairs(game_state.photo_objects) do
            if photo_info.obj_name == object:GetName() then
                return true;
            end -- if photo_info == object then
        end
    end
    
    return false;
end

function DefaultOnObjectMouseDown(object)
    local object_name = object:GetName()
    local is_wrong_click = true
    local pf = play_field
    local locked_objects = game_state.locked_objects
    
    if locked_objects[object_name] ~= true then
        --------------------------------------------------------------------
        -- Обработка нажатия объектов
        if game_state.locker:GetNReferences() == 0 then
            if pf:GetLevelScreen():IsTaskObject(object_name) and IsPhotoObjects(object) == false then
                pf:OnObjectFound(object)
                is_wrong_click = false
            end
        end
        
        --------------------------------------------------------------------
        -- Обработка zoom зоны на открытие
        if game_state.zones and
           game_state.locker:GetNReferences() == 0
        then
	        for i, info in ipairs(game_state.zones) do
		        if object_name == info.trigger_name then
                    info.zone.SetVisible(true);
	    		    is_wrong_click = false;
		        end
	        end -- for i, info in ipairs(game_state.zones) do
	    end
	    
	    --------------------------------------------------------------------
        -- Обработка zoom зоны на закрытие
        if game_state.zones and game_state.locker:GetNReferences() > 0 then
	        for i, info in ipairs(game_state.zones) do
		        if  object_name == info.zone.back_object:GetName() and
                    info.zone:IsVisible()
                then
                    info.zone.SetVisible(false)
	    		    is_wrong_click = false
		        end
	        end -- for i, info in ipairs(game_state.zones) do
	    end
	    
	    --------------------------------------------------------------------
        -- Обработка info item зоны на закрытие
        if game_state.info_item_zone and game_state.locker:GetNReferences() > 0 then
	        for i, info in ipairs(game_state.info_item_zone) do
		        if object_name == info.zone.back_object:GetName() then
	    		    is_wrong_click = false;
	    		    break;
		        end
		        for obj, present in pairs(info.zone.objects) do
		            if obj == object_name then
		                is_wrong_click = false;
	    		        break;
		            end
		        end
	        end -- for i, info in ipairs(game_state.info_item_zone) do
	    end
        
    end; -- if locked_objects[object_name] ~= true then
    
    --------------------------------------------------------------------
    -- Проверка на фото-зону для комиксов
    if game_state.photo_objects ~= nil then
        for i, photo_info in ipairs(game_state.photo_objects) do
            if photo_info.tool_target then
                for k, target in ipairs(photo_info.tool_target) do
                    if target == object_name and custom_cursors[target] == GetCursorMech() then
                        if photo_info.hint_comics_name then
                            PlayRegisterComics( photo_info.hint_comics_name )
                            is_wrong_click = false
                            break;
                        end
                        if photo_info.hint_comics then
                            photo_info.hint_comics:Play()
                            is_wrong_click = false
                            break
                        end
                    end
                end
            end
        end -- for i, photo_info in ipairs(game_state.photo_objects) do
    end -- if game_state.photo_objects ~= nil then
    
    --------------------------------------------------------------------
    -- Проверка на зону для фотографирования
    if game_state.photo_objects ~= nil then
        local success = false;
            
        for i, photo_info in ipairs(game_state.photo_objects) do
            if  photo_info.obj_name and photo_info.obj_name ~= '' then
                local obj_photo = pf:GetObject(photo_info.obj_name)
                if pf:HasObject(photo_info.obj_name) and
                   obj_photo:GetOpacity() == 1. and
                   object_name == photo_info.obj_name and
                   pf:GetLevelScreen():IsTaskObject(photo_info.obj_name) and
                   IsCanMakePhoto(photo_info.zone) and
                   not success then
                        local dist = Vector(GetMousePosX() - object:GetX(), GetMousePosY() - object:GetY())
                        if dist:Length() < 150. then
                            local objects_zone = nil
                            if game_state.zones ~= nil then
                                for i, info in ipairs(game_state.zones) do
                                    if info.zone.HasObject(photo_info.obj_name) then
                                        objects_zone = info
                                    end
                                end -- for i, info in ipairs(game_state.zones) do
                            end -- if game_state.zones ~= nil then
                        end -- if dist:Length() < 150. then
                        
                        -- Обработка этапа фотографирования
                        if objects_zone == nil then
                            success = true
                            local phone = pf:GetLevelScreen():GetPhone()
                            phone:MakePhoto( pf:GetObject(photo_info.obj_name) )
                        end -- if objects_zone == nil then
                        
                        is_wrong_click = false
                        return is_wrong_click
                end -- if
            end -- 
        end -- for i, photo_info in ipairs(game_state.photo_objects) do
    end -- if game_state.photo_objects ~= nil then
    
    if game_state.zones and locked_objects[object_name] ~= true then
	    for i, v in ipairs(game_state.zones) do
		    if v.zone.HasObject(object:GetName()) and
               pf:GetLevelScreen():IsTaskObject(object_name) and
               game_state.locker:GetNReferences() ~= 0
            then
    		    v.zone.RemoveObject(object_name, false)
				pf:OnObjectFound(object)
	    		is_wrong_click = false
		    end
	    end
    end
    
    -- active objects
    local state_value = '';
    if game_state.scene_path then
        state_value = game_state.scene_path..'('..obj_name..')'
    end            
    
    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 HasGameState('ObjectClicked', state_value) or game_state.active_objects[obj_name].multi_use)
    then
        if not HasGameState('ObjectClicked', state_value) then
            AddGameState('ObjectClicked', state_value)
        end
    
        local callback = game_state.active_objects[obj_name].on_click;
        if game_state.active_objects[obj_name].multi_use == false then
            play_field:IgnoreOnTestPoint(obj_name);
            game_state.active_objects[obj_name] = nil;             
        end
    
        callback();
        is_wrong_click = false;
    end
    
    if is_wrong_click then
        PlaySound('wrong_click')
    end

    return is_wrong_click;
end

function DefaultOnPhotographing()
    local pf = play_field;
    local ls = play_field:GetLevelScreen()
    local phone = ls:GetPhone();
    local success = false;
    local photo_info_buffer = nil;
    
    if game_state.photo_objects ~= nil then
        for i, photo_info in ipairs(game_state.photo_objects) do
            if photo_info.obj_name then
                local obj_photo = pf:GetObject(photo_info.obj_name);
                local obj_photo_opacity = obj_photo:GetOpacity();
                if obj_photo:GetOpacity() == 0. or
                   pf:HasObject(photo_info.obj_name) == false or
                   pf:GetLevelScreen():IsTaskObject(photo_info.obj_name) == false
                then
                else
                    if pf:GetLevelScreen():IsTaskObject(photo_info.obj_name) and
                       custom_cursors[photo_info.obj_name] == GetCursorPhoto()
                    then
                        local obj = pf:GetObject(photo_info.obj_name)
                        local photo_Object = phone:GetPhotoObject();
                        if photo_Object and photo_Object:GetName() == obj:GetName() then
                            photo_info_buffer = photo_info;
                            success = true;
                        end 
                    end
                end
            end
        end -- for i, photo_info in ipairs(game_state.photo_objects) do
        
        if photo_info_buffer then
            local objects_zone = nil;
            if game_state.zones ~= nil then
                for i, info in ipairs(game_state.zones) do
                    if info.zone.HasObject(photo_info_buffer.obj_name) then
                        objects_zone = info
                    end
                end
            end

            if objects_zone == nil or objects_zone.zone.IsVisible() then
                success = true;

                if objects_zone ~= nil then
                    objects_zone.zone.RemoveObject(photo_info_buffer.obj_name)
                end

                RemoveFromTask(photo_info_buffer.obj_name)
                if photo_info_buffer.photo_action ~= nil then
                    photo_info_buffer.photo_action()
--                  photo_info_buffer.photo = true
                end

                photo_info_buffer.photo = true
                pf:OnToolFound(pf:GetObject(photo_info_buffer.tool_name), pf:GetObject(photo_info_buffer.photo_name))
                if photo_info_buffer.tool_name ~= photo_info_buffer.obj_name then
                    pf:GetObject(photo_info_buffer.obj_name):SetOpacity(0.)
                end
                local state_value = game_state.scene_path.."("..photo_info_buffer.tool_name..")"
                AddGameState('OnToolFound', state_value)
                
                PlaySound('photo');
				TryToDisableZoomZonesIfPossible()
            end -- if objects_zone == nil or objects_zone.zone.IsVisible() then
        end -- if photo_info_buffer then
    end -- if game_state.photo_objects ~= nil then
end

function DefaultOnToolApply(tool, object)
    local tool_applied = false
    local no_right_tool = false

    if game_state.photo_objects ~= nil then
        for i, photo_info in ipairs(game_state.photo_objects) do
            if photo_info.tool_target then
                for k, target in ipairs(photo_info.tool_target) do
                    if photo_info.tool_name == tool:GetName() and
                       target == object:GetName() and
                       custom_cursors[target] == GetCursorMech()
                    then
                        -- save tool apply
                        local state_value_apply = 'OnToolApply__'..game_state.scene_path
						if photo_info.obj_name then
							state_value_apply = state_value_apply..'('..photo_info.obj_name..')'
						end
                        AddGameState(state_value_apply, object:GetName())
                        
                        custom_cursors[object:GetName()] = ''
                        if photo_info.apply_action ~= nil then
                            photo_info.zone.ClearHighlight(object:GetName())
                            photo_info.tool_target = nil
                            photo_info.apply_action(false, object:GetName())
                            SetCursor( GetCursorArrow() )
                        end
                        
                        if photo_info.remove_immediately then
                            UnregisterPhotoZone(photo_info.obj_name, i)
                        end                    
                        tool_applied = true
                        break
                    end
                    
                    if photo_info.tool_name ~= tool:GetName() and
                       target == object:GetName() and
                       custom_cursors[target] == GetCursorMech()
                    then
                        no_right_tool = true
                    end
                end
            end -- if photo_info.tool_target then
        end -- for i, photo_info in ipairs(game_state.photo_objects) do
    end
    
    if not tool_applied then
        local object_name = object:GetName();   
        if game_state.zones and
           game_state.locker:GetNReferences() == 0 and
           locked_objects[object_name] ~= true
        then
	        for i, info in ipairs(game_state.zones) do
		        if object_name == info.trigger_name then
                    info.zone.SetVisible(true)
                    tool_applied = true
                    
                    -- ///////////////////////////////////////////////////////
                    -- Обновляем положение Tools
                    local tool_current = play_field:GetTool()
                    tool_current:SetZ( info.zone.close_button:GetScrPosZ() + 10 )
		        end
	        end -- for i, info in ipairs(game_state.zones) do
	    end
    end
    
    if not tool_applied then
        play_field:SetTool(nil)
        if no_right_tool then
            TimerDo(play_field:GetTimer(), 0.3, function()
                                                    game_state.chain_no_right_tool:Play()
                                                end )
        end
    end
    
    return tool_applied
end

function CGameObject:Move(dx, dy, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_X, 0.0, self:GetX())
    af:SetKey(_TRACK_Y, 0.0, self:GetY())
    af:SetKey(_TRACK_X, time, self:GetX() + dx)
    af:SetKey(_TRACK_Y, time, self:GetY() + dy)
    af:Play()
end

function CGameObject:MoveTo(x, y, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_X, 0.0, self:GetX())
    af:SetKey(_TRACK_Y, 0.0, self:GetY())
    af:SetKey(_TRACK_X, time, x)
    af:SetKey(_TRACK_Y, time, y)
    af:Play()
end

function CGameObject:Rotate(dangle, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_A, 0.0, self:GetAngle())
    af:SetKey(_TRACK_A, time, self:GetAngle() + dangle)
    af:Play()
end

function CGameObject:Scale(dscale, time)
    local af = CreateAnimationEffector()--AnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_S, 0.0, self:GetAngle())
    af:SetKey(_TRACK_S, time, self:GetAngle() + dscale)
    af:Play()
end

function CGameObject:ChangeOpacity(start, finish, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_O, 0.0, start)
    af:SetKey(_TRACK_O, time, finish)
    af:Play()
end

function CGameObject:MarkWithPS()
    self:AddParticleSystem('data/ParticleSystem/ObjectHighlight.psi', 'SolveLaterObjectParticle')
end

function PlayField:GetObject(name)
--[[
    if name == 'layer_gate_open' then
        local ty = 0
    end
]]
    return play_field:GetObject_2(name)
end

function CTextEntity:ChangeOpacity(start, finish, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_O, 0.0, start)
    af:SetKey(_TRACK_O, time, finish)
    af:Play()
end

function CompositionSequence:ChangeOpacity(start, finish, time)
    local af = CreateAnimationEffector()
    af:AttachTo(self)
    af:SetKey(_TRACK_O, 0.0, start)
    af:SetKey(_TRACK_O, time, finish)
    af:Play()
    
    return af
end

function CompositionSequence:Stop()
    local ty = 0
end

function PlayField:GetTopRightCornerScr(game_object_name, obj_to_place)
    if play_field:HasObject(game_object_name) then
        local obj = play_field:GetObject(game_object_name)
        local pos = Vector(obj:GetX(), obj:GetY())
        pos.x = pos.x - obj:GetAnimation():GetHotX()
        pos.y = pos.y - obj:GetAnimation():GetHotY()
        pos.x = pos.x - obj_to_place:GetX()
        pos.y = pos.y - obj_to_place:GetY()
        pos.x = pos.x + obj_to_place:GetAnimation():GetHotX()
        pos.y = pos.y + obj_to_place:GetAnimation():GetHotY()
        return Vector(pos.x + obj:GetAnimation():GetWidth(), pos.y)
    else
        return Vector()
    end
end

function RemoveFromTask(obj_name)
    local level = play_field:GetLevelScreen()
    local task = level:GetTask(obj_name)
    task:OnObjectFound(obj_name)
    task:OnObjectFindAnimationFinished(obj_name)
--    level:GetPhone():FillTaskList();
    level:GetPhone():UpdateTaskList()
end

function StartMiniGame(func_to_create)
    play_field:GetLevelScreen():Hide()
    play_field:GetLevelScreen():Stop()
    game_state.mini_game = func_to_create(play_field:GetLevelScreen():GetPanel())
    GetGameApp():SetCustomCursor('CursorArrow')
    GTimerDoString("GetGameApp():SetCustomCursor('CursorArrow')", 0.2)
end

function DefaultOnMiniGameWin()
    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 StartLevel(level_path)
    if play_field then
        FinishComics();
        local load_next_level = "load_level('"..level_path.."')"
        --GTimerDoString('play_field:GetLevelScreen():ScreenDestroy()', 0.5);
        GTimerDoString('play_field:GetLevelScreen():Destroy()', 0.01);
        GTimerDoString(load_next_level, 0.01);
    else
        local current_screen = GetGameManager():GetCurrentScreen();
        if current_screen then
            current_screen:Destroy();
        end
        load_level(level_path);
    end
    
    menu = nil;
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 ExitToMainMenu()
    if play_field then
        FinishComics(false)
        DestroyCurrentLevel()
--      play_field:GetLevelScreen():Destroy()
--      play_field = nil
    end

    SetCursor(GetCursorArrow())
    load_screen("data/Lua/Screens/MainMenu.lua")
end

function DestroyCurrentLevel()
    if play_field ~= nil then
--      OnSceneDestroy(play_field)
        play_field:GetLevelScreen():DestroySelf()
        play_field:GetLevelScreen():Destroy()
        play_field = nil
    end

end

function CreateInGameMenu()
    panel:SetAllowInput(false)
    local screen = load_screen('data/Lua/Screens/OptionsScreen.lua')
    screen:SetLayer(LAYER_OPTIONS)
   
    if IsExistMiniGame() then     
        local screen_mini_game = game_state.screen_mini_game
        screen_mini_game:Stop()
        screen_mini_game:SetAllowInput(false)
    else
        play_field:Stop()
        play_field:GetLevelScreen():Stop()
    end
    
    return screen
end

function StartCycledAnimation(obj_name, delay, period)
    if play_field:HasObject(obj_name) then
        local obj = play_field:GetObject(obj_name)
        local timer = play_field:GetTimer()
        if obj:GetAnimation():GetMode() ~= HGEANIM_NOLOOP + HGEANIM_PINGPONG then
            obj:GetAnimation():SetMode(HGEANIM_NOLOOP)
        end

        if delay == 0 then
            delay = period
            obj:GetAnimation():Play()
        else
            obj:GetAnimation():Stop()
        end

        local action = "StartCycledAnimation('"..obj_name.."', 0, "..period..")"
        TimerDoString(action, delay, timer)
    end
end

function StartCycledAnimationEx(obj_name, delay, period, hide_when_not_playing)
    if play_field:HasObject(obj_name) then
        local obj = play_field:GetObject(obj_name)
        local timer = play_field:GetTimer()
        if obj:GetAnimation():GetMode() ~= HGEANIM_NOLOOP + HGEANIM_PINGPONG then
            obj:GetAnimation():SetMode(HGEANIM_NOLOOP)
        end

        local anim_length = GetAnimationLength(obj:GetAnimation())

        if delay == 0 then
            delay = period
            obj:GetAnimation():Play()
            obj:SetVisible(true)
            TimerDoString("play_field:GetObject('"..obj_name.."'):SetVisible(false)", anim_length, timer)
        else
            obj:SetVisible(false)
        end

        local action = "StartCycledAnimationEx('"..
                        obj_name..
                        "', 0, "..
                        period..
                        ','..
                        tostring(hide_when_not_playing)..
                        ")"

        TimerDoString(action, delay, timer)
    end
end

function LoadTutorial()
end

function ShowNextTutorialSlide()
	local pf = play_field
    pf:GetObject('notepad'):SetOpacity(1.)
    local curr_page
    local next_page
    if pf:GetObject('page1'):GetOpacity() > 0. then
        curr_page = 'page1'
        next_page = 'page2'
    end
    if pf:GetObject('page2'):GetOpacity() > 0. then
        curr_page = 'page2'
        next_page = 'page3'
    end
    if pf:GetObject('page3'):GetOpacity() > 0. then
        curr_page = 'page3'
        next_page = 'page4'
    end
    if pf:GetObject('page4'):GetOpacity() > 0. then
        curr_page = 'page4'
        next_page = ''
    end
    pf:GetObject(curr_page):ChangeOpacity(pf:GetObject(curr_page):GetOpacity(), 0., 0.2)
    if next_page ~= '' then
        pf:GetObject(next_page):ChangeOpacity(pf:GetObject(next_page):GetOpacity(), 1., 0.2)
    else
        pf:GetObject('notepad'):ChangeOpacity(1., 0., 0.2)
        game_state.next_button:SetVisible(false)
        game_state.next_button:Stop()

        if game_state.first_help_showing == true then
            game_state.first_help_showing = false
            TimerDoString('ShowFirstComics()', 0.2, pf:GetTimer())
        end
    end
end

function DefaultOnButtonPress(title)
    PlaySound("button_press")

    if game_state and game_state.zones ~= nil then
        for i, info in ipairs(game_state.zones) do
            if info.zone.close_button_message == title then
                info.zone.SetVisible(false)
            end
        end
    end
    
    if game_state and game_state.info_item_zone ~= nil then
        for i, info in ipairs(game_state.info_item_zone) do
            if info.zone.close_button:GetName() == title then
                info.zone.SetVisible(false)
            end
        end
    end
        
    if title == 'skip_tutorial' then
        SetTutorialEnabled('false')
        FinishComics(true)
        
        local pf = play_field
        if pf:HasObject('fishing_line') and pf:IsIgnoredOnTestPoint(pf:GetObject('fishing_line')) then
            pf:DoNotIgnoreOnTestPoint('fishing_line')
        end
    end
end

function GetHintZ(object)
    if game_state.current_visible_zone ~= nil and
       not game_state.current_visible_zone.HasObject(object:GetName())
    then
        return game_state.min_zone_z - 0.1
    else
        return object:GetZ() + 150.
    end
end

function Restart()
    local  panel = play_field:GetLevelScreen():GetPanel();
	play_field:GetLevelScreen():GetPanel():SetAllowInput(false);
    StartLevel(play_field:GetName());
    OnCompletedGame();
end

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

function RemoveTimerFunction(tag)
    FunctionDispatcher.Remove(tag)
end

function OnWin()
    OnWinLevel()
end

function OnWinLevel()
    local i, level = GetCurLevelId(play_field:GetName())
    if level == 36 then
        ExitToMainMenu()
        GameStateClear()
    else
        play_field:GetLevelScreen():GetPanel():SetAllowInput(false)
        play_field:GetLevelScreen():SetAllowInput(false)
        FinishLevel(play_field:GetName())
    end
end

function DefaultOnScreenShown()
    if not game_state.loaded then
        TryToDisableZoomZonesIfPossible()        
        OnScreenShown()
        CheckRegisterMiniGame()
        
        if play_field then
            local level = play_field:GetLevelScreen()
            local phone = level:GetPhone()
            if level:HasPhotoTasksOnly() then
		        phone:SetVisibleTool()
            else
                phone:SetVisibleTaskList()
            end
        end
        
    end
    if game_state.OnScreenShown then
        game_state.OnScreenShown()
    end
end

function DefaultOnSceneLoad(path)
    game_state = {}
    game_state.scene_path = path
    game_state.locker = ReferenceCounter()
    game_state.active_zone = nil
    game_state.locked_objects = {}
    game_state.active_objects = {}
    game_state.state = GetGameManager():GetCurrPlayer():GetGameState()
    game_state.register_comics = {}
    game_state.register_minigame = {}
    game_state.info_item_zone = {}
    custom_cursors = {}
    locked_objects = {}
    
    if GameStateExists('last_scene_path') then
        RemoveGameState('last_scene_path')
    end
    AddGameState('last_scene_path', path)
    
    if not HasGameState('HintDelayValue') then
        AddGameState('HintDelayValue', "0")        
    end
    play_field:GetLevelScreen():GetPhone():SetHintDelayValue(tonumber(GetGameState("HintDelayValue"):Get(0)))

    if play_field and not play_field:HasObject('fade_in_on_load') then
        local anim = CreateAnimation('data/Texture/Scenes/Common/pixel.jpg',0,0,8,8,1,0)
        anim:SetHotSpot(4,4)
        anim:SetBlendMode(2)
        anim:SetColor('0xFFFFFFFF')
        local fade_in_on_load = play_field:CreateObject('fade_in_on_load',anim,512,384,1000,0,128,96)
        play_field:IgnoreOnTestPoint('fade_in_on_load')
        play_field:GetObject('fade_in_on_load'):ChangeOpacity(1, 0, TIME_START_LEVEL)
        fade_in_on_load:SetLayer(16) -- layer phone + 1
        
        TimerDo(play_field:GetTimer(), TIME_START_LEVEL, function()
                                                            OnScreenShown(true)
                                                            if play_field:GetLevelScreen():HasTaskObjects() == false then
                                                                OnWin()
                                                            end
                                                         end )
    end        
    
    PlayRandomAmbientMusic()
    
    local PATH_COMICS_NO_RIGHT_TOOL = 'data/Scenes/Comics/HelpersComicsReplic.lua'
    game_state.chain_no_right_tool = RegisterComicsEx(function()
											            LoadComicsWithOneReplicHeroes(PATH_COMICS_NO_RIGHT_TOOL, 'COMICS_NO_RIGHT_TOOL')
											          end)
    game_state.chain_no_right_tool.bRepeat = true
    
    SetCursor( GetCursorArrow() )
    
    --NotesFound('Notes_1')
--    NotesFound('Notes_2')
    if HasGameState('ParticlesNotesFound', '') then
        play_field:GetLevelScreen():GetPanel():ShowNotes()
    end
end

function NotesFound(name)
    if not HasGameState('NotesFound', name) then
        AddGameState('NotesFound', name)
        
        if play_field --[[and name ~= 'Notes_1' and name ~= 'Notes_2']] then
            play_field:GetLevelScreen():GetPanel():ShowNotes()
            
            if not HasGameState('ParticlesNotesFound', '') then
                AddGameState('ParticlesNotesFound', '')
            end
        end
    end
end

function IsNotesFound(name)
    return HasGameState('NotesFound', name)
end

function DefaultOnUpdate(dt)
    if game_state.zones then
        for i, info in ipairs(game_state.zones) do
            local trigger = info.trigger_name
            local trigger_name = play_field:HasObject(trigger)
            if trigger_name then
                info.zone.Update(dt)
            end
        end
    end --  if game_state.zones then
    
    if game_state.photo_objects then
        for i, info in ipairs(game_state.photo_objects) do
            local action = info.apply_action
            if action then
                info.zone.Update(dt)
            end
        end
    end
    
    UpdateActiveObject(dt)
end

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

    PlaySound('item_found')
end

function TryToDisableZoomZonesIfPossible()
	if game_state and game_state.zones then
        for i, info in ipairs(game_state.zones) do
            DisableZoomZoneRequest(info.zone)
        end -- for i, info in ipairs(game_state.zones) do
    end
end

function HasObjectInZoomZones(object_name)
    if game_state and game_state.zones then
        for i, zoom_info in ipairs(game_state.zones) do
            for k, v in pairs(zoom_info.zone.objects) do
                if object_name == k and v then
                    return true
                end
            end
        end 
    end
    
    return false
end

function DefaultOnObjectReachPanel(object)
    TryToDisableZoomZonesIfPossible()
end

function CanBeClicked(object_name)
    if not play_field:HasObject(object_name) then
        return false
    end
    local object = play_field:GetObject(object_name)
    if object:IsVisible() and object:GetOpacity() > 0
       and not play_field:IsIgnoredOnTestPoint(object)
       and not game_state.locked_objects[object_name]
    then
        --[[
        if game_state.photo_objects then
            for i, photo_info in ipairs(game_state.photo_objects) do
                if photo_info.obj_name == object_name then
                    return false;
                end
            end -- for i, photo_info in ipairs(game_state.photo_objects) do
        end -- if game_state.photo_objects then
        ]]
        return true
    end
    return false
end

function GetFindableObjects(all_objects, findable_objects)
	if all_objects:empty() then
		return
	end
	for i = 0, all_objects:size() - 1 do	
		local object_name = all_objects:get(i)		
		local pf = play_field		
		if game_state.locker:GetNReferences() == 0 then
			if pf:GetLevelScreen():IsTaskObject(object_name) and CanBeClicked(object_name) then			    
				findable_objects:push_back(object_name)
			end
		end
		
		if game_state.zones then
	        for i, v in ipairs(game_state.zones) do
		        if v.zone.HasObject(object_name) and 
                   pf:GetLevelScreen():IsTaskObject(object_name) and
                   v.zone.IsVisible() and 
                   v.zone.HasObject(object_name) and
                   CanBeClicked(object_name)
                then
		            local unique = true
		            for k = 0, findable_objects:size() - 1 do		            
		                if object_name == findable_objects:get(k) then
		                    unique = false
		                end
		            end
		            if unique then
					    findable_objects:push_back(object_name)
					end
		        end
	        end
	    end
	end
end

function GetAdditionalHintObjects(objects)
    if game_state.zones == nil then
        game_state.zones = {}
    end
    if game_state.photo_objects == nil then
        game_state.photo_objects = {}
    end
    
    local objects_to_exclude = {}
    
    for i, v in pairs(game_state.photo_objects) do
        if v.tool_target then
            for k, w in ipairs(v.tool_target) do
                if not(w and CanBeClicked(w) and v.photo) then
                    table.insert(objects_to_exclude, w)
                end
            end
        end        
    end
	
    
    if not objects:size() == 0 then
        return
    end

    for i, v in pairs(custom_cursors) do
--        if v ~= "" and v ~= GetCursorMech() and v ~= GetCursorZoom() then
        local ignore = false
        for k, w in ipairs(objects_to_exclude) do
            if w == i then
                ignore = true
            end
        end
        if not ignore then
            if v ~= "" and v ~= GetCursorZoom() then
                if play_field:HasObject(i) then
                    local object = play_field:GetObject(i);
                    if object:GetOpacity() ~= 0 and object:IsVisible() and not game_state.locked_objects[i] and not play_field:IsIgnoredOnTestPoint(object) then
                        if game_state.locker:GetNReferences() == 0 then 
                            objects:push_back(i)
                        else
                            for k, w_zone in pairs(game_state.zones) do
                                if w_zone ~= nil and w_zone.zone.IsVisible() and w_zone.zone.HasObject(i) then                    
                                    objects:push_back(i);
                                    break; 
                                end
                            end -- for k, w_zone in pairs(game_state.zones) do
                        end
                    end -- if object:GetOpacity() ~= 0 and object:IsVisible() and not game_state.locked_objects[i] then
                end -- if play_field:HasObject(i) then            
            end
	    end
    end
	
	if game_state.locker:GetNReferences() == 0 then 
        for i, v in pairs(game_state.zones) do
            if v ~= nil and not v.zone.IsVisible() and play_field:HasObject(v.trigger_name) then
                local object = play_field:GetObject(v.trigger_name)
                if object:GetOpacity() ~= 0 and object:IsVisible() and not game_state.locked_objects[v.trigger_name]
                   and not play_field:IsIgnoredOnTestPoint(object) then
                    local must_be_added = false
                    if v.zone.IsManualUnregister() then
                        must_be_added = true
                    end
                    for k, w in pairs(v.zone.objects) do
                        if w then                             
                            if play_field:GetLevelScreen():IsTaskObject(k) and not game_state.locked_objects[k] 
                               and play_field:HasObject(k)
                               and not play_field:IsIgnoredOnTestPoint(play_field:GetObject(k)) then
                                must_be_added = true
                                break
                            end
                            
                            for l, y in pairs(game_state.photo_objects) do
                                if type(y.tool_target) == 'string' then
                                    if y.tool_target == k and y.photo then
                                        must_be_added = true
                                        break
                                    end                     
                                elseif type(y.tool_target) == 'table' then
                                    for index, target_object in ipairs(y.tool_target) do
                                        if target_object == k and y.photo then
                                            must_be_added = true
                                            break
                                        end
                                    end                                    
                                end
                                if must_be_added then
                                    break
                                end
                            end
							
							for l, y in pairs(game_state.active_objects) do
								if l == k and play_field:HasObject(l) and not play_field:IsIgnoredOnTestPoint(play_field:GetObject(l)) then
									must_be_added = true
									break
								end
                            end
                        end
                    end
                    if must_be_added then
                        objects:push_back(v.trigger_name)
                    end
                end
            end
        end
    end
	
	if not objects:size() == 0 then
        return
    end
	
	for k, w_zone in pairs(game_state.zones) do
		if w_zone ~= nil and w_zone.zone.IsVisible() then
			--Show hint over the close button
		end
	end
end

function RegisterInactiveGroup(name, ...)	
	if not game_state.inactive_groups then
		game_state.inactive_groups = {}
	end
	local args = {...}
	for i, v in ipairs(args) do
	    if not game_state.inactive_groups[name] then
	        game_state.inactive_groups[name] = {}
	    end
		play_field:IgnoreOnTestPoint(v)
		table.insert(game_state.inactive_groups[name], v)
	end
end

function UnregisterInactiveGroup(name)
	if not game_state.inactive_groups then
		return
	end
	for i, v in ipairs(game_state.inactive_groups[name]) do
		play_field:DoNotIgnoreOnTestPoint(v)
	end
	game_state.inactive_groups[name] = nil
end

function BuildCursorTrajectory(xStart, yStart, xEnd, yEnd)

    local slideUp = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, -9.03971612625702 + xOffset);
        effector:SetKey(_TRACK_Y, 0, -1.087678110588 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, -35.918712415678 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, -16.35894329572 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, -54.778451206019 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, -43.08024698882 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, -55.000616747998 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, -74.294729726322 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, -39.31565486476 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, -100.419120425399 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, -12.016902848862 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, -116.980770282281 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, 20.377160021529 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, -124.538072231797 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, 32.705230134861 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, -125.99738170922 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, 57.067546766499 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, -133.883052413855 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, 66.5737743546611 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, -175.427790836715 + yOffset);
        effector:SetKey(_TRACK_X, 0.84, 56.575004743915 + xOffset);
        effector:SetKey(_TRACK_Y, 0.84, -190.035905964726 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, 32.795787661179 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, -199.955445026108 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, 12.49305740035 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, -184.716271154549 + yOffset);
        effector:SetKey(_TRACK_X, 1, 10.519410621768 + xOffset);
        effector:SetKey(_TRACK_Y, 1, -159.032257687091 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, 56.9288506105451 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, -154.162446362942 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, 66.220540453102 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, -163.53247458599 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, 74.362822516088 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, -201.523985031131 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, 71.8900018406169 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, -217.849626297959 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, 55.310854730489 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, -255.024304284269 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, 22.532603990254 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, -283.324869591707 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, -15.771970680188 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, -294.170972215229 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, -24.881895883868 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, -294.069968892219 + yOffset);
    
    end
    
    local slideDown = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, 8.99022554700105 + xOffset);
        effector:SetKey(_TRACK_Y, 0, 1.440609568767 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, 36.028422765841 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, 18.546055327201 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, 51.758827942341 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, 43.721848202421 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, 51.9543208104041 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, 76.802873825099 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, 33.915009259882 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, 102.236925113631 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, 7.33533202798401 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, 115.800608694381 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, -37.038108729105 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, 126.154512958838 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, -61.603725710543 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, 133.014340473786 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, -72.292546397039 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, 172.63010834575 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, -41.560214157488 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, 198.041347119345 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, -19.594656236159 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, 184.30996732763 + yOffset);
        effector:SetKey(_TRACK_X, 1, -15.60402572249 + xOffset);
        effector:SetKey(_TRACK_Y, 1, 158.78166758374 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, -37.486530373706 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, 144.937110593006 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, -63.902383098331 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, 151.970715345113 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, -74.10574428385 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, 160.794966324392 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, -82.512748440206 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, 199.149385725767 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, -80.46989609807 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, 215.163812384836 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, -64.751154318798 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, 252.943713674407 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, -34.445818163542 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, 281.398356615652 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, 4.97390287573 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, 294.95593311782 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, 13.354557206115 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, 294.818437599949 + yOffset);
    
    end
    
    local slideLeft = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, -3.06837694027701 + xOffset);
        effector:SetKey(_TRACK_Y, 0, 8.57231440557001 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, -23.346525897375 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, 32.144703004253 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, -54.361318185972 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, 43.159717344779 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, -85.058067013573 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, 37.60679539729 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, -106.335384420515 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, 14.820133685241 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, -115.194379016148 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, -15.332627175341 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, -117.88602018778 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, -47.315762254909 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, -116.312229914534 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, -59.897253236076 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, -118.47198148653 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, -86.482669655594 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, -155.532039146131 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, -102.972016204771 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, -187.582449794214 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, -77.051565465185 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, -176.53605962583 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, -53.610245311822 + yOffset);
        effector:SetKey(_TRACK_X, 1, -152.025122527427 + xOffset);
        effector:SetKey(_TRACK_Y, 1, -45.378799658591 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, -135.591531392891 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, -64.107242004553 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, -144.854670589658 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, -100.882751703786 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, -179.705901494543 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, -117.376804892983 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, -195.991388208058 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, -119.037704339714 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, -237.468652981892 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, -110.138710052294 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, -270.465486425857 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, -85.591244310499 + yOffset);
        effector:SetKey(_TRACK_X, 1.48, -287.882403730511 + xOffset);
        effector:SetKey(_TRACK_Y, 1.48, -56.528686104181 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, -292.250825767185 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, -41.057401104364 + yOffset);
    
    end
    
    local slideRight = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, 0.88200225637797 + xOffset);
        effector:SetKey(_TRACK_Y, 0, -9.06209596811402 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, 16.432153162213 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, -36.143623431779 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, 40.391332829886 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, -54.169515400461 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, 73.1795266975751 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, -56.593508936083 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, 99.381135117946 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, -40.411261798199 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, 116.400912290091 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, -13.495503170807 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, 125.467187337876 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, 17.529095252879 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, 126.963398402066 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, 29.822352312656 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, 135.446975376384 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, 53.734010824257 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, 176.388552559478 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, 61.712341013617 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, 200.281768985106 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, 27.685499556685 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, 185.037680472858 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, 7.03477903415001 + yOffset);
        effector:SetKey(_TRACK_X, 1, 159.900757162837 + xOffset);
        effector:SetKey(_TRACK_Y, 1, 5.69631590068298 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, 146.758894186318 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, 28.162986476394 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, 153.622706639899 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, 53.681054986025 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, 163.345076132253 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, 62.797052901032 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, 202.535815934466 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, 70.764580548227 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, 219.72382370528 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, 67.615435689413 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, 257.503976005262 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, 49.245546043275 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, 283.967626310547 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, 16.081599360911 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, 294.221432444228 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, -22.82599589568 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, 293.428698836615 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, -31.557155228207 + yOffset);
    
    end
    
    local slideRightUp = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, -5.62294000513299 + xOffset);
        effector:SetKey(_TRACK_Y, 0, -7.16114914062302 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, -13.830845383165 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, -36.965884858066 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, -8.271879529687 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, -69.196549091659 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, 13.642998123952 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, -91.425616267855 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, 43.206674851926 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, -98.80740717525 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, 74.220662441736 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, -91.215129428317 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, 102.47054342355 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, -73.652887358457 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, 112.219693026967 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, -65.967513009712 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, 135.022463552062 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, -54.316764943519 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, 171.120947804581 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, -76.971413213853 + yOffset);
        effector:SetKey(_TRACK_X, 0.84, 174.38024727667 + xOffset);
        effector:SetKey(_TRACK_Y, 0.84, -94.371108276504 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, 164.579974962709 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, -118.199727263561 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, 139.448053534415 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, -121.780202323334 + yOffset);
        effector:SetKey(_TRACK_X, 1, 119.891134422686 + xOffset);
        effector:SetKey(_TRACK_Y, 1, -105.014441253211 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, 149.264087539416 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, -68.754534915671 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, 162.459914932197 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, -68.809928515744 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, 195.081432456547 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, -89.916520317952 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, 204.876855835255 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, -103.209040233228 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, 219.440095379941 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, -141.218734473635 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, 216.273893646389 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, -184.407979487791 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, 196.857821860992 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, -219.162756702264 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, 190.344711838745 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, -225.533026455266 + yOffset);
    
    end
    
    local slideRightDown = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, 7.03153928732297 + xOffset);
        effector:SetKey(_TRACK_Y, 0, -5.78419973430999 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, 37.176688155761 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, -13.938114294767 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, 66.86461701843 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, -9.74264632807899 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, 91.7633935117231 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, 11.728085632032 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, 98.848151817728 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, 41.697997310104 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, 91.850636224226 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, 72.765112609022 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, 76.32375686164 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, 101.113641104391 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, 68.689092421376 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, 110.864267523804 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, 57.779791345664 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, 133.771158214032 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, 81.088326824861 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, 168.362756452112 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, 122.043992520337 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, 161.197201474475 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, 125.867058678191 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, 135.815738596584 + yOffset);
        effector:SetKey(_TRACK_X, 1, 109.039006105551 + xOffset);
        effector:SetKey(_TRACK_Y, 1, 117.094813307859 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, 83.859970562661 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, 123.688447994507 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, 70.669419607438 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, 146.58589561117 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, 71.098189061699 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, 159.9066329514 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, 93.176334106938 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, 193.252563653885 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, 107.556872641374 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, 203.179538819108 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, 147.260948065385 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, 216.904667166253 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, 189.424026241209 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, 212.166842162061 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, 224.186386536905 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, 191.905553566562 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, 229.799701198938 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, 185.171144285294 + yOffset);
    
    end
    
    local slideLeftUp = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, -8.23121178834799 + xOffset);
        effector:SetKey(_TRACK_Y, 0, 3.89187150493598 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, -39.238224252716 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, 6.22125069435299 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, -68.957785532129 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, -7.92072791495298 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, -86.737156024036 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, -33.553115935798 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, -85.66988843075 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, -64.711054376899 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, -70.612921907803 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, -92.29653120598 + yOffset);
        effector:SetKey(_TRACK_X, 0.6, -49.900707934417 + xOffset);
        effector:SetKey(_TRACK_Y, 0.6, -116.81530062933 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, -39.891412569818 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, -124.598920445173 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, -22.619859321146 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, -144.924823658319 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, -37.165548641156 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, -182.789970502846 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, -78.157137839603 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, -187.124506722545 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, -86.921676884314 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, -162.73801288644 + yOffset);
        effector:SetKey(_TRACK_X, 1, -75.410338089164 + xOffset);
        effector:SetKey(_TRACK_Y, 1, -139.585652010555 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, -50.547025774795 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, -141.208356863968 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, -31.092842025985 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, -173.762597694997 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, -44.073326872195 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, -210.069196259872 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, -54.414471700592 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, -222.759207611574 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, -90.035866093615 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, -245.795523591847 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, -130.725830266494 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, -251.77012879079 + yOffset);
        effector:SetKey(_TRACK_X, 1.48, -163.591782586296 + xOffset);
        effector:SetKey(_TRACK_Y, 1.48, -243.53541713796 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, -177.620573968553 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, -235.684507446136 + yOffset);
    
    end
    
    local slideLeftDown = function(effector, xOffset, yOffset)
    
        effector:SetKey(_TRACK_X, 0, 5.33838465356303 + xOffset);
        effector:SetKey(_TRACK_Y, 0, 7.37571424379797 + yOffset);
        effector:SetKey(_TRACK_X, 0.1, 12.361900567057 + xOffset);
        effector:SetKey(_TRACK_Y, 0.1, 38.589983539307 + yOffset);
        effector:SetKey(_TRACK_X, 0.2, 5.68300287435704 + xOffset);
        effector:SetKey(_TRACK_Y, 0.2, 67.515033574238 + yOffset);
        effector:SetKey(_TRACK_X, 0.3, -17.570580339365 + xOffset);
        effector:SetKey(_TRACK_Y, 0.3, 91.045085453321 + yOffset);
        effector:SetKey(_TRACK_X, 0.4, -48.310890003843 + xOffset);
        effector:SetKey(_TRACK_Y, 0.4, 96.273956067177 + yOffset);
        effector:SetKey(_TRACK_X, 0.5, -76.696532654084 + xOffset);
        effector:SetKey(_TRACK_Y, 0.5, 87.070258692569 + yOffset);
        effector:SetKey(_TRACK_X, 0.64, -115.394609435155 + xOffset);
        effector:SetKey(_TRACK_Y, 0.64, 63.014813745806 + yOffset);
        effector:SetKey(_TRACK_X, 0.7, -137.615754340351 + xOffset);
        effector:SetKey(_TRACK_Y, 0.7, 50.494929947789 + yOffset);
        effector:SetKey(_TRACK_X, 0.8, -173.186470034838 + xOffset);
        effector:SetKey(_TRACK_Y, 0.8, 70.949370461659 + yOffset);
        effector:SetKey(_TRACK_X, 0.9, -169.423888761733 + xOffset);
        effector:SetKey(_TRACK_Y, 0.9, 110.648870245083 + yOffset);
        effector:SetKey(_TRACK_X, 0.96, -144.182342037245 + xOffset);
        effector:SetKey(_TRACK_Y, 0.96, 116.47131343803 + yOffset);
        effector:SetKey(_TRACK_X, 1, -123.309306278753 + xOffset);
        effector:SetKey(_TRACK_Y, 1, 101.241881474389 + yOffset);
        effector:SetKey(_TRACK_X, 1.04, -128.992993576302 + xOffset);
        effector:SetKey(_TRACK_Y, 1.04, 75.979033915497 + yOffset);
        effector:SetKey(_TRACK_X, 1.08, -152.64533178511 + xOffset);
        effector:SetKey(_TRACK_Y, 1.08, 62.273714939489 + yOffset);
        effector:SetKey(_TRACK_X, 1.1, -166.099885376626 + xOffset);
        effector:SetKey(_TRACK_Y, 1.1, 61.298536760653 + yOffset);
        effector:SetKey(_TRACK_X, 1.16, -199.165205072234 + xOffset);
        effector:SetKey(_TRACK_Y, 1.16, 82.474557159416 + yOffset);
        effector:SetKey(_TRACK_X, 1.2, -209.04460001559 + xOffset);
        effector:SetKey(_TRACK_Y, 1.2, 95.242981590946 + yOffset);
        effector:SetKey(_TRACK_X, 1.3, -224.64419550616 + xOffset);
        effector:SetKey(_TRACK_Y, 1.3, 133.072234889203 + yOffset);
        effector:SetKey(_TRACK_X, 1.4, -223.335557784637 + xOffset);
        effector:SetKey(_TRACK_Y, 1.4, 174.621814570719 + yOffset);
        effector:SetKey(_TRACK_X, 1.5, -205.048260006424 + xOffset);
        effector:SetKey(_TRACK_Y, 1.5, 212.082420911209 + yOffset);
        effector:SetKey(_TRACK_X, 1.52, -199.025018485559 + xOffset);
        effector:SetKey(_TRACK_Y, 1.52, 217.911214405935 + yOffset);
    
    end
    
    local functor = slideDown;
    
    if (xStart < 400)
    then
    
        if (yStart < 300)
        then
            functor = slideRightDown;
        elseif (yStart > 500)
        then
            functor = slideRightUp;
        else
            functor = slideRight;
        end
    
    elseif (xStart > 750)
    then
    
        if (yStart < 300)
        then
            functor = slideLeftDown;
        elseif (yStart > 500)
        then
            functor = slideLeftUp;
        else
            functor = slideLeft;
        end
    
    else
    
        if (yStart < 350)
        then
            functor = slideDown;
        else
            functor = slideUp;        
        end
    
    end
    
    local effector = CreateAnimationEffector();
    local eff = CreateAnimationEffector()
    functor(effector, xStart, yStart)
    functor(eff, xStart, yStart)
    return effector, eff;
end

function CreateDialogMiniGameHelp(text)
    local screen_help_minigame = load_screen('data/Lua/Screens/DialogMiniGameHelp.lua')
    screen_help_minigame:SetLayer(LAYER_MINIGAME + 1)
    SetCaptions(text)
                                                    
    local screen_mini_game = game_state.screen_mini_game
    screen_mini_game:Stop()
    screen_mini_game:SetAllowInput(false)
end

