fallout.ru

Список функций ТЕС Скрипта

Объяснение формата

Сперва я представлю формат списка функций их аргументов:

Код "строка", пер_числ, пер_float, [дополнительно] (возвращает short)

Аргументы функций: «строка» передаёт функции какой-то текст, «пер_числ» – строго определённые числовые константы, «пер_float» – значение переменной определённого типа (здесь – float). Квадратные скобки [] указывают на наличие дополнительных параметров. «(возвращает тип)» – тип возвращаемого значения. Я буду также использовать обозначение типа «Boolean/short», чтобы показать, что функция возвращает 0 или 1 (строго говоря, Boolean – это частный случай типа short).

Пример:

Код "ID", пер_числ, пер_float

Примеры скриптов приведены в рамках:

Begin script

[функции скрипта]

End script

Работа с объектами

Работа с инвентарём

Добавление и изъятие вещей из инвентаря

AddItem, "ID Объекта", количество_числ
RemoveItem, "ID Объекта", количество_числ

Actor -> AddItem, "item_ID", 1
Container -> RemoveItem, "itme_ID", 5

Эти функции довольно просты. Они добавляют вещь в инвентарь персонажа или удаляют её оттуда. Последняя функция просто «стирает» предмет с лица Нирна, в то время как Drop заставляет персонаж выкинуть объект на пол. Вероятно функции AddItem и RemoveItem могут принимать глобальную переменную в качестве параметров, но только в поле результата диалога и только если Вы не меняете её значения в том же поле. Изъятие из инвентаря предмета, которого там на самом деле нет, не является опасным, однако его вес будет всё равно вычтен из общей нагрузки игрока. Обойти это можно только путём проверки на присутствие предмета, который необходимо удалить. И ещё, не удаляйте предметы, на которых в этот момент работает скрипт – игра просто «вылетит»!

У меня не получалось корректно добавить в инвентарь персонажа более 32767 (2 в 15 степени) предметов одного типа за раз.

Пример: этот скрипт обсуждался на форумах. Как только игрок одевает предмет, ему предлагают очистить его. Если игрок соглашается, предмет заменяется на «очищенную» версию.

Begin scr_thing

short button
short OnPcEquip
short state

if ( MenuMode == 1 )
            return
endif

if ( OnPCEquip == 1 ) ;когда игрок одевает предмет
            set state to 1
            set OnPCEquip to 0 ;делать лишь единожды при каждом одевании
endif

if ( state ==1 )
            MessageBox "Очистить предмет?" , "Да", "Нет"
            set state to 2
elseif ( state == 2 )
            set button to GetButtonPressed
            if ( button == 0 )
                        PlaySound "mysticism cast"
                        player->RemoveItem "item_a", 1 ;Эта строка заставляет игру вылететь!!!
                        player->AddItem "item_b", 1
                        set state to 0
            elseif ( button == 1 )
                        set state to 0 ;когда сделано, сбросить всё
            endif
endif

end

Скрипт прекрасно работает без строки, выделенной жирным шрифтом, а с ней вылетает. Причина в том, что скрипт прикреплён к “item_a”. Таким образом, предмет пытается удалить сам себя, из-за чего игра вылетает. Поэтому подобные операции должны производиться с помощью глобальных скриптов.

Как заставить персонаж бросить предмет

Drop, "ID Объекта", количество_числ

"Actor_ID" -> Drop, "ITEM_ID", 1

По идее, функция должна изымать предмет из инвентаря любого персонажа и бросать его ему под ноги. Однако, она работает корректно лишь с игроком. Когда я тестировал её на NPC, предмет удалялся из инвентаря вызываемого объекта и бросался под ноги игрового персонажа!

Интересное замечание: если заставить персонаж бросить предмет, которого у него нет, то создастся его новая копия.

Отслеживание событий, происходящих в инвентаре

OnPCAdd (местная переменная типа short)

Игрок добавил объект в инвентарь.

OnPCDrop (местная переменная типа short)

Игрок выбросил предмет из инвентаря.

OnPCSoulGemUse (местная переменная типа short)

Объект является камнем души и был использован в целях перезарядки или создания зачарованного предмета.

Надевание предметов

Equip, "ID Объекта"

"Actor_ID" -> Equip, "p_restore_health_q"

(Также смотрите выше про OnPCEquip)

Частично не работает. Функция могла стать необычайно полезной, однако, к сожалению, большая часть её потенциала не реализована: Вы не можете надевать что-то на игрока с помощью программного кода; Вы также не можете заставлять NPC одевать броню или брать в руки оружие (это полностью зависит от значения соответствующих навыков); также Вы не можете создавать проклятые предметы, снять которые будет невозможно. Насколько мне известно, Вы лишь можете заставить NPC выпивать зелья.

Заметьте: с появлением Трибунала функция была исправлена, так что теперь Вы можете делать всё вышеперечисленное :)

