local FADE_INVISIBLE_OPACITY = 0.00
local FADE_VISIBLE_OPACITY 	 = 0.7
local FADE_DELTA_Z           = 0.01
local DEFAULT_ZOOM_Z_OFFSET  = 500
local PREFIX_TRIGER_NAME     = 'area_zoom_'
local INVISIBLE_ZOOM_Z       = -1
-------------------------------------------------------------------------------

ZOOM_TYPE_CIRCLE_BIG 		= 0
-- ZOOM_TYPE_SQUARE 	        = 1
ZOOM_TYPE_RECT_VERTICAL 	= 2
ZOOM_TYPE_RECT_HORIZONTAL 	= 3
ZOOM_TYPE_FULLSCREEN        = 4
ZOOM_TYPE_INFO_WINDOW       = 5
ZOOM_TYPE_NO_BORDER         = 6
ZOOM_TYPE_DISABLED          = 10
-------------------------------------------------------------------------------

local modeSizeTable  = {}
modeSizeTable[ZOOM_TYPE_FULLSCREEN]      = {w=SCR_W, h=SCR_H}
modeSizeTable[ZOOM_TYPE_CIRCLE_BIG]      = {w=350,   h=350  }
modeSizeTable[ZOOM_TYPE_RECT_VERTICAL]   = {w=384,   h=512  }
modeSizeTable[ZOOM_TYPE_RECT_HORIZONTAL] = {w=640,   h=480  }
modeSizeTable[ZOOM_TYPE_INFO_WINDOW]     = {w=0,     h=0    }
modeSizeTable[ZOOM_TYPE_NO_BORDER]       = {w=0,     h=0    }
modeSizeTable[ZOOM_TYPE_DISABLED]        = {w=8,     h=8    }
-------------------------------------------------------------------------------

function GetZoomPrototypeMode(objects, info_animation)
    for i, obj_name in ipairs(objects) do
        local objAnim = play_field:GetObject(obj_name):GetAnimation()
        local w = objAnim:GetWidth()
        local h = objAnim:GetHeight()
        
        if info_animation then
            for _, info in pairs(info_animation) do
                if info.objectName == obj_name then
                    w = info.tw
                    h = info.th
			        break
                end
            end
        end
        
        for mode, size in pairs(modeSizeTable) do
            if size.w == w and size.h == h then
                return mode
            end
        end
    end
    
    return nil
end
-------------------------------------------------------------------------------

function GetZoom(name)
    return GetCurrentLevel().zones[name]
end
-------------------------------------------------------------------------------

function CreateSmartZoomZone(name, mode, additionOffsetX, additionOffsetY, offsetZ)
    local level            = GetCurrentLevel()
    local initFunctionName = level.name..'_z_'..string.sub(name,1)
    local zoomFileName     = ExtractFilePath(GetLevelPath(level.name))..'/'..string.sub(initFunctionName, 3)..'.lua'
    
    if IsFileExists(zoomFileName) then
        dofile(zoomFileName)
    end
    
    local init_info = {}
    init_info.name = name
    init_info.mode = mode
    init_info.additionOffsetX = additionOffsetX
    init_info.additionOffsetY = additionOffsetY
    init_info.offsetZ = offsetZ
    
    local zoneObject = Helpers.CallFunctionByName(initFunctionName, init_info)
    local zoom
    
    if zoneObject.ZoomEventHandler then
        zoom = zoneObject.zone
    else
        zoom = CreateZoomZoneEx(zoneObject, PREFIX_TRIGER_NAME..name, mode, additionOffsetX, additionOffsetY, offsetZ)
    end
    
    return zoom, zoneObject
end
-------------------------------------------------------------------------------

