Если вы новичок в работе со скриптами, да и в программировании вообще, то мысль об использовании скриптов TES может показаться вам несколько обескураживающей. Однако я написал расширенный обучающий курс, который проведет вас через процедуру создания первого скрипта. Я также объясню значение и смысл основных элементов скриптового языка TES. Объяснений будет много, но ключевые положения будут выделены жирным шрифтом.
Поехали!
Мы начинаем с открытия редактора скриптов: Запускайте TES Construction Set, загружайте файл Morrowind.esm, и выбирайте Edit Scripts в меню Gameplay, чтобы запустить окно редактора скриптов.
Мы начинаем с открытия редактора скриптов: Запускайте TES Construction Set, загружайте файл Morrowind.esm, и выбирайте Edit Scripts в меню Gameplay, чтобы запустить окно редактора скриптов.
Окно редактора скриптов
Войдите в скриптовый редактор, выбрав в меню Gameplay – Edit Scripts, либо кликнув мышью по кнопке Scripts (та, что с карандашом) в панели задач, либо кликнув в окне NPC или Объекта по кнопке с троеточием […] в поле Script. Окно редактора выглядит довольно просто:
Давайте взглянем на кнопки в панели задач, слева направо: Open позволяет вам открыть любой скрипт для редактирования. Save проверяет текущий скрипт на предмет ошибок и компилирует его, либо выдает сообщение об ошибке – отметьте однако, что в этот момент плагин, а значит и скрипт, вовсе не сохраняются на диск (Помимо этого, если вы написали, но не смогли откомпилировать скрипт, а затем сохранили плагин, то написанный вами текст сохраняется, хотя и не компилируется. Так что сообщение “Complied script not saved” – небольшая ложь. В следующий раз, когда вы зайдете в CS, вам выдадут сообщение, что такой-то скрипт не откомпилирован. Открывайте его, исправляйте и компилируйте - все, что вы написали до этого, было сохранено.(Прим. Gwathlobal)). Работая над большими скриптами, чаще пользуйтесь командой сохранения в главном окне TES CS после того, как «сохранили» скрипт в редакторе, - просто на тот случай, если TES CS вылетит. Заметьте, что если вы сохранили плагин, не сохранив скрипт, то скрипта в плагине не будет, так что сохраняйте скрипты, а уж потом плагины (Спасибо Kir за эту инфу).
Forward и Backward (стрелки) позволяют вам переключаться на следующий или предыдущий скрипт соответственно (в алфавитном порядке). Если вы присвоите своим скриптам общее название, это позволит вам легче ориентироваться и переключаться между скриптами проекта. Например, если именовать свои скрипты в стиле «AA_имя_скрипта», редактор поместит их все вместе в начало списка по принципу алфавитной сортировки (Лично я рекомендую перед именем скрипта ставить знак _ (подчеркивание). Тогда ваши скрипты всегда будут первыми в списке. Можно также писать в имени скрипта сокращенное имя плагина (например BloodMoon - BM) или ваш сокращенный ник (напр. Gwathlobal - GW). Это практически со 100% вероятностью сделает имена ваших скриптов уникальными. А нужно это потому, что из двух скриптов с одинаковыми названиями выполняется тот, который сохранен позже, а не оба вместе. (прим. Gwathlobal)) . Compile all – эта команда перекомпилирует все скрипты (для чего это может потребоваться? честно говоря, не знаю). Наконец, кнопка Delete удаляет скрипт, а кнопка со стрелкой вниз – закрывает окно скриптового редактора.
Меню справки дает быстрый доступ к разделам функций и команд файла помощи (весьма скромных в плане полезности, учитывая необходимость создания данного руководства!)
Вы можете копировать и вставлять текст в окно скриптового редактора с помощью стандартных для Windows клавиатурных комбинаций: Ctrl+C – копировать, Ctrl+X – вырезать, Ctrl+V – вставить.
Меню справки дает быстрый доступ к разделам функций и команд файла помощи (весьма скромных в плане полезности, учитывая необходимость создания данного руководства!)
Вы можете копировать и вставлять текст в окно скриптового редактора с помощью стандартных для Windows клавиатурных комбинаций: Ctrl+C – копировать, Ctrl+X – вырезать, Ctrl+V – вставить.
Чего мы хотим?
Перед тем, как мы действительно начнем писать скрипт, нужно определиться, что он будет делать. Я решил, что для этого обучающего курса достойной задачей будет создать Сундук с загадкой: сундук загадывает загадку, и только правильный ответ открывает сундук. Если игрок даст неверный ответ, сработает ловушка, ранящая игрока, а сундук уже не откроется.
Это достаточно сложная задача, но мы решим ее шаг за шагом.
Это достаточно сложная задача, но мы решим ее шаг за шагом.
Написание скрипта
Откройте окно редактора скриптов и кликните по главной части окна редактора. Здесь вы будете писать скрипт.
Имя для скрипта: Begin и End
Прежде всего мы должны дать скрипту имя – каждый скрипт начинается с объявления его имени. Поэтому, напечатайте:
Прежде всего мы должны дать скрипту имя – каждый скрипт начинается с объявления его имени. Поэтому, напечатайте:
Begin my_first_script
в окне редактора. Обратите внимание на знаки подчеркивания: ваше имя должно быть одним словом, то есть не содержать пробелов. Также отметьте, что скриптовый язык нечувствителен к регистру, так что команда “Begin” могла быть напечатана, начиная со строчной (маленькой) буквы – “begin”. Данное скрипту имя – это указатель, по которому его опознает TES CS. Попробуйте теперь нажать кнопку Save: вы получите сообщение о том, что «you need to end your script with end scriptname» (скрипт следует заканчивать командой “end имя_скрипта”). Таким образом, для того, чтобы скрипт распознавался редактором как таковой, нам следует обозначить не только его начало, но и конец: далее, напишите
End
на новой строке ниже предыдущей. Как видите, указание имени скрипта в этой строке уже можно опустить, достаточно слова end. Вот теперь, если вы нажмете Save, вы увидите, что его имя появилось в строке заголовка, значит скрипт сохранится без ошибок. Это самый короткий возможный скрипт, и конечно, он ничего не делает.
Обнаружение действий игрока
Далее, нам нужен способ определить, пытается ли игрок открыть сундук. В скриптах TES мы различаем Объекты, Функции и Команды:
Объекты - это все вещи в игровом мире, такие как видимые предметы, монстры, NPC (персонажи) или просто звуки.
Функции - это все «слова», употребляемые в скриптах TES для манипуляции объектами или получения информации о них.
Команды - это те «слова» скриптового языка, которые его структурируют, но не оперируют какими-либо объектами игры – к примеру, команду “Begin” мы уже использовали, чтобы сообщить скриптовому редактору имя нашего скрипта. Чтобы объяснить игре, к какому объекту следует применить ту или иную функцию, мы используем «стрелку»: -> (на самом деле просто дефис и знак «больше чем»). Слева от этого знака мы указываем объект (он называется вызывающим объектом), а справа – функцию, которая к нему применяется:
Далее, нам нужен способ определить, пытается ли игрок открыть сундук. В скриптах TES мы различаем Объекты, Функции и Команды:
Объекты - это все вещи в игровом мире, такие как видимые предметы, монстры, NPC (персонажи) или просто звуки.
Функции - это все «слова», употребляемые в скриптах TES для манипуляции объектами или получения информации о них.
Команды - это те «слова» скриптового языка, которые его структурируют, но не оперируют какими-либо объектами игры – к примеру, команду “Begin” мы уже использовали, чтобы сообщить скриптовому редактору имя нашего скрипта. Чтобы объяснить игре, к какому объекту следует применить ту или иную функцию, мы используем «стрелку»: -> (на самом деле просто дефис и знак «больше чем»). Слева от этого знака мы указываем объект (он называется вызывающим объектом), а справа – функцию, которая к нему применяется:
Object_ID -> function, [parameters]
Функция может иметь или не иметь параметры. Параметрами могут быть ID (идентификаторы) других объектов, числа, и, в некоторых случаях, переменные.
То, что нам нужно для нашего сундука с загадкой, - это функция “OnActivate”: она информирует нас о том, «активировал» ли игрок определенный объект игрового мира или нет. Эта функция возвращает значение 1 (означает True, «истина», в терминах программирования), если объект был активирован, то есть игрок «прицелился» в него и нажал кнопку «использовать» (клавиша пробел в управлении по умолчанию). Таким образом, нам остается только проверить, не возвращает ли функция OnActivate значения «истина» в любое время в игре. Поэтому отредактируйте свой скрипт, чтобы он выглядел следующим образом:
То, что нам нужно для нашего сундука с загадкой, - это функция “OnActivate”: она информирует нас о том, «активировал» ли игрок определенный объект игрового мира или нет. Эта функция возвращает значение 1 (означает True, «истина», в терминах программирования), если объект был активирован, то есть игрок «прицелился» в него и нажал кнопку «использовать» (клавиша пробел в управлении по умолчанию). Таким образом, нам остается только проверить, не возвращает ли функция OnActivate значения «истина» в любое время в игре. Поэтому отредактируйте свой скрипт, чтобы он выглядел следующим образом:
Begin my_first_script
If ( OnActivate == 1 )
; здесь мы введем действия, совершаемые при открытии сундука
endif
End
Здесь нужно объяснить еще пару вещей: команда “If” нужна здесь для проверки условий – если выражение, заключенное в скобки, - истина, значит будут выполнены последующие линии кода до команды “EndIf”. Знак “==” (двойное «равно») проверяет, одинаково ли содержание выражений по его обе стороны (в нашем случае, равно ли значение, возвращаемое функцией “OnActivate”, единице. Если вы забудете команду “EndIf” после команды “If”, редактор будет жаловаться в вышестоящие инстанции на ошибки. Знак “;” (точка с запятой) обозначает комментарии – все, что вы напишете на этой строке после точки с запятой, будет проигнорировано при запуске скрипта. Если вы когда-нибудь станете делать большие скрипты, то наверняка научитесь любить писать комментарии.
Вывод текста и получение решений игрока
А теперь мы хотим, чтобы наш коварный сундук загадал игроку загадку. Для этого воспользуемся функцией MessageBox, которая позволяет нам отображать на экране какой-то текст, а также варианты выбора, с которыми игроку предстоит иметь дело. К сожалению, Морровинд не позволяет нам оформить все так, чтобы игрок напечатал ответ с клавиатуры , поэтому придется использовать вариант множественного выбора. Соответствующая строка в скрипте будет выглядеть так:
А теперь мы хотим, чтобы наш коварный сундук загадал игроку загадку. Для этого воспользуемся функцией MessageBox, которая позволяет нам отображать на экране какой-то текст, а также варианты выбора, с которыми игроку предстоит иметь дело. К сожалению, Морровинд не позволяет нам оформить все так, чтобы игрок напечатал ответ с клавиатуры , поэтому придется использовать вариант множественного выбора. Соответствующая строка в скрипте будет выглядеть так:
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Призрак”
Первый отрезок в кавычках – это собственно текст, который будет отображаться на экране, а другие отрезки в кавычках, разделенные запятой, игра преобразует в «кнопки», отображающиеся вместе с текстом.
Но как мы добьемся того, чтобы загадка загадывалась только один раз – когда игрок впервые попытается открыть сундук, а не при каждой попытке? Тут мы подходим к одному из центральных вопросов: использованию «одноразовых» условий и переменных. Большая часть проблем, возникающих у начинающих скриптеров, связана с непониманием того, как выполняются скрипты, и как поэтому их нужно структурировать. Объясняю.
Но как мы добьемся того, чтобы загадка загадывалась только один раз – когда игрок впервые попытается открыть сундук, а не при каждой попытке? Тут мы подходим к одному из центральных вопросов: использованию «одноразовых» условий и переменных. Большая часть проблем, возникающих у начинающих скриптеров, связана с непониманием того, как выполняются скрипты, и как поэтому их нужно структурировать. Объясняю.
Как выполняются локальные скрипты
Каждый скрипт, привязанный к определенному объекту или NPC (локальный скрипт), запускается с каждым новым фреймом (обновлением экрана), когда ячейка (cell), содержащая объект, активна. В помещении активна только та ячейка, в которой в данный момент находится игрок, а на улице – ячейка, в которой находится игрок, плюс все прилегающие ячейки. Так что полный скрипт (не просто какая-то строка из него) выполняется 10-60 раз в секунду, или настолько часто, насколько позволяет мощность вашего компьютера. Лучше всего представить себе, что каждый локальный скрипт бесконечно прокручивается в мертвой петле или как в конструкции “while-loop”:
Каждый скрипт, привязанный к определенному объекту или NPC (локальный скрипт), запускается с каждым новым фреймом (обновлением экрана), когда ячейка (cell), содержащая объект, активна. В помещении активна только та ячейка, в которой в данный момент находится игрок, а на улице – ячейка, в которой находится игрок, плюс все прилегающие ячейки. Так что полный скрипт (не просто какая-то строка из него) выполняется 10-60 раз в секунду, или настолько часто, насколько позволяет мощность вашего компьютера. Лучше всего представить себе, что каждый локальный скрипт бесконечно прокручивается в мертвой петле или как в конструкции “while-loop”:
while (Объект в активной ячейке)
[Код вашего скрипта]
endwhile
А потому следующий скрипт изливается непрерывным потоком сообщений (если скрипт привязан к объекту или NPC, находящемуся в той же ячейке, где находится игрок). Попробуйте его, если хотите:
Begin Message_script
MessageBox “Тысячи бессмысленных сообщений”
End Message_script
Этот пример относительно безобидный (Это как посмотреть. Желающие попробовать несомненно заметили резкое увеличение тормозов. Вот вам наука – все скрипты, которые хоть как-то взаимодействую с внешним миром (выводят сообщения, добавляют предметы и т.п.) в большинстве случаев, не должны делать этого при каждом исполнении.), но представьте себе, если бы вместо команды выдать сообщение вы использовали строчку кода, добавляющую какой-то предмет в инвентарь игрока, либо вызывающую монстра и т.п.!
Поэтому «одноразовые» конструкции очень важны, и пригодится вам в скриптерской деятельности еще не раз. Теперь давайте продолжим создание нашего учебного скрипта: мы должны объявить переменную и использовать ее, чтобы гарантировать «одноразовое» появление сообщения. Поменяйте скрипт следующим образом:
Поэтому «одноразовые» конструкции очень важны, и пригодится вам в скриптерской деятельности еще не раз. Теперь давайте продолжим создание нашего учебного скрипта: мы должны объявить переменную и использовать ее, чтобы гарантировать «одноразовое» появление сообщения. Поменяйте скрипт следующим образом:
Begin my_first_script
Short controlvar
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Призрак”
Set controlvar to 1
endif
endif
End
(Отметьте, что команда MessageBox должна быть на одной строке в редакторе!)
"Short controlvar" объявляет новую переменную, которую я назвал controlvar, и присвоил ей тип short. На данном этапе нам достаточно знать, что это переменная, которая может содержать целочисленные положительные и отрицательные значения. Переменная – это своего рода хранилище, которое может содержать различные значения. Команду “If” мы уже знаем, а вот команда “Set” для нас новая. Однако она очень простая, всего лишь помещает в наше «хранилище» новое значение, иными словами устанавливает значение переменной равным единице (до этого оно было равно нулю, как у всех свежеобъявленных переменных). В сочетании с выражением If (controlvar==0), это обеспечивает требуемые «одноразовые» условия. Уже на следующий фрейм, после того, как переменной присвоено значение единицы, условие, заданное командой “If”, становится не соответствующим истине, и сообщения-загадки на экране больше не появится.
Теперь скрипт может быть запущен, так что протестим его:
Теперь скрипт может быть запущен, так что протестим его:
• Сохраните скрипт и закройте окно редактора.
• Зайдите в CS, окно объектов, выберете закладку Containers и откройте "chest_small_01".
• Измените ID сундука на "tutorial_chest"
• В ниспадающем меню выберете my_first_script.
• Сохраните объект как новый объект, сохраните мод, выйдете из CS, подключите мод в MW Launcher. Запускайте Морровинд и загружайте свою игру.
• Откройте консоль (кнопкой ~ слева от “1”) и в окне консоли напишите:
PlaceAtPC tutorial_chest 1,1,1
и нажмите Enter.
• Зайдите в CS, окно объектов, выберете закладку Containers и откройте "chest_small_01".
• Измените ID сундука на "tutorial_chest"
• В ниспадающем меню выберете my_first_script.
• Сохраните объект как новый объект, сохраните мод, выйдете из CS, подключите мод в MW Launcher. Запускайте Морровинд и загружайте свою игру.
• Откройте консоль (кнопкой ~ слева от “1”) и в окне консоли напишите:
PlaceAtPC tutorial_chest 1,1,1
и нажмите Enter.
Сделайте шаг назад (эээ… пусть ваш персонаж сделает шаг назад!) – теперь вы должны видеть небольшой сундук напротив вас на полу. Попытка открыть его должна вызвать наше сообщение. В общем, выглядит так:
Кликая по этим кнопкам, вы закроете это сообщение. Если после этого снова попытаться открыть сундук, ничего не произойдет – очень хорошо, значит, наше «одноразовое» условие работает.
Теперь выходите из Морровинда обратно в редактор и снова загружайте ваш плагин.
Сейчас нам предстоит определить, какой вариант ответа выбрал игрок, и запрограммировать требуемые реакции сундука на правильный и неправильные ответы. Функция, которая позволит нам определить выбранный игроком вариант ответа, называется “GetButtonPressed”. Эта функция возвращает числовое значение, которое зависит от того, какую «кнопку» в сообщении нажмет игрок. То есть, она возвращает «0», если нажата первая кнопка (в нашем случае ответ «летучая мышь»), затем «1», «2», «3» и т. д. для следующих кнопок, нумерованных в том порядке, в каком вы расположили их в скрипте, вызывая функцию “MessageBox”. Пока игрок не нажал никакой кнопки, функция будет возвращать значение «-1», это мы тоже должны учесть.
Теперь выходите из Морровинда обратно в редактор и снова загружайте ваш плагин.
Сейчас нам предстоит определить, какой вариант ответа выбрал игрок, и запрограммировать требуемые реакции сундука на правильный и неправильные ответы. Функция, которая позволит нам определить выбранный игроком вариант ответа, называется “GetButtonPressed”. Эта функция возвращает числовое значение, которое зависит от того, какую «кнопку» в сообщении нажмет игрок. То есть, она возвращает «0», если нажата первая кнопка (в нашем случае ответ «летучая мышь»), затем «1», «2», «3» и т. д. для следующих кнопок, нумерованных в том порядке, в каком вы расположили их в скрипте, вызывая функцию “MessageBox”. Пока игрок не нажал никакой кнопки, функция будет возвращать значение «-1», это мы тоже должны учесть.
Функция “Activate” откроет наш сундук. По сути, эта функция просто запускает стандартное для данного объекта действие, которое производится по умолчанию при попытке игрока его «использовать». Например, двери будут открываться (закрываться), NPC будет вступать с игроком в диалог и т. п.
Следующее изменение нашего скрипта покажет вам, как можно использовать контролирующую переменную, чтобы заставить Морровинд запускать функции последовательно одну за другой, невзирая на то, что весь скрипт прокручивается каждый фрейм. Мы будем просто наращивать контролирующую переменную и проверять ее значение серией управляющих конструкций типа “If – ElseIf”. Это самый надежный стиль скриптового программирования в Морровинде, - может, не всегда необходимый, но надежный.
Следующее изменение нашего скрипта покажет вам, как можно использовать контролирующую переменную, чтобы заставить Морровинд запускать функции последовательно одну за другой, невзирая на то, что весь скрипт прокручивается каждый фрейм. Мы будем просто наращивать контролирующую переменную и проверять ее значение серией управляющих конструкций типа “If – ElseIf”. Это самый надежный стиль скриптового программирования в Морровинде, - может, не всегда необходимый, но надежный.
Отредактируйте скрипт вот так:
Begin my_first_script
Short controlvar
Short button
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar > 1
activate
endif
endif
if (controlvar == 1)
set button to GetButtonPressed
if ( button == -1 )
return
elseif ( button == 2)
MessageBox "Ответ верный"
Activate
set controlvar to 2
else
MessageBox "Ответ неверный"
set controlvar to -1
endif
endif
End
Обратите внимание на часть скрипта, начинающуюся с выражения "if (controlvar == 1)". Как только сундук был активирован, мы установили значение переменной controlvar равным 1. Теперь проверим, какую «кнопку» нажал игрок. Для этого присвоим новой переменной button значение, возвращаемое функцией “GetButtonPressed”. Поскольку скрипт продолжает выполняться, хоть игра, казалось бы, просто ждет выбора «кнопки» игроком, мы должны проверить, нажил ли игрок какую-либо кнопку. Если не нажимал, то используем команду “Return”, которая заставляет игровой движок прекратить выполнение скрипта на время текущего фрейма.
Наш правильный вариант ответа на загадку – это «Ветер», что соответствует «кнопке» под номером два (то есть, идущей третьей по счету в команде “MessageBox”). Так вот, если игрок нажал «кнопку» номер два, мы должны сообщить ему, что он дал правильный ответ, и с помощью функции “Activate” открыть сундук.
Наш правильный вариант ответа на загадку – это «Ветер», что соответствует «кнопке» под номером два (то есть, идущей третьей по счету в команде “MessageBox”). Так вот, если игрок нажал «кнопку» номер два, мы должны сообщить ему, что он дал правильный ответ, и с помощью функции “Activate” открыть сундук.
Все прочие номера «кнопок» означают, что игрок выбрал неправильный ответ, так что мы можем использовать в данном случае команду “Else”, чтобы их отсечь. В таком случае мы должны сообщить игроку, каким дураком он себя был, и не открывать сундук.
Теперь обратите внимание на небольшое дополнение в начале скрипта :
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar >= 1
activate
endif
endif
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar >= 1
activate
endif
endif
Это означает, что если игрок попытается еще раз открыть сундук в будущем, сундук откроется только в случае, если значение переменной controlvar будет больше единицы. Проверьте то, что мы напечатали выше: когда игрок дает неправильный ответ на загадку, переменной controlvar присваивается значение -1, так что он никогда уже не откроет сундук. Но если он выбрал правильный ответ, controlvar устанавливается равной 2, значит, с этого момента игрок может открывать сундук в любое время, и сколько захочет. Теперь сохраняйтесь и тестируйте свой плагин, как в прошлый раз.
Ваш первый баг
Теперь, как вы должно быть уже убедились, наш скрипт делает практически, но не все, как мы хотели. То есть, почти все, да как бы не совсем. После выбора игроком правильного варианта сундук не открывается, как мы планировали. Логика скрипта вроде в порядке, так в чем же дело? Давайте попробуем модифицировать скрипт (замените соответствующую часть своего скрипта на следующую):
Теперь, как вы должно быть уже убедились, наш скрипт делает практически, но не все, как мы хотели. То есть, почти все, да как бы не совсем. После выбора игроком правильного варианта сундук не открывается, как мы планировали. Логика скрипта вроде в порядке, так в чем же дело? Давайте попробуем модифицировать скрипт (замените соответствующую часть своего скрипта на следующую):
if (controlvar == 1)
set button to GetButtonPressed
if ( button == -1 )
return
elseif ( button == 2 )
MessageBox "Ответ верный"
set controlvar to 2
else
MessageBox "Ответ неверный"
set controlvar to -1
endif
elseif ( controlvar == 2 )
Activate
endif
Видите, как я переместил функцию “Activate” в ту часть скрипта, которая проверяет условие “controlvar == 2”? Это обеспечивает более четкую последовательность событий алгоритма, и как я уже отмечал ранее, для Морровинда это может оказаться очень важным, - старайтесь никогда не делать слишком много вещей одновременно! Ну, теперь запускайте и тестируйте новый вариант.
Здорово, теперь сундук открывается, как мы хотели, но что за такое? Курсор тормозит, и мы не можем закрыть меню инвентаря! Посмотрите, что мы запрограммировали выше - controlvar была установлена на «2», и так и осталась с этим значением, больше оно не меняется. Теперь игра непрерывно получает задание активировать сундук (функция “Activate”), это происходит каждый раз, когда выполняется скрипт, то есть каждый фрейм! Вот почему мы не можем закрыть сундук – он немедленно открывается снова. Тогда попробуем изменить следующий кусочек скрипта:
elseif ( controlvar == 2 )
Activate
Set controlvar to 3
endif
Тестируйте плагин еще раз: все работает так, как надо. Надеюсь, вас не смутила моя небольшая экскурсия по процессу отладки. Это очень важная часть обучения – вы должны постоянно прорабатывать свои скрипты и искать разные решения поставленных задач.
Чего не хватает? Конечно, ловушки!
Чего не хватает? Конечно, ловушки!
Наложение заклинания на игрока
Наш сундук проклянет игрока, если он даст неправильный ответ.
Для начала откройте закладку “Spellmaking” в TESCS. С помощью правого клика вызовите в списке выпадающее меню и выберите “New”. Дайте заклинанию ID “Frost_Curse”, имя «Ледяное проклятие», установите его тип как “Curse” (Проклятие) и присвойте ему мощность, например, от 1 до 5 пунктов. Результат должен выглядеть так:
Наш сундук проклянет игрока, если он даст неправильный ответ.
Для начала откройте закладку “Spellmaking” в TESCS. С помощью правого клика вызовите в списке выпадающее меню и выберите “New”. Дайте заклинанию ID “Frost_Curse”, имя «Ледяное проклятие», установите его тип как “Curse” (Проклятие) и присвойте ему мощность, например, от 1 до 5 пунктов. Результат должен выглядеть так:
Теперь нам нужно наложить это проклятие на игрока. Для этого мы воспользуемся функцией “AddSpell”. Через какое-то время мы снимем проклятие, используя функцию “RemoveSpell”, и чтобы это время отмерить, нам понадобится таймер. Отредактируйте свой скрипт еще раз:
Begin my_first_script
Short controlvar
Short button
Float timer
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar > 1
activate
endif
endif
if (controlvar == 1)
set button to GetButtonPressed
if ( button == -1 )
return
elseif ( button == 2)
MessageBox "Ответ верный"
Activate
set controlvar to 2
else
MessageBox "Ответ неверный"
Player -> AddSpell, "Frost_Curse"
set controlvar to –1
Endif
elseif ( controlvar == 2 )
Activate
Set controlvar to 3
elseif ( controlvar == -1 )
Set timer to ( timer + GetSecondsPassed )
if timer > 10
Player -> RemoveSpell, "Frost_Curse"
set controlvar to -2
endif
endif
End
Теперь давайте с этим разберемся. Player -> AddSpell, "Frost_Curse" накладывает проклятие на игрока. Обратите внимание, мы использовали выражение “Player -> “ – это гарантирует, что эффект будет наложен именно на игрока. Если бы мы этого не сделали, по умолчанию проклят был бы не игрок, а сундук, в чем я, признаться, не вижу особого смысла…
Теперь вот этот кусок:
Float timer
Set timer to ( timer + GetSecondsPassed )
if timer > 10
Set timer to ( timer + GetSecondsPassed )
if timer > 10
…вот так вы можете создать таймер в Морровинде. Функция “GetSecondsPassed” возвращает в секундах время, прошедшее с последнего фрейма. Обычно это какие-то доли секунды (потому что скрипт прокручивается заново каждый фрейм), отсюда ясна необходимость использовать для данной переменной тип Float – тип, позволяющий содержать значения с плавающей точкой. Таким образом, наш таймер отсчитает десять секунд, и проклятие будет снято. При этом позаботимся о том, чтобы это событие случилось лишь один раз:
Player -> RemoveSpell, "Frost_Curse"
set controlvar to –2
set controlvar to –2
Отлично, теперь сохраняйтесь и тестируйте свой мод. Работает отлично, не так ли? Ну, почти. Попробуйте вот что: дайте неправильный ответ, позвольте сундуку наложить на вас проклятие, и откройте меню инвентаря. Подождите. Видите, проклятие заканчивается спустя положенные десять секунд, никак вам не повредив? Ну конечно: скрипт продолжает работать и когда вы в меню, а вот заклинание работает только в игре. Мы же не хотим, чтобы игрок отделался от сундука так просто? В таком случае, нам нужно модифицировать скрипт таким образом, чтобы он приостанавливался на то время, пока игрок находится в меню. К счастью, для этого существует функция “MenuMode”, которая возвращает «1», если игрок находится в меню. Поэтому внесем в начало скрипта следующие изменения:
If ( MenuMode == 1 )
Return
Endif
Return
Endif
Помните, команда “Return” заставляет игру приостановить скрипт на время данного фрейма.
Ну все, наконец-то мы пришли к законченной версии нашего скрипта. Мои поздравления! Если хотите, поэкспериментируйте со своим скриптом еще немного. Например, можно поместить сундук в определенную локацию в Морровинде и закрыть его. Попробуйте его открыть с помощью функции “Unlock”, модифицировав скрипт дополнением к функции “Activate”. Попробуйте озвучить момент открытия сундука, например, с помощью строки: PlaySound3D, “skillraise”. Можно также попробовать заменить функцию “AddSpell” функцией “Cast”, и т.д. Предыдущие пользователи заметили один баг – что случится, если игрок уйдет из локации с сундуком, до того, как проклятие будет снято? Как можно все исправить?
Ну все, наконец-то мы пришли к законченной версии нашего скрипта. Мои поздравления! Если хотите, поэкспериментируйте со своим скриптом еще немного. Например, можно поместить сундук в определенную локацию в Морровинде и закрыть его. Попробуйте его открыть с помощью функции “Unlock”, модифицировав скрипт дополнением к функции “Activate”. Попробуйте озвучить момент открытия сундука, например, с помощью строки: PlaySound3D, “skillraise”. Можно также попробовать заменить функцию “AddSpell” функцией “Cast”, и т.д. Предыдущие пользователи заметили один баг – что случится, если игрок уйдет из локации с сундуком, до того, как проклятие будет снято? Как можно все исправить?
Вот окончательный скрипт:
Begin my_first_script
Short controlvar
Short button
Float timer
If ( MenuMode == 1 )
Return
Endif
If ( OnActivate == 1 )
If ( controlvar == 0 )
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar > 1
activate
endif
endif
if ( controlvar == 1 )
set button to GetButtonPressed
if ( button == -1 )
return
elseif ( button == 2 )
MessageBox "Ответ верный"
Activate
set controlvar to 2
else
MessageBox "Ответ неверный"
Player -> AddSpell, "Frost_Curse"
set controlvar to –1
Endif
elseif ( controlvar == 2 )
Activate
Set controlvar to 3
elseif ( controlvar == -1 )
Set timer to ( timer + GetSecondsPassed )
if timer > 10
Player -> RemoveSpell, "Frost_Curse"
set controlvar to -2
endif
endif
End
Как узнать больше?
После этого обучающего курса, вы можете спросить себя, как можно продолжить обучение скриптам. Хороший способ – это изучать примеры в этом руководстве или скрипты работающие в игре (от Bethesda или других модов). Найдите скрипт, результат которого сходен с тем, что вы хотели бы получить, скопируйте его и приспособьте под ваши нужды. Разбиение функций в этом руководстве по тематическим группам поможет вам найти нужную. Наконец, официальные форумы – это ценные места, где можно найти информацию или получить помощь по интересующему вопросу. Всего остального можно добиться практикой, практикой, практикой.