Пример: этот скрипт проклинает хитиновую дубину, так что игрок не сможет убрать её из рук даже с помощью «горячих» клавиш. Теперь ему придётся биться лишь с помощью этого оружия до конца своих дней, который, по-видимому, не за горами :) Однако, магию игрок всё же может использовать. Учтите, что для правильной работы скрипту необходим Трибунал.

Begin cursed_item

short state
short OnPCEquip

if ( OnPCEquip == 0 ) ; предмет ещё не надет или только что снят
            if ( state == 0 );если предмет ещё не надевался вообще, то пока ничего не делать
                        return
            else
                        Player -> Equip, "cursed_club" ;после попытки снять предмет, надеть его
                        MessageBox "Предмет проклят и не покинет ваших рук" ;Ну, и подразним игрока
            endif
else
            if ( state == 0 ) ;если предмет надевается впервые, ловушка захлопывается
                        set state to 1
            endif
endif

End

Определение того, был ли надет предмет

OnPCEquip (локальная переменная типа short)

Short OnPCEquip
If ( OnPCEquip == 1 )

Если игрок надел какой-либо предмет, переменная принимает значение 1 (ИСТИНА), которое сохраняется, пока он не будет снят. Иногда Вам придётся самим сбрасывать значение на 0:

if ( OnPCEquip == 1 ) ;когда игрок надевает предмет
            [делать что-то]
            set OnPCEquip to 0 ;делать лишь единожды при каждом надевании
endif

Если в данном примере снять и вновь надеть предмет, то код «[делать что-то]» будет повторён. Заметьте, что это может также выполняться в режиме меню:

If (MenuMode ==1)
            if ( OnPCEquip == 1 ) ;когда игрок надевает предмет
                        [делать что-то]
                        set OnPCEquip to 0 ;делать лишь единожды при каждом надевании
            endif
endif

Этот скрипт выполнится, даже когда игрок находится в меню, в то время как следующий фрагмент выполнить лишь по закрытии всех окон:

If (MenuMode ==1)
            Return
Endif

if ( OnPCEquip == 1 ) ; когда игрок надевает предмет
            [do something]
            set OnPCEquip to 0 ;делать лишь единожды при каждом надевании
endif

Дополнительный пример Вы найдёте после описания функции Equip.

Отключение возможности надеть предмет

PCSkipEquip (переменная типа short)

Установите значение этой переменной на 1, чтобы игрок не мог надеть предмет. Удобно для вывода сообщений перед открытием книг и т.д. Для примера более расширенного использования смотрите скрипт «SealedTreasuryReport».

Внимание: существует баг, приводящий к тому, что объекты, к которым применяется данная функция, дублируются. Чтобы это обойти, обновляйте инвентарь, используя (из секции, где используется функция OnPCEquip) добавление и моментальное удаление любого предмета.

Пример: этот скрипт я создал для мода об оборотнях. Он делает некоторые вещи неодеваемыми при определённых условиях.

Begin non_equippable

; Запрещает больному ликантропией игроку носить вещи охотников за оборотнями. Если игрок надевает
; предметы перед тем, как превратиться в оборотня, он может носить их пока не снимет сам.
; Однако, вновь он их надеть уже не сможет.

short PCSkipEquip
short OnPCEquip

if ( PCWerewolf != 1); если игрок не оборотень, он может носить броню
            set PCSkipEquip to 0
            return
else
            set PCSkipEquip to 1
endif

if ( OnPCEquip == 1 )
            MessageBox "Предмет содержит сильное заклинание против оборотней. Вы не можете его надеть!"
            set OnPCEquip to 0
endif

End

Проверка на присутствие предметов в инвентаре

GetItemCount, "ID Объект" (возвращает short)

Short objectcount
Set objectcount to ( "Mob_ID" -> GetItemCount, "Object_ID" )
If ( GetItemCount, "Object_ID" >= 1 )

Функция возвращает количество необходимых предметов в инвентаре вызываемого персонажа.

Ремонт объектов

OnPCRepair (локальная переменная типа short)

Переменная получает значение 1, когда игрок пытается отремонтировать некий объект.

RepairedOnMe, "ID Объекта" (возвращает Boolean/short)

if ( "daedric_mace"->RepairedOnMe, "repair_journeyman_01" == 1 )

Функция возвращает единицу, если вызываемый объект пытаются отремонтировать с помощью предмета “ID Объекта”. Причём вызываемый объект должен быть бронёй или оружием, а ремонтировать необходимо лишь предметами, для этого предназначенными.

Схожая функция OnRepair не работает.

UsedOnMe

UsedOnMe, “ID Объекта” (возвращает Boolean/short)
if ( UsedOnMe, Misc_pot_redware_01 )

В файле помощи написано:

«Функция возвращает 1, если “ID Объекта” был использован на вызываемом предмете. Необходима для скриптов, когда действия зависят от того, использовал ли игрок на необходимом предмете определённый объект».

Однако, насколько я знаю, эта функция, к сожалению, не работает.