function CreateZoomZoneEx(zoomZoneObject, trigger_name, mode, additionOffsetX, additionOffsetY, offsetZ)
	assert(zoomZoneObject and "zoom-zone object hasn't been created or result of copy-paste code !");
	additionOffsetX = additionOffsetX or 0.0
	additionOffsetY = additionOffsetY or 0.0
	--offsetZ = offsetZ or DEFAULT_ZOOM_Z_OFFSET
	trigger_name = trigger_name or ''
	
	local is_zoom_zone_disable = false
	do
        local value = GetCurrentLevel().name..'_'..trigger_name..'_disable'
        if HasGameState(ZOOM_DISABLED, value) then
            is_zoom_zone_disable = true
        end
    end
    
    local info_animation = {}

	local objects = {}
    local origPlayField_CreateObject = play_field.CreateObject
    local origCreateAnimation = CreateAnimation
    -- if is_zoom_zone_disable then
    do
        local auxCreateAnimation = function(texture_path, tx, ty, tw, th, nframes, fps)
            local anim
            if nframes == 1 then
                anim = origCreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
            else
                anim = origCreateAnimation('data/Texture/Common/fade.png', 0, 0, 1, 1, nframes, fps)
            end
             
            if not is_zoom_zone_disable then
                local info = {}
                info.texture_path = texture_path
                info.tx            = tx
                info.ty            = ty
                info.tw            = tw
                info.th            = th
                info.nframes       = nframes
                info.fps           = fps
                info.anim          = anim
                table.insert(info_animation, info)
             end
             return anim
        end
        CreateAnimation = auxCreateAnimation
    end
    
    local auxCreateObject = function (play_field, objectName,anim, x,y,z, angle, scaleX,scaleY)
                                table.insert(objects, objectName)
                                for i, info in pairs(info_animation) do
                                    if not info.objectName then
                                        info.objectName = objectName
                                        break
                                    end
                                end
                                origPlayField_CreateObject(play_field, objectName,anim, x,y,z, angle, scaleX,scaleY)
                            end
    play_field.CreateObject = auxCreateObject
    
    zoomZoneObject:LoadData(play_field)
    play_field.CreateObject = origPlayField_CreateObject
    CreateAnimation = origCreateAnimation
	
	mode = (mode ~= nil) and mode or GetZoomPrototypeMode(objects, info_animation)
	local zone = CreateZoomZone(trigger_name, mode, unpack(objects))
	zone:SetOffset(additionOffsetX + zone:GetOffsetX(), additionOffsetY + zone:GetOffsetY())
	zone.info_animation = info_animation
	if offsetZ then
	    zone:SetOffsetZ(offsetZ)
	else
	    zone:AutoDetectZ(true)
	end
	
	zone.ZoneObject = zoomZoneObject
	zone.ZoneObject.zone = zone
	
	if is_zoom_zone_disable then
        zone:Disable()
    end
    if zoomZoneObject.init ~= nil then
	    zoomZoneObject:init(zoomZone)
	end
	
	return zone
end
-------------------------------------------------------------------------------

class 'zoom_zone'
    
