Как сидеть на двух стульях. Совмещаем 1.26 и Reforged.

Добавлен , опубликован
Раздел:
Разное
Цель этой статьи собрать различные техники и фишки, позволяющие создать мультиверсионную карту полноценно работающую на клиенте с любым актуальным патчем (в частности, 1.26, 1.31, 1.32+ Reforged SD/HD). Показать, как внедрять механики из последних патчей, не теряя совместимости со старым клиентом.
Если у вас есть замечания по оптимальности описанных методов или другие предложения, то обязательно напишите об этом. Будем вместе дополнять статью.

Оглавление

Основы. Узнаём версию клиента

Всё описанное далее актуально для создания мультиверсионных карт в редакторе патча 1.26. Карты, созданные в более новых патчах, не будут работать в старых клиентах.
Для начала, полезно будет узнать текущую версию клиента (1.32, 1.31 или более старая версия). Для этого используются следующие функции:
//True, если версия клиента 1.32+
function Is132OrNewer takes nothing returns boolean
   return GetLocalizedString("REFORGED") != "REFORGED"
endfunction

//True, если версия клиента 1.31+
function Is131OrNewer takes nothing returns boolean
   return GetLocalizedString("WINDOW_MODE_WINDOWED") != "WINDOW_MODE_WINDOWED"
endfunction
Также, при игре на 1.32+ Reforged важно знать текущий графический режим (SD или HD). Для этого можно воспользоваться фактом, что у некоторых аудиофайлов разная длина в этих двух версиях:
function IsHD_Default takes nothing returns boolean
	return GetSoundFileDuration("sound\\buildings\\death\\ancientuprootdeath1.flac")==4631
endfunction
Тем не менее, предпочтительнее использовать вместо стандартного аудиофайла заранее импортированную пару. Это связано с тем, что некоторые игроки ставят модификации на замену аудиофайлов (в частности, очень популярен мод на "Возвращении старой русской озвучки"). И этот мод конфликтует с этим методом. Импортируйте файл_1 с полным путём "Check_SD_HD.mp3" и файл_2 с полным путём "_HD.w3mod\Check_SD_HD.mp3".
//True, если у локального игрока включена HD графика
function IsHD takes nothing returns boolean
	return GetSoundFileDuration("Check_SD_HD.mp3")==144
endfunction
Внимание! При игре по сети возможно, что у разных игроков будет включён различный режим графики. Используйте только локальный код (без создания новых объектов) при проверках на графический режим.
function GetVersion takes nothing returns nothing
	local string s
	if Is132OrNewer()      then
	    set s="1.32+ Reforged"
	elseif Is131OrNewer()  then
	    set s="1.31"
	else
	    set s="1.26-1.30.2"
	endif
	set s="Current version - "+s
	 if IsHD() then
        set s=s+" (HD)"
    else
        set s=s+" (SD)"
    endif
	call DisplayTextToPlayer(GetLocalPlayer(),0,0,s)
endfunction
Карта-пример (12.6 КБ) с определением версии игры и результаты её запуска на разных клиентах:

Импорт HD моделей. Альтернативные модели

Для импорта HD-версий моделей достаточно добавить к полному пути префикс "_HD.w3mod\". Аналогично импортируются текстуры.
При замене стандартных моделей стоит учесть, что у некоторых юнитов в HD режиме меняется путь к модели (поле file в Редакторе Объектов) и масштаб (scale), в случаях, если в эти поля не были внесены изменения. Используя этот факт можно проводить замену моделей только для одного из режимов, используя в другом версию по-умолчанию. К примеру, юнит Мечник (hhes) в SD режиме имеет модель человеческого капитана "units\human\TheCaptain\TheCaptain.mdx", а в HD эльфийского солдата "Units\Human\HighElfSwordsman\HighElfSwordsman.mdx".
Если же в поле масштаба внести изменение, то может получиться так, что юниты со стандартными моделями получаются сильно несоразмерными в SD и HD режимах. Это связно с тем, что несоразмерен масштаб самих моделей.
И изменение поля scale при переходе из SD в HD как раз компенсировало эту несоразмерность. В этих случаях можно задать необходимый размер юнита в коде карты. Это не вызывает рассинхронизацию.
function Rescale takes unit u, real sclSD, real sclHD returns nothing
	local real scl
	if IsHD() then
		set scl=sclHD
	else
		set scl=sclSD
	endif
	call SetUnitScale(u,scl,scl,scl)
