Мод доступности для MTG Arena обновлен до версии 1.3

Мод доступности для многопользовательской коллекционной карточной игры MTG Arena получил несколько обновлений.

v1.3

Вкладка PlayBlade / События:

  • Вкладка «События» теперь представляет собой правильную иерархию с детализацией: Вкладки → Фильтры-чипсы → Плитки событий, аналогично тому, как другие вкладки устроены по схеме Вкладки → Папки → Колоды. Раньше чипсы фильтров («Alle», «In Arbeit», «Neu», «Limited», «Constructed» и все привязанные к форматам) смешивались с плитками событий в единый плоский список из 23 элементов, отсортированный по позиции на экране, поэтому они оказывались между текущими событиями и остальными — это сбивало с толку и приводило к ложным активациям (чипс запускал переход к списку папок, которого не существовало, и навигатор зависал). Теперь чипсы фильтров образуют собственную группу между вкладками и плитками событий. Enter на вкладке «События» помещает фокус на строку чипсов, Enter на чипсе погружает в отфильтрованный список событий, Backspace на каждом уровне поднимается на шаг назад, а восстановление позиции запоминает, с какого чипса и вкладки пришёл пользователь. Для определения чипсов используется штатная ссылка EventBladeContentView._eventTileContainer: всё, что внутри этого контейнера, считается плиткой, всё остальное внутри панели событий — чипсом.
  • Активация плитки события больше не пытается отправить навигатор в несуществующий список папок. Код, который для каждой активации PlayBladeContent вызывал RequestFoldersEntry(), теперь пропускает этот шаг для плиток событий (обнаруживаемых через ту же проверку на вхождение в _eventTileContainer) и позволяет системе панелей естественным образом обработать переход на страницу события.