function zoom_zone:__init(trigger_name, mode, ...)
    local level = GetCurrentLevel()
    local zone  = {}
    local arg   = {...}

    self.trigger_name   = trigger_name
    self.mode           = mode
    self.fading_enabled = true
    self.is_fading      = false
    self.is_visible     = false
    self.frame          = nil
    self.objects        = {}
        
    self.baseX = 0
    self.baseY = 0
    self.baseZ = 0
    self.fadeUpperDeltaZ = FADE_DELTA_Z
    --------------------

    self.close_button_message = 'Close_'
    if trigger_name then
        self.close_button_message = self.close_button_message..trigger_name..'_ZoomZone'
    else
        self.close_button_message = self.close_button_message..'Modal'..'_ZoomZone'
    end
    --------------------
        
    -- Create empty object
    do
        local emptyObjectName = nil
        local emptyObjectZ    = (mode == ZOOM_TYPE_INFO_WINDOW) and 1000 or 0
            
        if trigger_name == nil then
            local emptyObjectPrefix = 'area_zoom_zone_'..tostring(self.mode)..'_'
            local index = 1
                
            repeat
                emptyObjectName = emptyObjectPrefix..tostring(index)
                index = index + 1
            until not play_field:HasObject(emptyObjectName)
        else
            emptyObjectName = 'area_zoom_zone_'..trigger_name
        end
            
        do
            local anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
            anim:SetHotSpot(4,4)
            anim:SetBlendMode(2)
            anim:SetColor('0x01FFFFFF')
                
            self.empty_obj = play_field:CreateObject(emptyObjectName,anim,512,384,emptyObjectZ,0,1,1)
            self.empty_obj:SetOpacity(0)
        end
            
        self.baseX = self.empty_obj:GetX()
        self.baseY = self.empty_obj:GetY()
        self.baseZ = self.empty_obj:GetZ()
    end
    --------------------
        
    local frameName = (trigger_name) and 'zoom_frame'..trigger_name or ''
    local frameZ    = nil
        
    -- add objects to zone
    for i, obj_name in ipairs(arg) do
        if play_field:HasObject(obj_name) then
            self:AddObject(obj_name)
                
            local object = play_field:GetObject(obj_name)
            if (frameZ == nil) or (object:GetZ() > frameZ) then
                frameZ = object:GetZ()
            end
        end
    end
    frameZ = frameZ and frameZ + 10 or 0
    --------------------
        
    local closeButtonPos = nil
        
    if mode == ZOOM_TYPE_CIRCLE_BIG then
        closeButtonPos = Vector(622, 223)
            
        local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom350.png',0,0,370,370,1,0)
        anim:SetHotSpot(185,185)
        anim:SetBlendMode(2)
        anim:SetColor('0xFFFFFFFF')
        self.frame = play_field:CreateObject(frameName,anim,512,384,frameZ,0,1,1)
    elseif mode == ZOOM_TYPE_RECT_HORIZONTAL then
        closeButtonPos = Vector(817, 156)
            
        local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom640.png',0,0,686,526,1,0)
        anim:SetHotSpot(343,263)
        anim:SetBlendMode(2)
        anim:SetColor('0xFFFFFFFF')
        self.frame = play_field:CreateObject(frameName,anim,512,384,frameZ,0,1,1)
    elseif mode == ZOOM_TYPE_RECT_VERTICAL then
        closeButtonPos = Vector(692, 140)
            
        local anim = CreateAnimation('data/Texture/GUI/Zoom/zoom_vertical.png',0,0,433,561,1,0)
        anim:SetHotSpot(216,280)
        anim:SetBlendMode(2)
        anim:SetColor('0xFFFFFFFF')
        self.frame = play_field:CreateObject(frameName,anim,512,384,frameZ,0,1,1)
    elseif mode == ZOOM_TYPE_FULLSCREEN  then
    elseif mode == ZOOM_TYPE_INFO_WINDOW then
    elseif mode == ZOOM_TYPE_NO_BORDER then
    elseif mode == ZOOM_TYPE_DISABLED then
    else
        assert(mode == nil and "Wrong zoom-zone type was specified!");
    end
    --------------------
        
    if self.frame then
        self:AddObject(frameName)
            
        self.close_button = CreateCloseButton(closeButtonPos, self.close_button_message, play_field:GetLevelScreen(), mode)
                                                  
        Helpers.AttachInPlace(self.close_button, play_field:GetObject(frameName))
        self.close_button:SetZ(1)
        self.close_button:Stop()
    end
    --------------------
        
    if mode == ZOOM_TYPE_RECT_HORIZONTAL then
        self:SetOffset(0, -45)
    elseif mode == ZOOM_TYPE_RECT_VERTICAL then
        self:SetOffset(0, -14)
        self.empty_obj:SetScale(0.95)
    elseif mode == ZOOM_TYPE_CIRCLE_BIG then
        self:SetOffset(-15, -45)
    end
end

function zoom_zone:__towatch()
    return tostring(self.trigger_name)
end
	
function zoom_zone:IsVisible()
    return self.is_visible
end
    
function zoom_zone:AutoDetectZ(Enabled)
    self.auto_detect_Z = Enabled
end
    
function zoom_zone:SetOffsetZ(offsetZ)
    self.empty_obj:SetZ(self.baseZ + offsetZ)
