(это урок по написания первого скрипта, который показывает общие правила и понятия, где, что жать и т.п.)
Мы начинаем с открытия редактора скриптов:
Запускайте 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 – вставить.
Чего мы хотим?
Перед тем, как мы действительно начнем писать скрипт, нужно определиться, что он будет делать. Я решил, что для этого обучающего курса достойной задачей будет создать Сундук с загадкой: сундук загадывает загадку, и только правильный ответ открывает сундук. Если игрок даст неверный ответ, сработает ловушка, ранящая игрока, а сундук уже не откроется.
Это достаточно сложная задача, но мы решим ее шаг за шагом.
Написание скрипта
Откройте окно редактора скриптов и кликните по главной части окна редактора. Здесь вы будете писать скрипт.
Имя для скрипта: Begin и End
Прежде всего мы должны дать скрипту имя – каждый скрипт начинается с объявления его имени. Поэтому, напечатайте:
Begin my_first_script
в окне редактора. Обратите внимание на знаки подчеркивания: ваше имя должно быть одним словом, то есть не содержать пробелов.
Также отметьте, что скриптовый язык нечувствителен к регистру, так что команда “Begin” могла быть напечатана, начиная со строчной (маленькой) буквы – “begin”. Данное скрипту имя – это указатель, по которому его опознает TES CS.
Попробуйте теперь нажать кнопку Save: вы получите сообщение о том, что «you need to end your script with end scriptname» (скрипт следует заканчивать командой “end" (и только опционально добавляя "имя_скрипта" указанное в загаловке)(здесь это будет End my_first_script) .
Таким образом, для того, чтобы скрипт распознавался редактором как таковой, нам следует обозначить не только его начало, но и конец: далее, напишите
End
на новой строке ниже предыдущей. Как видите, указание имени скрипта в этой строке уже можно опустить, достаточно слова end. Вот теперь, если вы нажмете Save, вы увидите, что его имя появилось в строке заголовка, значит скрипт сохранится без ошибок. Это самый короткий возможный скрипт, и конечно, он ничего не делает.Лично я рекомендую перед именем скрипта ставить знак _ (подчеркивание).
Тогда ваши скрипты всегда будут первыми в списке.
Можно также писать в имени скрипта сокращенное имя плагина (например BloodMoon - BM) или ваш сокращенный ник
(напр. Gwathlobal - GW). Это практически со 100% вероятностью сделает имена ваших скриптов уникальными.
А нужно это потому, что из двух скриптов с одинаковыми названиями выполняется тот, который сохранен позже,
а не оба вместе. (прим. Gwathlobal)
Обнаружение действий игрока
Далее, нам нужен способ определить, пытается ли игрок открыть сундук. В скриптах TES мы различаем Объекты, Функции и Команды:
Объекты - это все вещи в игровом мире, такие как видимые предметы, монстры, NPC (персонажи) или просто звуки.
Функции - это все «слова», употребляемые в скриптах TES для манипуляции объектами или получения информации о них.
Команды - это те «слова» скриптового языка, которые его структурируют, но не оперируют какими-либо объектами игры – к примеру, команду “Begin” мы уже использовали, чтобы сообщить скриптовому редактору имя нашего скрипта. Чтобы объяснить игре, к какому объекту следует применить ту или иную функцию, мы используем «стрелку»: -> (на самом деле просто дефис и знак «больше чем»). Слева от этого знака мы указываем объект (он называется вызывающим объектом), а справа – функцию, которая к нему применяется:
Object_ID -> function, [parameters]Самое интересное, что объект, который неактивен, невозможно изменять с помощью стрелки.
Функции получения информации об объекте работают
(вы можете узнать, сколько здоровья у Вивека, когда находитесь в Балморе),
а функции, изменяющие объект, - нет (то есть убить Вивека из Балморы вы не сможете).
(Прим. Gwathlobal)
Функция может иметь или не иметь параметры.
Параметрами могут быть ID (идентификаторы) других объектов, числа, и, в некоторых случаях, переменные.
То, что нам нужно для нашего сундука с загадкой, - это функция “OnActivate”: она информирует нас о том, «активировал» ли игрок определенный объект игрового мира или нет. Эта функция возвращает значение 1 (означает True, «истина», в терминах программирования), если объект был активирован, то есть игрок «прицелился» в него и нажал кнопку «использовать» (клавиша пробел в управлении по умолчанию). Таким образом, нам остается только проверить, не возвращает ли функция OnActivate значения «истина» в любое время в игре. Поэтому отредактируйте свой скрипт, чтобы он выглядел следующим образом:
|
*обратите внимание, что жирный стиль шрифта в окне редактора это своего рода "глюк".
Т.е. сразу после вставки в скрипт, все шрифты и их стили отображаются в том формате в котором были скопированы, например из МСФД.
Но стоит скрипт закрыть и открыть снова, как все форматы текста будут обнулены.
Цвет, размер, стиль и прочее.
Также, если текст был вставлен в РУ(RU) кодировке, то и вводится от продолжит в ней же!
Это может несколько раздражать, т.к. если вовремя этого не заметить, можно получить мелкие, но не приятные оказии.
"С" - это буква Эс или буква СИ? а вот зависит от кодировки в которой текст был вставлен!
Для сброса форматирования и кодировки, сохраните скрипт и нажмите стрелку (следующий скрипт)(это те влево-вправо вверху окна).
Перейдя к следующему скрипту, сразу жмите стрелку обратно и уже теперь вносите все исправления и настройки!
Если будет грозное сообщение об ошибках, или предложение сохранить - жмите ОК.
Сейчас это не играет роли, т.к. задача просто сбросить "стиль ввода" текста, а не отладить скрипт.
|
Как видите, после переоткрытия скрипта все форматирование было сброшено.
А "знаки вопроса" вместо текста, это "фича" кодировки Вынды.
В которой не была включена правильная таблица кодировок кирилицы.
Т.е. вместо правильной 1251-ой используется 1252 по умолчанию.
Но это уже другая история, которая в большинстве случаев не возникает.
|
Здесь нужно объяснить еще пару вещей: команда “If” нужна здесь для проверки условий – если выражение, заключенное в скобки, - истина, значит будут выполнены последующие линии кода до команды “EndIf”. Знак “==” (двойное «равно») проверяет, одинаково ли содержание выражений по его обе стороны (в нашем случае, равно ли значение, возвращаемое функцией “OnActivate”, единице. Если вы забудете команду “EndIf” после команды “If”, редактор будет жаловаться в вышестоящие инстанции на ошибки. Знак “;” (точка с запятой) обозначает комментарии – все, что вы напишете на этой строке после точки с запятой, будет проигнорировано при запуске скрипта. Если вы когда-нибудь станете делать большие скрипты, то наверняка научитесь любить писать комментарии.
Вывод текста и получение решений игрока
А теперь мы хотим, чтобы наш коварный сундук загадал игроку загадку. Для этого воспользуемся функцией MessageBox, которая позволяет нам отображать на экране какой-то текст, а также варианты выбора, с которыми игроку предстоит иметь дело. К сожалению, Морровинд не позволяет нам оформить все так, чтобы игрок напечатал ответ с клавиатурыА вот теперь появился мод MWE_Writing, там есть возможность писать собственные книги.
Правда, делается это отнюдь не с помощью скриптов. (Прим. Gwathlobal)
(и это был старый добрый 2006ой год).
В 2026ом для этого есть МВСЕ 2.Х с ЛУА скриптами, но это уже СОВСЕМ другая история(С), поэтому придется использовать вариант множественного выбора. Соответствующая строка в скрипте будет выглядеть так:
|
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха", “Ветер", “Призрак"
|
Первый отрезок в кавычках – это собственно текст, который будет отображаться на экране, а другие отрезки в кавычках, разделенные запятой, игра преобразует в «кнопки», отображающиеся вместе с текстом.
Но как мы добьемся того, чтобы загадка загадывалась только один раз – когда игрок впервые попытается открыть сундук, а не при каждой попытке? Тут мы подходим к одному из центральных вопросов: использованию «одноразовых» условий и переменных. Большая часть проблем, возникающих у начинающих скриптеров, связана с непониманием того, как выполняются скрипты, и как поэтому их нужно структурировать.
Объясняю.
Как выполняются локальные скрипты
Каждый скрипт, привязанный к определенному объекту или NPC (локальный скрипт), запускается с каждым новым фреймом (обновлением экрана), когда ячейка (cell), содержащая объект, активна. В помещении активна только та ячейка, в которой в данный момент находится игрок, а на улице – ячейка, в которой находится игрок, плюс все прилегающие ячейки. Так что полный скрипт (не просто какая-то строка из него) выполняется 10-60 раз в секунду, или настолько часто, насколько позволяет мощность вашего компьютера. Лучше всего представить себе, что каждый локальный скрипт бесконечно прокручивается в мертвой петле или как в конструкции “while-loop":
|
while (Объект в активной ячейке)
[Код вашего скрипта]
endwhile
|
А потому следующий скрипт изливается непрерывным потоком сообщений (если скрипт привязан к объекту или NPC, находящемуся в той же ячейке, где находится игрок). Попробуйте его, если хотите:
|
Begin Message_script
MessageBox “Тысячи бессмысленных сообщений”
End Message_script
|
Этот пример относительно безобидныйЭто как посмотреть.
Желающие попробовать несомненно заметили резкое увеличение тормозов.
Вот вам наука – все скрипты, которые хоть как-то взаимодействую с внешним миром
(выводят сообщения, добавляют предметы и т.п.) в большинстве случаев,
не должны делать этого при каждом исполнении. (Прим. Gwathlobal), но представьте себе, если бы вместо команды выдать сообщение вы использовали строчку кода, добавляющую какой-то предмет в инвентарь игрока, либо вызывающую монстра и т.п.!
Поэтому «одноразовые» конструкции очень важны, и пригодится вам в скриптерской деятельности еще не раз. Теперь давайте продолжим создание нашего учебного скрипта: мы должны объявить переменную и использовать ее, чтобы гарантировать «одноразовое» появление сообщения.
Поменяйте скрипт следующим образом:
(Отметьте, что команда MessageBox должна быть на одной строке в редакторе!)
|
Begin my_first_script ; редактор скриптов МВ НЕ имеет подсветки синтаксиса, да.
Short controlvar ;за точкой с запятой можно писать свои комментарии.
;одна строка одно предложение. Т.е. Знак работает на длину строки.
;нельзя переносить строки нажатием Enter, если они не влезли в окно редактора из одних соображений удобства чтения, так чтобы строка оставалась без знака комментария, как можно это видеть здесь.
;т.е. это будет приводит к ошибкам. Т.е. перенесите строку, а затем за комментируйте ее знаком ;
;также, это верно и для всех прочих типов строк. Особенно, для messageBox, т.е. для строк сообщений. Которые могут быть весьма протяженными!
If ( OnActivate == 1 ) ;это собственно тело скрипта в котором указывается "что ему делать". Базовое условие. В данном случае используется "хардварная" опция OnActivate.
If ( controlvar == 0) ;дополнительное условие. В данном случае, это описываемая переменная для блокировки спама сообщениями.
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Призрак” ;и это все одна строка! В которой указана СУТЬ действия.
;Т.е. две строки выше лишь задавали условия
;при которых должно произойти событие, в данном случае вывод сообщения.
; “” фигурные скобки (которыми активно пользовались при создании сего трактата, приводят к ошибкам компиляции скриптов.
; т.е. при вставке скриптов отсюда в ТЕс КС возникают ошибки. Просто учитывайте этот "нюанс". Они здесь часто встречаются.
Set controlvar to 1 ;а это фиксация достижения, т.е. смена переменной с нуля на единицу, что приводит к изменению состояния условий действия.
;т.е. указываем условия при которых происходит события, затем пишем само события, и меняем состояние условий = события свершилось.
endif ;это "ответчики" на IF их кол-во практически всегда равно кол-ву IF.
;Но если используется elseIf (о чем будет дальше) кол-во end If будет равно кол-ву IF но не elseIf!
endif
; комментарии можно писать у "стенки" а можно двигать TAB-ом, не важно.
; и ВСЕГДА используйте только TAB для смещения строк и никогда не используйте пробел! Т.е. можно, но не нужно(С) и вредно. А еще размер плагина разбухает.
; от такого лишнего текста.
; по возможности, пишите "красиво" каждая следующая строка смещается влево до середины блока текста, а ниже оного наоборот. Как это можно видеть выше.
End ;енд всегда пишется у "стены" как и Бегин и типы переменных.
|
И версия без комментариев:
|
Begin my_first_script
Short controlvar
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox "Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?", "Летучая мышь", "Старуха", "Ветер", "Призрак" ;а здесь правильны скобки, с такими проблем не возникает!
Set controlvar to 1
endif
endif
End
|
"Short controlvar" объявляет новую переменную, которую я назвал controlvar, и присвоил ей тип short.
На данном этапе нам достаточно знать, что это переменная, которая может содержать целочисленные положительные и отрицательные значения.
Примечание!
Short это жестко запрограммированное понятие, которое принимает любое произвольное значение.
Т.е. Short, Long, Float (и пр.) это названия "аппаратного хардкода" жестко зашитого в движок игры (и редактора).
А их значения, уже, могут быть написаны понятным и "простым" языком.
ControlVar, DoOnce, MyOnce, Shormat1, Vekh100tisiach, SdelaiOdinRaz, AlmaSterva, NereVarForever и т.п.
Т.е. вместо CotrolVar можно писать все что угодно, чем пользоваться дальше меняя состояние этого значения в + или в -.
Переменная – это своего рода хранилище, которое может содержать различные значения.
Это как раз то самое значение которое указанно для Short.
Здесь это ControlVar (control variable)( контролируемая переменная) согласно идеи автора сего сочинения, как можно полагать.
Команду “If” мы уже знаем, а вот команда “Set” для нас новая.
Однако она очень простая, всего лишь помещает в наше «хранилище» новое значение, иными словами устанавливает значение переменной равным единице (до этого оно было равно нулю, как у всех свежеобъявленных переменных).
В сочетании с выражением If ( controlvar == 0 ), это обеспечивает требуемые «одноразовые» условия.
Уже на следующий фрейм, после того, как переменной присвоено значение единицы, условие, заданное командой “If”, становится не соответствующим истине, и сообщения-загадки на экране больше не появится.
Примечание!
Обращу отдельное внимание, что нельзя писать в скриптах слитно, это приводит к неожиданным багам и косякам.
If ( controlvar == 0 ) верно, все слова и знаки разделены пробелами.
If (controlvar==0) не верно! Хотя формально это допустимо, но компилятор скриптов, по видимости, имеет какой-то "баг" (или фичу) которая может приводить к непредсказуемым последствиям.
Теперь скрипт может быть запущен, так что протестим его:
Сохраните скрипт и закройте окно редактора скриптов.
-
Перейдите в окно объектов (Object Window), выберете закладку Containers и откройте "chest_small_01".
-
Измените ID сундука на "tutorial_chest"
-
В ниспадающем меню выберете my_first_script.
-
Сохраните объект как новый объект, сохраните мод, (редактор не следует закрывать!) подключите мод в MW Launcher.
-
Запускайте Морровинд и загрузитесь в некоторую ячейку (оптимально использовать параметр в MW.ini Starting Cell = xxx, где ххх имя ячейки).
-
Использование существующих сохранений, крайне нежелательно! Т.к. могут возникать всякого рода конфликты и наложения.
-
Откройте консоль (кнопкой ~ слева от “1”) и в окне консоли напишите: PlaceAtPC tutorial_chest 1 1 1 и нажмите Enter.
Либо разместите сундук в Гильдии Магов Балморы (или той ячейки которая указана в Starting Cell) куда и загляните.
Сделайте шаг назад (эээ… пусть ваш персонаж сделает шаг назад!)От редакции заметок.
Конечно лучше было бы все переписать здесь, так чтобы по грамотному, а не в стиле оригинального автора.
Тут, звиняйте, бананьев нема(С)
Заметки по моддингу "наше все" а скрипты "по остаточному принципу"!
Т.е. редактирование этого руководства по скриптам, проводится в минимальных приделах, там где вот-прям-совсем-все-криво.
– теперь вы должны видеть небольшой сундук напротив вас на полу.
Попытка открыть его должна вызвать наше сообщение. В общем, выглядит так:
Кликая по этим кнопкам, вы закроете это сообщение.
Если после этого снова попытаться открыть сундук, ничего не произойдет – очень хорошо, значит, наше «одноразовое» условие работает.
Теперь выходите из Морровинда обратно в редактор.
Сейчас нам предстоит определить, какой вариант ответа выбрал игрок, и запрограммировать требуемые реакции сундука на правильный и неправильные ответы. Функция, которая позволит нам определить выбранный игроком вариант ответа, называется “GetButtonPressed”. Эта функция возвращает числовое значение, которое зависит от того, какую «кнопку» в сообщении нажмет игрок. То есть, она возвращает «0», если нажата первая кнопка (в нашем случае ответ «летучая мышь»), затем «1», «2», «3» и т. д. для следующих кнопок, нумерованных в том порядке, в каком вы расположили их в скрипте, вызывая функцию “MessageBox”. Пока игрок не нажал никакой кнопки, функция будет возвращать значение «-1», это мы тоже должны учесть.
Функция “Activate” откроет наш сундук. По сути, эта функция просто запускает стандартное для данного объекта действие, которое производится по умолчанию при попытке игрока его «использовать». Например, двери будут открываться (закрываться), NPC будет вступать с игроком в диалог и т. п.
Следующее изменение нашего скрипта покажет вам, как можно использовать контролирующую переменную, чтобы заставить Морровинд запускать функции последовательно одну за другой, невзирая на то, что весь скрипт прокручивается каждый фрейм.
Мы будем просто наращивать контролирующую переменную и проверять ее значение серией управляющих конструкций типа “If – ElseIf”.
Это самый надежный стиль скриптового программирования в Морровинде, - может, не всегда необходимый, но надежный.
Отредактируйте скрипт вот так:
|
Begin my_first_script
Short controlvar
Short button
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar > 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” открыть сундук.
Все прочие номера «кнопок» означают, что игрок выбрал неправильный ответ, так что мы можем использовать в данном случае команду “Else”, чтобы их отсечь.
В таком случае мы должны сообщить игроку, каким дураком он себя был, и не открывать сундук.
Теперь обратите внимание на небольшое дополнение в начале скриптаЕще совет.
Пишите скрипты – всегда отделяйте табуляцией все вложенные команды,
то есть команды внутри if-endif, while-endwhile и т.д.
Читать потом ваши скрипты будет легче и вам и другим людям. (Прим. Gwathlobal):
|
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif ( controlvar >= 1 )
activate
endif
endif
;(Прим. Gwathlobal)
;Пишите скрипты – всегда отделяйте табуляцией все вложенные команды,
;то есть команды внутри if-endif, while-endwhile и т.д.
;Читать потом ваши скрипты будет легче и вам и другим людям.
|
Это означает, что если игрок попытается еще раз открыть сундук в будущем, сундук откроется только в случае, если значение переменной 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 пунктов.
Результат должен выглядеть так:
Примечание.
Стоит заметить, что накладывать лучше обычный спелл, как это сделано в ловушках.
Т.е. кастовать заклинание через ExplodeSpell, а не через наложение "убийственного проклятья".
Особенно в виду того, что в оригинальной игре, крайне туго со спеллами "снятия проклятья".
Собственно самая Крутость, это иметь возможность "натянуть" все брошенные Вызовы на стальное колено.
А не поставить Игрока (т.е. себя) таким раком, что остается только "тихо околеть в канаве".
Собственно в данном случае, у игрока на остается шанса выжить (если использовать скрипт, как он есть сейчас в основной игре).
Поскольку проклятье нечем будет снимать. Или же, игрок, уже имеет иммунитет к холоду...
Но, это только риторическое примечание по геймплею, а не по скриптам в частности. :)
Теперь нам нужно наложить это проклятие на игрока.
Для этого мы воспользуемся функцией “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" накладывает проклятие на игрока. *т.е. указываем ID цели, здесь это Player ака ID игрока в редакторе.
Обратите внимание, мы использовали выражение “Player-> “ – это гарантирует, что эффект будет наложен именно на игрока.
Если бы мы этого не сделали, по умолчанию проклят был бы не игрок, а сундук, в чем я, признаться, не вижу особого смысла…
Т.е. если прописать AddSpell, "Frost_Curse", то заклинание добавиться в инвентарь объекта, здесь это сундук, ака ничего не будет, хотя может случится и КТД.
Теперь вот этот кусок:
Float timer
Set timer to ( timer + GetSecondsPassed )
if timer > 10
Обычно это какие-то доли секунды (потому что скрипт прокручивается заново каждый фрейм).
…вот так вы можете создать таймер в Морровинде.
Функция “GetSecondsPassed” возвращает в секундах время, прошедшее с последнего фрейма.
Отсюда ясна необходимость использовать для данной переменной тип Float – тип, позволяющий содержать значения с плавающей точкой.
Таким образом, наш таймер отсчитает десять секунд, и проклятие будет снято. При этом позаботимся о том, чтобы это событие случилось лишь один раз:
Player->RemoveSpell, "Frost_Curse"
set controlvar to –2
Отлично, теперь сохраняйтесь и тестируйте свой мод.
Работает отлично, не так ли? Ну, почти.
Попробуйте вот что: дайте неправильный ответ, позвольте сундуку наложить на вас проклятие, и откройте меню инвентаря.
Подождите.
Видите, проклятие заканчивается спустя положенные десять секунд, никак вам не повредив?
Ну конечно: скрипт продолжает работать и когда вы в меню, а вот заклинание работает только в игре.
Мы же не хотим, чтобы игрок отделался от сундука так просто?
В таком случае, нам нужно модифицировать скрипт таким образом, чтобы он приостанавливался на то время, пока игрок находится в меню.
К счастью, для этого существует функция “MenuMode”, которая возвращает «1», если игрок находится в меню. Поэтому внесем в начало скрипта следующие изменения:
If ( MenuMode == 1 )
Return
Endif
Примечание:
If ( MenuMode )
Return
Endif
Работает с равным успехом, т.е. ==1 не обязательно указывать.
Но вот если требуется определение выхода из меню, тогда имеет смысл прописать == 0.
Такое, впрочем, следует использовать в особых случаях.
А для проверки режима меню достаточно вызвать If ( MenuMode ).
Помните, команда “Return” заставляет игру приостановить скрипт на время данного фрейма?
На деле, она просто дает команду возврата каретки, т.е. в данном случае выключает опрос скрипта на этом блоке условий.
Отчего этот блок, в 99% случаев пишется в начале скрипта и имеется практически во всех скриптах, кроме "специфических" которые должны работать под меню.
Ну все, наконец-то мы пришли к законченной версии нашего скрипта. Мои поздравления!
Если хотите, поэкспериментируйте со своим скриптом еще немного.
Например, можно поместить сундук в определенную локацию в Морровинде и закрыть его.
Попробуйте его открыть с помощью функции “Unlock”, модифицировав скрипт дополнением к функции “Activate”.
Попробуйте озвучить момент открытия сундука, например, с помощью строки: PlaySound3D, “skillraise”.
Можно также попробовать заменить функцию “AddSpell” функцией “Cast”, и т.д.
Предыдущие пользователи заметили один баг – что случится, если игрок уйдет из локации с сундуком, до того, как проклятие будет снято? Как можно все исправить?
Вот "промежуточно-окончательный" скрипт:
|
Begin my_first_script
Short controlvar
Short button
Float timer
;**************************************** кроме пустых строк удобно использовать "линии" для отделения логических блоков друг от друга. Визуально удобнее читать, да.
;****************************************
If ( MenuMode )
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 "Ответ верный"
Unlock
Activate
PlaySound3D, “skillraise” ;еще раз, обратите внимание на форму кавычек, при вставке их в таком виде в КС, компилятор выдаст ошибку! Т.е. при копипасте отсюда.
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 timer to 0 ;таймер надо на нуль всегда сбрасывать!
set controlvar to -2
endif
endif
;****************************************
End
|
|
|
И пред финальная версия, которая компилируется в редакторе (при условии наличия заклинания Frost_curse, конечно.
|
Begin my_first_script
;****************************************
;****************************************
Short controlvar
Short button
Float timer
;****************************************
If ( MenuMode )
Return
Endif
;****************************************
if ( controlvar == 1 )
set button to GetButtonPressed
if ( button == -1 )
return
elseif ( button == 2 )
MessageBox "Ответ верный"
Unlock
Activate
PlaySound3D, "skillraise"
set controlvar to 2
else
MessageBox "Ответ неверный"
Player->AddSpell "Frost_Curse" ;обратите внимание, что пробелы вокруг указателя убраны. В 9ой мсфд автор прям заморочился на это!
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
set timer to 0
endif
endif
;****************************************
If ( OnActivate == 1 ) ;да-да, самый первый блок помещен в самом низу. Т.к. скрипт читается сверху, но выполняется снизу!
If ( controlvar == 0 )
MessageBox "Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?" "Летучая мышь" "Старуха" "Ветер" "Дух"
Set controlvar to 1
elseif ( controlvar > 1 )
activate
endif
endif
;****************************************
End
|
|
Begin my_first_script ;это версия из оригинального МСФД 9 аглицкой версии. В редакторе компилируется нормально, в игре тоже все работает.
;вероятно какие-то проблемы набежали при локализации. А в целом, скрипт "(можно") переписать в целом и общем, о чем. см. ниже.
Short controlvar
Short button
Float timer
If ( MenuMode == 1 )
Return
Endif
If ( OnActivate == 1 )
If ( controlvar == 0 )
MessageBox "Voiceless it cries, wingless flutters, toothless bites, mouthless mutters. What is it?", "Bat", "Old woman", "Wind", "Wraith"
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 "The answer was correct"
Activate
set controlvar to 2
else
MessageBox "The answer was wrong"
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
|
Совсем финальная версия, будет вот:
- добавлены сообщения при активации под "проклятьем"
- исправлены ошибки с кавычками.
- какая то ерунда была с таймером, т.е. проклятье не снималось вообще...
- также, в оригинале срабатывала двойная активация сундука без нажатия на оный.
- убраны лишние пробелы вокруг указателей.
- добавлена проверка на замок.
- добавлена установка замка если ответ не верный.
|
Begin my_first_script
Short controlvar
Short button
Float timer
If ( MenuMode )
Return
Endif
if ( controlvar == 3 ) ; контролВар = 3 дает проверку на наличие проклятья и запускает таймер на снятие оного.
Set Timer to ( timer + GetSecondsPassed )
if ( timer > 10 )
FadeTo 0, 2.0
Player->RemoveSpell, "Frost_Curse"
MessageBox "Проклятье снято!"
set timer to 0 ; таймер сброшен на нуль!
set controlvar to 0 ;контролВар сброшен на нуль, что и дает возможность попробовать выбрать ответ еще раз.
endif
endif
if ( controlvar == 1 )
set button to GetButtonPressed
if ( button == 2 ) ;номер верной кнопки. 0, 1, 2, 3. Т.е. по порядку от начала строки.
MessageBox "Ответ верный"
Activate
PlaySound3D, "skillraise" ;дополнительный звук в честь верного ответа.
set controlvar to 2 ;установка контролВар =2 что даст открывать сундук в следующий раз без вопросов.
Unlock ; если сундук не залочен, ничего не произойдет. Т.е. ошибки это не вызовет. А если закрыт на замок, то откроет, да.
PlaySound3D, "Open Lock" ;это звук на открытие замка.
else ;т.е. если кнопка не верна будет засчитан ошибочный ответ и на игрока будет положено "благословение".
MessageBox "Ответ неверный"
Player->AddSpell "Frost_Curse"
PlaySound3D, "frost_hit" ;в целом вместо звука лучше дать игрока зелье выпить, или скастовать спелл, так "Эффектнее" будет.
Lock 100 ;закроем сундук на замок уровня 100
FadeTo 55, 2.0 ;добавим затенение экрана для заметности "благословения".
set controlvar to 3 ; устанавливает контролВар на 3, чтобы запустить таймер на снятие проклятья и не дать открыть сундук.
Endif
Endif
If ( OnActivate == 1 )
If ( controlvar == 0 )
MessageBox "Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?" "Летучая мышь" "Старуха" "Ветер" "Дух"
Set controlvar to 1
elseIf ( controlvar == 3 ) ;если контролВар = 3 значит на игроке есть проклятье и сундук откажется открываться.
MessageBox "На вас лежит ПРОКЛЯТЬЕ и замок сей не откроется, пока!"
Lock 100 ;если игрок пытался взломать замок и снял оный, запрем снова.
elseIf ( controlvar == 2 ) ;если контролВар = 2, значит ответ был дан верно и сундук будет свободно открываться.
Activate
endif
endif
End
|
Как узнать больше?
После этого обучающего курса, вы можете спросить себя, как можно продолжить обучение скриптам. Хороший способ – это изучать примеры в этом руководстве или скрипты работающие в игре (от Bethesda или других модов). Найдите скрипт, результат которого сходен с тем, что вы хотели бы получить, скопируйте его и приспособьте под ваши нужды. Разбиение функций в этом руководстве по тематическим группам поможет вам найти нужную. Наконец, официальные форумыА нету их больше!
В 2017ом похоронили в 2021ом прикопали.
Т.е. официальный форум беседки был уничтожен (первый раз) в 2008ом примерно, тогда как-раз много чего похоронили
на тему скриптов и прочего по мелочи.
А затем в 2017ом закрыли для постов.
А в 2020-21ом воовсе удалили, так что толька на Веб АРхивах тени одни остались.
(прим. от 2026го да). – это ценные места, где можно найти информацию или получить помощь по интересующему вопросу. Всего остального можно добиться практикой, практикой, практикой!