Перейти к содержанию
  • Статья «OBSE в Массы» специально для TESALL.RU

    Операторы

    Операторы присваивания  
    := Присвоить
    += Прибавить и присвоить
    -+ Отнять и присвоить
    *= Умножить и присвоить
    /= Разделить и присвоить
    ^= Возвести в степень и присвоить
    Логические операторы  
    || Логическое ИЛИ
    && Логическое И
    Строго меньше
    > Строго больше
    >= Больше или равно
    = Меньше или равно
    == Равно
    != Не равно
    ! Логическое отрицание (НЕТ)
    Математические операторы  
    + Прибавление
    - Вычитание
    * Умножение
    / Деление
    % Остаток от деления
    ^ Возведение в степень
    Побитовые операторы  
    >> Побитовый сдвиг влево
    Побитовый сдвиг вправо
    | Побитовое ИЛИ
    & Побитовое И
    ! Побитовое НЕТ
    Операторы для работы с данными
    :: Пара "ключ"::"значение"
    -> Оператор обращения к элементу массива Массив -> Элемент, тоже, что и Массив[Элемент].
    $ Строка
    # Номер

    Оператор присваивания

    Для всех новых конструкций языка стоит использовать новый оператор присваивания Let вместо стандартного Set. Синтаксис:

    Let имя_переменной> := значение>

    Например:

    Let a := 10

    Что эквивалентно:

    Set a to 10

    Также можно использовать вместо знака присваивания знаки: прибавить и присвоить (+=), отнять и присвоить (-=), умножить и присвоить (*=), поделить и присвоить (/=), возвести в степень и присвоить (^=):

    Let a := 10    ; Set a to 10
    Let b := 2     ; Set b to 2
    Let c := a + b ; Set c to a + b 
                   ; c == 12
    Let c += b     ; Set c to c + b 
                   ; c == 14
    Let c -= a     ; Set c to c - a 
                   ; c == 4
    Let c *= a     ; Set c to c * a 
                   ; c == 40
    Let c /= b     ; Set c to c / b 
                   ; c == 20
    Let c ^= b     ; Set c to c ^ b 
                   ; c == 400

    Проверка истинности

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

    ScriptName SomeScript
    array_var SomeArr
    Begin GameMode
          Let SomeArr := ar_Construct Array
          Let SomeArr[0] := GetSelf
          If SomeArr[0] == Player
                ;…
          EndIf
    End

    Его стоит переписать следующим образом:

    ScriptName SomeScript
    array_var SomeArr
    Begin GameMode
          Let SomeArr := ar_Construct Array
          Let Arr[0] := GetSelf
          If Eval (SomeArr[0] == Player)
                ;…
          EndIf
    End

    Обработка исключений

    В OBSE существует оператор TestExpr, он служит для проверки исключений. Что такое исключение? Исключение - это события, приводящие к ошибке. Этим оператором стоит пользоваться, чтобы избежать ошибок в ходе работы скрипта. Например, к заранее непредвиденным ошибкам может приводить выход индекса массива за диапазон допустимых значений, продемонстрируем это в следующем примере:

    ScriptName SomeScript
    array_var Arr
    short Iterator
    Begin GameMode
          Let Arr := ar_Construct Array
          Let Arr[0] := 1
          Let Arr[1] := 2
          Let Arr[2] := 3
          While Iterator  10
                If TestExpr (Arr[Iterator])
                      Print ToString Arr[Iterator]
                Else
                      Break
                Endif
                Let Iterator += 1
          Loop   
    End

    Операторы ToString и ToNumber

    Операторы ToNumber применяются для преобразования строк в числа и чисел в строки соответственно.

    Print ToString 10
    ; Оператор ToString имеет сокращение: $
    ; Следующая строка эквивалентна предыдущей
    Print $10

    Оператор ToNumber и его сокращение # преобразовывает строку, содержащую численные символы, в численное значение.

    Let a += ToNumber "10"
    ; a == 10

    Возможно преобразование строки, содержащей шестнадцатеричное значение, если она начинается с символов "0x" или установлен флаг bIsHex

    Let a += ToNumber "0xFF" 
    ; a == 255
    Let a += ToNumber "abc" 1
    ; a == 2748

    Оператор TypeOf

    Оператор TypeOf возвращает строку с типом данных. Возможные значения:

    "String" Строка
    "Number" Число
    "Form" Форма
    "Array" Индексный массив
    "Map" Вещественный массив
    "StringMap" Ассоциативный массив

    Операторы цикла

    В родном скриптовом языке Oblivion-а операторов цикла не существовало, по той простой причине, что сами скрипты выполняются довольно часто, и дополнительно зацикливаться на выполнении какого-либо участка кода не было нужды, но с появлением массивов и строковых переменных в OBSE оказалось невозможным обработать весь массив, например, за один проход.

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

    Обратите внимание на то, что в теле операторов цикла (это не касается операторов безусловного перехода) нельзя использовать ключевое слово Return.

    Операторы безусловного перехода Label

    Операторы безусловного перехода служат для управления ходом выполнением скрипта. Оператор Goto.

    Операторы безусловного перехода не являются операторами цикла по сути, но могут быть использованы с этой целью, поэтому они описаны в этой главе.

    ScriptName SomeScript
    Begin GameMode
      ; …
      Label 1 ; Метка 1
      ; …  
      Label 2 ; Метка 2
      If ; Если что-то произошло
        ; Переходим к метке 1
        Goto 1
      Else
        ; Иначе – к метке 2
        Goto 2
      EndIf
    End

    Несмотря на то, что операторы безусловного перехода являются несложной конструкцией, их использование не рекомендовано и требует повышенного внимания. Сами по себе безусловные переходы не есть ошибками с точки зрения выполнимости кода, но неправильное их использование может приводить к неявным ошибкам и неточностям в работе скрипта, в частности, к зацикливанию.

    Пожалуй, стоит упомянуть, что операторы RestoreIP соответственно, какие из них и в какой комбинации использовать – зависит только от вас.

    Цикл с предусловием While

    Синтаксис:

    ScriptName SomeScript
    Begin GameMode
      While ; Условие
        ; Тело цикла
      Loop
    End

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

    ScriptName SomeScript
    short i
    Begin GameMode
      While i  10
        Let i += 1
        Print ToString i
      Loop
    End

    Цикл перебора массивов ForEach

    Оператор ForEach специально предназначен для перебора массивов, строк и содержимого контейнеров элемент за элементом. За каждый проход цикла переменная-итератор получает значение следующего элемента итерируемой переменой.

    В случае с контейнерами переменная-итератор должна иметь тип ref. За каждый проход цикла она будет получать значение ссылки на следующий объект выбранного контейнера.

    Следующий скрипт находит количество непустых камней душ в инвентаре игрока.

    ScriptName GetPlayerSoulGemCount
    ref Iterator
    short SoulGemCount
    Begin ScriptEffectStart
      ForEach Iterator - GetSelf
        If Iterator.IsSoulGem && Iterator.GetSoulLevel > 0
          Let SoulGemCount += Iterator.GetRefCount
        EndIf
      Loop
      Print ToString SoulGemCount
    End

    Если цикл используется для перебора массивов, то переменная-итератор должна быть типа array_var. За каждый проход цикла два элемента массива-итератора (переменной-итератора), "key" и "value" получают следующую пару значений «ключ – значение» итерируемого массива.

    ScriptName ArrayForEachScript
    array_var Arr
    array_var StrMp
    array_var Mp
    array_var Iterator
    Begin ScriptEffectStart
      Let Arr   := ar_Construct Array
      ; Arr – индексный массив
      Let StrMp := ar_Construct StringMap
      ; StrMp – ассоциативный массив
      Let Mp    := ar_Construct Map
      ; Mp – вещественный массив
    ; ----------------------------------------------------------------------
      Let Arr[0] := 100500
      Let Arr[1] := 100501
      Let Arr[2] := 100502
      ForEach Iterator - Arr
        ; Первый проход скрипта
        ; Iterator["key"]   == 0         Ключ
        ; Iterator["value"] == 100500    Значение
        ; Второй проход скрипта
        ; Iterator["key"]   == 1         Ключ
        ; Iterator["value"] == 100500    Значение
        ; Третий проход скрипта
        ; Iterator["key"]   == 2         Ключ
        ; Iterator["value"] == 100500    Значение
      Loop
    ; ----------------------------------------------------------------------
      Let StrMp["Elem0"] := 100500
      Let StrMp["Elem1"] := 100501
      Let StrMp["Elem2"] := 100502
      ForEach Iterator - StrMp
        ; Первый проход скрипта
        ; Iterator["key"]   == "Elem0"   Ключ
        ; Iterator["value"] == 100500    Значение
        ; Второй проход скрипта
        ; Iterator["key"]   == "Elem1"   Ключ
        ; Iterator["value"] == 100500    Значение
        ; Третий проход скрипта
        ; Iterator["key"]   == "Elem2"   Ключ
        ; Iterator["value"] == 100500    Значение
      Loop
    ; ----------------------------------------------------------------------
      Let Mp[0.15] := 100500
      Let Mp[3.14] := 100501
      Let Mp[6.21] := 100502
      ForEach Iterator - Mp
        ; Первый проход скрипта
        ; Iterator["key"]   == 0.15      Ключ
        ; Iterator["value"] == 100500    Значение
        ; Второй проход скрипта
        ; Iterator["key"]   == 3.14      Ключ
        ; Iterator["value"] == 100500    Значение
        ; Третий проход скрипта
        ; Iterator["key"]   == 6.21      Ключ
        ; Iterator["value"] == 100500    Значение
      Loop
    End

    При переборе строковых переменных, переменная-итератор должна быть типа string_var. За каждый проход скрипта переменная-итератор будет получать значение каждого последующего символа итерируемой строковой переменой. Длина переменной-итератора - всегда один символ.

    Для примера составим небольшой скрипт, который будет искать заданное слово в строке.

    ScriptName StringForEachScript
    string_var Source   ; Строка-источник, в ней и будем искать слово.
                        ; Итерируемая переменная.
    string_var Iterator ; Переменная-итератор
    string_var Tmp      ; Промежуточная переменная
    string_var Valid    ; Искомая строка.
    Begin GameMode
      ; Задаём значение строке, в которой будем искать слово.
      Let Source := "Honey, I'm home."
      ; Задаём значение строке, которую будем искать.
      Let Valide := "home"
      ForEach Iterator - Source
        ; Если найдём в строке символ пробела, или символы пунктуации
        If Eval (Iterator == " " || Iterator == "," || Iterator == "." )
          ; Очистим промежуточную строку и
          sv_Destruct Tmp
          ; пропустим текущую фазу цикла.
          Continue
        ; Иначе
        Else
          ; Будем наращивать промежуточное значение символами из строки-источника.
          Let Tmp += Iterator
        EndIf
        ; Как только найдём то, что искали -
        If Eval (Tmp == Valid)
          ; - скажем об этом
          Print Valid + "was found"
          ; и прервём цикл.
          Break
        EndIf
      Loop
    End

    Операторы Break и Continue

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

    ScriptName SomeScript
    short Var
    Begin GameMode
      ; Зацикливаем : условие всегда истинно (бесконечный цикл)
      While 1
        ; Давайте выведем в консоль только парные числа: от двух до двадцати
        ; Увеличиваем значение переменной на единицу
        Let Var += 1
        ; Если значение нашей переменной превысило 20 -
        If Var > 20
          ; - прерываем цикл.
          Break
        EndIf
        ; Проверяем парность значения переменной
        If Var % 2 != 0
          ; Если число непарное – прерываем текущую фазу цикла и переходим к следующей.
          ; Все последующие операторы в теле цикла не будут выполнены в этой фазе.
          Continue
        EndIf
        ; Выводим значение переменной в консоль
        Print ToString Var
      Loop
    End

    Массивы и строки

    В OBSE появилось два принципиально новых типа данных, это массивы (array_var) и строковые переменные (string_var).

    Массивы являются неотъемлемым элементом любого языка программирования, так как представляют собою очень удобную и гибкую систему управления данными. Теперь они доступны и скриптерам для игры TES IV: Oblivion и серии игр Fallout 3.

    Что такое массивы?

    Массив – это область хранения данных, объединенная под общим именем. Доступ к элементам массива осуществляется по ключу. В зависимости от того, что представляет собою ключ, массивы делятся на индексные, вещественные и ассоциативные.

    Основными характеристиками массива есть его имя и размер. Массивы могут быть одномерными и многомерными, но, в основном, используются только одно-, двух- и реже трёхмерные массивы.

    Индексные массивы

    Массив, ключами элементов которого есть множество натуральных чисел, включая ноль, называется индексным.

    Для создания индексного массива следует вызвать функцию ar_Construct с аргументом Array:

    ScriptName ArrayExample
    array_var SomeArr
    Begin GameMode
      Let SomeArr := ar_Construct Array
      ; Теперь SomeArr – индексный массив
    End

    Теперь массив стал доступным для работы. Можем присваивать значение его элементам, умножать, делить, в общем обращаться как с обычными переменными.

    ScriptName ArrayExample
    
    array_var Arr1
    
    Begin GameMode
          Let Arr1 := ar_Construct Array
          Let Arr1[0] := "Этому элементу присвоено строковое значение"
          Let Arr1[1] := 107
    End

    Индексные массивы лучше подходят для использования в автоматизированных процессах, поскольку намного проще оперировать численными значениями ключей элементов массива.

    Для примера рассмотрим следующий скрипт, который создаёт десять элементов массива, присваивая им точно такие значения, будем использовать уже знакомый нам цикл While:

    ScriptName SomeScript
    array_var Arr
    short Tmp
    Begin GameMode
      Let Arr := ar_Construct Array
      While Tmp  10
        Let Arr[Tmp] := Tmp
        Let Tmp += 1
      Loop
    End

    Значение переменной Tmp меняется в диапазоне от нуля до девяти. В цикле создаются десять элементов массива, которым присваивается текущее значение переменной. Если вывести по порядку содержимое массива Arr, то мы увидим следующее:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    Как я уже говорил раньше, но, возможно, вы не поняли, отсчёт элементов индексных массивов всегда следует начинать с нуля. Т.е. первый элемент всегда нулевой, второй – первый, и так далее.

    Let Arr1 := ar_Construct Array
    Let Arr1[0] := 100500 ; Это правильная последовательность
    Let Arr1[1] := 100501 ; задания значения элементам массива
    Let Arr1[2] := 100502
    ; …
    ; --------------------------------------------------------
    Let Arr2 := ar_Construct Array
    Let Arr2[1] := 1 ; Это неправильная последовательность!
    Let Arr2[2] := 2 ; Элементы с индексами 1, 2, 3 не могут
    Let Arr2[3] := 3 ; существовать, так как элемент с
                     ; индексом 0 не существует

    Стоит помнить, что в OBSE массивы безразмерные, т.е. нет необходимости заранее задавать размер массива и чётко его придерживаться в процессе написания, а в последующем и обеспечивать это в процессе выполнения скриптов, но нельзя допускать выход индекса за диапазон его допустимых значений (размер массива).

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

    Ассоциативные массивы

    Массив, ключами элементов которого есть строковые константы, называется ассоциативным.

    Для создания ассоциативного массива следует вызвать функцию ar_Construct с аргументом StringMap:

    ScriptName ArrayExample
    array_var SomeArr
    Begin GameMode
      Let SomeArr := ar_Construct StringMap 
      ; SomeArr – ассоциативный массив 
    End

    Давайте создадим несколько элементов ассоциативного массива:

    ScriptName ArrayExample
    array_var SomeArr
    ref this
    Begin GameMode
      Let SomeArr := ar_Construct StringMap 
      ; SomeArr – ассоциативный массив 
      Let SomeArr["Reference"] := this := GetSelf 
      Let SomeArr["Name"] := this.GetName
      Let SomeArr["Health"] := this.GetAV Health
    End

    В случае ассоциативного массива совершенно не важна последовательность ключей массива.

    Запомните, что ключом ассоциативного массива в OBSE может быть только строковая константа, применение индексов недопустимо! Во всех остальных отношениях индексные и ассоциативные массивы одинаковы.

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

    Вещественные массивы

    Вещественные массивы ранее никогда не встречались мною ни в одном языке программирования, с которым приходилось работать, да и при подготовке этого учебника мне не удалось найти что-то подобное в литературе, поэтому я взял на себя право дать им такое название исходя из их сущности и, конечно, моего мнения. Если где ошибся – поправьте.

    Итак. Вещественным массивом будем назвать массив, ключами элементов которого есть множество вещественных чисел.

    Для вещественных массивов не важен порядок ключей, как для индексного массива.

    Создаётся вещественный массив функцией ar_Construct с аргументом Map.

    ScriptName ArrayExample
    array_var SomeArr
    Begin GameMode
      Let SomeArr               := ar_Construct Map 
      ; SomeArr – вещественный массив
      Let SomeArr[3.14]         := 100500
      Let SomeArr[5 ^ (-1 / 2)] := "trololo"
    End

    Как видите, число π допустимый ключ вещественного массива. Да что там, как вы можете себе представить элемент с ключом 1/√5 ? Элементарно! Ну вот, теперь можете ощущать простор для экспериментов в ваших работах.

    Если, а главное – для чего, вы будете использовать вещественные массивы – решите сами.

    Многомерные массивы

    До этого момента мы были знакомы только с одномерными массивами. Мы уже слегка коснулись темы размерности массивов на начале этой главы, теперь рассмотрим этот вопрос более подробно.

    Механизм создания многомерного массива в OBSE несколько отличается от подобной процедуры в большинстве из языков программирования, если вам приходилось ранее быть знакомым с каким-либо из них. Для увеличения размерности массива от выбранного элемента следует присвоить ему значение типа array_var, т.е. создать массив в массиве. Давайте посмотрим на примере, так будет понятнее:

    ScriptName ArrayExample
    array_var Arr
    Begin GameMode
      Let Arr       := ar_Construct Array
      Let Arr[0]    := ar_Construct Array
      Let Arr[0][0] := 1
      Let Arr[0][1] := 2
      Let Arr[0]    := ar_Construct Array
      Let Arr[0][0] := 1
      Let Arr[0][1] := 2
    End

    Получается, что мы сначала создали массив Arr, потом его нулевой элемент тоже сделали массивом. Нулевому элементу уже массива Arr[0] присвоили значение 1, а его первому элементу – значение 2. Затем тоже самое мы проделали с первым элементом массива Arr…

    Делаем всё точно также, если хотим создать трёхмерный и массив больших размерностей.

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

    • Элемент индексного массива может быть ассоциативным или вещественным массивом, и наоборот:
    ScriptName ArrayExample
    array_var Arr
    Begin GameMode
      Let Arr                 := ar_Construct Array
      Let Arr[0]              := ar_Construct StringMap
      Let Arr[0]["SomeElem"]  := 1
      Let Arr[0]["OtherElem"] := 2
    End
    • Массив может иметь неодинаковую размерность в направлении любого из своих элементов
    ScriptName ArrayExample
    array_var Arr
    Begin GameMode
      Let Arr          := ar_Construct Array
      Let Arr[0]       := ar_Construct Array
      Let Arr[0][0]    := ar_Construct Array
      Let Arr[0][0][0] := 36.6
      Let Arr[0][1]    := "ololo"
      Let Arr[1]       := Player
    End

    Строковые переменные

    Строковые перемененные имеют тип string_var.

    До семнадцатой версии OBSE требовалось определять строковые переменные при помощи функции sv_Construct, подобно массивам, но теперь делать этого не нужно.

    ScriptName StringVarExample
    string_var Str
    Begin GameMode
      Let Str := "Это значение строковой переменной"
    End

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

    ScriptName StringVarExample
    string_var Str1
    string_var Str2
    Begin GameMode
          Let Str1 := "Это значение одной строковой переменной"
          Let Str2 := "Это значение другой строковой переменной."
          Let Str1 += ". " + Str2
          Print Str1
          ; Str1 == "Это значение одной строковой переменной. Это значение другой строковой переменной."
    End

    К элементам строковых переменных можно обращаться так же, как и к элементам одномерного индексного массива. Каждый элемент строки есть один её символ.

    Let Str := "Привет"
    ; Str[0] == "П"
    ; Str[1] == "р"
    ; Str[2] == "и"
    ; Str[3] == "в"
    ; Str[4] == "е"
    ; Str[5] == "т"

    Изменение значения элемента строковой переменной приводит к его замене. В случае если мы заменяем один элемент более длинной строкой, то её значение просто подставляется в заменяемую строку начиная с выбранного положения.

    Let Str := "Здесь что-то написано"
    ;           ^    ^
    ;           0    5
    Let Str[5] := " может быть "
    ; Str == "Здесь может быть что-то написано"

    Массивы строк

    Массивы строк – обычный массив, может быть индексным, строковым или вещественным, произвольной размерности, элементу которого присвоено значение строковой переменной или строковой константы.

    ScriptName ArrayOfString
    array_var Arr
    string_var Str
    Begin GameMode
      Let Arr := ar_Construct Array
      Let Str := "Hello"
      Let Arr[0] := Str
      ; Arr[0][0] == "H"
      ; Arr[0][1] == "e"
      ; Arr[0][2] == "l"
      ; Arr[0][3] == "l"
      ; Arr[0][4] == "o"
    End

    Диапазоны элементов

    Диапазоны элементов доступны для массивов и строк. Для того чтобы задать диапазон элементов массива или строки, стоит написать их индексы через двоеточие: слева – стартовый индекс, справа – конечный. В случае с массивами вызов диапазона элементов создаёт их копию:

    ScriptName ArrayRange
    array_var Arr1
    array_var Arr2
    Begin GameMode
      Let Arr1 := Arr2 := ar_Construct Array
      Let Arr1[0] := 1
      Let Arr1[1] := 2
      Let Arr1[2] := 3
      Let Arr1[3] := 4
      Let Arr1[4] := 5
      Let Arr2 := Arr1[1:3]
      ; Arr2[0] == 2
      ; Arr2[1] == 3
      ; Arr2[2] == 4
      Let Arr1[0] := Arr1[0:4]
      ; Arr1[0][0] == 1
      ; Arr1[0][1] == 2
      ; Arr1[0][2] == 3
      ; Arr1[0][3] == 4
      ; Arr1[0][4] == 5
    End

    В случае со строками, вызов диапазона создаст подстроку от стартового до конечного элемента.

    ScriptName StringRange
    string_var Str
    Begin GameMode
      Let Str := "Здесь что-то написано"
      ;           ^   ^ ^    ^ ^      ^
      ;           0   4 6   11 13    20
      Print Str[0:4]   ; Здесь
      Print Str[6:11]  ; что-то
      Print Str[13:20] ; написано
    ; ----------------------------------------------------------------------
      Let Str := "Это строковая переменная"
      ;           ^ ^ ^       ^ ^        ^
      ;           0 2 4      12 14      23
      Let Str[4:23] := "СПАРТА!!!"
      ; Str == "Это СПАРТА!!!"
    End

    Пользовательские функции

    Что такое функция?

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

    Функция, которая не предусматривает возвращение результатов (не имеет значения), называется процедурой.

    Вызов функцией самой себя называется рекурсией.

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

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

    Пользовательские функции

    Для того, чтобы создать функцию, следует создать новый скрипт. Имя скрипта – это имя функции. На то, что этот скрипт есть функцией, указывает ключевое слово Function, которое используется в качестве типа блока с ключевым словом Begin. В скрипте функции может находиться только один блок Begin – End и только типа Function.

    Аргументы функции перечисляются в системных скобках, за ключевым словом Function, через запятую. Функция в OBSE может иметь до десяти аргументов, или не иметь их вовсе. Все аргументы функции – переменные, объявляются как в обычном скрипте.

    Значение функции определяется ключевым словом SetFunctionValue. Значением функции может служить как константа, так и переменная. Возврат из функции, имеющей или не имеющей значения, выполняется ключевым словом Return. Возврат из функции означает прекращение её выполнения. Помните, нельзя использовать Return в циклах!

    ScriptName FunctionName ; Имя функции
    type arg1    ; Первый аргумент функции
    type arg2    ; Второй аргумент функции
    …
    type arg10   ; Десятый аргумент функции
    type RetVal  ; Значение функции
    Begin Function {arg1, arg2, …, arg10}
      ; …
      SetFunctionValue RetVal ; Определение значения функции
      Return                  ; Возврат из функции
    End

    Для примера составим функцию, суммирующую два числа.

    ScriptName Sum
    float Num1 ; Первое число
    float Num2 ; Второе число
    Begin Function {Num1, Num2}
      Let Num1 += Num2       ; Находим сумму чисел
      SetFunctionValue Num1  ; Задаём возвращаемое значение
      Return                 ; Возвращаем результат
    End

    Для вызова пользовательской функции нужно использовать ключевое слово Call.

    ScriptName SomeScript
    float Var
    Begin GameMode
      ; Вызываем только что созданную намии функцию
      Let Var := Call Sum 10 20
      ; Var == 30
    End

    Как я уже говорил ранее, не все функции обязательно должны иметь аргументы и/или возвращать значение. Давайте составим пример такой функции, она будет просто добавлять сто Септимов в карман игрока.

    ScriptName ProcedureExample
    Begin Function {}
      Player.AddItem Gold001 100
    End

    В OBSE существует функция, которая позволяет определить скрипт (функция возвращает ссылку на него), из которого произошёл вызов функции, она называется GetCallingScript.

    ScriptName GetCallingScriptExample
    Begin Function {}
      If GetCallingScript == MyQuestScript
        SetFunctionValue 1
      Else
        SetFunctionValue 0
      EndIf
      Return
    End

    Функция GetCallingScript ничего не возвращает, если вызов функции произошёл без помощи оператора Call, или функция была использована как обработчик события.

    Обработчики событий

    Понятие заимствованное из .NET языков программирования. Обработчики событий являются функциями, которые автоматически вызываются программой, если происходит определённое событие. Все события в OBSE предопределены, с большинством из них вы должны быть уже знакомы, если ранее вам уже приходилось писать скрипты на родном скриптовом языке Oblivion-а.

    OnHit Цель:ref Атакующий:ref Происходит при нанесении атакующим урона цели
    OnHitWith Цель:ref Оружие:ref Происходит при нанесении оружием урона цели
    OnMagicEffectHit Цель:ref Магический_эффект:string (четырёхсимвольный код) Происходит при наложении магического эффекта на цель
    OnActorEquip Цель:ref Предмет:form Происходит при надевании предмета целью
    OnDeath Цель:ref Убийца:form Происходит при смерти цели
    OnMurder Цель:ref Убийца:form Происходит при убийстве цели убийцей
    OnKnockout Цель:ref   Происходит при нокаутировании цели
    OnActorUnequip Цель:ref Предмет:form Происходит при снятии предмета целью
    OnAlarm Trespass Заметивший:ref Нарушитель:ref Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил взлом.
    OnAlarm Steal Заметивший:ref Нарушитель:ref Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил кражу.
    OnAlarm Attack Заметивший:ref Нарушитель:ref Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил нападение.
    OnAlarm Pickpocket Заметивший:ref Нарушитель:ref Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил карманную кражу.
    OnAlarm Murder Заметивший:ref Нарушитель:ref Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил убийство.
    OnPackageChange Цель:ref Пакет:form Происходит при смене целью AI пакета
    OnPackageStart Цель:ref Пакет:form Происходит при старте целью AI пакета
    OnPackageDone Цель:ref Пакет:form Происходит при завершении целью AI пакета
    OnStartCombat Цель:ref Опонент:ref Происходит, когда цель нападает на опонента
    OnActivate Активатор:ref Цель:ref Происходит, когда цель активирует активатор
    OnVampireFeed NONE   Происходит, когда вампир перестаёт питаться
    OnSkillUp Код_навыка:int   Происходит при повышении навыка игрока
    OnScriptedSkillUp skillActorValueCode:int amount:int   Происходит перед изменением навыка игрока скриптовой командой
    OnDrinkPotion Цель:ref Зелье:form Происходит, когда цель принимает зелье
    OnEatIngredient Цель:ref Ингридиент:form Происходит, когда цель съедает ингридиент
    OnActorDrop Цель:ref Предмет:ref Происходит, когда цель выбрасывает/роняет предмет
    OnSpellCast Цель:ref Заклинание:form Происходит, когда цель произносит заклинание
    OnScrollCast Цель:ref свиток:form Происходит, когда цель читает заклинание со свитка
    OnFallImpact Цель:ref   Происходит, когда цель упадёт с опасной высоты, перед тем, как получить повреждение
    OnMapMarkerAdd Маркер:ref   Происходит при добавлении маркера на карту игрока
    OnHealthDamage Величина_повреждения:float Атакующий:ref Происходит перед тем, как актёр получит повреждение. Если актёр свалится с высоты, то аргумент «Атакующий» будет иметь нулевое значение. Для определения актёра получившего повреждение стоит использовать функцию GetSelf.
    OnCreateSpell Заклинание:ref   Происходит, когда игрок создаёт новое заклинание
    OnCreatePotion Зелье:ref Уникальное_ли:int Происходит, когда игрок приготовит новое зелье. Второй аргумент будет равен 1, если игрок впервые приготовил такое зелье, иначе он будет равен 0.
    OnEnchant Предмет:ref   Происходит, когда игрок зачаровывает предмет.
    OnAttack Цель:ref   Происходит, когда цель начинает проигрывать анимацию атаки или наложения заклинания.
    OnBowAttack Цель:ref   Происходит, когда цель начинает проигрывать анимацию стрельбы из лука.
    OnRelease Цель:ref   Происходит, когда цель заканчивает проигрывать анимацию атаки, стрельбы или наложения заклятья.
    OnBlock Цель:ref   Происходит, когда цель начинает проигрывать анимацию блокирования ударов.
    OnRecoil Цель:ref   Происходит, когда цель начинает проигрывать анимацию получения отдачи.
    OnStagger Цель:ref   Происходит, когда цель начинает проигрывать анимацию получения урона.
    OnDodge Цель:ref   Происходит, когда цель начинает проигрывать анимацию уклонения от атаки.
    LoadGame Имя_файла:string   Происходит при загрузке игры
    SaveGame Имя_файла:string   Происходит при сохранени игры
    PostLoadGame Игра_загружена_удачно:bool   Происходит после загрузки игры. Передаёт функции-обработчику события 1, если игра была загружена удачно, 0 - если нет
    ExitGame NONE   Происходит при выходе из игры
    ExitToMainMenu NONE   Происходит при выходе в главное меню
    QQQ NONE   Происходит при выходе из игры посредствам вызова консольной команды QQQ
    OnNewGame NONE   Происходит при старте новой игры
     

    Для того, чтобы задать обработчик события существует функция SetEventHandler, её синтаксис таков:

    (success:bool) SetEventHandler eventID:string functionScript:ref filter1:pair filter2:pair

    Где,

    • eventID – имя события, одно из тех, которые наведены в таблице;
    • functionScript – имя функция, которая будет использована для обработки события;
    • filter1 – фильтр первого аргумента функции-обработчика;
    • filter2 – фильтр второго аргумента функции-обработчика.

    Функция SetEventHandler возвращает единицу, если удалось назначить указанную функцию обработчиком события, или 0 – если нет.

    Аргументы filter1 и filter2 применяются для фильтрации аргументов передаваемых функции-обработчику события. Если их не задавать, то обработчику события будут передаваться все аргументы инициируемые определённым событием.

    Например, давайте составим функцию, которая будет использована для обработки события OnHit

    ScriptName OnHitHandlerExample
    ref Target
    ref Attacker
    
    Begin Function {Target, Attacker}
      Print Attacker.GetName + " attacked " + Target.GetName
    End

    И инициализируем её как обработчик события:

    ScriptName OnHitHandlerQuestScript
    Begin GameMode
      SetEventHandler "OnHit" OnHitHandlerExample
    End

    В таком случае, в ячейке, где разворачиваются игровые действия, на данный момент, любое событие OnHit инициализирует выполнение функции OnHitd]

    Если мы хотим, допустим, чтобы это событие выполнялось только когда атакуют игрока, следует установить фильтр первому аргументу, согласно описания события OnHit.

    ScriptName OnHitHandlerQuestScript
    Begin GameMode
      SetEventHandler "OnHit" OnHitHandlerExample "ref"::Player.GetSelf
    End

    Оператор :: создаёт пару "Ключ"::"Значение". Ключ должен иметь значение типа соответствующего аргумента согласно перечню событий.

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

    Если мы хотим избавиться от обработчика события, следует использовать функцию RemoveEventHandler. Её синтаксис аналогичен функции SetEventHandler.

    ScriptName OnHitHandlerQuestScript
    Begin GameMode
      RemoveEventHandler "OnHit" OnHitHandlerExample
    End

    [p]Если у нас есть несколько обработчиков события OnHit, то вызов функции RemoveEventd]

    ScriptName OnHitHandlerQuestScript
    Begin GameMode
      SetEventHandler "OnHit" OnHitHandlerExample
      SetEventHandler "OnHit" OnHitHandlerExample "ref"::Player.GetSelf
    ; ----------------------------------------------------------------------
      RemoveEventHandler "OnHit" OnHitHandlerExample
      ; Удалит все события OnHit
    ; ----------------------------------------------------------------------
      RemoveEventHandler "OnHit" OnHitHandlerExample "ref"::Player.GetSelf
      ; Удалит события только с соответствующим фильтром
    End

    [p]С помощью функции GetCurrentEventName, вызванной из функции-обработчика события, можно узнать имя текущего события. Это полезно, если одна функция используется как обработчик нескольких событий.

    ScriptName MultiEventHandlerFunction
    ref Arg1
    ref Arg2
    Begin Function {Arg1, Arg2}
      If Eval (GetCurrentEventName == "OnHit")
        Print Arg2.GetName + " атаковал " + Arg1.GetName
      ElseIf Eval (GetCurrentEventName == "OnActorEquip")
        Print "Игрок " + Arg1.GetName + " одел " + Arg2.GetName
      EndIf
    End
    • Нравится 22

    Обратная связь

    Рекомендуемые комментарии

    ArtemSH

    Опубликовано

    Супер. Спасибо, хоть начну понимать, как использовать хендлеры.

    вопрос остался по массивам: я так понял foreach по массиву не работает? тк выдает ошибку когда пишу нечто вроде foreach refVar <- ArrayVar



    Для публикации сообщений создайте учётную запись или авторизуйтесь

    Вы должны быть пользователем, чтобы оставить комментарий

    Создать аккаунт

    Зарегистрируйте новый аккаунт в нашем сообществе. Это очень просто!

    Регистрация нового пользователя

    Войти

    Уже есть аккаунт? Войти в систему.

    Войти

×
×
  • Создать...