end
    
function zoom_zone:GetOffsetZ()
    return self.empty_obj:GetZ() - self.baseZ
end
    
function zoom_zone:SetOffset(offsetX, offsetY)
    local x = self.baseX + offsetX
    local y = self.baseY + offsetY
        
	self.empty_obj:SetX(x)
	self.empty_obj:SetY(y)
end
    
function zoom_zone:GetOffsetX()
    return self.empty_obj:GetX() - self.baseX
end
    
function zoom_zone:GetOffsetY()
    return self.empty_obj:GetY() - self.baseY
end
    
function zoom_zone:GetScrX()
    return self.empty_obj:GetScrX()
end
    
function zoom_zone:GetScrY()
    return self.empty_obj:GetScrY()
end

function zoom_zone:loading_resource()
    if self.info_animation then
        for i, info in pairs(self.info_animation) do
            local anim_old = info.anim
            local anim = CreateAnimation(info.texture_path, info.tx, info.ty, info.tw, info.th, info.nframes, info.fps)
            anim:SetHotSpot(anim_old:GetHotX(), anim_old:GetHotY())
            anim:SetBlendMode( anim_old:GetBlendMode() )
            anim:SetColor( anim_old:GetColorStr()  )
            anim:SetMode( anim_old:GetMode() )
            anim:SetFrame( anim_old:GetFrame() )
            if( anim_old:IsPlaying() ) then
                anim:Resume()
            end 
            Object(info.objectName):SetAnimation(anim)
        end
        
        self.info_animation = nil
    end
end
	