endfunction
Карта-пример (4.15 МБ) с новой импортированной SD и HD моделью; только с изменённой SD моделью; с изменением масштаба юнита в зависимости от режима графики:

Исполнение новых функций через Preloader

Внимание! Описанный ниже метод подходит только для версии 1.32+ Reforged. Он не работает для 1.31.
К версии 1.32+ Reforged в Warcraft было добавлено много новых native-функций. Но при попытке обратиться к ним напрямую в редакторе 1.26 происходит ошибка компиляции (так как они не описаны в текущих common.j и blizzard.j). Обойти эту ошибку можно использовав следующую команду:
call Preloader("CustomPreloader.pld") 
Эта команда выполняет функцию PreloadFiles() из импортированного и предварительно созданного файла предзагрузки "CustomPreloader.pld" (название файла произвольно). Воспользуемся здесь тем свойством этой команды, что при синтаксической ошибке в этой функции она просто игнорируется, а не вызывает вылет игры с ошибкой. Использовать эту команду можно в любой момент игры. Разрешено иметь несколько различных .pld-файлов.
Внутри функции PreloadFiles() нельзя ссылаться на пользовательские функции из карты, а также на глобальные переменные. Разрешено использование локальных переменных.
В качестве примера, используем функцию BlzSetAbilityIcon() для изменения иконки способности Самопожертвование (Adtn) у Светлячка, когда игра запущена в версиях 1.32+ Reforged. С помощью блокнота создаём новый файл с названием и разрешением "CustomPreloader.pld" и вставляем туда следующий текст:
function PreloadFiles takes nothing returns nothing
	call BlzSetAbilityIcon('Adtn',"ReplaceableTextures\\CommandButtons\\BTNSacrifice.blp")
endfunction
Импортируем этот .pld-файл в карту. Вызываем выполнение этой команды через call Preloader("CustomPreloader.pld").
Карта-пример (13 КБ) с вызовом новой native-функции через Preloader:

Работа с фреймами

Для работы с фреймами в рамках мультиверсионной карты воспользуемся тем свойством функции Preloader(), что при вызове она наследует поток исполнения. И хотя внутри функции PreloadFiles() мы не можем обратиться к глобальным переменным или другим пользовательским функциям, но через функцию GetTriggeringTrigger() разрешено получить триггер, вызвавший исходный поток.
В качестве примера создадим кнопку, выводящую всем игрокам простой текст по нажатию.
Подготовим файл ButtonExample.pld, в котором будет создаваться элемент-кнопка
function PreloadFiles takes nothing returns nothing
	local trigger trig = GetTriggeringTrigger()
	local framehandle mainButton =BlzCreateFrame("ScriptDialogButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI,0), 0,0)
	call BlzFrameSetSize(mainButton, 0.12,0.05)
	call BlzFrameSetAbsPoint(mainButton, FRAMEPOINT_CENTER, 0.3,0.3)
	call BlzFrameSetText(mainButton, "Hello, world!")
	call BlzTriggerRegisterFrameEvent(trig, mainButton, FRAMEEVENT_CONTROL_CLICK)
	set trig=null
	set mainButton=null
endfunction
В коде карты прописываем функцию ButtonTrigger_Init():

globals
	trigger udg_trig
	triggeraction udg_trigact
endglobals

//Функция с действиями по нажатию кнопки
function ButtonTrigger_ClickAction takes nothing returns nothing
	call DisplayTextToPlayer(GetLocalPlayer(),0,0,"Hello, world!")
	call Preloader("ButtonResetFocus.pld")	//Исправление залипания фокуса на кнопке после нажатия
endfunction

//Функция создания кнопки
function ButtonTrigger_CreateButton takes nothing returns nothing
	call TriggerRemoveAction(udg_trig,udg_trigact)	//Уберём с триггера действие по созданию кнопки
	set udg_trigact=null
	call Preloader("ButtonExample.pld")
	call TriggerAddAction(udg_trig,function ButtonTrigger_ClickAction)	//Добавим действие по нажатию кнопки
endfunction

//Создаём и запускаем триггер
function ButtonTrigger_Init takes nothing returns nothing
	set udg_trig=CreateTrigger()	//Создаём триггер. В итоге он будет запускаться при нажатии кнопки.
	set udg_trigact=TriggerAddAction(udg_trig,function ButtonTrigger_CreateButton)	//Понадобиться только для первонального создания кнопки.
	call TriggerExecute(udg_trig)
endfunction
Дополнительно, для исправления фичи (или бага?) с залипанием фокуса на кнопке после нажатия, создадим и импортируем файл ButtonResetFocus.pld:
function PreloadFiles takes nothing returns nothing
	call BlzFrameSetEnable(BlzGetTriggerFrame(), false)
	call BlzFrameSetEnable(BlzGetTriggerFrame(), true)
endfunction
Карта-пример (15 КБ) с действующей кнопкой:

Превью и миникарта

Обычной проблемой карт созданных в редакторе 1.26 является неотображение картинки-превью в Reforged, так как стандартный метод с импортом файла "war3mapPreview.tga" не работает в новом клиенте. Роль превью теперь здесь играет изображение игровой карты, которое можно задать импортом файла с именем и форматом "war3mapMap.dds".
Далее, для того, чтобы сделать обратную замену preview на истинное изображение карты, при инициализации вызывается функция BlzChangeMinimapTerrainTex(). В картах, сделанных в старых редакторах, это можно сделать через загрузку соответствующего .pld-файла.
С помощью блокнота создаём новый файл с названием и разрешением "ChangeMinimap.pld" и вставляем туда следующий текст:
function PreloadFiles takes nothing returns nothing
	call BlzChangeMinimapTerrainTex("war3mapMapTrue.dds")
endfunction
Здесь "war3mapMapTrue.dds" - полный путь к файлу с истинным изображением игровой карты, который необходимо дополнительно импортировать (название файла произвольно). Файл "ChangeMinimap.pld" сохраняем и импортируем в карту. Добавляем "Preloader("ChangeMinimap.pld")" в функцию инициализации:
function Trig_MapInit_Actions takes nothing returns nothing
    call Preloader( "ChangeMinimap.pld" )
endfunction
Карта-пример (69 КБ) с рабочей миникартой и превью в разных версиях клиента:

Анимируем портреты в Reforged HD

Одной из проблем при запуске старых карт в Reforged HD является отсутствие эффекта движения губ во время роликов у портретов юнитов со стандартными HD моделями. Это связано с тем, что в Reforged HD предусмотрена синхронизация движения губ и воспроизведения звуковых дорожек. Отсутствие как самих специальных файлов с набором движения губ, так и привязки их к соответствующим звуковым дорожкам, приводят к тому, что юниты вообще перестают открывать рты, даже если параллельно идёт озвучка.
Оставим за рамками этой статьи описание способа, как самостоятельно создать файл, описывающий правильное движение губ для конкретных аудиодорожек (например, с помощью пакета FaceFX Studio). Но покажем, как можно подключить произвольную (не связанную с аудиодорожкой) анимацию движения губ, используя редактор 1.26.
За основу порядка движения губ возьмём набор анимации Акамы из 5 миссии компании Альянса из The Frothen Throne. Эта анимация длиться довольно долго (41.2 с в русской версии и 34.7 с в английской) и она совместима со всеми основными гуманоидными портретами.
Создаём в блокноте и после импортируем в карту файлы conversation.json (название файла строгое) и LipsStart.pld (название файла произвольно) со следующим содержанием:
{
    "stringTablePath": "war3map.wts",
    "conversation": {
        "All_conversations": [          
	    {
                "conversationOrder": 0,
                "animationLabel": "A05Akama11",
                "animationGroupLabel": "Map-DranaiAkama",
                "animationSetFilepath": "sound\\dialogue\\faceanimation\\humanx05\\facialanimation\\dranaiakama.animset_ingame",
                "animationSetFilepathMapRelative": true
            }
        ]
    }
}
function PreloadFiles takes nothing returns nothing //41.225 - Rus; 34.709 - Eng
local sound soundhandle=CreateSound("sound\\ambient\\doodadeffects\\elevator.flac", false, false, false, 10, 10, "DefaultEAXON")
call SetSoundFacialAnimationLabel(soundhandle, "A05Akama11")
call SetSoundFacialAnimationGroupLabel(soundhandle, "Map-DranaiAkama")
call SetSoundFacialAnimationSetFilepath(soundhandle, "sound\\dialogue\\faceanimation\\humanx05\\facialanimation\\dranaiakama.animset_ingame")
call SetSoundChannel(soundhandle,36)
call SetSoundVolume(soundhandle,0)
call StartSound(soundhandle)
call KillSoundWhenDone(soundhandle)
set soundhandle=null
endfunction
Здесь за основу soundhandle может быть выбран любой звук произвольной длины. Если нужно добавить аудиодорожку с реальной озвучкой персонажа, то её можно запустить параллельно используя другой звуковой канал. Теперь при фоновом запуске звука soundhandle (через функцию Preloader("LipsStart.pld") ) текущий отображаемый портрет начнёт шевелить губами.
call CinematicModeBJ( true, GetPlayersAll() )
call DoTransmissionBasicsXYBJ('hrif', GetPlayerColor(Player(0)),0, 0, null, "Sniper", "Hello, Worker!", 7.0)
call Preloader( "LipsStart.pld" )
call PolledWait( 7.00 )
call DoTransmissionBasicsXYBJ( 'hpea', GetPlayerColor(Player(1)),0, 0, null, "Worker", "Hello, Sniper!", 7.0)
call Preloader( "LipsStart.pld" )
call PolledWait( 7.00 )
call CinematicModeBJ( false, GetPlayersAll() )

Два стула и табуретка. Совместимость с патчем 1.31

Хотя клиент 1.31 пользуются меньшей популярностью чем 1.26 или актуальный Reforged, здесь есть своя ненулевая аудитория. Если вам нужна совместимость с этим патчем, необходимо учесть некоторые особенности.
В 1.31 существует баг, проявляющийся после загрузок сохранённых игр. В этом случае перестают работать почти все раннее добавленные события триггеров типа Specific Unit Event (EVENT_UNIT_SPELL_EFFECT, EVENT_UNIT_USE_ITEM и т.п.) Из подобных остаются работать всего несколько (EVENT_UNIT_DAMAGED, EVENT_UNIT_DEATH).
Если ваша карта не односессионная, то необходимо либо использовать только события типа Generic Unit Event (EVENT_PLAYER_UNIT_SPELL_EFFECT, EVENT_PLAYER_UNIT_USE_ITEM и т.п.). Либо отслеживать событие загрузки игры (EVENT_GAME_LOADED) и добавлять заново потерянные события.
`
ОЖИДАНИЕ РЕКЛАМЫ...
4 комментария удалено
3
32
2 года назад
3
Наконец-то дошли руки всё прочитать полностью и внимательно, отличные примеры в статье, особенно видос, проорал с
Очень интересная тема, жду статью про "создание совпадающего движения губ"
Вообще очень печально и обидно за makkad, 90% комментов в этом посте не имеют никакого отношения к ней, каждый приходит со своие ненужным мнение "Мне это не нужно я сижу на 1.26, и у меня топовый комп", уверен даже статью не читали, какое же все дно, статья не достойна этого сайта с такими юзерами.
Загруженные файлы
4
22
2 года назад
4
Bergi, Сегодня дополню статью главой про фреймы. Вот пока спойлер:
0
35
2 года назад
0
makkad, о, годнота и актуальная информация подъехала. Жду.
2 комментария удалено
2
22
2 года назад
2
13.02.2022
Статья обновлена. Добавлена глава Работа с фреймами.
3
22
2 года назад
3
О май гад, какие же страшные костыли! Хорошо что я на модинг варкрафта забил хер еще давным давно. Вспоминаю кастылестроение в кошмарах.
7
18
2 года назад
Отредактирован SNART
7
О май гад, какие же страшные костыли! Хорошо что я на модинг варкрафта забил хер еще давным давно. Вспоминаю кастылестроение в кошмарах.
Мне послышалось, или сказал:
О май гад, какие же потрясающие костыли! Хорошо что я на модинг варкрафта положил глаз еще давным давно. Вспоминаю кастылестроение в сладких снах.
4 комментария удалено
2
35
2 года назад
Отредактирован tysch_tysch
2
mistwood, я тоже за то чтобы проверять факты

так я всё тру, вы опять сделали это – отклонились от темы, но уже не накидали друг другу членов за воротник и то хорошо
0
1
1 год назад
0
Здравствуйте, скажите пожалуйста куда писать все эти функций?
0
22
1 год назад
0
Здравствуйте, скажите пожалуйста куда писать все эти функций?
Это jass-код карты. На сайте есть несколько обучающих статей, как работать с ним.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.