- •Скрипты Морровинда для чайников
- •Предисловие к восьмому изданию
- •Вступление Как использовать это руководство.
- •Что такое скрипт?
- •Что могут скрипты?
- •Чего не могут скрипты:
- •Обучающий курс
- •Поехали!
- •Окно редактора скриптов
- •Чего мы хотим?
- •Написание скрипта
- •Имя для скрипта: Begin и End
- •Обнаружение действий игрока
- •Вывод текста и получение решений игрока
- •Как выполняются локальные скрипты
- •Ваш первый баг
- •Наложение заклинания на игрока
- •Как узнать больше?
- •Синтаксис
- •Начало и завершение скриптов
- •Общий синтаксис функций:
- •Общий синтаксис: запятые, скобки и пробелы.
- •Комментарии
- •Отступы / использование табуляторов
- •Переменные Типы переменных
- •Локальные переменные
- •Глобальные переменные
- •Использование переменных в других локальных скриптах и объектах
- •Использование переменных в функциях
- •Операторы / математические расчеты
- •Проверка условий Использование условий if… elseif
- •Условия While
- •Создание булевых операторов в скриптах tes
- •Список функций tes Объяснение формата
- •Работа с объектами Работа с вещами в инвентаре Добавление и удаление вещей из инвентаря
- •Сброс предмета на пол
- •Отслеживание активности инвентаря: Добавление, сброс и использование камней душ
- •Надевание предметов
- •Отслеживание, был ли надет предмет
- •Выключение возможности надеть предмет
- •Проверка присутствия предметов в инвентаре
- •Починка предметов
- •Информация о надетых объектах
- •Функция UsedOnMe
- •Движение и создание объектов
- •Движение вдоль оси объекта
- •Движение вдоль оси мира
- •Установка позиции (другой путь, чтобы сгенерировать движение)
- •Позиционирование объектов в мире или во внутренних ячейках
- •Перемещение объекта в его оригинальную позицию
- •Помещение предмета рядом с игроком
- •Создание предметов рядом с объектом
- •Создание копий объектов с помощью PlaceItem
- •Вращение и углы
- •Вращение объектов
- •Установка углов
- •Функции размеров
- •Определения локации, относительного положения и движения Определение нахождения игрока в интерьере или в экстерьере
- •Определения ячейки игрока
- •Расстояние от одного объекта до другого
- •Определяем позицию и поворот объекта
- •Линия видимости
- •Определяем, замечен ли один актер другим
- •Определяем, когда игрок покинул ячейку
- •Определяем, путешествует ли игрок
- •Триггеры для актеров, стоящих на объектах
- •Повреждение актера, стоящего на объекте
- •Функции сталкивания объектов
- •Проверка активации предмета и его активация
- •Запирание и отпирание дверей или сундуков
- •Анимирование объектов
- •Доступные и недоступные объекты
- •Полное удаление копии
- •Не сохранять изменений объекта
- •Скрипты для npc: ии и движение npc идет в новую локацию
- •Проверка, завершил ли npc свое движение
- •Поворачиваем актера в нужном направлении
- •Задание случайного перемещения Актера
- •Актеры активируют объекты
- •Следование и эскорт
- •Определение текущего пакета ии
- •Заставляем актера красться
- •Заставляем актера бегать и прыгать: Движения в Трибунале
- •Проверка действий игрока: бежит, прыгает, крадется?
- •Определение готовности к бою
- •Заставляем кого-то падать
- •Доля в экипировке и другие функции компаньонов
- •Раса, Фракция и Ранг Определение расы
- •Определение статуса игрока во фракции
- •Изменение реакции и положения во фракции
- •Определение и изменение реакции
- •Функции для оборотней Установка атрибутов оборотня
- •Специальные глобальные переменные для оборотней
- •Текст и Диалог Краткая инструкция по диалогам
- •Концепция диалогов в Морре
- •Как работает диалог
- •Несколько золотых правил
- •Диалог 101
- •Функции для диалогов
- •Показ сообщений
- •Показ переменных и предопределенного текста в окне сообщения
- •Пример: Дурацкий простой скрипт, демонстрирующий весь возможный синтаксис:
- •Добавление темы для диалога
- •Начало и окончание диалога
- •Инициация диалога с оборотнем (Bloodmoon)
- •Множественный выбор – как задавать вопросы
- •Добавление записей в журнал и тест записей журнала
- •Специальные диалоговые функции
- •Изменение значения Hello
- •Полезные диалоговые переменные
- •Изменяем и проверяем Навыки, Атрибуты и другие характеристики Get, Set, and Mod Stats – общие замечания
- •Определение и изменение характеристик игрока и актеров: Определение и изменение атрибутов:
- •Определение и изменение Здоровья, Магии и Усталости:
- •Определяем и изменяем скиллы:
- •Определение и изменение уровня
- •Начало и конец боя
- •Обнаружение атак
- •Функции Get/Mod/Set ии для Боя: Fight, Flee, Alarm
- •Отслеживание убийств и нокаутов
- •Воскрешение мертвого Актера
- •Преступления Определение и изменение уровня преступлений
- •Заключение игрока в тюрьму
- •Очистка игрока от преступлений
- •Уровень преступления игрока
- •Полезные глобальные переменные
- •Магия Ограничение на телепортацию
- •Ограничение левитации
- •Проверка и работа с душами и камнями душ
- •Id камней душ:
- •Добавление и удаление заклинаний и проклятий
- •Кастование заклинаний
- •Управление и тестирование заклинаний
- •Управление и тестирование эффектов заклинаний
- •Тестирование болезней
- •Функции Get/Mod/Set для магии:
- •Звук Пусть актеры говорят
- •Проигрывание музыки
- •Проигрывание звуков
- •Управление звуком
- •Форматы звуковых файлов:
- •Следим за временем
- •Глобальные переменные, зависящие от времени
- •Течение дней
- •Фазы лун
- •Погода Изменение погоды
- •Изменение установок погоды для региона
- •Определение текущей погоды
- •Определение скорости ветра
- •Средства управления игрока Игрок спит
- •Включение и выключение средств управления и интерфейса Отключение средств управления
- •Включение средств управления
- •Проверка статуса средств управления
- •Переключение в вид от первого и от третьего лица
- •Функции для меню генерации персонажа
- •Определение открыл ли игрок меню
- •Использование MenuTest, чтобы открыть и закрыть меню
- •Различные функции и переменные Прерывание выполнения скрипта
- •Управление глобальными скриптами
- •Уменьшение и увеличение яркости
- •Добавление локации на карту
- •Присваивание случайных значений переменным
- •Проигрывание видео
- •Функции Уровневых Списков
- •Квадратный корень
- •Функции уровня воды
- •Советы и трюки Маленькие помощники: поиск, копирование и вставка текста
- •Альтернативные скриптовые редакторы
- •Используйте стиль для написания нормальных скриптов
- •Чистка вашего мода
- •Ограничения редактора скриптов
- •Сохранение процессорного времени
- •Нацеленные скрипты: запуск «глобальных» скриптов, привязанных к объекту
- •Проверяем, когда игрок загружает игру
- •Использование переменной CharGenState – отключение сохранения и меню
- •Обнаружение использования свитков или книг
- •Заставляем Актеров переключать оружие
- •Магические и механические ловушки
- •Скриптовая телепортация
- •Тестируем на присутствие другого мода
- •Безопасный старт глобальных скриптов — избегая скрипт main
- •Использование звука для обнаружения событий
- •Большие сражения
- •Руководство по созданию объектов для езды
- •Выбор объектов
- •Создание/Удаление объектов
- •Падение с объектов
- •Обнаружение столкновений
- •Проблема с сохранением
- •Скрипт по тригонометрии – быстрый синус и косинус
- •Манекены
- •Она на меня смотрит?
- •Кинематическая последовательность
- •Решение проблем Основные советы
- •Консоль Использование консоли для проверки переменных:
- •Использование консоли для быстрой проверки скриптов:
- •Сообщения об ошибках, неправильная работа и обычные причины в игре, когда исполняется скрипт: в редакторе
- •Внутриигровые сообщения:
- •AiTravel не работает
- •Вылет при исполнении скрипта
- •Вылет при загрузке плагина
- •Приложение Новые функции Трибунала
- •Изменения / исправления скриптов Морровинда:
- •Список новых функций Трибунала:
- •Новые функции Бладмуна
- •Список новых функций и переменных Бладмуна:
- •Ранее недокументированные функции
- •Единицы измерения:
- •Список магических эффектов
- •Список консольных команд
- •Игровые установки
- •Алфавитный указатель
Вывод текста и получение решений игрока
А теперь мы хотим, чтобы наш коварный сундук загадал игроку загадку. Для этого воспользуемся функцией MessageBox, которая позволяет нам отображать на экране какой-то текст, а также варианты выбора, с которыми игроку предстоит иметь дело. К сожалению, Морровинд не позволяет нам оформить все так, чтобы игрок напечатал ответ с клавиатуры4, поэтому придется использовать вариант множественного выбора. Соответствующая строка в скрипте будет выглядеть так:
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Призрак”
Первый отрезок в кавычках – это собственно текст, который будет отображаться на экране, а другие отрезки в кавычках, разделенные запятой, игра преобразует в «кнопки», отображающиеся вместе с текстом.
Но как мы добьемся того, чтобы загадка загадывалась только один раз – когда игрок впервые попытается открыть сундук, а не при каждой попытке? Тут мы подходим к одному из центральных вопросов: использованию «одноразовых» условий и переменных. Большая часть проблем, возникающих у начинающих скриптеров, связана с непониманием того, как выполняются скрипты, и как поэтому их нужно структурировать. Объясняю.
Как выполняются локальные скрипты
Каждый скрипт, привязанный к определенному объекту или NPC (локальный скрипт), запускается с каждым новым фреймом (обновлением экрана), когда ячейка (cell), содержащая объект, активна. В помещении активна только та ячейка, в которой в данный момент находится игрок, а на улице – ячейка, в которой находится игрок, плюс все прилегающие ячейки. Так что полный скрипт (не просто какая-то строка из него) выполняется 10-60 раз в секунду, или настолько часто, насколько позволяет мощность вашего компьютера. Лучше всего представить себе, что каждый локальный скрипт бесконечно прокручивается в мертвой петле или как в конструкции “while-loop”:
while (Объект в активной ячейке)
[Код вашего скрипта]
endwhile
А потому следующий скрипт изливается непрерывным потоком сообщений (если скрипт привязан к объекту или NPC, находящемуся в той же ячейке, где находится игрок). Попробуйте его, если хотите:
Begin
Message_script
MessageBox
“Тысячи
бессмысленных
сообщений”
End
Message_script
Этот пример относительно безобидный5, но представьте себе, если бы вместо команды выдать сообщение вы использовали строчку кода, добавляющую какой-то предмет в инвентарь игрока, либо вызывающую монстра и т.п.!
Поэтому «одноразовые» конструкции очень важны, и пригодится вам в скриптерской деятельности еще не раз. Теперь давайте продолжим создание нашего учебного скрипта: мы должны объявить переменную и использовать ее, чтобы гарантировать «одноразовое» появление сообщения. Поменяйте скрипт следующим образом:
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.
Сделайте шаг назад (эээ… пусть ваш персонаж сделает шаг назад!) – теперь вы должны видеть небольшой сундук напротив вас на полу. Попытка открыть его должна вызвать наше сообщение. В общем, выглядит так:
Кликая по этим кнопкам, вы закроете это сообщение. Если после этого снова попытаться открыть сундук, ничего не произойдет – очень хорошо, значит, наше «одноразовое» условие работает.
Теперь выходите из Морровинда обратно в редактор и снова загружайте ваш плагин.
Сейчас нам предстоит определить, какой вариант ответа выбрал игрок, и запрограммировать требуемые реакции сундука на правильный и неправильные ответы. Функция, которая позволит нам определить выбранный игроком вариант ответа, называется “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
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”, чтобы их отсечь. В таком случае мы должны сообщить игроку, каким дураком он себя был, и не открывать сундук.
Теперь обратите внимание на небольшое дополнение в начале скрипта6:
If ( OnActivate == 1 )
If ( controlvar == 0)
MessageBox “Нету голоса - все же кричит, нету крыльев, а чем-то полощет, нет зубов - норовит укусить, нету рта, но все время бормочет. Что это?”, “Летучая мышь”, “Старуха”, “Ветер”, “Дух”
Set controlvar to 1
elseif controlvar >= 1
activate
endif
endif
Это означает, что если игрок попытается еще раз открыть сундук в будущем, сундук откроется только в случае, если значение переменной controlvar будет больше единицы. Проверьте то, что мы напечатали выше: когда игрок дает неправильный ответ на загадку, переменной controlvar присваивается значение -1, так что он никогда уже не откроет сундук. Но если он выбрал правильный ответ, controlvar устанавливается равной 2, значит, с этого момента игрок может открывать сундук в любое время, и сколько захочет. Теперь сохраняйтесь и тестируйте свой плагин, как в прошлый раз.