function CreateComicsFrame(character_name, caption, ...)
    local frame = {}

    frame.character_name = character_name
    frame.character_info = GetCharacterInfo(character_name)

    local objects = {...}
    frame.objects = {}
    for i, v in ipairs(objects) do
        local obj = play_field:GetObject(v)
        frame.objects[v] = obj
        obj:GetAnimation():SetColor('0xFFFFFFFF')
        obj:SetOpacity(0)
    end

    local text_width = 920
    local text_scale = 1.
    local text_spacing = 0.6
    local text_tracking = -17.

    if frame.character_info.width then
        text_width = frame.character_info.width
    end
    if frame.character_info.scale then
        text_scale = frame.character_info.scale
    end
    if frame.character_info.spacing then
        text_spacing = frame.character_info.spacing
    end

    local font_name = "data/Font/comics_big.fnt"

    frame.replics = {}
    frame.replic_index = 1
    local te = CreateTextEntity(font_name, caption)
    te:SetVisible(false)
    local text = te:GetText()
    local words = ExplodeString(' ', te:GetText())

    while words:size() ~= 0 do
        local replic = CreateTextEntity(font_name, caption)
        local postfix = CreateTextEntity(font_name, caption)
        replic:SetText('')
        replic:SetTracking(text_tracking)
        replic:SetScale(text_scale)
        postfix:SetText('')
        postfix:SetTracking(text_tracking)
        postfix:SetScale(text_scale)

        local prefix
        local longer_then_max_width

        for i = 0, 2 do
            local replic_filled = false
            while not replic_filled and words:size() ~= 0 do
                local word = words:get(0)
                replic:SetText(replic:GetText()..postfix:GetText())

                if replic:GetText() == '' and not prefix then
                    --postfix:SetText(' '..word)
                    postfix:SetText(word)
                else
                    postfix:SetText(' '..word)
                end

                longer_then_max_width = (replic:GetWidth() + postfix:GetWidth())--[[ * text_scale]] >= text_width
                replic_filled = words:size() == 0 or longer_then_max_width

                if not replic_filled then
                    words:erase(0)
                    replic_filled = replic_filled or words:size() == 0
                end
            end
            if not prefix then
                prefix = ''
            end
            prefix = prefix..replic:GetText()
            replic:SetText('')
            if words:size() ~= 0 then
                postfix:SetText('')
            end
        end

        replic:SetText(prefix..replic:GetText())
        if not longer_then_max_width then
            replic:SetText(replic:GetText()..postfix:GetText())
            if words:size() ~= 0 then
                words:erase(0)
            end
        end

        replic:SetBorderSize(text_width / text_scale, 0)
        replic:SetXY(60, 640)
        replic:SetZ(205)
        replic:SetScale(text_scale)
        replic:SetSpacing(text_spacing)
        replic:SetTracking(text_tracking)
        replic:SetTextAlignment(HGETEXT_TOP + HGETEXT_LEFT)
        replic:AttachTo(play_field:GetLevelScreen():GetPanel())
        replic:SetVisible(false)
        replic:SetColor(frame.character_info.text_color)

        if postfix then postfix:SetVisible(false) end

        table.insert(frame.replics, replic)
    end

    local nr = table.getn(frame.replics)

	frame.SetVisible = function(is_visible, ignore_objects, time)
	    if not time then
	        time = ZOOM_ZONE_APPEARING_TIME
	    end
        if is_visible then
	        frame.replic_index = 0
	        frame.ShowNextReplic(0.25)
        else
            if frame.replic_index > 0 and frame.replic_index <= table.getn(frame.replics) then
                frame.replics[frame.replic_index]:SetVisible(false)
            end
        end
        for i, v in pairs(frame.objects) do
			if not ignore_objects or not ignore_objects[i] then
				obj = v
				if is_visible and obj:GetOpacity() < 1. then
					obj:ChangeOpacity(obj:GetOpacity(), 1., time)
				end
				if not is_visible and obj:GetOpacity() > 0. then
					obj:ChangeOpacity(obj:GetOpacity(), 0., time)
				end
			end
		end
    end

	frame.ShowNextReplic = function(delay)
	    if not frame.AllReplicsShown() then
	        if frame.replic_index > 0 then
                frame.replics[frame.replic_index]:SetVisible(false)
            end
	        frame.replic_index = frame.replic_index + 1
	        frame.replics[frame.replic_index]:SetVisible(true)

            if delay == nil then
                delay = 0.
            end

            local af = CreateAnimationEffector()
            af:AttachTo(frame.replics[frame.replic_index])
            af:SetKey(_TRACK_O, delay + 0.0, 0.)
            af:SetKey(_TRACK_O, delay + 0.3, 1.)
            af:Play()
	    else
	        frame.replics[frame.replic_index]:SetVisible(false)
	    end
    end

	frame.AllReplicsShown = function()
	    return frame.replic_index == table.getn(frame.replics)
    end

    return frame