Движение и поворот объектов

Следующие функции не работают с игроком, NPC и монстрами. Со статичными объектами всё в порядке. Заметьте: персонажи, стоящие на движущемся предмете вскоре сквозь него проваливаются – этого можно избежать постоянной быстрой деактивацией и активизированием вновь перемещающегося объекта. Также можно снабдить персонажа возможностью левитации или замедления падения (смотрите раздел Секретов и Приёмов).

Движение вдоль осей координат объекта

Move ось(x/y/z), юнит/сек_числ

Move x, 100

Функция перемещает объект по необходимой оси координат с заданной скоростью, которая измеряется в юнитах за секунду, а не за фрейм, что освобождает вас от зависимости от производительности компьютера. Движение производится относительно осей координат самого объекта и зависит от его угла поворота. Таким образом, по оси y объект всегда будет перемещаться вперёд:

Помните: функция не работает на NPC и игроке.

Движение вдоль глобальной оси координат

MoveWorld ось(x/y/z), юнит/сек_числ

MoveWorld z, 100

Функция перемещает объект по глобальной оси координат с заданной скоростью. Движение не зависит от угла поворота. Таким образом, по оси z объект всегда будет двигаться наверх. В глобальной системе координат z всегда вверх-вниз, x восток-запад, y север-юг.

Помните: функция не работает на NPC и игроке.

Пример скрипта контролирует движение платформы, если на ней стоит игрок:

Begin platform_script

Short PlatformMoving
Short ActivateMe
Float Timer

If ( GetStandingPC == 1 )
            Set ActivateMe to 1
Endif

If ( ActivateMe == 1 )
            If ( PlatformMoving == 0 )
                        Set Timer to Timer + GetSecondsPassed
                        If ( Timer <= 15 )
                                    "floating_platform_01"->MoveWorld X 10
                        Else
                                    Set Timer to 0
                                    Set PlatformMoving to -1
                        Endif
            Endif
            If ( PlatformMoving == -1 )
                        Set Timer to Timer + GetSecondsPassed
                        If ( Timer <= 15 )
                                    "floating_platform_01"->MoveWorld X -10
                        Else
                                    Set Timer to 0
                                    Set PlatformMoving to 0
                                    Set ActivateMe to -1
                        Endif
            Endif
Else
            "floating_platform_01"->SetAtStart
Endif

End platform_script

Поворачивание объектов

Также как и в случае с движением Вы можете поворачивать объект относительно его собственной или глобальной оси координат. Также существует функция, позволяющая определить угол поворота:

Rotate, ось, угол/сек_числ
RotateWorld, ось, угол/сек_числ
GetAngle, ось(x/y/z) (возвращает float)

Rotate, z, -30; поворот 30° за секунду вокруг оси Z
If ( GetAngle, z == 180 )

Учтите, что последняя функция возвращает угол поворота относительно глобальной оси координат. Также заметьте, что угол поворота измеряется не в градусах, а является скоростью. Таким образом, чтобы повернуть объект на 90 градусов, Вам придётся либо просто установить значение угла (мгновенный изменение), либо использовать эти функции вкупе с GetAngle.

Установление позиции и направление объекта по осям

SetAngle, ось, float_числ_угол
SetAngle, z, 30

SetPos, ось, float_числ_позиция
SetPos, z, 477

Эти функции, в отличие от Move и MoveWorld, работают и с персонажами, в том числе с игроком. Они устанавливают необходимый параметр на заданную величину. Система координат всегда локальная для объекта. Заметьте: при условии наличия Трибунала аргументами могут быть переменные типа float, но только если объект находится в активной локации.

Пример: скрипт, прикреплённый к ящикам в канализации Мурнхолда, заставляет их дрейфовать на волнах.

begin floatAboveStartHeight

float timer
float swingTime
float startAngle
float startHeight
float currangle
float xvalue
float zvalue
float zoffset
float tmpoffset
float weightoffset
float waterlevel
short reset
short initialized

if ( initialized == 0 ) ;эта секция содержит начальное положение и поворот ящиков
            set startAngle to GetAngle, X
            set startHeight to GetPos, Z
            set swingTime to 1
            set initialized to 1
endif