function zoom_zone:SetVisible(is_visible, time)
    local level = GetCurrentLevel()
    time = (time == nil) and ZOOM_ZONE_APPEARING_TIME or time
    self.is_visible = is_visible
        
    if self.visible_timers then
        HelpersTimer.KillTimers(self.visible_timers)
    end
    
    self.visible_timers = {}
    local timer
        
    local function AddTimer(timer)
        table.insert(self.visible_timers, timer)
    end

    local stop_highlighting = function(obj_name)
        local obj = play_field:GetObject(obj_name)
        obj:DestroyChildren()
        obj:ChangeOpacity(obj:GetOpacity(), 0, 0.1)
    end
        
    stop_highlighting('object_highlight')
    stop_highlighting('quest_object_highlight')
        
    local function setFadingFlag()
        self.is_fading = true
            
        timer = level.timer:Add(function()
                                    self.is_fading = false
                                end, time)
        AddTimer(timer)
    end
        
    local showStitchTime = nil
    local fadeObject = play_field:GetObject('zoom_fade')
    fadeObject:DestroyEffectors()
    fadeObject:DestroyChildren()
        
    if is_visible then
        self:loading_resource()
    
        if self.fading_enabled then
            if self.mode == ZOOM_TYPE_FULLSCREEN then
                local eff = CreateAnimationEffector()
                eff:AttachTo(fadeObject)
                eff:SetDestroysOnEnd(true)
                eff:SetKey(_TRACK_O, 0,          0)
                eff:SetKey(_TRACK_O, time * 0.5, 1)
                eff:SetKey(_TRACK_O, time,       0)
                eff:Play()
                    
                showStitchTime = time / 2
                    
                fadeObject:SetScrZ(self:GetMaxScrZ() + self.fadeUpperDeltaZ)
                if time > 0. then
                    timer = level.timer:Add(function()
                                                fadeObject:SetScrZ(self:GetMinScrZ() - FADE_DELTA_Z)
                                            end, time)
                    AddTimer(timer)
                else
                    fadeObject:SetScrZ(self:GetMinScrZ() - FADE_DELTA_Z)
                end
            else
                local eff = fadeObject:ChangeOpacity(fadeObject:GetOpacity(), FADE_VISIBLE_OPACITY, time)
                eff:SetDestroysOnEnd(true)
                    
                fadeObject:SetScrZ(self:GetMinScrZ() - FADE_DELTA_Z)
            end
                
            setFadingFlag()
        else
            fadeObject:SetOpacity(FADE_INVISIBLE_OPACITY)
        end
            
        if level.active_zone and level.active_zone.close_button ~= nil then
            level.active_zone.close_button:Stop()
        end

        level.active_zone = self
        active_zones:Push(self)
        level.locker:AddRef()
            
        if self.close_button then
            self.close_button:Show()
            self.close_button:Stop()
                
            timer = level.timer:Add(EnableActiveZoneCloseButton, 0.3)
            AddTimer(timer)
        end
    else
        assert(active_zones:GetTop() == self)
            
        active_zones:Pop()
        level.active_zone = active_zones:GetTop()
        level.locker:Release()
        play_field:GetObject('object_highlight'):Hide()
            
        if self.fading_enabled then
            if self.mode == ZOOM_TYPE_FULLSCREEN then
                local eff = CreateAnimationEffector()
                eff:AttachTo(fadeObject)
                eff:SetDestroysOnEnd(true)
                eff:SetKey(_TRACK_O, 0,          0)
                eff:SetKey(_TRACK_O, time * 0.5, 1)
                eff:SetKey(_TRACK_O, time,       0)
                eff:Play()
                    
                showStitchTime = time / 2
                    
                fadeObject:SetScrZ(self:GetMaxScrZ() + self.fadeUpperDeltaZ)
                timer = level.timer:Add(function()
                                            if level.active_zone then
                                                fadeObject:SetScrZ(level.active_zone:GetMinScrZ() - FADE_DELTA_Z)
                                            end
                                        end, time)
                AddTimer(timer)
            else
                if level.active_zone then
                    if level.active_zone.mode == ZOOM_TYPE_FULLSCREEN then
                        local eff = fadeObject:ChangeOpacity(fadeObject:GetOpacity(), 0, time)
                        eff:SetDestroysOnEnd(true)
                        setFadingFlag()
                            
                        timer = level.timer:Add(function()
                                                    if level.active_zone then
                                                        fadeObject:SetScrZ(level.active_zone:GetMinScrZ() - FADE_DELTA_Z)
                                                    end
                                                end, time)
                        AddTimer(timer)
                    else
                        fadeObject:SetScrZ(level.active_zone:GetMinScrZ() - FADE_DELTA_Z)
                    end
                else
                    local eff = fadeObject:ChangeOpacity(fadeObject:GetOpacity(), 0, time)
                    eff:SetDestroysOnEnd(true)
                    setFadingFlag()
                end
            end
        end
            
        if self.close_button then
            self.close_button:Stop()
        end
                
        if level.active_zone and level.active_zone.close_button ~= nil then
            level.active_zone.close_button:Go()
        end
    end
        
    local opacity = (is_visible) and 1 or 0
    self.empty_obj:DestroyEffectors()
        
    if showStitchTime and showStitchTime>0. then
        timer = level.timer:Add(function()
                                    self.empty_obj:SetOpacity(opacity)
                                end, showStitchTime)
        AddTimer(timer)
    else
        self.empty_obj:SetOpacity(opacity)
    end
        
end
    
function zoom_zone:Open(time)
    if not( self:IsBlocked() or self:IsVisible() or self:IsDisabled() ) then
        time = (time == nil) and ZOOM_ZONE_APPEARING_TIME or time
            
        if self.auto_Z_timer then
            self.auto_Z_timer:Kill()
        end
                
        if self.auto_detect_Z then
            self.empty_obj:SetScrZ(play_field:GetMaxObjectZ() + DEFAULT_ZOOM_Z_OFFSET)
        end
            
        self:SetVisible(true, time)
            
        if self.ZoneObject and self.ZoneObject.OnDefaultOpen then
            self.ZoneObject:OnDefaultOpen()
        end
            
        if self.on_open then
            self:on_open(zone)
        end
            
        SetZoomZoneVisited(GetCurrentLevel().name, self.trigger_name)
    end
end
    