Дуэль:

  • Озвучивание информации о карте теперь показывает данные о выбранном / названном объекте. Карты, которые запоминают тип существа (Cavern of Souls, Engineered Plague), цвет (Iona, Shield of Emeria) или имя карты (Pithing Needle, Meddling Mage, Cabal Therapy), отображают этот выбор в своём внутриигровом тексте правил для зрячих игроков, а мод его терял. Теперь стрелка вниз после текста правил зачитывает «Выбрано: Wizard» и «Названа карта: Cabal Therapy», получая данные из MtgCardInstance.LinkedInfoText и LinkedInfoTitleLocIds через тот же путь IGreLocProvider, который используют штатные LinkedInfoTextParser / LinkedInfoTitleTextParser, так что результирующие строки совпадают с тем, что отрисовано на карте. Добавлено @LordLuceus (PR #99).
  • Новая горячая клавиша N озвучивает, что в данный момент разрешается на стеке — самый верхний элемент стека с его текстом правил, в том же формате, что мод использует при появлении нового объекта в стеке. Подсказки процесса во время длинных комбинаций («Подтвердить 0», «выберите цель», «вы можете сбросить карту») часто не дают понять, к какому триггеру они относятся: автоматическое объявление стека срабатывает лишь раз при обновлении зоны, поэтому запрос, появляющийся на несколько тактов позже, не имеет голосовой привязки. N закрывает этот пробел по требованию. Если стек пуст, произносится «Ничего в стеке». Добавлено @LordLuceus (PR #102).

Исправления ошибок:

  • Было сломано отслеживание зон при Слежке (Surveil): список _bottomCards мода всегда оставался пустым, поэтому каждая карта читалась как «keep» (оставить), независимо от того, куда игрок её переместил, объявления по Tab лгали о выборе, а Tab → Enter на «сброшенной» карте повторял попытку в том же направлении, вместо того чтобы переключить её обратно в библиотеку (не было способа снять выбор, кроме как отменить весь браузер). Коренная причина: RefreshSurveilCardLists обходил BrowserCardHolder_ViewDismiss для нижней зоны, которого у Surveil нет — Surveil хранит обе зоны в одном cardHolder со списками libraryGroup / graveyardGroup, отслеживающими разделение. Теперь обновление читает эти списки напрямую через публичные геттеры GetLibraryCards() / GetGraveyardCards() из SurveilBrowser, как это делает сама игра при компоновке карт. В качестве дополнительной меры отслеживаемые списки также обновляются сразу же после успешного перемещения по Enter, так что DetectCardZone корректен в течение 0,2-секундного окна до срабатывания сверочной корутины (это также закрывает аналогичный зазор для Scry, где списки были корректны после задержки, но устаревали внутри неё). Добавлено @LordLuceus (PR #100).
  • Карты с нулевой стоимостью теперь озвучиваются корректно. PR #91 подключил текущую стоимость разыгрывания действия, чтобы учитывались эффекты снижения стоимости (Warden of Evos Isle, Goblin Electromancer и т.д.), но пустой Action.ManaCost нулевой длины воспринимался как «ещё не вычислено» и пропускался — в итоге происходил возврат к PrintedCastingCost. Результат: карта, чья стоимость снижена до 0, всё равно читалась с напечатанной стоимостью, так что «бесплатное» разыгрывание объявлялось как требующее одной маны, которую игрок на самом деле никогда не заплатит. Теперь пустой список мана-стоимости считается настоящим сигналом нулевой стоимости (снижатели стоимости довели заклинание до нуля, либо это Suspend/Cascade разыгрывание из изгнания, либо это напечатанный 0-стоимостный артефакт вроде Memnite); ParseManaQuantityArray преобразует пустой массив в локализованное ManaFree («Бесплатно» в русской локализации), обрывая цепочку возврата. Блок мана-стоимости теперь читается и для напечатанных карт с нулевой стоимостью (Memnite, Mox Amber, Ornithopter), а не замалчивается. Добавлено @LordLuceus (PR #101).
  • Браузер Слежки больше не объявляет «перемещено в низ библиотеки» при нажатии Enter на карте. Surveil сбрасывает выбранные карты на кладбище, а не под низ библиотеки, но GetZoneName / GetShortZoneName использовали фразу из Scry. Теперь нижняя зона читается как «Graveyard» (кладбище) для Surveil (объявления становятся «X перемещён на кладбище», «Кладбище: пусто» и т.д.); Scry, Read Ahead и Split сохраняют свои формулировки. Добавлено @LordLuceus (PR #98).
  • Shift+Enter на карте в конструкторе колоды при активном фильтре «Крафт» больше не тратит дикую карту автоматически. TryOpenCardViewerForFocusedCard через рефлексию вызывал DeckBuilderActionsHandler.OpenCardViewer, не устанавливая InputManager.BlockNextEnterKeyUp, поэтому последнее событие Return KeyUp попадало в PopupManager.HandleKeyUpCardViewerController.OnEnter()OnCraftClicked(), инициируя крафт без повторного подтверждения. Обычный Enter и активация правой кнопкой мыши уже ставили защиту через CardTileActivator / UIActivator; теперь путь через Shift+Enter (через рефлексию) тоже её устанавливает, аналогично исправлению, применённому для всплывающего окна приглашения в испытание в версии v0.9.4.
  • Земли больше не объявляют «Мана-стоимость: Бесплатно» после исправления для карт с нулевой стоимостью в v1.3. Земли не имеют стоимости разыгрывания, поэтому пустой массив всегда превращал их в «Бесплатно» — бесполезный шум, добавлявший лишнее нажатие стрелки вниз перед чтением строки типа и текста правил. Теперь подавляется, если CardTypes содержит Land; Memnite, Mox Amber и сниженные до 0 заклинания по-прежнему объявляются как «Бесплатно», потому что их действительно можно разыграть. Добавлено @LordLuceus (PR #103).

v1.2

Дуэль:

  • Эмоции, отключение звука и взаимодействие с питомцем во время дуэли теперь работают из зоны информации об игроке (клавиша V). Enter на себе открывает колесо экипированных эмоций — читается напрямую из EmoteOptionsController._equippedEmoteOptions и отправляется через UIMessageHandler.TrySendEmote, так что колесо не обязано быть визуально открыто. Enter на противнике переключает отключение звука для данного дуэля через OpponentDialogController.UpdateIsMuted, зеркально повторяя иконку отключения звука. Shift+Enter на себе открывает меню взаимодействия с питомцем (Погладить, Похлопать или Грудь/Лапа/Нога/Голова в зависимости от питомца), направляемое через активный AccessoryController. Входящие эмоции противника объявляются через коллбэк UIMessageHandler.EmoteRecievedCallback, локализованный через EmoteUtils.GetFullLocKey, и фильтруются в соответствии с текущим состоянием отключения звука. Объявления эмоций противника используют приоритет High, чтобы их не заглушали чтения карт с приоритетом Immediate, когда вы перемещаетесь по картам.
  • Меню действий питомца скрывает действия, чей префабный UnityEvent не имеет постоянных слушателей. Некоторые питомцы (например, питомец мастерства ONE_Skitterling) привязывают события наведения к ничему — показ «Погладить» привёл бы к объявлению «Streicheln ausgelöst» без какого-либо игрового эффекта. Теперь меню показывает только действия, действительно запрограммированные что-то делать (триггер аниматора, звуковое событие и т.д.); питомец без реальных действий объявляется как «У этого питомца нет взаимодействий», чтобы не вводить пользователя в заблуждение фантомными опциями.
  • Ctrl+F1 внутри зоны информации об игроке теперь показывает контекстную подсказку (Left/Right переключают между вами и противником, Up/Down листают значения, Enter для эмоции / отключения звука, Shift+Enter для питомца, Backspace для выхода) вместо полного списка клавиш управления дуэлью.

Косметика:

  • Теперь полностью доступен выбор косметики для каждой колоды (аватар, питомец, рубашка). Активация кнопки названия колоды открывает всплывающее окно деталей колоды; плитки озвучивают текущий выбор («Аватар: Стандартный, нажмите Enter для смены»), а Enter открывает соответствующий селектор. Всплывающие окна питомцев и рубашек накладываются поверх — питомцы, рубашки и бюсты аватаров читаются с их локализованными именами плюс статус (выбрано, по умолчанию, имеется, заблокировано) вместо неподписанных хитбоксов, с которыми раньше сталкивалась программа чтения с экрана. Backspace закрывает активный селектор и возвращает к плиткам деталей колоды.
  • Селектор питомца в профиле теперь действительно применяет выбор при нажатии Enter. PetPopUpV2 использует двухэтапный процесс (предпросмотр, затем явное подтверждение); теперь мод вызывает OnConfirm после клика предпросмотра, так что выбранный питомец сохраняется, а не сбрасывается при закрытии всплывающего окна.
  • Селектор аватара работает как встроенный, а не как отдельное всплывающее окно. Активация бюста аватара теперь запускает бесшумное обновление, чтобы текст названия и биографии (TMP_Text) справа обновлялся для каждого предпросмотра — стрелка вверх читает новую биографию, не покидая селектор.
  • Метки косметических значений обновляются после закрытия суб-селектора, так что плитка деталей колоды отражает только что выбранный аватар / питомца / рубашку.
  • Под капотом внедрена новая инфраструктура режима всплывающих окон: поддержка вложенных окон в BaseNavigator позволяет любому суб-окну, открывшемуся поверх существующего, корректно навигироваться, а затем возвращаться к родительскому окну с обновлёнными метками. Это пригодится для любых будущих сценариев с вложенными окнами, а не только для косметики.
  • Блоки информации о карте в конструкторе колоды (стрелка вниз) заканчиваются строкой «Стиль» (например, «Стиль: Showcase Etched»), считываемой напрямую с каждой плитки — как на стороне колоды, так и в пуле коллекции. Коды рубашек читаются для каждой строки из собственного MtgCardInstance.SkinCode (плитки колоды) или _lastDisplayInfo.Skin (плитки пула), а не из словаря рубашек уровня колоды, поэтому две строки одной карты с разными рубашками озвучиваются по-разному. Когда стиль не переопределён, строка откатывается к идентификатору печати (название выпуска + номер коллекционера, например, «Стиль: Dominaria 26»), так что две строки одной карты из разных печатей — с разными GrpId — тоже можно различить на слух.
  • Новая комбинация Shift+Enter на сфокусированной карте в конструкторе колоды открывает всплывающее окно просмотра карты, аналогично правому клику у зрячих игроков. Работает на картах коллекции, записях списка колоды, сайтборда, командиров и картах только для чтения. Само всплывающее окно крафта теперь начинается с заголовка «Стиль:», так что пользователь сразу получает подтверждение, на какую именно версию печати он тратит дикие карты (разные версии одной карты хранятся в инвентаре как отдельные GrpId; окно по своей сути одностилевое).
  • Новая комбинация Ctrl+Enter на сфокусированной карте пула раскрывает название в ряд смежных плиток — по одной на каждый доступный вариант оформления или печати. Под капотом вызывается игровое раскрытие предпочитаемой печати через шеврон (PagesMetaCardView.ExpandClicked()PreferredPrintingState.ExpandCard / CollapseCard), то есть то же разворачивание, которое получает зрячий игрок при клике по ленте плитки. Повторное нажатие сворачивает. Внутри активного развёрнутого списка каждая плитка варианта сразу озвучивает своё отличие (например, «Sagenumwobener Durchgang, Showcase Etched» против «Sagenumwobener Durchgang, Avatar TFTUR Extra 57»), не повторяя голое имя для каждого элемента; объявление читает _lastDisplayInfo.ExpandedStyle (Expanded_First/Mid/Last против Solo/Stacked), чтобы обычная навигация оставалась тихой. Одиночные карты (без альтернатив) объявляют «Нет доступных альтернативных стилей» вместо вводящей в заблуждение реплики о разворачивании/сворачивании. Shift+Enter на выбранном варианте затем открывает всплывающее окно крафта именно для этой (печати, рубашки). В справке F1 появился новый раздел «Горячие клавиши карт конструктора колод», охватывающий обе комбинации; в подсказке Ctrl+F1 конструктора они также упоминаются.

Исправления ошибок:

  • Универсальная мана в стоимости карт теперь озвучивается как «универсальная», а не «бесцветная» (например, {5}{U}{B} → «5 универсальной, синяя, чёрная»). «Бесцветная» имеет особый смысл в Magic — символ {C} и цветовой идентификатор Colorless по-прежнему читаются как «бесцветная». Добавлено @LordLuceus (PR #90).
  • Повторяющиеся символы маны внутри одной фигурной скобки больше не теряют токены. Игра кодирует пары типа {B}{B} как {oBoB}, а регулярное выражение поглощало ведущую o перед разбором — так что строка Spree + {B}{B} — читалась как + Black —. Теперь читается + Black, Black —. Добавлено @LordLuceus (PR #90).
  • Гибридные альтернативные стоимости, такие как Evoke, больше не пропускаются. Игра кодирует {U/B}{U/B} как {o(U/B)o(U/B)}, что парсер вообще не мог разобрать (скобки не распознавались) — поэтому строка Deceit «Evoke {U/B}{U/B}» читалась просто как «Evoke». Теперь читается «Evoke Blue or Black, Blue or Black». Исправление парсера для формы в скобках добавлено @LordLuceus (PR #90); впоследствии расширено на голый текст и параметризованные последовательности hanger для полного соответствия.
  • Магазин / офлайн: открытие магазина при отсутствии соединения больше не зачитывает пустое модальное окно Welcome Bundle («000000 Edelsteine, $X.XX, …»). Модальное подтверждение магазина теперь игнорируется, пока его кнопки валют содержат замещающие значения, поэтому реальное всплывающее окно SystemMessage «Store-Fehler» / «Не удалось получить предметы» объявляется и доступно для навигации с клавиатуры.
  • Всплывающие окна SystemMessageView (офлайн / ошибка / требуется перезапуск) теперь имеют приоритет над обычными модальными подтверждениями, открывшимися в том же кадре, так что системное сообщение становится активной панелью, а не маскируется.
  • Всплывающее окно награды за бустер больше не озвучивает название предыдущего выпуска. Игровой объект наград живёт на Canvas — Screenspace Popups и остаётся активным между покупками, поэтому кэш названия бустера и флаг _revealingWasSeen переживали разные всплывающие окна — второй бустер повторно использовал название первого, а 2-секундный тайм-аут резервного режима ненадолго активировал навигатор после каждого получения. Состояние для каждого окна теперь сбрасывается при переходе True→False, так что каждое новое получение чисто извлекает название набора бустеров.
  • Четыре строки из версии 1.1, связанные с наложением на поле боя (StackSelect_Unavailable, SettingBattlefieldStacking + Desc, HelpCtrlEnterStack), переведены на es, fr, it, ja, ko, pl, pt-BR, ru, zh-CN, zh-TW — ранее были только на английском.
  • Ctrl+F1 в дуэли теперь читает подсказку для активного подсостояния (Выбор X, спиннер, выбор цвета маны, назначение блокирующих, браузер), а не всегда выдаёт полный список клавиш дуэли.
  • Предупреждение о пропуске фазы теперь также срабатывает во время шага «Назначение блокирующих»: нажатие Space для подтверждения «нет блокирующих», пока у вас ещё есть развёрнутые существа и ничего не назначено, один раз предупреждает и требует второго нажатия для подтверждения. Предупреждение действует только в ход оппонента — именно в этот момент пользователь является защищающимся игроком и может назначать блокирующих — так что атака в ваш собственный ход больше не вызывает ложного срабатывания защиты на шаге блокирования оппонента.
  • Объявления пула маны теперь отражают реальное количество, когда одна способность даёт несколько единиц маны одного цвета. Игра упаковывает одинаковую ману в одну запись MtgMana с Count=N (Rofellos = одна запись с Count=3); парсер прибавлял по 1 на запись, так что тап Rofellos объявлялся как «1 зелёная» вместо «3 зелёных». Теперь суммируется MtgMana.Count по цветам.
  • Запросы выбора цели для опциональных триггеров (по типу Tinybones «вы можете разыграть выбранную карту») больше не заточают игрока. Браузер представлял каркас SelectCardsMultiZone с AllowCancel.No и словарём провайдера, содержащим только DoneButton; выбор единственной допустимой цели и последующая отмена разыгрывания зацикливались обратно в браузер с залипшим выбором, который автоматически отправлялся снова, без возможности выхода через существующий путь CancelButton. Теперь Backspace зеркалит защиту подтверждения через Space: первое нажатие предупреждает, второе нажатие снимает выделение через SimulatePointerClick для каждой карты и вызывает OnButtonCallback("DoneButton"), который игра переключает на вариант FailToFind, когда счётчик падает до 0. Две защиты взаимно сбрасывают свои предупредительные биты при первом нажатии, чтобы смена намерения с Space на Backspace не могла оставить заряженное состояние при последующем нажатии той же клавиши.
  • Браузеры SelectCardsMultiZone с AllowCancel.No и отключённой кнопкой DoneButton (счётчик ниже минимума, нет вкладки зоны для переключения) больше не заточают игрока в цикле Exit→Re-enter. ClickConfirmButton безусловно ставил _pendingRescan = true в самом начале; затем каждый путь провайдера/рабочего процесса не кликал никуда, но повторное сканирование всё равно срабатывало на следующем Update — заново объявляло вход в браузер и переносило фокус обратно на карту 1, заглушая сообщение о неудаче. Теперь путь без действий очищает _pendingRescan (не было клика, нечего отслеживать), а прежнее общее сообщение «No confirm button found» заменено на уже переведённое «No target selected», когда словарь провайдера сообщает, что DoneButton отключена — так игрок слышит причину, связанную с игровым состоянием, а не строку ошибки мода.
  • Объявления стоимости карт теперь отражают активные снижения стоимости, а не всегда читают напечатанную стоимость. При наличии Warden of Evos Isle на поле боя, второй Warden в руке теперь читается как 1 универсальная, синяя (соответствуя плитке в руке), а не 2 универсальные, синие (напечатанная стоимость). Мод теперь читает стоимость разыгрывания действия в реальном времени (Action.ManaCost из списка Actions конкретной карты, преобразованную через GreClient.CardData.ManaUtilities.ConvertManaCostsToList) — тот же источник, который использует отрисовщик плиток в MTGA — и откатывается к PrintedCastingCost, когда действие разыгрывания недоступно. Представления библиотеки / коллекции / конструктора (без живого экземпляра, а значит, без действий) по-прежнему читают напечатанную стоимость. Добавлено @LordLuceus (PR #91).
  • Добавления в стек теперь объявляют каждый новый элемент, а не только видимую вершину. Когда оппонент отвечает на ваше заклинание своим (так что два объекта попадают в стек почти одновременно — его ответ плюс, зачастую, одно из его сработавших triggered-способностей, активирующихся от той же стоимости или триггера), сам разыгрываемый ответ замалчивался: мод проверял только верхний CDC, который обычно был триггером, и никогда не произносил сам ответ. Теперь мод отслеживает объявленные элементы стека по MtgCardInstance.InstanceId и объявляет каждый вновь добавленный CDC при каждом обновлении счётчика зоны Stack, очищая трекер, когда стек опустошается.
  • В конструкторе колоды стрелка вниз на первой карте после поиска/фильтра теперь читает детали карты, а не переходит к следующей карте. Повторное сканирование после поиска переопределяло фокус EventSystem на первый элемент в целом (например, на строку Wildcards), а не на восстановленный сгруппированный элемент, что деактивировало CardInfoNavigator и превращало первую стрелку вниз в шаг навигации.
  • Браузер распределения повреждений больше не перехватывается общим SpinnerNavigator. Оба навигатора активировались на виджетах SpinnerAnimated, но SpinnerNavigator выполнялся первым и перехватывал ввод — объявлял блокирующих как Card #483 (его распознаватель, заточенный только на поле боя, пропускал карты, перемещённые в браузер), сообщал «Verteile 0» (нет родительской SpinnerGroup → max=0) и отправлял через PromptButton_Primary вместо DoneAction браузера, так что подтверждение летального распределения по умолчанию ничего не делало. Теперь SpinnerNavigator уступает, когда активен браузер AssignDamage, позволяя BrowserNavigator.AssignDamage обрабатывать его корректно (правильные имена карт, общий урон, статус летальности, Space → DoneAction, Backspace → UndoAction).
  • Запрос Yes/No для Мутации (например, Huntmaster Liger спрашивает «мутировать на X?») больше не блокирует игрока. Каркас BrowserScaffold_YesNo_Mutate_… приводился к простому YesNo с помощью ExtractBrowserTypeFromScaffold, теряя вариант: Tab перебирал только карты из стопки мутации (никогда не доходя до кнопок Yes/No), Space проваливался до PromptButton_Primary уровня дуэли (кнопка паса — неверная цель, браузер оставался зависшим), входная подсказка объявляла буквальный ключ локали BrowserHint, а дружественное имя читалось «Choose yes or no» вместо «Mutate choice». Теперь BrowserDetector помечает вариант Mutate в BrowserInfo, и BrowserNavigator зеркалит для него поток OptionalAction: Tab перебирает карты, затем кнопки, Space направляется через логическую DoneButton провайдера (превращается в 2Button_Right = Yes), Backspace через CancelButton (= No), с правильной подсказкой BrowserHint_Mutate, переведённой на все 12 языков.
  • Запросы браузера Yes/No теперь читают сам вопрос, а не просто «Choose yes or no». Всплывающее окно Ancestral Recall «Are You Sure?» (и любой другой браузер только с кнопками, такой как запросы Optional-action / Mutate) читает Вы хотите сделать оппонента целью Ancestral Recall? Choose yes or no — текст подзаголовка каркаса извлекается через существующий помощник ExtractBrowserHeaderText и добавляется перед меткой типа. Добавлено @LordLuceus (PR #93).
  • P/T существ на поле боя теперь объявляет текущий урон отдельным элементом после счётчиков, соответствуя тому, как игра показывает красное число рядом с выносливостью для зрячих игроков. Существо 3/3, получившее 2 повреждения, читается как 3/3, 2 повреждения (или со счётчиками: 4/4, 1 +1/+1, 2 повреждения); напечатанная/изменённая эффектами выносливость остаётся нетронутой, так что понятно, как значение было получено. Читает MtgCardInstance.Damage из модели — 0 при отсутствии живого экземпляра, так что конструктор / коллекция / показы библиотеки остаются без изменений. Функция первоначально предложена @LordLuceus (PR #92, как модификация выносливости); переработана для вывода урона отдельной строкой, чтобы объявление соответствовало визуальной компоновке игры.
  • Рука оппонента (Shift+C) больше не показывает карты, которые уже покинули руку. Карты, которые оппонент разыграл (их CDC остаётся под держателем OpponentHand с IsDisplayedFaceUp=true даже после перемещения на кладбище / в изгнание / на поле боя), раньше оставались в списке раскрытых карт, создавая несоответствие «Рука оппонента, 2 карты. … 1 из 3», где отображаемое количество и навигационный список расходились, а карта, которую оппонент уже разыграл, всё ещё выглядела так, будто она у него в руке. Теперь список OpponentHand применяет ту же проверку IsRealHandCard по зоне модели, которую уже использовал подсчёт карт в руке, так что список и счётчик совпадают. Добавлено @LordLuceus (PR #95).
  • Навигация по Tab больше не пропускает карты с доступными действиями. Визуальная HotHighlight в MTGA не всегда надёжно обновляется в переходных состояниях — как у разыгрываемых карт в руке (особенно сразу после розыгрыша земли, когда подсветка активируемой способности существа на поле боя остаётся, а у карт в руке игрока, которые всё ещё можно разыграть, визуальная подсказка пропадает), так и у собственных перманентов игрока на поле боя с активируемыми способностями (например, существо с {T}: do X, чьё свечение не обновилось после разрешения вражеского заклинания на кладбище). Сканирование мода совпадало только с GameObject’ами, чьи имена содержали буквальную подстроку "HotHighlight", поэтому Tab перебирал лишь тот элемент, у которого случайно сохранилась подсветка. Теперь и рука, и ваша сторона поля боя дополнены запросом к штатному ActionsAvailableWorkflow: workflow отслеживает GreInteraction для каждого доступного действия с предвычисленным флагом CanAffordToCast (устанавливаемым при создании взаимодействия из Action.CanAffordToCast()), и мы включаем любую карту, у которой есть играбельное взаимодействие — любой вариант Cast* (половинки разделённых карт, приключения, MDFC, прототипы, Rooms, Omens), обычный Activate (циклинг, ченнелинг, предсказание карты, заговор карты, ниндзюцу, активируемые способности на поле боя, способности planeswalker-ов) или не требующие оплаты Special/SpecialTurnFaceUp (сговор, монарх, продвижение по подземелью, переворот morph/manifest) — которое сам workflow считает доступным по средствам. Мана-способности (ActivateMana), земельные броски (Play*), перманенты оппонента и поддействия оплаты отфильтровываются, так что Tab по-прежнему перебирает только значимые действия. Никаких собственных эвристик доступности — игра сама отвечает на вопрос. Основа для разыгрываемой руки предоставлена @LordLuceus (PR #96); впоследствии расширена на типы действий Activate/Special и на перманенты на своей стороне поля боя.

v1.1

Комментарии

Добавить комментарий