end

function CreateComics(name, ...)
    if not HasGameState('ComicsShown', name) then
        local comics = {}

        comics.frames = {...}
        comics.frame_index = 1
        comics.is_visible = false
        comics.locker_nreferences = game_state.locker:GetNReferences()

        game_state.comics = comics

        comics.SetVisible = function(is_visible, time)
            if comics.frames and
               comics.is_visible ~= is_visible--[[ and
               not game_state.comics]]
            then
                comics.is_visible = is_visible

                local is_correct_frame = comics.frame_index >= 1 and comics.frame_index <= table.getn(comics.frames)

                if is_correct_frame then
                    local frame = comics.frames[comics.frame_index]
                    frame.SetVisible(is_visible, time)
                end

                comics.is_monolog = false
                if comics.frames[1].character_name == 'PlayerMonolog' then
                    assert(table.getn(comics.frames) == 1)
                    assert(table.getn(comics.frames[1].replics) == 1)
                    comics.is_monolog = true
                    local replic = comics.frames[1].replics[1]
                    replic:SetXY(145., 715.)
                end

                if is_visible then
                    assert(is_correct_frame)
                    game_state.locker:AddRef()
                    comics.panel_mode_to_restore = play_field:GetLevelScreen():GetPanel():GetMode()
                    if comics.is_monolog then
                        play_field:GetLevelScreen():GetPanel():SetMode(PM_COMICSMONOLOG)
                    else
                        play_field:GetLevelScreen():GetPanel():SetMode(PM_COMICS)
                        if game_state.is_inventory_opened then
                            OnInventoryButtonPress()
                        end
                    end
                else
                    game_state.locker:Release()
                    play_field:GetLevelScreen():GetPanel():SetMode(comics.panel_mode_to_restore)
                    AddGameState('ComicsShown', name)
                    if comics.on_finish then
                        comics.on_finish()
                    end
                end
            end
        end

        comics.ShowNextFrame = function()
            if comics.frame_index <= table.getn(comics.frames) then
                local previous_frame = comics.frames[comics.frame_index]

                if not previous_frame:AllReplicsShown() then
                    previous_frame.ShowNextReplic()
                else
                    local ignore_objects_on_hide
                
                    comics.frame_index = comics.frame_index + 1
                    if comics.frame_index <= table.getn(comics.frames) then
                        local frame = comics.frames[comics.frame_index]
                        frame.SetVisible(true)
                        ignore_objects_on_hide = frame.objects
                    else
                        game_state.previous_comics = game_state.comics
                        game_state.comics = nil
                        comics.SetVisible(false)
                        comics.frame_index = 1
                    end

                    previous_frame.SetVisible(false, ignore_objects_on_hide)
				end
            end
        end

        comics.IsChangingReplic = function()
            local frame = comics.frames[comics.frame_index]
            local replic = frame.replics[frame.replic_index]
            return not replic:IsVisible() or
                   replic:GetOpacity() < 1.
        end

        return comics
    else
        return nil
    end
end