function zoom_zone:Close(time)
    if not( self:IsBlocked() or (not self:IsVisible()) ) then
        time = (time == nil) and ZOOM_ZONE_APPEARING_TIME or time
        self:SetVisible(false, time)
            
        if self.auto_detect_Z then
            if self.auto_Z_timer then
                self.auto_Z_timer:Kill()
            end
            self.auto_Z_timer = GetCurrentLevel().timer:Add(function() 
                                                                self.empty_obj:SetScrZ(INVISIBLE_ZOOM_Z) 
                                    
                                                                local fadeObject = play_field:GetObject('zoom_fade')
                                                                local active_zone = active_zones:GetTop()
                                                                local MinZ = active_zone and active_zone:GetMinScrZ() - FADE_DELTA_Z or INVISIBLE_ZOOM_Z
                                                                fadeObject:SetScrZ( MinZ )
                                                            end, time)
        end
            
        if self.ZoneObject and self.ZoneObject.OnDefaultClose then
            self.ZoneObject:OnDefaultClose()
        end
            
        if self.on_close then
            self:on_close(self)
        end
    end
end
    
function zoom_zone:Block(time)
    self:UnBlock()
    self.blocker = true
        
    if time then
        self.timer_blocker = GetCurrentLevel().timer:Add(function() self:UnBlock()end , time)
    end
end
    
function zoom_zone:UnBlock()
    if self.timer_blocker then
        self.timer_blocker:Kill()
        self.timer_blocker = nil
    end
    self.blocker = nil
end
    
function zoom_zone:IsBlocked()
    return self.blocker
end
    
function zoom_zone:IsObjectVisible(object_name)
    return true
end

function zoom_zone:HasObject(object_name)
    return self.objects[object_name] == true
end

function zoom_zone:AddObject(object_name)
    assert(play_field:HasObject(object_name))
        
    local object = play_field:GetObject(object_name)
    Helpers.AttachInPlace(object, self.empty_obj)
    self.objects[object_name] = true
end

function zoom_zone:RemoveObject(object_name)
    self.objects[object_name] = false
    if play_field:HasObject(object_name) then
        local obj = play_field:GetObject(object_name)
        Helpers.AttachInPlace(obj, play_field)
    end
end
    
function zoom_zone:SetObjectUnderFrame(object_name, deltaZ)
    assert(self:HasObject(object_name))
        
    deltaZ = deltaZ or 1
        
    local obj = play_field:GetObject(object_name)
    obj:SetZ(self.frame:GetZ() + deltaZ)
end

function zoom_zone:Disable()
    local level = GetCurrentLevel()

    self.disabled = true
    play_field:IgnoreOnTestPoint(self.trigger_name)
    level:LockObject(self.trigger_name)
    level.custom_cursors[self.trigger_name] = nil
    Object(self.trigger_name):SetVisible(false)
        
    local value = level.name..'_'..self.trigger_name..'_disable'
    AddUniqueGameState(ZOOM_DISABLED, value)
end
    
function zoom_zone:CloseAndDisable()
    self:Close()
    self:Disable()
end

function zoom_zone:IsDisabled()
    return self.disabled
end
    
function zoom_zone:SetTriggerVisible(visible)
    if self.trigger_name then
        play_field:GetObject(self.trigger_name):SetVisible(visible)
    end
end
   
function zoom_zone:GetClippingInfo()
    local x,y,w,h
    local scale = self.empty_obj:GetScale()
    local size  = modeSizeTable[self.mode]
        
    assert(size)
        
    w = size.w
    h = size.h
        
    w = w * scale
    h = h * scale
        
    x = self:GetScrX() - w/2
    y = self:GetScrY() - h/2
        
    return x,y,w,h
end
	
function zoom_zone:GetMaxScrZ()
    local maxZ = nil
	    
	for obj_name, visible in pairs(self.objects) do
	    local objZ = play_field:GetObject(obj_name):GetScrZ()
	        
	    if maxZ == nil or maxZ < objZ then
	        maxZ = objZ
        end
    end
	    
	return maxZ
end
	