if ( MenuMode == 0 )

            set waterlevel to GetWaterLevel

            if ( waterlevel > startHeight )
                        if ( timer == 0 )
                                    if ( reset == 0 )
                                                set timer to Random 100
                                                set timer to timer / 4
                                    endif
                        endif

                        set timer to ( timer + GetSecondsPassed )
                        set currangle to GetAngle X
                        ;Этот фрагмент устанавливает размер перемещения и поворота:
                        set xvalue to 10 * GetSecondsPassed
                        set zvalue to 5 * GetSecondsPassed
                        ;ящик поворачивается вокруг оси x
                        ;поворот вверх

                        if ( timer < swingTime )
                                    set currangle to currangle + xvalue
                                    SetAngle X currangle
                                    set zoffset to zoffset + zvalue

                                    ;вновь поворот

                        elseif ( timer < (swingTime * 3) )
                                    set currangle to currangle - xvalue
                                    SetAngle X currangle
                                    set zoffset to zoffset - zvalue

                                    ;опять вверх

                        elseif (timer < (swingTime * 4 ) )
                                    set currangle to currangle + xvalue
                                    SetAngle X currangle
                                    set zoffset to zoffset + zvalue

                                    ;сброс таймера на нули

                        else
                                    set timer to 0
                                    set reset to 1
                                    set zoffset to 0
                                    SetAngle, x, startangle
                        endif

                        set tmpoffset to waterlevel
                        set tmpoffset to tmpoffset + zoffset

                        ;Ящик двигается вверх и вниз

                        SetPos Z tmpoffset

            Else ;Нормальный уровень воды
                        SetAngle, X, startAngle
                        SetPos Z startHeight
            endif
endif

end

Сброс параметров объекта на начальные значения

SetAtStart

Object_ID -> SetAtStart

Перемещает объект на начальное положение, которое было установлено в редакторе. Для примера смотрите скрипт под описанием функции Move.

Размещение объекта на открытых пространствах и в интерьере

Position, float_числ_x, float_числ_y, float_числ_z, float_числ_ПоворотПоОсиZ
(для открытых пространств)

PositionCell, float_числ_x, float_числ_y, float_числ_z, float_числ_ ПоворотПоОсиZ, “ID локации”
(для интерьеров и открытых пространств)

Player -> position –23515, -15355, 3355, 90
"Actor_ID" -> PositionCell, -254, 475, -376, 360, "Balmora, Council Club"

Эти функции является превосходным решением для кольца телепортации. Также их можно использовать для перемещения NPC и предметов в новое место. В стандартной версии игры в качестве аргументов принимались лишь числовые константы. Возможно, это изменилось с Трибуналом.

Учтите: использование этих функций в поле результата диалога ненадёжно и может вызвать ошибку. Чтобы избежать этого, можно запускать глобальный скрипт, который будет провоцировать перемещения.

Пример: скрипт для кольца телепортации.

Begin TeleportScript

;прикрепляется, например, к кольцу

short status
short button
short OnPCEquip

if ( MenuMode == 1 )
            return
endif

if ( OnPCEquip == 1 )
            Set Status to 10
            Set OnPCEquip to 0
Endif

If ( status == 10 ) ;показать меню
            MessageBox "Телепортироваться в", "Балмору", "Вивек", "Отмена"
            Set Status to 20
Elseif ( status == 20 ) ;ждать ответа
            Set button to GetButtonPressed
            If ( button == -1 ) ;пока нет ответа
                        Return
            Elseif ( button == 0 ) ;выбрана Балмора
                        Player -> PositionCell -21278, -17613, 534, 0, "Balmora (-3, -3)"
            Elseif ( button == 1 ) ;выбран Вивек
                        Player -> Position 29872, -82108, 578, 180
            Elseif ( button >= 2 ) ;выбрана отмена телепортации
                        Set status to 0
            Endif
Endif

End

Заметьте, что оба пункта назначения – открытые пространства, однако, эффект достигается двумя разными функциями. Если место появления является небезопасным, например, из-за наличия стены, персонаж очутится в ближайшем к пункту назначения свободном пространстве.

Как положить предмет рядом с игроком

PlaceAtPC, "ID Объекта", количество_числ, расстояние_числ, направление_числ

PlaceAtPC, "Secret Message", 0, 30, 1
PlaceAtPC, "ancestor_ghost", 1, 256, 1

Функция позволяет расположить некий объект рядом с игроком, причём можно очень чётко задать его положение – выбрать необходимое направление и расстояние до игрового персонажа. Если предмет нельзя поставить в необходимое место ввиду наличия стены, то он появится прямо у игрока под ногами. Также возможно его появление по одной из других, свободных от преград осей.

Направления:

0 = спереди
1 = сзади
2 = слева
3 = справа

CellUpdate

CellUpdate

Не работает! В файле помощи написано, что функция обновляет информацию о нынешнем положении объекта в локации. Она была призвана помочь при перемещении предметов на большие расстояния. Дело в том, что движок следит за объектами относительно базы данных, в которой хранится их текущее местоположение. Так что после смены локации объект может неправильно выполнять свой скрипт. Это значит, что некоторые предметы, будучи перемещёнными далеко от места их создания, могут просто исчезнуть. К сожалению, все мои попытки протестировать эту функцию закончились ошибкой выполнения программы. Компилятор призывал поставить некий код после функции.

Заметка: в Трибунале подобные глюки можно обойти, деактивируя и удаляя объект (SetDelete), а потом помещяя его копию там, где это необходимо. Смотри секцию Секретов и Приёмов.

Определение положения и относительное движение

Снаружи или внутри?

GetInterior (возвращает Boolean/short)

If ( GetInterior == 1 )

Недокументированно:

Функция возвращает 1, если игрок находится в помещении, и 0 – если на улице. Следующий скрипт отлично иллюстрирует работу функции. Если захотите его проверить в игре, просто напишите в консоли: “StartScript Outside_Check”.

Begin Outside_Check

short doonce

if (MenuMode == 1)
            Return
EndIf

if (doOnce == 0) ;если игрок перешёл в другую локацию или скрипт только начал работать
            if ( GetInterior == 1 )
                        MessageBox "1: внутри"
            elseif ( GetInterior == 0 )
                        MessageBox "0: снаружи"
            else
                        MessageBox "Чёрт знает где!"
            endif             set doOnce to 1
            Return
endif

if (doOnce == 1)
            if (CellChanged == 0)
                        Return
            else ;если игрок поменял локацию
                        set doOnce to 2 ;игра ждёт один фрейм (долю секунды)
            endif
            Return
endif

if (doOnce == 2) ;затем всё по новой
            set doOnce to 0
            Return
endif

End Outside_Check

Определение местонахождения игрока

GetPCCell, "ID Локации" (возвращает Boolean/short)

if ( GetPCCell "Balmora" == 1 )
            Set dream to 1
endif

Функция проверяет, находится ли игрок в определённой локации. Заметьте, что условие “Vivec” будет ИСТИНОЙ, если игрок находится и у поста силт страйдера, и в помещениях кантонов.

Пример: этот маленький скрипт Bethesda создала для проверки того, покинул ли игрок локацию до того, как взял определённый предмет с тела NPC.

Begin DrothPost

if ( GetJournalIndex "MS_EstateSale" >= 70 )
            if ( GetPCCell "Mournhold, Geon Auline's House" == 0 )
                        "Geon Auline"->RemoveItem "silver dagger_droth_unique" 1
                        Journal MS_EstateSale 80
                        StopScript DrothPost
            endif
endif

End DrothPost

Определение перехода между локациями

CellChanged

If ( CellChanged == 1)

Функция возвращает 1 в тот самый единственный момент, когда игрок совершает переход между локациями. Если функция находится в местном скрипте, то функция будет принимать значение TRUE, только когда игрок будет входить в помещение. Если же он будет покидать локацию, то скрипт будет закончен раньше, чем переход будет зарегистрирован. Существует один неприятный баг – при телепортации функция, к сожалению, не провоцируется.

Пример: в скрипте “SlaveScript”, прикреплённом ко всем рабам в игре, эта функция определяет, покинул ли игрок помещение, освободив невольников. Если ответ положительный, то рабы покидают место заключения навсегда.

Begin SlaveScript

[…]

if ( slaveStatus == 3 )
            if ( GetCurrentAIPackage == 3 )
                        AIWander 512 0 0 0 0 0 0 0 0 0 0 0
            endif
            if ( GetItemCount Slave_Bracer_Left > 0 )
                        Drop Slave_Bracer_Left 1
            endif
            if ( GetItemCount Slave_Bracer_Right > 0 )
                        Drop Slave_Bracer_Right 1
            endif
            if ( CellChanged == 1 )
                        Disable
            endif
endif

end slaveScript

Ещё один прекрасный пример иллюстрирует появление вновь и вновь призрака в гостинице в воротах Садрит Моры, как только игрок покидает её.

Begin ResurrectHaunt

;квест Садрит Моры
;призрак появляется вновь, пока индекс журнальной записи town_Sadrith не превысит 35

if ( CellChanged == 1 )
            if ( gateway_haunt->GetHealth < 1 )
                        gateway_haunt->Resurrect
            endif
endif

end ResurrectHaunt

Расстояние между объектами

GetDistance, "ID Объекта" (возвращает float)

"ObjectID1" -> GetDistance, "ObjectID2"

Функция возвращает расстояние (в юнитах) между двумя объектами. Она может быть использована для провоцирования начала боя или определения местоположения игрока. Ниже приведён отрывок одного из скриптов оригинальной игры:

; Из скрипта прикреплённого к NPC, по имени Ashamanu:
; Ashamanu даст журнальную запись с индексом 60, когда игрок будет рядом

if ( GetDisabled != 1 )
            if ( GetDistance Player <= 256 )
                        if ( GetDistance "guar_white_unique" <= 256 )
                                    if ( GetJournalIndex "MS_WhiteGuar" <= 50 )
                                                Journal "MS_WhiteGuar" 60
                                    endif
                        endif
            endif
endif

Ограничения:

  • Учтите, что функция работает корректно лишь с аргументами-объектами, представленными в игре единственным числом. Иначе движок в качестве экземпляра возьмёт первый попавшийся объект, скорее всего, не тот, который Вам нужен. Таким образом, скрипт, предупреждающий игрока об опасной близости хищника, должен прикрепляться к животному и в качестве параметра получать “player”, то есть «игрок», который представлен в игре единственным числом.
  • Если Вы определяете расстояние до объекта, передвинутого функциями Move или MoveWorld, то GetDistance будет возвращать длину отрезка до первоначального местоположения этого объекта. В таких случаях Вам придётся пользоваться функцией GetPos или старой доброй теоремой Пифагора.

