Приветствуем вас на форуме проекта WoW Circle. Если вы читаете это, значит не зарегистрировались у нас. Для того, чтобы получить доступ к расширенным возможностям нашего форума нажмите сюда и пройди регистрацию, которая не займет у вас много времени. После регистрации будут доступны новые, более расширенные, возможности.
[Guide] oUF (Otravi Unit Frames)

Упомянутые в теме пользователи:

Страница 1 из 7 123 ... ПоследняяПоследняя
Показано с 1 по 12 из 77
  1. #1
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157

    [Guide] oUF (Otravi Unit Frames)

    oUF(Otravi Unit Frames) by Haste.
    1. Description.
    Долго размышлял, что же такое гайд по аддону и зачем, собственно, он нужен. И все-таки решился взяться за очень непростую часть интерфейса рядового пользователя, а точнее речь пойдёт об oUF, довольно хардкорный аддон, но не пугайтесь. Зачем всё это? Не могу дать точный ответ, наверно стандартные GUI аддоны не так интересны, да и настройка их не так сложна и к слову, они никогда не смогут дать тот функционал, который идеален для Вас.

    Что же такое этот oUF? Это фреймвёрк, то есть, собственная среда разработки аддона на юнитфреймы. Но сам oUF это не юнитфреймы, по сути это даже не аддон, а библиотека для создания своих собственных фреймов, он не создаёт их, он следит за тем, что мы напишем в oUF-layout и обновляет все эти события. С его помощью можно создать свои, уникальные фреймы называемые oUF-layout. Собственно не нужно очень хорошо знать LUA чтобы приступить к работе, в данном гайде, я постараюсь разобрать основы его и прикладные WoWAPI при создании layout-ов.

    С чего начать? Сперва, посоветую обзавестись терпением (Если на на GUI аддоны может уйти час другой, то на oUF можно потратить сотни часов). По существу, очень советую скачать какой-нибудь LUA эдитор (Можно конечно обойтись любым текстовым редактором, который просто способен запомнить текст и открыть его, можно даже использовать блокнот, если вам угодно). Из всевозможных очень хорошо себя показал Notepad ++, там есть всё для комфортного программирования, множество библиотек, кодировок (В том числе нужная нам в будущем UTF – 8, которая используется клиентом WoW для отображения ру-символов, подсветка синтаксиса), смена стилей, шрифтов и прочего.

    Более подробно про LUA-редакторы читаем тут.
    Что такое UTF-8 можно прочитать тут.

    Немного о LUA от себя.
    Я считаю, что сухая теория для новичка ни к чему, это не наш выбор, проще начать с практики, документация LUA может отпугнуть человека далёкого от программирования, именно поэтому я не буду прилагать тут стены текста из документации, то, что нужно человеку, он откроет и прочитает сам.

    Полезные ссылки:
    1. Подробнейший мануал по LUA на Русском языке.
    2. Widget WoW API.
    3. XML.


    Постараюсь разобрать преимущества oUF и его недостатки.
    1. Плюсы. Во-первых, малая ресурсопотребляемость, на моей памяти, из всех oUF-layout-ов, что я встречал, максимум было потребление памяти 160кб, не много, правда? Именно поэтому он стал столь излюбленным аддоном у более менее серьзных UI-мейкеров. Тогда как Pitbull, STUF, SUF, X-Perl могут разрастись до неприличных размеров, если сравнить по функционалу, то даже становиться смешно, авторы вышеперечисленных 4х аддонов попросту ограничивают пользователей в функционале, Вы никогда не сделаете там так, как для вас было бы идеально. В oUF всё ограничивается вашими знаниями языка LUA и фантазией, ни больше, ни меньше того, можно сделать абсолютно всё.

    В последней версии Pitbull автор добавил офигенную фичу, возможность написать свой скрипт прям в окошке аддона, очень полезная штука чего не встретишь в остальных аддонах такого плана.

    2. Минусы. Всё-таки, как не крути, у oUF отсутствует настройка в игре, из-за чего он кажется всем очень сложным и неподъемным.

    2. Begin.
    Для начала, конечно же, нужно скачать сам oUF он для версии 3.3.5.
    Кидаем его в папку AddOns. Там же создаём папку oUF_Test.
    В паке oUF_Test создаем файл с расширением .toc , называем его oUF_Test. Так же создадим файл oUF_Test.lua (обязательно с таким расширением!) Как раз в этом файле будет находиться код вашего будущего лэйаута. Все, что будет в нём находится это ВАШ код, это ВАШЕ видение о том, какой должен быть юнитфрейм.

    Открываем oUF_Test.toc, заполняем его таким текстом ==>
    PHP код:
    ## Interface: 30300
    ## Title: oUF_Test
    ## Notes: My oUF layout.
    ## RequiredDeps: oUF

    oUF_Test.lua 

    Разберу вкратце, что же означают все эти строчки в .toc файле.
    Interface – Эта строка определяет версию клиента для данного аддона, для 3.3.5 версии клиента используется определение 30300, существует множество методов, как по-другому определить версию клиента, но на этом не будем останавливаться. Это же встречается, если жать на галочку “Использовать устаревшие модификации”, клиент проверяет эту строку, если клиент 3.3.5 и он видит, что в строке не 30300 он будет помечать этот аддон как устаревший.

    Title – Это то что отображается в Вашем списке модификаций.

    Notes – Тут всё проще, это окно дополнительной информации о данной модификации, можно навести мышкой на аддон и увидеть окошко (тултип), обычно туда записывают описание аддона.

    RequiredDeps – Сюда записывается зависимый аддон для загрузки этого, тут же нужно это вписать, чтобы oUF загружался вместе с вашим layout-ом, на деле можно записать множество аддонов, но нужно их отделять запятой.

    Более подробно про TOC формат читаем тут.

    Создадим папку media, в ней будут храниться ваши медиа-файлы, то есть, шрифты, текстуры.


    Сразу же наполним папку media, чтобы не возвращаться к этому. Я очень неравнодушен к пиксельным шрифтам, но заметил, что он не всем приходится по душе из-за своего требования к размеру, т.е. или мелкий, но четкий, или крупный, но уже не такой четкий, поэтому тут компромисс, я выбрал шрифт Calibri Bold, в принципе тут не важно, можете взять любой свой. Назовём его Simple.TTF и отправим в папку.
    Текстура, тут ничего сказать не могу, кто-то любит вогнутые текстуры, кто-то прямые и монотонные, кто-то глянцевые, кидайте какую хотите, назовём её Statusbar и отправим туда же. Эта текстура послужил нам для фонов, полоски хп/мп, кастбаров и т.д.
    Последний раз редактировалось Alt; 18.05.2013 в 10:03.

  2. 7 пользователей сказали cпасибо Alt за это полезное сообщение:

    •Pervyi•™ (15.03.2016),Error (05.01.2012),Feedanza (05.01.2012),haikulolz (12.01.2012),Leecat (20.01.2013),Планктон (18.11.2012),ДядькаЗмей (07.09.2012)

  3. #2
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    3. Coding.
    Маленький словарик.

    1. True – истина, правда.
    2. False – ложь.
    3. If – если.
    4. Else – еще.
    5. Elseif – еще если (так же переводится если имеется, но это когда вычисляется True, пишется именно вместе, в PHP языке насколько помню можно двумя словами, так, небольшое отсутпление).
    6. Not – не.
    7. Then – значит, то.
    8. End – конец.
    9. Nil – неизвестно.
    10. String – строка.

    Постараюсь разобрать, какие знания нам нужны в LUA и WoWAPI для успешного кодинга.

    PHP код:
    local arg = *** 
    Это представление о функции (переменная внутри аддона) она доступна только на этом сегменте (тобишь только для этого аддона). Начинается с local, где arg имя функции, как правило, говорит само за себя.

    По local хочу еще добавить, что если Вы пишите например так:
    PHP код:
    local test 
    И
    PHP код:
    local tEst 
    Это абсолютно 2 разные вещи, это я к тому, что LUA чувствителен к регисту.

    Есть такой вариант
    PHP код:
    local func = function(arg
    ***
    end 
    Тоже функция, тоже начинается с local, но заканчивается на end, func – это название самой функции, этим вариантом мы будем пользоваться когда будем создавать backdrop, fontstring и т.д.

    PHP код:
    if(argthen
    ***
    end 
    Это условие. Начинается с if, кончается на end. Каков смысл этого? Идёт проверка условия, где условие это (arg), мы проверяем его на ложь (false), истина (true). Можно делать в условии, можно в функции.
    Пример использования.
    PHP код:
    if (arg) == true then
    elseif (arg2then
    end 
    Среда применения? Незаменимая штука когда, к примеру, для одного фрейма хочется задать один параметр, а для другого, второй.

    Ниже рассмотрим прикладные WidgetAPI и т.д.

    PHP код:
    ***:SetStatusBarTexture(texture
    Как раз таким способом задается текстура для нашей полосы хп, мп и т.д.

    PHP код:
    ***:SetText(text
    Пишем какой-либо текст. Альтернативы? Существует :SetFormattedText, он намного эффективней по ресурсам и более продвинут.

    PHP код:
    ***:SetFont(fontsizestyle
    Устанавливаем шрифт, где font – путь к шрифту, size – размер, style – вид шрифта, может быть "OUTLINE", "MONOCHROME", "THIOUTLINE", насчет стиля это по вкусу. Я для пиксельного шрифта советую "MONOCHROME", для обычного можно и не делать вовсе. Опять же чисто косметическое решение.

    PHP код:
    ***:SetStatusBarColor(rgb
    Ставим цвет для Вашего фрейма, можно задать абсолютно любой цвет. По цветам могу добавить, что прописываются они таким образом:
    Прописывается от 0 до 1 это в "%", тоесть х/255

    пример:

    PHP код:
    0.22 54/255 
    Выглядит так
    PHP код:
    ***:SetStatusBarColor(0.22,  0.22,  0.22
    PHP код:
    ***:SetVertexColor(rgb
    Применяется обычно для фонов фреймов, для затемнения можно сделать 0.3, 0.3, 0.3.
    PHP код:
    ***:SetBackdrop(backdrop
    Делаем подложку Вашему фрейму, детальней будет чуть позже.
    PHP код:
    ***:SetFontString(framefontsizestyle
    Аналогично с ***: SetFont, только тут идет привязка к определенному фрейму, где фрейм это frame, опять же, об этом позже.
    PHP код:
    ***:SetFrameLevel(level
    Устанавливаем уровень вашего фрейма, таким образом можно сделать, допустим, полоску хп над полоской мп. От 0-100.
    PHP код:
    ***:SetFrameStrata(strata
    Как бы уровень важности данного фрейма, множество вариантов, например, "HIGH", "MEDIUM","LOW", "BACKGROUND" - названия, думаю, говорят сами за себя.
    PHP код:
    ***:SetSize(size
    Размер чего-либо. Им можно заменять :SetWidth и :SetHeight. Прописывается так:
    PHP код:
    ***:SetSize(124124где 124 это ширина и высота
    PHP код:
    ***:SetAlpha(alpha
    Задаем прозрачность фрейму от 0 до 1. Тут замечу, что не всем элементам можно задавать прозрачность именно таким способом, для экшнбаров (например, когда делается маусовер для них) надо делать функцию, точно так же и для портретов в юнитфреймах.

    PHP код:
    ***:SetWidth(width
    Ширина.
    PHP код:
    ***:SetHeight(height
    Высота.
    PHP код:
    ***:SetScale(scale
    Задаем значение скейла для фрейма.
    PHP код:
    ***:SetParent(frame
    Прописываем принадлежность к фрейму.
    PHP код:
    ***:SetPoint(point
    Прописываем положение объекту. Это самый простой вариант, он содержит только 1 аргумент (point). Область применения? Это основа всего, практически, ни один фрейм не появится на экране без этого аргумента, к примеру, можно представить варианты расположения точек на экране таким образом:



    Обязательно (!) пишется в кавычках. Есть более "продвинутый" вариант.
    PHP код:
    ***:SetPoint(pointframerelativepointxy
    Пояснение:
    Point – Точка объекта которую мы привязываем к чему-нибудь.
    Frame – Сам фрейм, к которому идёт привязка всего, в данном случае если говорить о экране то пишем UIParent (обязательно так).
    Relativepoint – Точка на frame к которой идёт привязка.
    X, Y – отступы по горизонтали и вертикале, вспомните школу, представьте координатную плоскость, тут всё так же, x с плюсом значит фрейм двигается вправо, x с минусом значит фрейм двигается влево, y с плюсом значит фрейм двигается наверх, y с минусом значит фрейм двигается вниз. Всё просто.
    PHP код:
    ***:SetJustifyH(point
    Выравнивание, допустим, шрифта, где point обычно это "CENTER". Косметическое решение.
    Есть вот такое еще решение:

    PHP код:
    ***:SetJustifyV(point
    На самом деле можно продолжать бесконечно, ибо WoWAPI на это дело очень расщедрилось, в общем, если когда-то, что-то в коде не понятно, не понятно, что значит та или иная функция просто заходим сюда и ищем то, что нужно. Всё довольно-таки просто.
    Последний раз редактировалось Alt; 10.08.2012 в 06:04.

  4. 5 пользователей сказали cпасибо Alt за это полезное сообщение:

    •Pervyi•™ (15.03.2016),Error (05.01.2012),haikulolz (12.01.2012),Leecat (20.01.2013),ДядькаЗмей (07.09.2012)

  5. #3
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    4. Programming.
    Итак, начинаем наполнять файл oUF_Test.lua
    Для удобства советую разделять части кода комментариями, код становится качественнее, читабельнее и, что самое главное, понятней. Понятней Вам. Но замечу, что в слишком частые комментарии не стоит убиваться, разделяйте по смыслу, блок настроек медиа файлов под один комментарий, блок размеров под другой и т.д.
    Ниже покажу самые распространенные способы, как закомментировать код.
    1.
    PHP код:
     -- Бла бла бла 
    Это самый распространенный способ и мне он очень нравится, мало занимает места, комментирует целую строку, да и приятен глазу.

    2.
    PHP код:
    ------------------------------------------------------------------------
    -- 
    Бла бла бла
    ------------------------------------------------------------------------ 
    Можно и так, но советую все же комментировать так особо большие куски с 1 смыслом.

    Так же есть способ закомментировать код, то есть спрятать его, делается так
    PHP код:
    --[[ 
    Кусок кода который комментируется.

    ]] 
    PHP код:
    --[[ бла бла бла ]]-- 
    Главное преимущество такого способа это скрыть код, допустим, вы что-то пишите и у вас это не получается закончить, просто закомментируйте подобным образом, не будет мешаться глазу да и ошибок LUA выдавать не будет.

    Переходим к коду. Далее я решил разбить все это дело на смысловые куски, так более понятней. Let's start!

    PHP код:
    local font = [=[Interface\AddOns\oUF_Test\media\Simple.TTF]=]
    local texture = [=[Interface\AddOns\oUF_Test\media\Statusbar]=]
    local font_size 11
    local font_style 
    'THINOUTLINE' 
    Начнём с этого, как видите это медиа-блок, тут у нас путь к шрифту, текстурке, размер шрифта и его стиль, советую всегда начинать именно так, потому что если это затерять где-то в середине кода, то будут неприятные ошибки в стиле "собстно вы у нас тут написали текстуру, а текстуры то нет, не порядок". Зачем это? Это допустим нужно, когда надо где-то прописать текстуру, шрифт или еще что, что бы не писать по 20 раз полный путь пользуемся этим. Хочу сказать по части пути, можно прописывать в "", можно в ' ', мне больше нравится такой вариант, через слэши [=[]=].

    PHP код:
    local sfix CreateFrame("Frame")
    sfix:RegisterEvent("PLAYER_LOGIN")
    sfix:SetScript("OnEvent", function()
        
    SetCVar("useUiScale"1)
        
    SetCVar("uiScale"768/string.match(({GetScreenResolutions()})[GetCurrentResolution()], "%d+x(%d+)"))
    end
    Пропишем скрипт на правильный UIScale по Вашему разрешению. Зачем он? Я буду использовать Backdrop, он очень чувствителен к значению скейла, малейшие неполадки и он искажется. Еще очень важная часть про UIScale, когда он правильный, то и координаты будут верные и размер фреймов будет верный, соответственно, при изменении значения Scale через игровые опции, пропадут ровные линии, покосится шрифт и еще много других неприятных вещей.

    Подробнее узнать о UI Scale можно тут.

    PHP код:
    _G["BuffFrame"]:Hide()
    _G["BuffFrame"]:UnregisterAllEvents()
    _G["BuffFrame"]:SetScript("OnUpdate"nil
    Спрячем близзард-баффы и дебаффы, можно делать и так:
    PHP код:
    _G["BuffFrame"]:Hide() 
    Тут есть 1 НО, вторым вариантом мы просто спрячем их, если же брать первый, то мы объявляем функцию OnUpdate неизвестной и убираем все эвенты связанные с аурами, то есть ресурсы на баффы больше тратятся не будут по сравнению со 2ым вариатом, выигрываем копейки памяти, но и это очень неплохо.

    PHP код:
    local backdrop = {
        
    bgFile = [=[Interface\Buttons\WHITE8x8]=],
        
    edgeFile = [=[Interface\Buttons\WHITE8x8]=], edgeSize 1,
        
    insets = {top 0bottom 0right 0left 0}

    Делаем бэкдроп. Что это? Это наша подложка фрейма, на деле будет выглядеть как 1пиксельная граница для всех элементов.
    Мы берем текстуры из клиента WoW, это белый квадрат 8 на 8. Размер границы ставим 1. Insets это как бы ''вогнутость" нашей границы, если значение > 0 то граница выгибается наружу и мы получаем её в виде 3х пикселей, в данном случае у нас она 1 пиксельная поэтому оставим 0.

    Что такое Backdrop (очень подробно) написано тут.

    Про edgeFile читаем тут.

    PHP код:
    local CreateBackdrop = function(parent)
        
    local bg CreateFrame("Frame"nilparent)
        
    bg:SetPoint('TOPLEFT'parent'TOPLEFT', -11)
        
    bg:SetPoint('BOTTOMRIGHT'parent'BOTTOMRIGHT'1, -1)
        
    bg:SetFrameLevel(parent:GetFrameLevel() - 1)
        
    bg:SetBackdrop(backdrop)
        
    bg:SetBackdropColor(0000.6)
        
    bg:SetBackdropBorderColor(0001)
        return 
    bg
    end 
    Делаем функцию для этого бэкдропа, что бы каждый раз не прописывать к фреймам:

    PHP код:
    ***:SetBackdrop(backdrop)
    ***:
    SetBackdropColor(rgb)
    ***:
    SetBackdropBorderColor(rgb
    PHP код:
    local CreateFontString = function(framefsizefstylesfont)
        
    local fstring frame:CreateFontString(nil'OVERLAY')
        
    fstring:SetFont(fontfont_sizefont_style)
        
    fstring:SetShadowColor(0000)
        return 
    fstring
    end 
    То же самое что и в случае с бэкдропом, только тут шрифт, что бы не прописывать 100 раз путь к шрифту просто внесём пути шрифта в функцию.
    PHP код:
    ***:SetShadowColor(0000
    Этим убираем тень у шрифта.

    PHP код:
    local menu = function(self)
        
    local unit self.unit:gsub("(.)"string.upper1
        if 
    _G[unit.."FrameDropDown"then
            ToggleDropDownMenu
    (1nil_G[unit.."FrameDropDown"], "cursor")
        elseif 
    self.unit:match("party"then
            ToggleDropDownMenu
    (1nil_G["PartyMemberFrame"..self.id.."DropDown"], "cursor")
        else
            
    FriendsDropDown.unit self.unit
            FriendsDropDown
    .id self.id
            FriendsDropDown
    .initialize RaidFrameDropDown_Initialize
            ToggleDropDownMenu
    (1nilFriendsDropDown"cursor")
        
    end
    end 
    Меню по правой кнопки мыши с инвайтами и прочим. Это только функция, как помним, функцию надо прописать что бы она появилась, это позже.

    Дальше будет блок аур, их настройка, вид.
    Код:
    local FormatTime = function(s)
            Прописываем формат времени на баффах, дебаффах (аурах), можно делать до десятых долей секунд.
    	local DAY, HOUR, MINUTE = 86400, 3600, 60
    	if s >= DAY then
    		return format('%dd', floor(s/DAY + 0.5)), s % DAY
    	elseif s >= HOUR then
    		return format('%dh', floor(s/HOUR + 0.5)), s % HOUR
    	elseif s >= MINUTE then
    		return format('%dm', floor(s/MINUTE + 0.5)), s % MINUTE
    	end
    	return floor(s + 0.5), s - floor(s)
    end
    
    UpdateAuraTimer = function(self, elapsed)
            Тут как будет отображаться этот формат вывода времени.
    	if self.timeLeft then
    		self.elapsed = (self.elapsed or 0) + elapsed
    		if self.elapsed >= 0.1 then
    			if not self.first then
    				self.timeLeft = self.timeLeft - self.elapsed
    			else
    				self.timeLeft = self.timeLeft - GetTime()
    				self.first = false
    			end
    			if self.timeLeft > 0 then
    				local time = FormatTime(self.timeLeft)
                                    Не забываем прописать формат времени.
    				self.remaining:SetText(time)
    				if self.timeLeft < 5 then
                                    Если срок действия ауры < 5 секунд
                                    значит цвет текста на аурах будет красный.
    					self.remaining:SetTextColor(1, 0.2, 0.2)
    				else
                                    Для остальных случаев белый.
    					self.remaining:SetTextColor(1, 1, 1)
    				end
    			else
    				self.remaining:Hide()
    				self:SetScript("OnUpdate", nil)
    			end
    			self.elapsed = 0
    		end
    	end
    end
    
    CreateAuraTimer = function(icon, duration, expTime)
    	icon.first = true
    	if duration and duration > 0 then
    		icon.remaining:Show()
    		icon.timeLeft = expTime
    		icon:SetScript("OnUpdate", UpdateAuraTimer)
                    Прописываем обновление иконок аур.
    	else
    		icon.remaining:Hide()
    		icon.timeLeft = 0
    		icon:SetScript("OnUpdate", nil)
    	end
    end
    
    PostUpdateDebuff = function(self, unit, icon, index)
    Создаём вид дебаффов.
    	local name, _, _, _, dtype, duration, expTime = UnitAura(unit, index, icon.filter)
    	local c = DebuffTypeColor[dtype] or DebuffTypeColor.none
    Делаем границу аур по цвету дебаффа.
    	icon.bg:SetBackdropBorderColor(c.r, c.g, c.b)
            icon.icon:SetDesaturated(true)
    Делаем не свои ауры серыми, удобно мониторить свои дебаффы на цели.
    	CreateAuraTimer(icon, duration, expTime)
    end
    
    PostUpdateBuff = function(self, unit, icon, index)
    Аналогично с дебаффами только баффы.
    	local name, _, _, _, dtype, duration, expTime = UnitAura(unit, index, icon.filter)
    	CreateAuraTimer(icon, duration, expTime)
    end
    
    local CancelAura = function(self, button)
    Отменяем ауры правой кнопкой мыши.
    	if button == "RightButton" and not self.debuff then
    		CancelUnitBuff("player", self:GetID())
    	end
    end
    
    PostCreateAuraIcon = function(self, button)
    	button.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
    	button.icon:SetDrawLayer('ARTWORK')
            Прописываем иконку ауры.
    	
    	button.overlay:SetTexture(nil)
            Задаем нулевое значение текстуры для аур,
            мы ведь пользуемся бэкдропом.
    
    	button.remaining = CreateFontString(button)
    	button.remaining:SetPoint('CENTER', 1, 1)
            Время аур по центру.
    	
    	button.count:SetFont(font, font_size, font_style)
    	button.count:SetPoint('TOPRIGHT', button, 3, 19)
            В правом верхнем углу стаки аур.
    	
    	button.bg = CreateBackdrop(button)
            Бэкдроп.
    	
    	if unit == "player" then
            Прописываем чтобы только игроку можно было отменять ауры правой кнопкой мыши.
    		button:SetScript("OnMouseUp", CancelAura)
    	end
    end
    Последний раз редактировалось Alt; 10.08.2012 в 06:07.

  6. 5 пользователей сказали cпасибо Alt за это полезное сообщение:

    •Pervyi•™ (15.03.2016),Error (05.01.2012),haikulolz (12.01.2012),Leecat (20.01.2013),ДядькаЗмей (07.09.2012)

  7. #4
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    Код:
    local SetColors = setmetatable({
    Та самая metatable, она будет использоваться не только для хп или мана бара, а так же для цветов текста маны, энергии, ярости.
    	power = setmetatable({
    		["MANA"] = {0.31, 0.45, 0.63},
                    Цвет маны, для кастеров :3
    		["RAGE"] = {0.69, 0.31, 0.31},
                    Для воинов и мобов.
    		["FOCUS"] = {0.71, 0.43, 0.27},
    		["ENERGY"] = {0.65, 0.63, 0.35},
                    Энергия разбойников и друидов.
    		["HAPPINESS"] = {0.19, 0.58, 0.58},
    		["RUNES"] = {0.55, 0.57, 0.61},
    		["RUNIC_POWER"] = {0, 0.82, 1},
                     РП для дк. Ну и так далее.
    	}, {__index = oUF.colors.power}),
    	reaction = setmetatable({
    		[1] = {0.85, 0.27, 0.27}, 	-- Hated (Цвет ненависти)
    		[2] = {0.85, 0.27, 0.27}, 	-- Hostile (Неприязнь)
    		[3] = {0.85, 0.27, 0.27}, 	-- Unfriendly(Недружелюбие)
    		[4] = {0.85, 0.77, 0.36}, 	-- Neutral(Нейтральное отношение)
    		[5] = {0.33, 0.59, 0.33}, 	-- Friendly(Дружеское)
    		[6] = {0.33, 0.59, 0.33}, 	-- Honored(Почти Уважение)
    		[7] = {0.33, 0.59, 0.33}, 	-- Revered(Уважение)
    		[8] = {0.33, 0.59, 0.33}, 	-- Exalted(Превознесение)
    Это для фракций, какой цвет будет у хп баров для мобов и НПС.	
    	}, {__index = oUF.colors.reaction}),
    }, {__index = oUF.colors})
    Код:
    local ShortValue = function(value)
    	Это нужно для точных вычислений значений, 
            к слову, с помощью этого формата числа уменьшаются в 10млн. раз с точностью до 1(!) знака.
    	if value >= 10000000 then
    		return string.format('%.1fm', value / 1000000)
    	elseif value >= 1000000 then
    		return string.format('%.2fm', value / 1000000)
    	elseif value >= 100000 then
    		return string.format('%.0fk', value / 1000)
    	elseif value >= 10000 then
    		return string.format('%.1fk', value / 1000)
    	else
    		return value
    	end
    end
    
    local PostUpdateHealthBar = function(health, unit, min, max)
    	if not UnitIsConnected(unit) or UnitIsGhost(unit) or UnitIsDead(unit) then
    		if not UnitIsConnected(unit) then
    			health.value:SetText("|cffD7BEA5".."Offline".."|r")
    			Тут цвет когда юнит оффлайн.
    		elseif UnitIsDead(unit) then
    			health.value:SetText("|cffD7BEA5".."Dead".."|r")
    			Серенький цвет для тех, кто не очень жив.
    		elseif UnitIsGhost(unit) then
    			health.value:SetText("|cffD7BEA5".."Ghost".."|r")
    			По-серее цвет для тех кто бежит с кладбища.
    		end
    	else
    	
    		local r, g, b
    		
    		if min ~= max then
    			r, g, b = oUF.ColorGradient(min/max, 0.69, 0.31, 0.31, 0.65, 0.63, 0.35, 0.33, 0.59, 0.33)
    			Цветовой градиент для значений на 100% хп будет зеленый, на половине желтый, >30% будет красный.
    			if unit == "player" and health:GetAttribute("normalUnit") ~= "pet" then
    				health.value:SetFormattedText("|cffAF5050%d|r |cffD7BEA5-|r |cff%02x%02x%02x%s%%|r", min, r * 255, g * 255, b * 255, math.floor(min /max*100+.5))
    				SetFormattedText очень сильная и эффективная штука, намного экономней чем ***:SetText(text), но есть минус, это временный файл, то есть нельзя использовать как FontString.
    				Описание есть в интернете. Здесь я поставил разделитель текста символ "-", тут же и сокращаются цифры.
    			elseif unit == "pet" then
    				health.value:SetFormattedText("|cff%02x%02x%02x%s|r", r * 255, g * 255, b * 255, ShortValue(min))
    			else
    				health.value:SetFormattedText("|cffAF5050%s|r |cffD7BEA5-|r |cff%02x%02x%02x%s%%|r", ShortValue(min), r * 255, g * 255, b * 255, math.floor(min /max*100+.5))
    			end
    		else
    			if unit ~= "player" and unit ~= "pet" then
    				health.value:SetText("|cff559655"..ShortValue(max).."|r")
    			else
    				health.value:SetText("|cff559655"..max.."|r")
    			end
    		end
    	end
    end
    
    PreUpdatePower = function(power, unit)
    	local _, pType = UnitPowerType(unit)
    	local color = SetColors.power[pType]
    	Тут прописываем что бы цвет текста мп был по типу маны, или энергии, опять же зависит от класса.
    	if color then
    		power:SetStatusBarColor(color[1], color[2], color[3])
    	end
    end
    
    PostUpdatePower = function(power, unit, min, max)
            Тут аналогично с PostUpdateHealthBar,
             смотрим там же. 
             Но все же есть некоторые косметические правки, 
             не показывать ману у мертвых, не показывать ману для духов и прочее.
    	local self = power:GetParent()
    	local pType, pToken = UnitPowerType(unit)
    	local color = SetColors.power[pToken]
    
    	if color then
    		power.value:SetTextColor(color[1], color[2], color[3])
    	end
    
    	if not UnitIsPlayer(unit) and not UnitPlayerControlled(unit) or not UnitIsConnected(unit) then
    		power.value:SetText()
    	elseif UnitIsDead(unit) or UnitIsGhost(unit) then
    		power.value:SetText()
    	else
    		if min ~= max then
    			if pType == 0 then
    				if unit == "target" then
    					power.value:SetText(ShortValue(min))
    				elseif unit == "player" and power:GetAttribute("normalUnit") == "pet" or unit == "pet" or unit == 'arena' then
    					power.value:SetFormattedText("%%s%%", math.floor(min /max*100+.5))
    				elseif unit == "player" then
    					power.value:SetFormattedText("%s%% |cffD7BEA5-|r %d", math.floor(min /max*100+.5), min)
    				else
    					power.value:SetText(min)
    				end
    			else
    				power.value:SetText(min)
    			end
    		else
    			if unit == "player" or unit == 'arena' then
    				power.value:SetText(min)
    			else
    				power.value:SetText(ShortValue(min))
    			end
    	end	end
    end
    Что такое metatable читаем тут.

  8. 5 пользователей сказали cпасибо Alt за это полезное сообщение:

    •Pervyi•™ (15.03.2016),Error (05.01.2012),haikulolz (12.01.2012),Leecat (20.01.2013),ДядькаЗмей (07.09.2012)

  9. #5
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    Код:
    local CreateStyle = function(self, unit)		
    	Главная функция, основная, запоминаем её название.(CreateStyle)
    	self:RegisterForClicks('AnyUp')							
    	self:SetScript('OnEnter', UnitFrame_OnEnter)
    	self:SetScript('OnLeave', UnitFrame_OnLeave)
    	Делаем фреймы кликабельными.
    	self.colors = SetColors	
    	Прописываем цвета фреймов которые берутся из metatable.
    	self:SetAttribute('type2', 'menu')
    	self.menu = menu					
    	Помните я писал про функцию меню?
            Не забываем прописать.
    	
    	self.Health = CreateFrame('StatusBar', nil, self)
    	Создаем полоску с хп, статусбар.
    	self.Health:SetStatusBarTexture(texture)
    	self.Health:SetHeight(25)														
    	На высоту полоски хп выделим 25 пикселей.
    	self.Health:SetPoint('TOPLEFT')								
    	self.Health:SetPoint('TOPRIGHT')
    	self.Health.PostUpdate = PostUpdateHealthBar									
    	Указываем к чему относятся наши переменные, а то не видать нам текста хп.
    
    	self.Health.frequentUpdates = true
    	Обновление хп по функции OnUpdate, это значит при каждой новой отрисовке фрейма,
            т.е. (ваше фпс) то есть изменению вашего фрейма раз в секунду. 
            Он будет обновляться, не указав это он попросту "забудет" об этом.
            То же самое будет и с маной (задастся в соотв. месте)
    	self.Health.colorTapping = true
    	self.Health.colorClass = true													
    	О чем я и писал где-то, цвет хп бара по цвету класса.
    	self.Health.colorReaction = true												
    	Это для НПС и мобов, делаем цвета по отношению к вам(дружелюбны ли, враждебны и т.д.)
    	
    	self.Health.bg = self.Health:CreateTexture(nil, 'BORDER')						
    	Фон полоски хп.
    	self.Health.bg:SetAllPoints()
    	self.Health.bg:SetTexture(texture)
    	self.Health.bg.multiplier = 0.25												
    	Делаем фон полоски хп по-темнее чем цвет самой полоски.
    	self.Health.background = CreateBackdrop(self.Health)							
    	Прописываем подложку (бэкдроп.)
    	
    	self.Health.value = CreateFontString(self.Health)								
    	(!) важно. Прописываем текст хп, где он будут отображаться,
            как видно тут используется конструкция 
            которую можно посмотреть в блоке моего гайда под названием 'Coding'.
    	if unit == 'player' or unit == 'target'  then
    		self.Health.value:SetPoint('RIGHT', 0, 4)
    	elseif  unit == 'pet' or unit == 'focus' or unit == 'targettarget' then
    		self.Health.value:SetPoint('LEFT', 0, 1)
    	end
    	
    	self.Power = CreateFrame('StatusBar', nil, self)								
    	Создаем полоску маны, статусбар.
    	self.Power:SetStatusBarTexture(texture)
    	self.Power:SetHeight(4)															
    	Делаем её высоту 4 пикселя.
    	self.Power:SetFrameLevel(10)													
    	Устанавливаем её поверх полоски с хп.
    	self.Power:SetPoint('TOPRIGHT', self.Health, 'BOTTOMRIGHT', 0, 0)				
    	Положение полоски.
            Цепляем за нижний правый и нижний левый края полоски со здоровьем.
    	self.Power:SetPoint('TOPLEFT', self.Health, 'BOTTOMLEFT', 0, 0)
    	self.Power.PreUpdate = PreUpdatePower											
    	self.Power.PostUpdate = PostUpdatePower
    	Не забываем указывать к чему относятся наши переменные.
    	
    	self.Power.bg = self.Power:CreateTexture(nil, 'BORDER')							
    	self.Power.bg:SetAllPoints()
    	self.Power.bg:SetTexture(texture)
    	self.Power.bg.multiplier = 0.3
    	self.Power.background = CreateBackdrop(self.Power)	
    	
    	self.Power.frequentUpdates = true
    	Аналогично с хп баром (смотрим туда).
    	self.Power.colorDisconnected = true
    	self.Power.colorTapping = true
    	self.Power.colorPower = true													
    	Делаем цвет полоски маны по цвету маны, энергии, ярости, рун, в зависимости от вашего класса.
    	
    	self.Power.value = CreateFontString(self.Power)
    	if unit == 'player' or unit == 'target' then
    		self.Power.value:SetPoint('RIGHT', 0, 3)
    	end
    	(!) важно. Прописываем текст маны, где он будут отображаться, как видно тут используется конструкция которую можно посмотреть в блоке моего гайда под названием 'Coding'.
    	
    	self.Info = CreateFontString(self.Health)										
    	Прописываем тэги, difficulty и level означают, что на полоске с хп будет отоборажаться уровень цели. Тут же можно задать и сокращение имени.
    	if unit == 'target' then
    		self.Info:SetPoint('LEFT', 0, 4)
    		self:Tag(self.Info, '[difficulty][level][shortclassification] [raidcolor][name]')	
    	 raidcolor, name - Соответственно цвет вашего никнейма по классу.
    	elseif (not unit == 'player') or unit == 'pet' or unit == 'focus' or unit == 'targettarget' then 
    		self.Info:SetPoint('RIGHT', 0, 1)
    		self:Tag(self.Info, '[raidcolor][name]')
    	end
    	
    	if unit == 'player' or unit == 'target' then												
    	Размеры всех фреймов, 205 это ширина player и target фреймов.
    		self:SetAttribute('initial-height', 25)
    		self:SetAttribute('initial-width', 205)
    	elseif unit == 'pet' or unit == 'focus' or unit == 'targettarget' then
    		self:SetAttribute('initial-height', 15)
    		self:SetAttribute('initial-width', 130)													
    		 Аналогично только с фреймами пэта, ТоТ и фокуса.
    		self.Health:SetHeight(15)																
    		Отводим 15 пикселей под высоту их хп бара.
    		self.Power:SetHeight(2)																	
    		Точно так же, но с мана баром.
    	end
    	
    	if unit == 'player' then																	
    	Блок аур, тут уже жестко прописываем их положение, размер и их обновление.
    		self.Buffs = CreateFrame("Frame", nil, self)
    		self.Buffs:SetPoint('TOPRIGHT', UIParent, 'TOPRIGHT', -150, -9)
    		self.Buffs.initialAnchor = 'TOPRIGHT'
                    Привязка фрейма баффов идёт к верхнему правому углу.
    		self.Buffs["growth-y"] = 'DOWN'
                    Расти будем вниз
    		self.Buffs["growth-x"] = 'LEFT'
                    И влево...
    		self.Buffs:SetHeight(55)
                    Максимальная высота бара с баффами.
     		self.Buffs:SetWidth(450)
                    Максимальная ширина бара с баффами.
    		self.Buffs.spacing = 3																	
    		Расстояние между аурами.
    		self.Buffs.size = 26																	
    		Размер аур.
    		self.Buffs.num = 32																		
    		Количество аур (баффы).
    		self.Buffs.disableCooldown = true														
    		Отключаем спиральку на эффектах об их окончании.
    		self.Buffs.PostCreateIcon = PostCreateAuraIcon
    		self.Buffs.PostUpdateIcon = PostUpdateBuff
    		Указываем переменные.
    
    		self.Debuffs = CreateFrame("Frame", nil, self)
    		self.Debuffs:SetPoint('TOPRIGHT', UIParent, 'TOPRIGHT', -150, -116)
    		self.Debuffs.initialAnchor = 'TOPRIGHT'
    		self.Debuffs["growth-x"] = 'LEFT'
    		self.Debuffs["growth-y"] = 'DOWN'
    		self.Debuffs:SetHeight(55)
    		self.Debuffs:SetWidth(450)
    		self.Debuffs.spacing = 3
    		self.Debuffs.size = 26
    		self.Debuffs.num = 32
    		self.Debuffs.disableCooldown = true
    		self.Debuffs.PostCreateIcon = PostCreateAuraIcon
    		self.Debuffs.PostUpdateIcon = PostUpdateDebuff
    	end
    	
    	if unit == 'target' then
    		self.Auras = CreateFrame("Frame", nil, self)
    		self.Auras:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', 0, 3)
    		self.Auras.initialAnchor = 'BOTTOMLEFT'
    		self.Auras["growth-x"] = 'RIGHT'
    		self.Auras["growth-y"] = 'UP'
    		self.Auras.numDebuffs = 16
    		self.Auras.numBuffs = 32
    		self.Auras:SetHeight(165)
    		self.Auras:SetWidth(221)
    		self.Auras.spacing = 3
    		self.Auras.size = 23
    		self.Auras.gap = true
    		self.Auras.disableCooldown = true
    		self.Auras.PostCreateIcon = PostCreateAuraIcon
    		self.Auras.PostUpdateIcon = PostUpdateBuff
    	end
    	
    	self.Castbar = CreateFrame("StatusBar", nil, self)												
    	Создаем простенький кастбар.  
    	self.Castbar:SetStatusBarTexture(texture)
    	self.Castbar:SetStatusBarColor(0.31, 0.45, 0.63)												
    	Цвет кастбара когда мы кастуем.
    	self.Castbar.bg = CreateBackdrop(self.Castbar)													
    	Прописываем подложку (бэкдроп.)
    	
    	self.Castbar.Text = CreateFontString(self.Castbar)
    	self.Castbar.Text:SetPoint('LEFT', 0, 1)														
    	Положение на кастбаре текста заклинания.
    	
    	self.Castbar.Time = CreateFontString(self.Castbar)
    	self.Castbar.Time:SetPoint('RIGHT', 0, 1)														
    	Положение на кастбаре времени сколько произносится заклинание.
    
    	self.Castbar.Button = CreateFrame('Frame', nil, self.Castbar)
            Добавляем иконку заклинания.
    	self.Castbar.Button:SetHeight(18)
    	self.Castbar.Button:SetWidth(18)
    	if unit == 'player' then
    		self.Castbar.Button:SetPoint('BOTTOMRIGHT', self.Castbar, 'BOTTOMLEFT', -3, 0)
    	elseif unit == 'target' then
    		self.Castbar.Button:SetPoint('BOTTOMLEFT', self.Castbar, 'BOTTOMRIGHT', 3, 0)
    	end
    	self.Castbar.Button.bg = CreateBackdrop(self.Castbar.Button)
    
    	self.Castbar.Icon = self.Castbar.Button:CreateTexture(nil, 'ARTWORK')
    	self.Castbar.Icon:SetAllPoints(self.Castbar.Button)
    	self.Castbar.Icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    	
    	if unit == 'player' or unit == 'target' then													
    	Условия для player и target фреймов, размер кастбара и его положение.
    		self.Castbar:SetWidth(205)
    		self.Castbar:SetHeight(15)
    		self.Castbar:SetPoint('BOTTOM', self.Health, 0, -22)
    	end
    Специально не пользовался библиотекой UTF-8 для вывода тэгов на наши хп и мп бары, выигрываем с этого неплохую долю памяти, приятно всё-таки.

  10. 5 пользователей сказали cпасибо Alt за это полезное сообщение:

    Error (05.01.2012),haikulolz (12.01.2012),Leecat (20.01.2013),Parkan_vdv (28.04.2012),ДядькаЗмей (07.09.2012)

  11. #6
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    Ну и последний наш блок, блок спавна фреймов, то есть где будут находится наш player, target, focus, ToT, pet фреймы.
    Код:
    oUF:Factory(function(self)
    oUF:RegisterStyle('Alt', CreateStyle)
            Помните я в начале комментариев 
            просил запомнить название главной функции? Не зря. 
            Вспоминаем и создаем стиль, не забываем обозначить какая функция его задаёт.
    	self:SetActiveStyle('Alt')
    	
    	local player = self:Spawn('player')
    	player:SetPoint('BOTTOM', UIParent, -150, 300)
    	Читаем пункт про ***:SetPoint.
            Вообще это значит, что player фрейм будет на 150 пикселей левее и на 300 пикселей выше чем низ экрана (UIParent).       
    	
    	local target = self:Spawn('target')
    	target:SetPoint('BOTTOM', UIParent, 150, 300)
    	Остальные фреймы аналогично.
    	
    	local focus = self:Spawn('focus')
    	focus:SetPoint('BOTTOM', UIParent, 321, 310)
    	
    	local pet = self:Spawn('pet')
    	pet:SetPoint('BOTTOM', UIParent, -187, 260)
    	
    	local targettarget = self:Spawn('targettarget')
    	targettarget:SetPoint('BOTTOM', UIParent, 188, 260)
    end)
    Скриншоты, какими получились фреймы:

    1. Player frame (Игрок).
    2. Target frame (Цель).
    3. Focus frame (Фокус).
    4. ToT frame (ЦельЦели).
    5. Pet Frame (Пет).

    Весь код получился таким (уже готовый), тыкаем.
    Код

    Собственно, вот. Такой получился гайд по этому замечательному аддону. Может весь этот код не полностью оптимизирован, может есть моменты где стоит подумать еще раз, но тем не менее, мы получили лёгкие, красивые, юнитфреймы, которые потребляют (внимание!) 30кб и самое главное, что они СВОИ, главное, что весь этот код каким бы он не был понятен ВАМ. Они в лёгкую могут вам стать платформой для разгона функционала, они годны, в принципе, для любого интерфейса и я думаю, они смогут вас удовлетворить.

    5. Дисклеймер.
    В моем гайде активно использовались некоторые статьи и решения ALZA, DonKaban, за что им огромный респект :good:.

    Из-за ограничения в 10к символов в посте под катами не полностью вся информация, что я хотел написать и донести до Вас, если что-то интересует, а этого не нашли ищем или в конце последнего поста там ссылка на полный код или спрашиваем об этом в теме/пишем в ЛС. Всем спасибо за внимание.

    ©Alt
    Прошу при размещении на других ресурсах/порталах указывать автора/соавторство и ссылку на оригинал.
    Последний раз редактировалось Alt; 10.08.2012 в 06:12.

  12. 14 пользователей сказали cпасибо Alt за это полезное сообщение:

    •Pervyi•™ (15.03.2016),BadStyle (23.01.2013),Bearpaw (28.06.2012),Denastir (17.11.2014),Error (05.01.2012),EvilBringer (05.01.2012),Finest (16.06.2012),haikulolz (12.01.2012),LadyBoy (21.01.2012),Leecat (20.01.2013),Parkan_vdv (28.04.2012),Skyalen (04.10.2014),Пират (26.01.2012),Мужжикк (08.11.2013)

  13. #7
    Заблокирован
    Регистрация
    05.01.2012
    Адрес
    New York
    Сообщений
    19
    Поблагодарил(а)
    64
    Получено благодарностей: 2 (сообщений: 2).
    Репутация: 3
    Как всегда ок гайд)!smile

  14. #8
    Заблокирован Аватар для EvilBringer
    Регистрация
    05.01.2012
    Сообщений
    40
    Поблагодарил(а)
    8
    Получено благодарностей: 7 (сообщений: 6).
    Репутация: 7
    Отличная вещь! Надо будет на досуге выделить время и попробовать запилить что-то)
    Amat victoria curam

  15. #9
    Новичок Аватар для Thekarter
    Регистрация
    05.01.2012
    Адрес
    Кремень
    Сообщений
    13
    Поблагодарил(а)
    0
    Получено благодарностей: 1 (сообщений: 1).
    Репутация: 1
    как называетса адон на отображения всех скилов?

  16. #10
    Новичок
    Регистрация
    05.01.2012
    Сообщений
    7
    Поблагодарил(а)
    0
    Получено благодарностей: 1 (сообщений: 1).
    Репутация: 2
    Для 4.0.6 прокатит такая схема?) и есть ли оЮФ для каты?

  17. #11
    Старожил Аватар для Alt
    Регистрация
    05.01.2012
    Адрес
    43° 7' 131° 54'
    Сообщений
    156
    Поблагодарил(а)
    44
    Получено благодарностей: 170 (сообщений: 83).
    Репутация: 157
    Да. Некоторые вещи изменились, но это мелочи.

  18. #12
    Новичок
    Регистрация
    06.01.2012
    Сообщений
    4
    Поблагодарил(а)
    0
    Получено благодарностей: 0 (сообщений: 0).
    Репутация: 0
    В этой штуке разберутся только программисты и маньяки, топ для 10 человек, но отличная работа qq6

    http://paste.org.ru/?dqf510
    Можно такое же, только для 4.0.6? У меня пишет, что "несовместим". В чём может быть проблема? Версия клиента, мои кривые руки? Хочу попробовать разобраться по частям, а для этого нужно знать "рабочий вариант". Такие огромные куски кода даются мне с большим трудом. Буду благодарен за консультацию qq6
    Последний раз редактировалось Знамение; 11.01.2012 в 13:59.

Страница 1 из 7 123 ... ПоследняяПоследняя

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •