Это первый из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей.
Эти статьи написаны для тех, кто в целом уже понимает скриптинг, знаком с теми или иными функциями, но теряется, глядя на документацию ОБСЕ, не понимая, как применить всё это добро на практике.
Начнем с основ. Немного новенького синтакикса, делающего больше меньшими усилиями. Посмотрим на выражения ОБСЕ.
Если скриптинг – язык, давайте назовем функции вокабуляром и идиомами, а то, что ниже – синтаксис, собирающий всё воедино и придающий ему смысл.
Кое-что уже должно быть вам известно: == != > < >= <= + - * / () && ||
Сравнение, базовая математика, скобки, меняющие последовательность, логические операторы AND и OR, уяснили, да? Если нет, то отложите статью пока не усвоите базу.
Метнемся к самым полезным из новых особенностей, под которыми я имею в виду то, что поможет связать весь скрипт воедино, тем более некоторые из них уж очень удобны, чтобы их игнорировать: 1. LET 2. IF EVAL 3. "МАТЕМАТИКА " И ПРИСВОЕНИЕ 4. ЦИКЛЫ WHILE 5. SCRIPT COMPILER OVERRIDE
1. LET
В NVSE4 «let» заменяет «set». Конечно set всё еще можно использовать как и раньше, но let может больше, и правильнее перейти на него при работе с более продвинутым синтаксом, да и со строками и массивами, о которых в другой раз. Собственно, всё, что нужно запомнить, так это то, что вместо этого:
мы пишем:
Видите штучку := в коде? Не забудьте о ней. Это «присваивание», то есть «пусть А := Б», означает «Пусть А будет равно Б», в библейском духе «Да будет свет».
2. IF EVAL
Та же история с if-if eval. If eval нужен чтобы компилятор понял более продвинутые вещи. Всегда используйте его со всем, что связано с массивами и строками, и он не подведет.
Заметьте, что можно миксовать старые if’ы и новые if eval’ы, если хотите.
3. "МАТЕМАТИКА" И ПРИСВОЕНИЕ
+= прибавить и присвоить
-= вычесть и присвоить
*= умножить и присвоить
/= разделить и присвоить
Незаменимо для быстрого манипулирования любыми float, если спросите меня. Через математическую функцию присваивает переменной слева переменнную справа и сразу устанавливает переменную слева в результат. Кажется, ну и что, но наконец-то мы можем перестать писать это:
И начать писать так:
Меньше писанины, короче скрипты, нет смысла делать по старому. О, и еще кое-что о таймерах:
Теперь могут быть записаны:
Пользуйтесь, обожайте, не возвращайтесь к старым практикам.
4. ЦИКЛЫ WHILE
Эти циклы комбинируют циклический аспект циклов Label/GoTo с возможностью делать циклы зависящими от условия “if eval” (включая математику и присвоение из пункта выше).
Цикл while – все, что между условием "while" и командой "цикл"(loop): это блок в духе блока if-endif:
Если условие while ложно, содержимое блока проматывается. Если условие while истино, блок между ним и командой «цикл» работает и доходя до Loop, скрипт возвращается к условию while и выполняет его снова. Немного примеров:
Хотите чтобы отрывок кода отработал 80 раз? Раньше это писали так:
Теперь нынешний эквивалент:
Опять же, он короче, и NVSE не нужно париться над отслеживанием индексов лейбла, и вам в том числе. К тому же цикл представлен в виде блока, когда как GoTo едва ли дотягивает до статуса лейбла, а endif вашего if редко когда-либо использовался.
Это может звучать вьедливо, но чем сложнее скрипт, тем важнее его читаемость. Label/GoTo – уродство.
Однако имейте в виду, что iNum уже добавлена в первое условие while, потому оно начнет с 1, а не с 0 в условии if из старого примера, так что чтобы получить именно 80 нужно указать значение 81.
Применим это к классическому циклу. С label/goto типичный цикл, работающий с каждым элементом в списке и затем удаляющий его из него, будет выглядеть так:
Теперь же:
И, конечно, удалить что-либо без задействования элементов списка теперь проще простого:
Хоть циклы намного красивее и элегантнее, чем Label/GoTo, но как и любые другие циклы они нуждаются во внимательности, иначе циклы перейдут в бесконечность и крашнут игру.
Это ужаснейший пример, непременно приведет к крашу игры.
Прежде чем запустить в цикл огромный кусок кода сто раз подумайте. Циклы while работают каждый фрейм как и скрипт, запускающий цикл. Поэтому ограничьте циклы тем, что действительно необходимо сделать.
Сделать это можно командой break, что заставит скрипт пойти ниже команды loop, это позволит избежать работы того участка кода, который не должен сейчас проигрываться.
Промотать часть кода с телом условия while можно командой continue, которая подобна return, и возвращает скрипт обратно к условию while.
Как обычно, правильная практика – помещать continue как можно выше в теле цикла как и return в скрипте заклинания или обьектном скрипте.
SCRIPT COMPILER OVERRIDE
Примечание. Возможно, вам потребуется узнать немного больше о строковых переменных (StringVar), переменных массивов (arrayVar) и определяемых пользователем функциях (UDF), чтобы действительно понять часть того, что изложено ниже. Но переопределение компилятора определенно подпадает под синтаксис и не ограничивается ни одной из этих тем.
Конечно, let, if eval, while, math & assig и т.д. уже сами по себе хороши, а мы еще и не дошли до комбинаций UDF, строк, массивов и переменных nx. Впрочем, когда дойдем, нас доканает компилятор оригинальных скриптов, не принимающих их как параметры к изначальным и старым NVSE функциям.
Пример #1: представим, у нас есть число, для которого мы хотим получить нижний предел (get the floor of), ближайшее целое число меньше числа с плавающей запятой (float). Ванильный компилятор будет знать, что мы хотим сделать, если мы укажем это как переменную с плавающей запятой (float), которую мы передадим функции Floor в качестве параметра:
Однако он не поймет, чего мы хотим, если мы скажем ему получить число с плавающей запятой (float) из элемента массива или из вызова UDF:
это не скомпилируется. Ванильный компилятор принимает лишь переменные float как параметр к функции floor и при любом раскладе не имеет понятия, чего ожидать от массива или UDF во время компиляции. Там может быть что угодно, и э-э-э на деле это не правда.
Чтобы обойти это, мы должны перемотнуть через скриптовый блок, задействовав компилятор NVSE, что можно сделать, предварив нужный блок кода подчеркиванием:
И затем идти по своим делам, вычислять числа, быть красавчиком. Вы буквально говорите компилятору принять всё как есть и что, мол, всё будет окей, и вы знаете, что там float в ключе массива, или что float вернется из UDF. Этим ходом вы забираете щепотку ответственности у компилятора в обмен на свободу. В итоге вам нужно быть осознанным, не запороть всё дело, протестировать всё в игре вместо того чтобы опираться на отчеты об ошибках из ГЕККа, и использовать скобки чуть больше чтобы убедиться, что компилятор не скомпилировал то, что не может работать в игре.
Пример #2: мы хотим проверить пачку разных свойств актора (actor values) SPECIAL на НПС и дать им bump если они меньше 5:
Теперь вы должны увидеть, что параметры Харизма, Интеллект и другие на деле – строки, комбинация знаков. Чтобы сузить круг поиска и установить 7 свойств актора мы прилепим их к массиву, извлечем их через цикл foreach и привяжем к строковой переменной, и проверим вот таким образом:
но это всего лишь окольный способ показать, что переопределение позволит использовать string_var, а не фактическую строку для значения актера. Строго говоря, мы можем сократить это еще больше:
Пример # 3: пример ОБСЕ документации, которую вы уже можете усвоить, если усвоили предыдущую информацию:
Становится эпически коротким:
Еще одним достоинством нового компилятора является то, что вместо этого:
Можно сосласться на свойство актора по его кодовому числу, который вы можете найти на дне ужасающе устаревшей документации NVSE:
Почему это преимущество? Ну потому что с числами можно делать то, чего нельзя со строками, типа «Здоровье». Ну например, привязать их к int:
Так что можно привязать кучу свойств актора через цикл с условием while и циклом математики и присвоения тоже, вот так:
Если Вы хотите «Супер-Дупер Мешок Доктора» или «Кнопку Немедленного Подхила».