Определение положения и направления объекта

GetPos, ось(x/y/z)

Object_ID -> GetPos, z

При передвижении объекта, Вам зачастую может понадобиться информация о его текущем местоположении. В следующем примере я контролировал перемещение источника света с помощью этой функции. Огонь начинает медленно разгораться и затухает каждый раз к концу дня. Первоначальное значение позиции по оси Z – 511.

Begin _HB_Scheduled fire

short control_fire

;Скрипт прикрепляется к охраннику огня и определяет порядок горения.

if ( GetDistance, "HB_Furn_De_Firepit_camp" < 600 )
            If ( GameHour < 17 )
                        if ( HB_Light_Fire_camp -> GetPos Z >= 400 )
                                    HB_Light_Fire_camp -> MoveWorld z, -0.1 ;Подвинуть огонь вниз
                        else
                                    HB_Light_Fire_camp -> disable
                        endif
            elseif ( GameHour >= 17)
                        HB_Light_Fire_camp -> enable
                        if ( HB_Light_Fire_camp -> GetPos Z < 511 )
                                    HB_Light_Fire_camp -> MoveWorld z, 0.1 ;Подвинуть огонь вверх
                        else
                                    HB_Light_Fire_camp -> enable
                        endif
            endif
endif

end

GetAngle , ось(x/y/z) (возвращает float)

Функция возвращает угол поворота относительно глобальной оси координат. Значение может быть от 0 до 180 и от –180 до 0.

Обзор

GetLOS, “ID Объекта” (возвращает Boolean/short)

Actor_ID -> GetLOS, Player

Недокументированно:

GetLineOfSight (returns Boolean/short?)

(может эта функция работает лучше? Не тестировал)

Функция определяет, находится ли объект, ID которого передаётся в качестве параметра, в зоне видимости вызываемого объекта. Насколько я знаю, эта функция работает лишь с персонажами. Также помните, что функция не принимает во внимание направление глаз NPC, поэтому не воспринимайте зону видимости слишком буквально.

Пример скрипта:

Begin balynScript

float timer
short doOnce

[…];эта часть кода относится к настройкам журнала

Set timer to ( timer + GetSecondsPassed )

if ( timer < 5 )             Return ; Таймер, позволяющий избегать проблемы исполнения
endif

Set timer to 0

if ( doOnce == 0 )
            if ( GetDistance Player <= 1024 )
                        if ( player->GetDistance "hlaalu_loaddoor_ 02_balyn" <= 256 )
                                    if ( GetLOS Player == 1 )
                                                ForceGreeting
                                                Journal DA_Mephala 55
                                                set doOnce to –1
                                    endif
                        endif
            endif
endif

End

Определение того, заметил ли один NPC другого

GetDetected, "ID Персонажа" (возвращает Boolean/short)

If ( Actor_1->GetDetected, Player == 1 )

Функция возвращает ИСТИНУ, если один NPC может заметить другого. Она будет возвращать 0, если, например, объект “ID Персонажа” удачно прячется или наложил на себя заклинание невидимости или хамелеона. Как написано в файле помощи, это крайне медленная функция, поэтому её частый вызов нежелателен (например, поставьте трёхсекундный таймер).

Пример скрипта: игрок должен незамеченным прокрасться к предмету, иначе будет пойман.

Begin jeanneScript

float timer
short nolore

if ( GetJournalIndex "EB_Bone" < 20 )
            Return
endif

if ( GetJournalIndex EB_Bone >= 40 )
            Return
endif

Set timer to ( timer + GetSecondsPassed )

if ( timer < 5 ) ;Таймер, необходимый для вызова функции GetDetected не чаще каждых 5 секунд
            Return
endif

Set timer to 0

if ( GetDistance Player <= 1024 )
            if ( player->GetDistance "com_chest_02 " <=128 )
                        if ( GetDetected Player == 1 )
                                    ForceGreeting ;Игрок замечен и будет наказан
                                    Journal EB_Bone 50
                        endif
            endif
endif

End jeanneScript

События для персонажей, стоящих на объекте

GetStandingPC (short) возвращает 1, если игрок стоит на объекте
GetStandingActor (short) возвращает 1, если любой персонаж (в том числе игрок) стоит на объекте

If ( GetStandingPC == 1)
            [… срабатывает ужасная ловушка]
endif

Идеальная функция для провоцирования разнообразных событий, особенно в интерьерах. Зачастую используется для создания ловушек. Мой же пример скрипта включает все огни в коридоре, как только игрок наступает на определённое место пола:

Begin HBHallLighting

if ( GetStandingPC == 1 )
            set HB_hallfire to 1
endif

end

HB_hallfire – это глобальная переменная, которую я создал, чтобы определить, когда нужно включать огонь:

