Приветствуем вас на форуме проекта WoW Circle. Если вы читаете это, значит не зарегистрировались у нас. Для того, чтобы получить доступ к расширенным возможностям нашего форума нажмите сюда и пройди регистрацию, которая не займет у вас много времени. После регистрации будут доступны новые, более расширенные, возможности.
МАКРОСЫ - основы. Гайд

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

Показано с 1 по 9 из 9
  1. #1
    Активист Аватар для SelenaLuna
    Регистрация
    07.12.2017
    Сообщений
    54
    Поблагодарил(а)
    4
    Получено благодарностей: 32 (сообщений: 12).
    Репутация: 787

    МАКРОСЫ - основы. Гайд

    Внимание! Гайд написан для английского клиента ВоВ.

    Макросы в вов, как и любой другой игре, где они поддерживаются, позволяют частично автоматизировать или просто ускорить некоторые действия и их последовательности. Применений им можно найти много, но я хочу рассказать про наиболее очевидное – упрощение управлением персонажем в бою: человеческая реакция неидеальна, но благодаря макросам мы можем сократить задержку, например, между использованиями способностей и переключением целей до минимума, избежать ошибок таргетинга и некоторых других.
    Сразу оговорюсь для тех кто более-менее в теме – язык макросов классика отличается от последующих и имеет куда более ограниченные возможности, а также требует частого использования скриптов. Про аддоны, расширяющие функциональность макросов я знаю, но в этом гайде речь пойдет о тех возможностях, которые доступны на любом клиенте.


    1. Что такое макросы и как их создавать

    Все макросы в игре представляют собой исполняемые прямо через интерфейс чата команды, начинающиеся с символа «/». То есть, эмоции (например, /wave, /train и др), сообщения в определенный канал чата и т.д. – тоже являются такими командами. Суть макроса в том, что мы запоминаем определенную команду или их последовательность, выполнение которых можно выносить на экшн бар и, следовательно, забиндить на хоткеи.
    Чтобы открыть интерфейс создания макросов, воспользуйтесь главным меню или просто напишите в строку чата /macro. Обратите внимание на две вкладки – макросы можно создавать как общие, так и отдельно для каждого персонажа, это очень удобно – меньше шансов запутаться.
    Создавая макрос, сразу выберем ему имя и иконку из предложенных, после чего в нашем распоряжении будет окно для ввода наших команд. Каждая команда выполняется построчно, то есть, нажимая Enter и перенося строку, мы создаем другую команду, например:
    --------------
    /s Hi!
    /hello
    --------------
    (две команды, выполняемые поочередно)

    2. Базовые команды
    Если мы хотим повысить эффективность управления персонажем, приоритетными командами являются использование способностей /cast и выбор цели /target. Применять их следует следующим образом:
    --------------
    /target Nickname
    /cast Healing Wave
    --------------
    (первая строчка выбирает в качестве цели юнит с ником Nickname, вторая кастует спелл Healing Wave максимально доступного ранга)

    /target часто используется для поиска рарных мобов, при охоте за определенным лутом – можно забить в макрос имя моба и проспамить его, пробегая мимо зоны его респауна. Единственная особенность в том, что если юнит с точно таким именем не будет найден, команда постарается найти какой-нибудь с похожим, в случае если и это не выйдет – сообщит об ошибке Unknown Unit.
    А вот используя /cast необходимо указать название способности максимально точно, вплоть до каждого символа. Если необходимо скастовать спел определенного ранга, например, первого, команда пишется так:
    --------------
    /cast Healing Wave(Rank 1)
    --------------

    Для того, чтобы атаковать цель другого игрока (например, вашего танка), используется команда /assist, в которую также нужно добавить имя персонажа, которому мы будем помогать, обычно это выглядит так:
    --------------
    /assist %t
    --------------
    (команда сама подставляет вместо %t имя вашей текущей цели)

    3. ГКД
    У новичков на этом моменте загораются глаза – появляется желание написать комбо-макрос для убийства моба (или даже игрока) одной кнопкой, но сейчас, дорогие мои, я вас обломаю. Как вы могли заметить, почти все действия в игре, активируют так называемый глобальный кулдаун (ГКД), равный одной секунде. Это сделано как раз для того, чтобы, например, маг не мог забив на макрос все свои инстант-спеллы за доли секунды (а именно с такой скоростью исполняются команды) выдать много урона.
    Кроме этого, близзард не дали возможности устанавливать между действиями какую-либо задержку, команды исполняются мгновенно, одна за другой, и как только первая активирует способность, запускается вторая, за ней третья и т.д. – но все они натыкаются на ГКД и выдают ошибки о том, что способность еще не готова.
    Но не все так плохо: если первая способность не активирует ГКД (а такие есть), то можно сразу запустить вторую. Например, я хочу заюзать талант шамана Nature's Swiftness, который делает следущий спелл инстантом, после чего сразу кастануть Healing Wave. Пока не будем усложнять этот макрос выбором цели:
    ------------—
    /cast Nature's Swiftness
    /script SpellStopCasting()
    /cast Healing Wave
    ------------—

    Закономерный вопрос - какова роль второй строчки? Эта команда прерывает каст, в данном случае она нужна, чтобы разделить действия - я точно не знаю, как это обрабатывает игра, но без нее не работает :)
    Точно также, разделяя прерыванием каста, можно использовать тринкеты, которые не запускают ГКД и инстант-спеллы, например, временно увеличивающие урон.
    Если первый спелл все же активирует ГКД, но сам имеет кулдаун, работать этот макрос будет следующим образом: спамим одну кнопку несколько раз, при первом нажатии срабатывает первое действие как только проходит ГКД, повторное нажатие выполняет следующий шаг.

    4. Выполнение скриптов
    На самом деле команда /script, она же /run позволяет писать самые интересные макросы. Дело в том, что она исполняет строчку скрипта на языке Lua, с помощью которого можно вызывать множество полезных функций игры. Так, если в БК и последующих версиях WoW большинство макросов можно было написать, используя стандартные команды, то в классике они весьма ограничены в возможностях, а весь функционал доступен, как правило, через скрипты.

    Например, /cast Healing Wave равнозначно /script CastSpellByName("Healing Wave") – при этом обязательно учитывать, что в одном скрипте может быть несколько действий, разделенных точкой с запятой (БЕЗ ПЕРЕНОСА!). Например, предыдущий макрос можно переписать следующим образом:
    ------------—
    /script CastSpellByName("Nature's Swiftness"); SpellStopCasting(); CastSpellByName("Healing Wave")
    ------------—

    Не стоит забывать, что SpellStopCasrting() прерывает каст во время вызова, чем могут пользоваться маги для контрспелла и шаманы для земляного шока – иногда нужно срочно сбить врагу каст, сбросив свой. Чтобы не делать этого вручную, напишите такой макрос:
    ------------—
    /script SpellStopCasting(); CastSpellByName("Earth Shock");
    ------------—

    Допустим, у моего персонажа кончилась мана, я иду в ближний бой, а противник начал кастовать что-то. В таком случае мне подойдет даже шок 1 ранга, лишь бы сбить его каст. Добавим третье действие в скрипт:
    ------------—
    /script SpellStopCasting(); CastSpellByName("Earth Shock"); CastSpellByName("Earth Shock(Rank 1)")
    ------------—
    (если маны хватит на шок высокого ранга, то третья фукнция не выполнится из-за КД на шок, если же не хватит на вторую, то вместо нее выполнится третья)

    5. Выбор целей

    Допустим, этим скриптом мы хотим хилить себя в экстренной ситуации. Для этого можно добавить в начало строчку /target Nickname, чтобы гарантированно выбрать себя, однако было бы здорово не терять текущую цель при этом. Это достигается при помощи скрипта – выбираем цель мы с помощью TargetUnit(“player”) (тут не нужно писать свой ник, тк переменная player всегда хранит наш ID), после чего кастуем спел и возвращаем прошлый таргет, используя TargetLastTarget(), вот так:
    ------------—
    /script TargetUnit("player"); CastSpellByName("Nature's Swiftness"); SpellStopCasting();CastSpellByName("Healing Wave"); TargetLastTarget()
    ------------—

    Помимо "player" существуют и другие переменные для таргетинга, например:
    "pet" – ваш питомец;
    "party1" – первый (по порядку в меню слева) член группы, аналогично можно выделять и других по номеру;
    "mouseover" - цель, на которую в данный момент наведен курсор;
    "target" – выбранная цель. Зачем, спросите вы? Дело в том, что их можно сочетать, например "targetpet" – питомец выбранной цели, или "targettarget" – цель выбранной цели.

    Кроме этого, цели в скриптах можно выбирать функциями:
    TargetLastEnemy() – выбор последней враждебной цели;
    TargetNearestEnemy() – работает как клавиша Tab в настройках по умолчанию;
    TargetNearestFriend() – аналогично, только по дружественным целям;
    TargetByName("Nickname") – по имени юнита;
    AssistByName("Nickname") – аналогично /assist Nickname.

    6. Использование предметов
    Использовать экипированые и находящиеся в сумках предметы по имени в классике нельзя – необходимо точно знать номер слота, в которых они размещены. Для экипировки это:
    0 = боеприпасы
    1 = голова
    2 = шея
    3 = плечи
    4 = рубашка
    5 = нагрудник
    6 = пояс
    7 = штаны
    8 = ботинки
    9 = наручи
    10 = перчатки
    11 = кольцо 1
    12 = кольцо 2
    13 = тринкет 1
    14 = тринкет 2
    15 = плащ
    16 = мейн хенд
    17 = офф хенд
    18 = оружие дальнего боя
    19 = табард

    С сумками все проще – номер состоит из двух чисел через запятую, где первая – номер сумки (начиная с основного пака), а вторая – номер слота в этой сумке (начиная с верхнего левого угла и направо).
    Зная номер, можно использовать одну из нескольких команд:
    UseInventoryItem(n) – использование предмета экипировки;
    PickupInventoryItem(n) – взять предмет из экипировки (аналогично клику мышкой);
    UseContainerItem(n1, n2) – использование предмета в сумке;
    PickupContainerItem(n1, n2) – взять предмет из сумки, где n – номер слота экипировки, n1 - номер сумки, n2 – номер слота в сумке.
    Нужно отметить, что пикать можно и пустой слот – если до этого был пикнут какой-либо предмет, он будет перемещен туда, например:
    ------------—
    /script PickupContainerItem(4, 1); PickupInventoryItem(1)
    ------------—
    (берем шлем из первого слота четвертой сумки и надеваем на голову)

    ------------—
    /script UseContainerItem (2,2); PickupInventoryItem (16)
    ------------—
    (используем яд/заточку из 2 слота 2 сумки на оружие в мейнхенде)

    Пример полезного макроса для шамана – объединить использование маунта и духа волка на одной кнопке, тк если мы не вышли из комбата, замаунтится нельзя, а вот в волка превратиться все еще можно:
    ------------—
    /script UseContainerItem(4,1); CastSpellByName("Ghost Wolf")
    ------------—
    (тут как и в случае с шоками разного ранга, если получается выполнить действие, вызываемое раньше, то следующее не выполнится, тк персонаж будет занят, если же первое невозможно, начнет выполнятся второе)

    7. Логика и условия
    А теперь самое интересное – благодаря тому, что в одной команде можно выполнить более 1 действия и использовать скрипты, мы можем заставлять макрос вызывать разные функции в зависимости от определенных условий. Синтаксис выглядит следующим образом:
    if (условие)
    then действия, которые совершаются если условие выполнено
    else действия, которые совершаются, если условие не выполнено
    end – завершение логического блока, после него все действия будут выполняться вне зависимости от условия.

    С помощью подобного макроса я повесил три разных спелла шамана (снятие баффов с враждебной цели, лечение союзника от ядов и лечение от болезней) на одну клавишу следующим макросом:
    ------------—
    /script if (UnitCanAttack("player","target")) then CastSpellByName("Purge"); else CastSpellByName("Cure Poison"); CastSpellByName("Cure Disease"); end
    ------------—
    (если моя цель враждебна – кастуем пурж, иначе пробуем скастовать снятие яда и после - болезни, разумеется, если на союзнике и яд и болезнь, то придется нажимать кнопку дважды)

    Допустим, мы хотим менять одноручное оружие + щит на двуручное и наоборот одной клавишей:
    ------------—
    /script PickupInventoryItem(17); if (CursorHasItem()) then PickupContainerItem(4,4); end; PickupContainerItem(4,3); PickupInventoryItem(16); PickupContainerItem(4,4); PickupInventoryItem(17)
    ------------—
    Разберем код по шагам:
    PickupInventoryItem(17); - берем предмет из левой руки;
    if (CursorHasItem()) then PickupContainerItem(4,4); - если у нас была одноручка + щит, курсор схватит щит и условие CursorHasItem() будет верным, в таком случае дропаем его в 4 слот 4 сумки (в случае если условие не выполнится – не произойдет ничего);
    end; - завершаем блок логики, дальнейшие команды выполняются в обычном порядке;
    PickupContainerItem(4,3); - берем предмет из 3 слота 4 сумки (там где всегда будет лежать сменное оружие);
    PickupInventoryItem(16); - дропаем его в правую руку (при этом, экипированное оружие займет тот слот, откуда мы взяли сменное);
    PickupContainerItem(4,4); - пробуем взять щит из сумки;
    PickupInventoryItem(17) - пробуем дропнуть его в оффхенд;
    Последние две команды нужны в случае, если мы меняем двуручку на одноручку + щит. В обратной ситуации ничего не произойдет – щит нельзя использовать с двуручкой и он вернется на свое место в сумке.


    Еще пример:
    Еще один пример, просто для лучшего понимания логики и демонстрации еще нескольких условий. Допустим, мы хотим по нажатию макроса использовать бинты на дружественную цель, но если цель – враг или отсутствует, бинтовать себя. Так сейчас работает опция AutoSelfCast, поэтому достаточно просто перенести бинты на панель и включить соответствующий пункт в настройках, но я покажу какой скрипт сможет сделать это без настройки (снова с пошаговым объяснением):

    if (UnitCanAttack("player","target")) then TargetUnit("player"); - если выполняется условие: юнит “player” (то есть наш перс) может атаковать юнит “target” (то есть нашу цель), выбрать целью себя;
    UseContainerItem(4,2); - использовать бинты из 2 слота 4 сумки;
    TargetLastTarget(); - вернуть прошлую цель;
    AttackTarget(); - включить автоатаку;
    else UseContainerItem(4,2); - если условие выше не выполнилось, используем бинты без смены цели;
    end; - завершаем первый блок с условием;
    if(SpellIsTargeting()) then SpellTargetUnit("player"); - если у нас не было цели и после юза бинтов курсор загорелся подсветкой, выбираем целью для способности (в данном случае, использования бинтов) себя;
    end - завершаем второй блок с условием.

    Однако, в случае с бинтами, чаще есть необходимость лечить только себя, даже если выбранная цель дружественная, это реализуется намного проще:
    ------------—
    /script TargetUnit("player"); UseContainerItem(4,2); TargetLastTarget();
    ------------—

    Помимо CursorHasItem(),UnitCanAttack("unit1", "unit2") и SpellIsTargeting(), существует еще много других полезных условий, например:
    IsShiftKeyDown() / IsControlKeyDown() / IsAltKeyDown() – проверяет, зажата ли клавиша шифт/контрол/альт (позволяет выполнять одним макросом разные действия в зависимости от использования этих клавиш);
    UnitInParty("unit") – состоит ли указанный игрок в нашей группе;
    UnitIsPlayer("unit") – является ли указанный юнит игроком;
    UnitIsUnit("unit1", "unit2") – сравнивает два юнита, выполняется если они совпадают (например UnitIsUnit("player", "target") проверяет, выбрали ли мы целью самого себя).


    8. Более сложная логика, переменные и сокращение длины скрипта
    В качестве подходящего условия можно использовать и наоборот, невыполнение указанного. Для этого используется ключевое слово not, вот так : if not (условие) then ; else ; end.
    Также помимо обычных условий, можно создавать и свои. Делается это благодаря сравнению значений с помощью следующих операторов:
    < - меньше;
    > - больше;
    == - равняется;
    ~= - не равно.

    То есть, если мы пишем if (a<b) then …, то код после then выполнится при условии если число a меньше чем b.
    Откуда брать значения для сравнения? Для этого игра предоставляет специальные функции, например:
    UnitClass("unit") – название класса юнита, например “Shaman”;
    UnitCreatureType("unit") – для зверей возвращает “Beast”, для хуманоидов “Humanoid” и т.д.
    UnitHealth("unit") – текущее количество хп у юнита;
    UnitHealthMax("unit") – максимальное количество хп у юнита;
    UnitMana("unit") – текущее количество маны у юнита;
    UnitManaMax("unit") – максимальное количество маны у юнита;
    UnitLevel("unit") – уровень;

    Условия можно сочетать с помощью логических операторов "and" (и) и "or" (или). Самый наглядный пример – макрос для баффа паладином различных классов:
    ------------—
    /script if (UnitClass("target")=="Warrior" or UnitClass("target")=="Rogue") then CastSpellByName("Blessing of Might"); else CastSpellByName("Blessing of Wisdom"); end
    ------------—
    (если цель рога или воин, баффаем на мили ап, во всех остальных случаях – на реген маны, единственный косяк может произойти с друидами – в зависимости от спека им могут понадобиться различные баффы)

    Еще один пример – для ленивых хилеров, как по одной кнопке кастовать большой или быстрый хил в зависимости от процентов хп цели. Чтобы узнать, насколько заполнен хелсбар юнита делим его текущие хп на максимальные:
    ------------—
    /script if (UnitHealth("target")/UnitHealthMax("target")<0.3 or UnitHealth("target")/UnitHealthMax("target")>0.8) then CastSpellByName("Lesser Healing Wave"); else CastSpellByName("Healing Wave"); end
    ------------—
    (если у цели меньше 30% или больше 80% здоровья – кастуем быстрый хил, чтобы не сильно оверхилить или не уронить, иначе – большой)
    Видно, что довольно большой кусок кода, где мы вычисляем текущее хп повторяется. Чтобы сократить скрипт (не забываем, что у нас есть всего 255 знаков) можно создать лосальную (local) переменную hp, присвоив ей вычисленное значение с помощью оператора = вот так:
    ------------—
    /script local hp=UnitHealth("target")/UnitHealthMax("target"); if (hp<0.3 or hp>0.8) then CastSpellByName("Lesser Healing Wave"); else CastSpellByName("Healing Wave"); end
    --------------

    Чтобы не оверхилить, можно доработать макрос до следующего вида:
    --------------
    /script local hp=UnitHealth("target")/UnitHealthMax("target"); if (hp>0.8) then SpellStopCasting(); elseif (hp<0.3) then CastSpellByName("Lesser Healing Wave"); else CastSpellByName("Healing Wave"); end
    --------------
    (по факту требуется выбрать целью танка и спамить одну кнопку - при количестве хп больше 80% мы ничего не кастуем и даже прерываем текущий каст, если хп меньше 30% - кастуем быстрый хил, в остальных случаях - обычный большой хил)

    Вернемся к моему скрипту на сбитие каста шоком. Иногда мне нужно очень быстро сбить каст у другой цели, например, если мобы или игроки другой фракции хилят друг друга. Переключать цель вручную неудобно, автоматически выбрать нужную не всегда возможно, да и шока достаточно 1 уровня: все равно я буду фокусить прежнюю цель.
    Поэтому я решил доработать макрос на сбивание каста шоком, чтобы можно было при зажатой клавише альт кидать 1 ранг в другую цель по курсору. Так как он не влезал в 255 символов, пришлось сократить его длину, переименовав часто вызываемую функцию CastSpellName в с:
    ------------—
    /script local c=CastSpellByName; SpellStopCasting(); if (IsAltKeyDown() and UnitCanAttack("player","mouseover")) then TargetUnit("mouseover"); c("Earth Shock(Rank 1)"); TargetLastTarget(); else c("Earth Shock"); c("Earth Shock(Rank 1)"); end
    ------------—
    (если зажата клавиша альт и мы можем атаковать юнит под курсором, переключаем цель на него, кидаем шок 1 ранга и возвращаем прошлую цель, иначе просто кастуем шок)

    Кстати, еще несколько символов можно сэкономить, используя вместо /script /run – эти команды абсолютно равнозначны.
    Сильно сократить код можно, кастуя спелы не по названию, а по порядковому номеру, но определить его бывает довольно сложно, поэтому я не буду сейчас углубляться в эту тему.

    9. Бонус - действия пета
    С помощью скриптов можно также управлять петом хантеров и локов:
    PetAttack() / PetStopAttack() – атакует/прекращает атаковать текущую цель;
    PetFollow() – следовать за вами;
    PetDefensiveMode() / PetPassiveMode() / PetAggressiveMode() – переводит пета в один из выбранных режимов.

    Пригодится это может, например, чтобы не сбивать контроль в сочетании со ScatterShot или страхом, например:
    ------------—
    /script PetPassiveMode(); CastSpellByName("Scatter Shot");
    ------------—
    ------------—
    /script CastSpellByName("Fear"); PetFollow();
    ------------—
    Также мы можем использовать с помощью скрипта абилки пета, вынесенные на его панель по порядковому номеру – CastPetAction(n);

    Гайд является вольным переводом человека:Veteranwow + если личные примеры (Большое ему спасибо). Англ. версия гайда была опубликована на официальном сайте WoW во времена Классика и дополнялась разными игроками.

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

    Azooldan (21.05.2019),H3VDL3$$ (14.12.2017),nxtlvl (07.12.2017),qvijibo (07.02.2018),Santaria (08.12.2017),Сфера (03.01.2018)

  3. #2
    Местный
    Регистрация
    01.08.2014
    Сообщений
    45
    Поблагодарил(а)
    1
    Получено благодарностей: 0 (сообщений: 0).
    Репутация: 0
    _-/

  4. #3
    Активист Аватар для Evangelion667
    Регистрация
    19.12.2017
    Сообщений
    50
    Поблагодарил(а)
    3
    Получено благодарностей: 2 (сообщений: 2).
    Репутация: 2
    Крайне полезная штука, благодарю.

  5. #4
    Новичок
    Регистрация
    04.05.2017
    Сообщений
    17
    Поблагодарил(а)
    0
    Получено благодарностей: 1 (сообщений: 1).
    Репутация: 1
    Ты эти макросы и скрипты пробовал на этом серве? Я тут даже хилбот настроить не смог. Если тут все будет так работать как ты изложил то спасибо тебе огромное за инфу.

  6. #5
    Новичок
    Регистрация
    25.12.2017
    Сообщений
    2
    Поблагодарил(а)
    0
    Получено благодарностей: 0 (сообщений: 0).
    Репутация: 0
    А на этом серве кто-то пробовал скрипты?Оо Если да, отпишите по работоспособности :з

  7. #6
    Активист Аватар для Evangelion667
    Регистрация
    19.12.2017
    Сообщений
    50
    Поблагодарил(а)
    3
    Получено благодарностей: 2 (сообщений: 2).
    Репутация: 2
    Увы но /script SpellStopCasting() не позволяет кастануть даже 1й скил)

  8. #7
    Активист
    Регистрация
    20.01.2014
    Сообщений
    33
    Поблагодарил(а)
    39
    Получено благодарностей: 7 (сообщений: 4).
    Репутация: 7
    как прописать крик например, ну что первое в голову приходит, "Админы казлы!" (я про другой сервер)
    /cry Админы казлы не работает

  9. #8
    Активист Аватар для Ko4eR
    Регистрация
    28.03.2012
    Сообщений
    53
    Поблагодарил(а)
    2
    Получено благодарностей: 1 (сообщений: 1).
    Репутация: 1
    Цитата Сообщение от qvijibo Посмотреть сообщение
    как прописать крик например, ну что первое в голову приходит, "Админы казлы!" (я про другой сервер)
    /cry Админы казлы не работает
    потому что крик это /yell а "cry" переводится как плач
    Как бы мы увидели свет, если бы не было тьмы?

  10. #9
    Новичок
    Регистрация
    29.08.2019
    Сообщений
    1
    Поблагодарил(а)
    0
    Получено благодарностей: 0 (сообщений: 0).
    Репутация: 0
    Хау. Слушай а есть Макрос к примеру чтобы Если я не в бою то сменил стойку и юзнул чардж ) к примеру

    И еще как сделать чтобы в макросе писалось его КД

Похожие темы

  1. Ответов: 18
    Последнее сообщение: 29.05.2016, 17:48
  2. [MoP] новый up9 и новые возможности?
    от Hibor в разделе Вопросы Logon, TBC, Legion, BFa, SL
    Ответов: 1
    Последнее сообщение: 16.11.2015, 10:49
  3. Ответов: 13
    Последнее сообщение: 31.03.2015, 08:15

Ваши права

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