function zoom_zone:GetMinScrZ()
    return self.empty_obj:GetScrZ()
end

function zoom_zone:IsFirstVisited()
    return not IsZoomZoneVisited(self.trigger_name, GetCurrentLevel().name)
end
-------------------------------------------------------------------------------
function CreateZoomZone(trigger_name, mode, ...)
    assert(    mode == ZOOM_TYPE_CIRCLE_BIG
            or mode == ZOOM_TYPE_RECT_HORIZONTAL
            or mode == ZOOM_TYPE_RECT_VERTICAL
            or mode == ZOOM_TYPE_FULLSCREEN
            or mode == ZOOM_TYPE_INFO_WINDOW
            or mode == ZOOM_TYPE_NO_BORDER
            or mode == ZOOM_TYPE_DISABLED)
    
    local zone = zoom_zone(trigger_name, mode, ...)
    RegisterZoomZone(zone)
    
    return zone
end
-------------------------------------------------------------------------------

function CreateCloseButton(pos, message, listener, zoom_mode)
    local button = nil
    
    if  zoom_mode == ZOOM_TYPE_RECT_HORIZONTAL or
        zoom_mode == ZOOM_TYPE_RECT_VERTICAL   or
        zoom_mode == ZOOM_TYPE_CIRCLE_BIG
    then
        if not play_field:HasObject('button_zoom_close') then
            local anim = CreateAnimation('data/Texture/GUI/Zoom/close.png',0,0,40,40,3,0)
            anim:SetHotSpot(20,20)
            anim:SetBlendMode(2)
            anim:SetColor('0xFFFFFFFF')
            play_field:CreateObject('button_zoom_close',anim,512,384,100,0,1,1)
            play_field:IgnoreOnTestPoint('button_zoom_close')
        end
	
        button = ScriptCreateButton(Object('button_zoom_close'), message, listener)
        button:AttachTo(play_field)
        button:SetX(pos.x)
        button:SetY(pos.y)
        button:SetZ(500)
    end
	
    return button
end
-----------------------------------------------------------------------------------

function RegisterZoomZone(zone)
    local level = GetCurrentLevel()
    
    if zone.trigger_name then
        if string.find(zone.trigger_name, PREFIX_TRIGER_NAME) then
            local name = string.sub(zone.trigger_name, string.len(PREFIX_TRIGER_NAME) + 1 )
            level.zones[name]    = zone
            level['zoom_'..name] = zone
        else
            level.zones[zone.trigger_name] = zone
        end
        level.custom_cursors[zone.trigger_name] = 'CursorGlass'
    else
        if not zonesLeftCounter then
            zonesLeftCounter = 2
        end
        zonesLeftCounter = zonesLeftCounter + 1
        level.zones["zone with number" .. zonesLeftCounter] = zone;
    end
end
-----------------------------------------------------------------------------------

function UnregisterZoomZone(zone)
    GetCurrentLevel().zones[zone.trigger_name] = nil
end
-----------------------------------------------------------------------------------

function EnableActiveZoneCloseButton()
	if GetCurrentLevel().active_zone then
		GetCurrentLevel().active_zone.close_button:Go()
	end
end
--------------------------------------------------------------------------------

ActiveZonesStackClass = CreateInheritedClass(LuaStack);

function ActiveZonesStackClass:Pop()
    --assert(self:GetTop() == GetCurrentLevel().active_zone)
    LuaStack.Pop(self);
end

active_zones = ActiveZonesStackClass:New();

--------------------------------------------------------------------------------
 hidden_zoom_zones = {}
 hidden_zoom_zones.zones = LuaStack:New();
 hidden_zoom_zones.count = LuaStack:New();
    
--------------------------------------------------------------------------------
function HideZoomZones()
	local count = 0
	
    while (active_zones:GetTop()) do
        local zone = active_zones:GetTop()
        
        if zone.mode ~= ZOOM_TYPE_FULLSCREEN then
            hidden_zoom_zones.zones:Push(zone)
            
            zone:SetVisible(false)
			count = count + 1
        else
            break
        end
    end
    
    hidden_zoom_zones.count:Push(count)