Begin HBHallfireon

if ( HB_hallfire == 1)
            if ( GetPos, z, < -736 )
                        MoveWorld, z, 3 ;огонь поднимается, пока не достигает полной своей высоты
                        if ( GetPos, z, > -780)
                                    enable
                        endif
            endif
else
            disable
endif

end

Урон стоящему на объекте персонажу

HurtStandingActor, float_ОчкиЖизни/сек

HurtStandingActor –3
HurtStandingActor 1
Float hurt_variable
HurtStandingActor, hurt_variable

Эта функция влияет на значение очков здоровья всех персонажей (в том числе и игрока), которые стоят на определённом объекте. Отрицательный аргумент восстанавливает здоровье, положительный – наносит урон (в очках жизни за секунду). Примечательно, что функция принимает переменные в качестве параметра.

Пример скрипта: возможно, этот эффект больше всего известен по действию полей лавы.

begin lava

if ( menumode == 1 )
            return
endif

if ( CellChanged == 0 )
            if ( GetSoundPlaying "lava layer" == 0 )
                        PlayLoopSound3DVP "lava layer", 1.0, 1.0
            endif
endif

HurtStandingActor, 20.0 ;20 пунктов урона в секунду

end lava

Проверка и активация объектов

Объект обычно активируется, когда игрок нажимает на нём пробел (по умолчанию). Большинство предметов имеют стандартные действия на этот случай: двери и сундуки открываются, NPC разговаривают. Функция OnActivate позволяет вместо стандартных действий выполнить нечто особенное.

OnActivate

if ( OnActivate == 1 )

Функция возвращает 1 в тот самый единственный момент, когда объект активирован. Чтобы выполнить стандартное действие (например, после функции OnActivate) существует функция Activate.

Activate

"Object_ID" -> activate

Стандартные действия при активации:

Двери –> открываются (не работает в стандартном Морровинде)
Двери между локациями –> ничего (не работает?)
Контейнеры –> открываются (меню отображения содержимого)
Буквы/Свитки –> читаются (но отключается возможность взять объект)
Активаторы –> ничего
NPC –> диалог
Оружие, Броня и т.п. –> подбираются

Учтите: существует множество проблем, связанных с этими функциями. Во-первых, Activate не будет работать, если перед этим не была вызвана OnActivate. Причём, если спустя 72 игровых часа после OnActivate не была вызвана Activate, то движок забывает о вызове первой функции, что ведёт к тому, что вторая отказывает в работе. Во-вторых, во избежание других ошибок, я бы рекомендовал использовать только один экземпляр функции OnActivate. И, наконец, для предметов, находящихся в инвентаре эта функция не работает и должна быть заменена на OnPCEquip.

Также если на контейнере стоит галочка “References persist”, то он может быть открыт, где бы игрок ни находился. Это идеальный способ обращаться к удалённому контейнеру, предварительно прикрепив следующий скрипт, скажем, к кольцу:

begin RemoteContainer

short OnPCEquip

if ( OnPCEquip == 1 )
            set OnPCEquip to 0
            "dh_remote_chest_01"->Activate, player
endif

end

Пример: скрипт прикреплён к сундуку, который не будет афишировать наличие на нём ловушки, как это делают все прочие контейнеры.

Begin Trap_script

short done

if ( OnActivate == 1 )
            if ( done == 1 ) ;одноразовое условие
                        Activate
                        return
            else
                        Cast, "flame", Player ;урон игроку
                        set done to 1
                        Activate ;вызов стандартного действия: открытие сундука
            endif
endif

End trap_script

Активизирование и деактивация

Enable
Disable
GetDisabled (возвращает Boolean/short)

"ObjectID" -> enable

Функция Disable заставляет объект полностью исчезнуть из игры, однако, прикреплённые к нему скрипты продолжают работать. Enable противоположна ей по предназначению. GetDisabled возвращает 1, если объект деактивирован. Вместе эти функции являются довольно мощным инструментом. Например, они могут заменить нормальный дом на горящий. Они также используются при строительстве крепости.

Пример: стандартный скрипт “SlaveScript” заставляет освобождённых рабов исчезать, как только игрок покидает локацию.

begin slaveScript

short slaveStatus
short doOnce
short NoLore

[существуют и другие проверки состояния раба – смотрите их в оригинальном скрипте!]

if ( slaveStatus == 3 )
            if ( GetCurrentAIPackage == 3 )
                        AIWander 512 0 0 0 0 0 0 0 0 0 0 0
            endif
            if ( GetItemCount Slave_Bracer_Left > 0 )
                        Drop Slave_Bracer_Left 1
            endif
            if ( GetItemCount Slave_Bracer_Right > 0 )
                        Drop Slave_Bracer_Right 1
            endif
            if ( CellChanged == 1 )
                        Disable ;****** Заставляет раба исчезнуть, как только игрок уходит
            endif
endif

end slaveScript

