- •Скрипты Морровинда для чайников
- •Предисловие к восьмому изданию
- •Вступление Как использовать это руководство.
- •Что такое скрипт?
- •Что могут скрипты?
- •Чего не могут скрипты:
- •Обучающий курс
- •Поехали!
- •Окно редактора скриптов
- •Чего мы хотим?
- •Написание скрипта
- •Имя для скрипта: 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 не работает
- •Вылет при исполнении скрипта
- •Вылет при загрузке плагина
- •Приложение Новые функции Трибунала
- •Изменения / исправления скриптов Морровинда:
- •Список новых функций Трибунала:
- •Новые функции Бладмуна
- •Список новых функций и переменных Бладмуна:
- •Ранее недокументированные функции
- •Единицы измерения:
- •Список магических эффектов
- •Список консольных команд
- •Игровые установки
- •Алфавитный указатель
Ваш первый баг
Теперь, как вы должно быть уже убедились, наш скрипт делает практически, но не все, как мы хотели. То есть, почти все, да как бы не совсем. После выбора игроком правильного варианта сундук не открывается, как мы планировали. Логика скрипта вроде в порядке, так в чем же дело? Давайте попробуем модифицировать скрипт (замените соответствующую часть своего скрипта на следующую):
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 пунктов. Результат должен выглядеть так:
Теперь нам нужно наложить это проклятие на игрока. Для этого мы воспользуемся функцией “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
…вот так вы можете создать таймер в Морровинде. Функция “GetSecondsPassed” возвращает в секундах время, прошедшее с последнего фрейма. Обычно это какие-то доли секунды (потому что скрипт прокручивается заново каждый фрейм), отсюда ясна необходимость использовать для данной переменной тип Float – тип, позволяющий содержать значения с плавающей точкой. Таким образом, наш таймер отсчитает десять секунд, и проклятие будет снято. При этом позаботимся о том, чтобы это событие случилось лишь один раз:
Player -> RemoveSpell, "Frost_Curse"
set controlvar to –2
Отлично, теперь сохраняйтесь и тестируйте свой мод. Работает отлично, не так ли? Ну, почти. Попробуйте вот что: дайте неправильный ответ, позвольте сундуку наложить на вас проклятие, и откройте меню инвентаря. Подождите. Видите, проклятие заканчивается спустя положенные десять секунд, никак вам не повредив? Ну конечно: скрипт продолжает работать и когда вы в меню, а вот заклинание работает только в игре. Мы же не хотим, чтобы игрок отделался от сундука так просто? В таком случае, нам нужно модифицировать скрипт таким образом, чтобы он приостанавливался на то время, пока игрок находится в меню. К счастью, для этого существует функция “MenuMode”, которая возвращает «1», если игрок находится в меню. Поэтому внесем в начало скрипта следующие изменения:
If ( MenuMode == 1 )
Return
Endif
Помните, команда “Return” заставляет игру приостановить скрипт на время данного фрейма.
Ну все, наконец-то мы пришли к законченной версии нашего скрипта. Мои поздравления! Если хотите, поэкспериментируйте со своим скриптом еще немного. Например, можно поместить сундук в определенную локацию в Морровинде и закрыть его. Попробуйте его открыть с помощью функции “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