end
--------------------------------------------------------------------------------

function ShowHiddenZoomZones()
    local level = GetCurrentLevel()
    
	for i = 1, hidden_zoom_zones.count:GetTop() do
        local zone = hidden_zoom_zones.zones:GetTop()
        
        if not zone:IsDisabled() then
            zone:SetVisible(true)
        end
        hidden_zoom_zones.zones:Pop()
    end
	hidden_zoom_zones.count:Pop()
end

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

function SetZoomZoneVisited(name_scene, trigger_name)
    if not trigger_name then
        return
    end

    local value = name_scene..'('..trigger_name..')'
    AddUniqueGameState(ZOOM_VISITED, value)
end
--------------------------------------------------------------------------------    

function IsZoomZoneVisited(trigger_name, name_scene)
    name_scene = name_scene or GetCurrentLevel().name
    local value = name_scene..'('..trigger_name..')'
    return HasGameState(ZOOM_VISITED, value)
end
-------------------------------------------------------------------------------

function IsInNotVisibleZoom(obj_name)
	local zone = GetObjectsZone(obj_name)
	return zone and not zone:IsVisible()
end
-------------------------------------------------------------------------------

function IsInActiveZone(obj_name)
    local level = GetCurrentLevel()
    
    if level.active_zone then
        return level.active_zone:HasObject(obj_name)
    end
        
    return GetObjectsZone(obj_name) == nil
end
-------------------------------------------------------------------------------

function GetActiveZone()
    local level = GetCurrentLevel()
    return (level ~= nil) and level.active_zone or nil
end
-------------------------------------------------------------------------------

function IsZoneExist(mode)
    local level = GetCurrentLevel()
    
    for _, zone in pairs(level.zones) do
        if zone.mode == mode then
            return true
        end
    end
    
    return false
end
-------------------------------------------------------------------------------

function IsZoomZoneNeedsScreenBottomExitZone(zone)
    return zone.mode == ZOOM_TYPE_FULLSCREEN
end

function IsClickInZoom(zone)
    local result = true
    local x,y,w,h = zone:GetClippingInfo()
    local mousePos = GetMousePos()
    
    if w > 0 and h > 0 then
        result = x<=mousePos.x and mousePos.x<=(x+w) and
                 y<=mousePos.y and mousePos.y<=(y+h)
    end
    
    return result
end
-------------------------------------------------------------------------------

function GetObjectsZone(obj_name)
    local objects_zone = nil
    local level        = GetCurrentLevel()
	
    for _, zone in pairs(level.zones) do
        if zone:HasObject(obj_name) then
            objects_zone = zone
            break
        end
    end
    
	return objects_zone
end
-----------------------------------------------------------------------------------

function IsActiveZoneObject(obj_name)
    local level = GetCurrentLevel()
    return level.active_zone and level.active_zone:HasObject(obj_name)
end
-------------------------------------------------------------------------------

function AutoCloseZoomZones()
    local level_screen = play_field:GetLevelScreen()
    local level        = GetCurrentLevel()
    local mode         = level:GetPanelLuaObject():GetMode()
    
    if mode ~= EPanelMode.PM_HIDDENOBJECT then
        return
    end
    
    for _, zone in pairs(level.zones) do
        if  not zone:IsDisabled() and
            play_field:HasObject(zone.trigger_name) and
            play_field:GetObject(zone.trigger_name):GetOpacity() > 0 and
            play_field:GetObject(zone.trigger_name):IsVisible() 
        then
            local bAutoClose = true
        
            for zoneObjName, _ in pairs(zone.objects) do 
                if level_screen:IsTaskObject(zoneObjName) and
                    not IsTaskObjectFound(zoneObjName)
                then
                    bAutoClose = false
                    break
                end
            end
            
            if bAutoClose then
                zone:SetTriggerVisible(false)
                zone:Close()
            end
        end
    end
end
-------------------------------------------------------------------------------