Внимание: деактивация источников света

У движка существует проблема с отключением подобных объектов – NPC и некоторые другие предметы продолжают быть освещенными, в то время как мир вокруг них – нет. Я не пробовал избежать этого, однако, проблема может быть решена путём простого перемещения источника света, например, на 3 метра под пол. Также во избежание ненужной иллюминации объектов можно после выключения стандартного включить инвертированный источник, значения света которого установлено на противоположное (то есть он генерирует темноту).

Отпирание и запирание дверей и сундуков

Lock, short_числ_УровеньЗамка
Unlock
GetLocked (возвращает Boolean/short)

(Только двери и контейнеры)

My_Door -> Lock, 50
If ( GetLocked == 1 )
            Unlock
Endif

Эти функции используются для того, чтобы запереть или отпереть замок на двери или контейнере. Функция GetLocked возвращает 1, если объект закрыт. Lock запирает замок, причём можно установить степень закрытия (0-100). Учтите, что замок, закрытый на 0 будет невозможно ни взломать, ни открыть. Unlock открывает любой замок, вне зависимости от его уровня сложности.

Пример: скрипт позволяет создать тренажёр для прокачки навыка взлома – сундук, который автоматически закрывается вновь после открытия.

Begin PC_Security_Skill_Trainer

float timer

if ( menumode == 1)
            return
endif

set timer to timer + GetSecondsPassed

if ( timer > 10 )
            set timer to 0
endif

if ( timer == 0 ) ;таймер используется для перезакрытия по прошествии 10 секунд
            "Storm_Chest_Trainer"->Lock 50
endif

End

Не сохранять изменения, произошедшие с объектом

DontSaveObject

Вызывайте эту функцию, если изменения, произошедшие с объектом (по ходу игры активируемым/деактивируемым/двигающимся) не должны запоминаться при сохранении игры. Это позволит Вас избежать надоедливых сообщений об ошибке, что информация об объекте изменилась. В оригинальной игре функция использовалась в скрипте “SignRotate” и в примере ниже.

Пример:

Begin diseaseAscended

DontSaveObject

;ascended sleeper каким-то образом получил все моровые заболевания…

if ( CellChanged == 0 )
            return
endif

AddSpell "ash woe blight"
AddSpell "black-heart blight"
AddSpell "chanthrax blight"
AddSpell "ash-chancre"
End

Анимация объектов

Эта группа функций позволяет добавлять анимацию к объектам, заранее определённую в модели (файлы типа nif). Вы можете узнать имена анимационных групп, загрузив модель в окно предварительного просмотра и прокручивая разные варианты. Также это можно увидеть в меню “Character” в окне “Base animation”. Превосходная коллекция анимационных групп располагается во всемирной паутине по следующему адресу: http://morrowind.preik.net/animationgroups.html

Однако только группы, определённые в окне базовой анимации могут быть вызваны этими функциями. Не у всех моделей есть анимационные группы, однако разнообразные флаги (под активаторами) являются отличным примером того, что предполагается. Примеры имён групп: idle, idle2, idle3, walk и т.д.

PlayGroup, ИмяГруппы, [Флаги]

PlayGroup, walk, 1

Функция проигрывает определённую группу анимации. Необязательные флаги могут быть использованы для запуска группы разными способами (смотрите ниже).

LoopGroup, ИмяГруппы, раз_числ, [Флаги]

Анимационная группа будет проиграна соответствующим образом заданное количество раз. Необязательные флаги могут быть использованы для запуска группы разными способами (смотрите ниже).

SkipAnim

Предотвращает проигрыш анимации на данный момент.

Флаги

0 = Нормальный
Текущая анимация проигрывает свой цикл, новая анимация начинается с самого начала.
1 = Мгновенный запуск
Текущая анимация прекращает свой проигрыш вне зависимости от выполняемого фрейма. Новая анимация начинается со своего начала.
2 = Мгновенный проигрыш
Текущая анимация прекращает свой проигрыш вне зависимости от выполняемого фрейма. Новая анимация начинается с начала своего цикла повторения.

Пример: этот скрипт прикреплён ко всем уличным вывескам, заставляя их двигаться в зависимости от погоды.

begin OutsideBanner

;Этот скрипт предназначен для уличных вывесок.
;Он заставляет их развеваться на ветру.
;Idle - штиль, Idle2 – лёгкий ветерок, Idle3 – шторм

short ran

if ( MenuMode == 0 )
            set ran to random 100
            if ( ran < 30 ) ;30% шанс на то, что флаг двинется как-то по-новому
                        if (GetCurrentWeather >= 5 ) ;гром, пыльная или моровая буря
                                    LoopGroup, Idle3, 5
                        endif
                        ;последняя вызванная анимация и будет проиграна
                        if ( ran <= 10 )
                                    PlayGroup, Idle
                        elseif ( GetCurrentWeather < 5 )
                                    PlayGroup, Idle2
                        endif
            endif
endif

end OutsideBanner