TESAll.Ru OBSE В МАССЫ YourBunnyWrote 2011 1 СОДЕРЖАНИЕ От автора 3 1. Глава 1. ОПЕРАТОРЫ 4 1.1. Оператор присваивания 4 1.2. Проверка истинности 5 1.3. Обработка исключений 6 1.4. Операторы ToString и ToNumber 6 1.5. Оператор TypeOf 7 2. Глава 2. ОПЕРАТОРЫ ЦИКЛА 8 2.1. Операторы безусловного перехода Goto и Label 8 2.2. Цикл с предусловием While 9 2.3. Цикл перебора массивов ForEach 9 2.4. Операторы Break и Continue 3. Глава 3. МАССИВЫ И СТРОКИ 12 13 3.1. Что такое массивы? 13 3.2. Индексные массивы 13 3.3. Ассоциативные массивы 15 3.4. Вещественные массивы 16 3.5. Многомерные массивы 17 3.6. Строковые переменные 18 3.7. Массивы строк 19 3.8. Диапазоны элементов 20 4. Глава 4. ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ 21 4.1. Что такое функция? 21 4.2. Пользовательские функции 21 4.3. Обработчики событий 23 5. Глава 5. СПЕЦИАЛЬНЫЕ ФУНКЦИИ OBSE 5.1. Функции для работы с массивами 29 29 2 От автора Уважаемые читатели, в этом небольшом пособии я попытался объяснить суть всех нововведений, ставших доступными скриптерам игры TES IV: Oblivion с появлением OBSE. Я не стану описывать все функции ввиду их значительного количества, речь пойдёт лишь об основных конструкциях. Эта книга ориентирована на людей, ранее знакомых со скриптовым языком для игры TES V: Oblivion; если вы к таким не относитесь, то для начала советовал бы вам ознакомится с учебником по скриптовому языку, написанному командой OSFM. Я пытался изложить материал в учебнике последовательно, так, чтобы при чтении последующего материала вы могли понимать всё, что там написано, но, признаться, далеко не везде в этой книге получилось достичь этого результата, потому, возможно, вам придётся переходить от одной главы к другой для выяснения некоторых нюансов. Конечно, виной тому мой скромный авторский опыт, но обещаю, что учту все пожелания читателей и постараюсь исправить этот недочёт в последующих изданиях этого учебника. Выражаю благодарность команде Silverlock.org: Иену Паттерсону, Стефану Абелю и Паулю Коннелли за их огромный труд и результаты этих трудов: расширители скриптового языка для игр TES IV: Oblivion, Fallout 3 и Fallout New Vegas. Спасибо сообществу TESAll.Ru за интересно проведенное время в общении на форуме и за гигантский труд членов команды сайта по локализации модов и поддержке русскоязычного сообщества почитателей серии игр TES и Fallout. Огромная благодарность Хельге Skaldi за приведение всего этого, мною написанного, в соответствие с правилами русского языка. Ещё раз тебе спасибо! Уважаемые читатели, буду рад слышать все предложения и замечания касательно данной книги. Найти меня можно здесь. Все права на приведенные в этой книге торговые марки принадлежат их владельцам. Все права на этот учебник оставляю за собой. Запрещена публикация и/или воспроизведение материала этого учебника целиком или какой-либо его части без разрешения автора, т.е. меня :р 3 Глава 1. ОПЕРАТОРЫ Операторы присваивания := Присвоить += Прибавить и присвоить -+ Отнять и присвоить *= Умножить и присвоить /= Разделить и присвоить ^= Возвести в степень и присвоить Логические операторы || Логическое ИЛИ && Логическое И < Строго меньше > Строго больше >= Больше или равно <= Меньше или равно == Равно != Не равно ! Логическое отрицание (НЕТ) Математические операторы + Прибавление Вычитание * Умножение / Деление % Остаток от деления ^ Возведение в степень Побитовые операторы >> Побитовый сдвиг влево << Побитовый сдвиг вправо | Побитовое ИЛИ & Побитовое И ! Побитовое НЕТ Операторы для работы с данными :: Пара «ключ»::»значение» -> Оператор обращения к элементу массива Массив -> Элемент, тоже, что и Массив[Элемент]. $ Строка # Номер Оператор присваивания Для всех новых конструкций языка стоит использовать новый оператор присваивания Let вместо стандартного Set. Синтаксис: Let <Имя_переменной> := <значение> Например: 1. Let a := 10 Что эквивалентно: 4 1. Set a to 10 Также можно использовать вместо знака присваивания знаки: прибавить и присвоить (+=), отнять и присвоить (-=), умножить и присвоить (*=), поделить и присвоить (/=), возвести в степень и присвоить (^=): 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. Let a := 10 ; Set a to Let b := 2 ; Set b to Let c := a + b ; Set c to ; c == 12 Let c += b ; Set c to ; c == 14 Let c -= a ; Set c to ; c == 4 Let c *= a ; Set c to ; c == 40 Let c /= b ; Set c to ; c == 20 Let c ^= b ; Set c to ; c == 400 10 2 a + b c + b c - a c * a c / b c ^ b Проверка истинности В связи с тем, что некоторые новые конструкции языка, появившиеся с OBSE, некорректно работают со стандартными конструкциями, был введён новый оператор Eval для использования вместе с оператором If. Так следующий фрагмент кода не будет работать: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. ScriptName SomeScript array_var SomeArr Begin GameMode Let SomeArr := ar_Construct Array Let SomeArr[0] := GetSelf If SomeArr[0] == Player ;… EndIf End Его стоит переписать следующим образом: 1. ScriptName SomeScript 2. 3. array_var SomeArr 4. 5. Begin GameMode 6. Let SomeArr := ar_Construct Array 7. Let Arr[0] := GetSelf 8. If Eval (SomeArr[0] == Player) 5 9. 10. 11. 12. 13. End ;… EndIf Обработка исключений В OBSE существует оператор TestExpr, он служит для проверки исключений. Что такое исключение? Исключение - это события, приводящие к ошибке. Этим оператором стоит пользоваться, чтобы избежать ошибок в ходе работы скрипта. Например, к заранее непредвиденным ошибкам может приводить выход индекса массива за диапазон допустимых значений, продемонстрируем это в следующем примере: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 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 Операторы ToString и ToNumber применяются для преобразования строк в числа и чисел в строки соответственно. 1. 2. 3. 4. Print ToString 10 ; Оператор ToString имеет сокращение: $ ; Следующая строка эквивалентна предыдущей Print $10 Оператор ToNumber и его сокращение # преобразовывает строку, содержащую численные символы, в численное значение. 1. Let a += ToNumber “10” 2. ; a == 10 6 Возможно преобразование строки, содержащей шестнадцатеричное значение, если она начинается с символов “0x” или установлен флаг bIsHex 1. 2. 3. 4. Let ; a Let ; a a += ToNumber “0xFF” == 255 a += ToNumber “abc” 1 == 2748 Оператор TypeOf Оператор TypeOf возвращает строку с типом данных. Возможные значения: “String” “Number” “Form” “Array” “Map” “StringMap” Строка Число Форма Индексный массив Вещественный массив Ассоциативный массив 7 Глава 2. ОПЕРАТОРЫ ЦИКЛА В родном скриптовом языке Oblivion-а операторов цикла не существовало, по той простой причине, что сами скрипты выполняются довольно часто, и дополнительно зацикливаться на выполнении какого-либо участка кода не было нужды, но с появлением массивов и строковых переменных в OBSE оказалось невозможным обработать весь массив, например, за один проход. Понятие цикла не таит в себе ничего невероятного, это просто обусловленное циклическое повторение операторов замкнутых в тело цикла. Обратите внимание на то, что в теле операторов цикла (это не касается операторов безусловного перехода) нельзя использовать ключевое слово Return. Операторы безусловного перехода Goto и Label Операторы безусловного перехода служат для управления ходом выполнением скрипта. Оператор Label создаёт метку в том месте, где он стоит, к которой можно вернуться с помощью оператора Goto. Операторы безусловного перехода не являются операторами цикла по сути, но могут быть использованы с этой целью, поэтому они описаны в этой главе. 1. ScriptName SomeScript 2. 3. Begin GameMode 4. 5. ; … 6. 7. Label 1 ; Метка 1 8. 9. ; … 10. 11. Label 2 ; Метка 2 12. 13. If ; Если что-то произошло 14. Goto 1 ; Переходим к метке 1 15. Else 16. Goto 2 ; Иначе – к метке 2 17. EndIf 18. 19. End Несмотря на то, что операторы безусловного перехода являются несложной конструкцией, их использование не рекомендовано и требует повышенного внимания. Сами по себе безусловные переходы не есть ошибками с точки зрения выполнимости кода, но неправильное их использование может приводить к неявным ошибкам и неточностям в работе скрипта, в частности, к зацикливанию. Пожалуй, стоит упомянуть, что операторы Label и Goto имеют псевдонимы SaveIP и RestoreIP соответственно, какие из них и в какой комбинации использовать – зависит только от вас. 8 Цикл с предусловием While Синтаксис: 1. ScriptName SomeScript 2. 3. Begin GameMode 4. 5. While ; Условие 6. ; Тело цикла 7. Loop 8. 9. End Последовательность операторов заключённая в теле цикла будет выполняться до тех пор, пока условие будет истинным. Следующий скрипт просто выводит в консоль числа от одного до десяти. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. ScriptName SomeScript short i Begin GameMode While i < 10 Let i += 1 Print ToString i Loop End Цикл перебора массивов ForEach Оператор ForEach специально предназначен для перебора массивов, строк и содержимого контейнеров элемент за элементом. За каждый проход цикла переменная-итератор получает значение следующего элемента итерируемой переменой. В случае с контейнерами переменная-итератор должна иметь тип ref. За каждый проход цикла она будет получать значение ссылки на следующий объект выбранного контейнера. Следующий скрипт находит количество непустых камней душ в инвентаре игрока. 1. 2. 3. 4. 5. 6. 7. 8. ScriptName GetPlayerSoulGemCount ref Iterator short SoulGemCount Begin ScriptEffectStart ForEach Iterator <- GetSelf If Iterator.IsSoulGem && Iterator.GetCurrentSoulLevel > 0 9 9. 10. 11. 12. 13. End Let SoulGemCount += Iterator.GetRefCount EndIf Loop Print ToString SoulGemCount Если цикл используется для перебора массивов, то переменная-итератор должна быть типа array_var. За каждый проход цикла два элемента массиваитератора (переменной-итератора), “key” и “value” получают следующую пару значений «ключ – значение» итерируемого массива. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. ScriptName ArrayForEachScript array_var array_var array_var array_var Arr StrMp Mp 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 Значение 10 44. Loop 45. ; ---------------------------------------------------------------------46. Let Mp[0.15] := 100500 47. Let Mp[3.14] := 100501 48. Let Mp[6.21] := 100502 49. ForEach Iterator <- Mp 50. ; Первый проход скрипта 51. ; Iterator[“key”] == 0.15 Ключ 52. ; Iterator[“value”] == 100500 Значение 53. ; Второй проход скрипта 54. ; Iterator[“key”] == 3.14 Ключ 55. ; Iterator[“value”] == 100500 Значение 56. ; Третий проход скрипта 57. ; Iterator[“key”] == 6.21 Ключ 58. ; Iterator[“value”] == 100500 Значение 59. Loop 60. End При переборе строковых переменных, переменная-итератор должна быть типа string_var. За каждый проход скрипта переменная-итератор будет получать значение каждого последующего символа итерируемой строковой переменой. Длина переменной-итератора - всегда один символ. Для примера составим небольшой скрипт, который будет искать заданное слово в строке. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 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 11 Операторы Break и Continue Эти операторы используются в теле операторов цикла и служат для прерывания всего цикла или только текущей фазы, соответственно. Возможно, сначала не совсем понятно, для чего они нужны, но давайте посмотрим на конкретном примере. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. ScriptName SomeScript short Var Begin GameMode While 1 ; Зацикливаем : условие всегда истинно (бесконечный цикл) ; Давайте выведем в консоль только парные числа: ; от двух до двадцати Let Var += 1 ; Увеличиваем значение переменной на единицу If Var > 20 ; Если значение нашей переменной ; превысило 20 Break ; - прерываем цикл. EndIf If Var % 2 != 0 ; Проверяем парность значения переменной Continue ; Если число непарное – прерываем текущую ; фазу цикла и переходим к следующей. Все последующие операторы в теле ; цикла не будут выполнены в этой фазе. EndIf Print ToString Var ; Выводим значение переменной в консоль Loop End 12 Глава 3. МАССИВЫ И СТРОКИ В OBSE появилось два принципиально новых типа данных, это массивы (array_var) и строковые переменные (string_var). Массивы являются неотъемлемым элементом любого языка программирования, так как представляют собою очень удобную и гибкую систему управления данными. Теперь они доступны и скриптерам для игры TES IV: Oblivion и серии игр Fallout 3. Что такое массивы? Массив – это область хранения данных, объединенная под общим именем. Доступ к элементам массива осуществляется по ключу. В зависимости от того, что представляет собою ключ, массивы делятся на индексные, вещественные и ассоциативные. Основными характеристиками массива есть его имя и размер. Массивы могут быть одномерными и многомерными, но, в основном, используются только одно-, двух- и реже трёхмерные массивы. Если представить себе массив в виде таблицы, то одномерный массив будет иметь вид: Индекс Значение 0 1 2 … n 0 1 2 … m А двумерный: Индекс 0 1 2 … n Массив задаётся как переменная типа array_var и создаётся функцией ar_Construct, которая должна иметь один из следующих аргументов: StringMap Map Array Ассоциативный массив Вещественный массив Индексный массив Индексные массивы Массив, ключами элементов которого есть множество натуральных чисел, включая ноль, называется индексным. Для создания индексного массива следует вызвать функцию ar_Construct с аргументом Array: 1. ScriptName ArrayExample 2. 13 3. array_var SomeArr 4. 5. Begin GameMode 6. Let SomeArr := ar_Construct Array ; Теперь SomeArr – индексный 7. ; массив 8. End Теперь массив стал доступным для работы. Можем присваивать значение его элементам, умножать, делить, в общем, обращаться как с обычными переменными. 1. 2. 3. 4. 5. 6. 7. 8. 9. ScriptName ArrayExample array_var Arr1 Begin GameMode Let Arr1 := ar_Construct Array Let Arr1[0] := “Этому элементу присвоено строковое значение” Let Arr1[1] := 107 End Индексные массивы лучше подходят для использования в автоматизированных процессах, поскольку намного проще оперировать численными значениями ключей элементов массива. Для примера рассмотрим следующий скрипт, который создаёт десять элементов массива, присваивая им точно такие значения, будем использовать уже знакомый нам цикл While: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 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 14 5 6 7 8 9 Как я уже говорил раньше, но, возможно, вы не поняли, отсчёт элементов индексных массивов всегда следует начинать с нуля. Т.е. первый элемент всегда нулевой, второй – первый и так далее. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 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: 1. 2. 3. 4. 5. 6. 7. 8. ScriptName ArrayExample array_var SomeArr Begin GameMode Let SomeArr := ar_Construct StringMap ; SomeArr – ассоциативный массив End Давайте создадим несколько элементов ассоциативного массива: 1. ScriptName ArrayExample 2. 3. array_var SomeArr 4. ref this 15 5. 6. Begin GameMode 7. Let SomeArr := ar_Construct StringMap 8. ; SomeArr – ассоциативный массив 10. Let SomeArr[“Reference”] := this := GetSelf 11. Let SomeArr[“Name”] := this.GetName 12. Let SomeArr[“Health”] := this.GetAV Health 13. End В случае ассоциативного массива совершенно не важна последовательность ключей массива. Запомните, что ключом ассоциативного массива в OBSE может быть только строковая константа, применение индексов недопустимо! Во всех остальных отношениях индексные и ассоциативные массивы одинаковы. Ассоциативные массивы, безусловно, проще с точки зрения человеческого восприятия, но ими сложнее манипулировать в автоматизированных операциях программ, опять же, с точки зрения нашего восприятия. Вещественные массивы Вещественные массивы ранее никогда не встречались мною ни в одном языке программирования, с которым приходилось работать, да и при подготовке этого учебника мне не удалось найти что-то подобное в литературе, поэтому я взял на себя право дать им такое название исходя из их сущности и, конечно, моего мнения. Если где ошибся – поправьте. Итак. Вещественным массивом будем называть массив, ключами элементов которого есть множество вещественных чисел. Для вещественных массивов не важен порядок ключей, как для индексного массива. Создаётся вещественный массив функцией ar_Construct с аргументом Map. 1. 2. 3. 4. 5. 6. 7. 8. 10. 11. 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 Как видите, число - допустимый ключ вещественного массива. Да что там, как вы можете себе представить элемент с ключом? Элементарно! Ну вот, теперь можете ощущать простор для экспериментов в ваших работах. 16 Если, а главное – для чего, вы будете использовать вещественные массивы – решите сами. Многомерные массивы До этого момента мы были знакомы только с одномерными массивами. Мы уже слегка коснулись темы размерности массивов на начале этой главы, теперь рассмотрим этот вопрос более подробно. Механизм создания многомерного массива в OBSE несколько отличается от подобной процедуры в большинстве из языков программирования, если вам приходилось ранее быть знакомым с каким-либо из них. Для увеличения размерности массива от выбранного элемента следует присвоить ему значение типа array_var, т.е. создать массив в массиве. Давайте посмотрим на примере, так будет понятнее: 1. 2. 3. 4. 5. 6. 7. 8. 10. 11. 12. 13. 14. 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… Думаю, что все наши действия будут нагляднее видны на древовидной диаграмме ниже: Arr [0] [1] [0] [1] [0] [1] Arr == Array Arr[0] == Array Arr[0][0] == 1 Arr[0][1] == 2 Arr[1] == Array Arr[0][0] == 1 Arr[0][1] == 2 Делаем всё точно так же, если хотим создать трёхмерный и массив больших размерностей. Из такого принципа построения массивов следует, что: Элемент индексного массива может быть ассоциативным или вещественным массивом, и наоборот: 17 1. 2. 3. 4. 5. 6. 7. 8. 10. 11. ScriptName ArrayExample 1. 2. 3. 4. 5. 6. 7. 8. 10. 11. 12. 13. 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 Массив может иметь неодинаковую размерность в направлении любого из своих элементов 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, подобно массивам, но теперь делать этого не нужно. 1. 2. 3. 4. 5. 6. 7. ScriptName StringVarExample string_var Str Begin GameMode Let Str := “Это значение строковой переменной” End К значению строковой переменной всегда можно прибавлять значение другой строковой переменной или строковой константы. 1. 2. 3. 4. 5. 6. 7. 8. ScriptName StringVarExample string_var Str1 string_var Str2 Begin GameMode Let Str1 := “Это значение одной строковой переменной” Let Str2 := “Это значение другой строковой переменной.” 18 9. 10. 11. Let Str1 += “. ” + Str2 Print Str1 ; Str1 == “Это значение одной строковой переменной. Это значение другой строковой переменной.” 12. End К элементам строковых переменных можно обращаться так же, как и к элементам одномерного индексного массива. Каждый элемент строки есть один её символ. 1. 2. 3. 4. 5. 6. 7. Let Str := “Привет” ; Str[0] == “П” ; Str[1] == “р” ; Str[2] == “и” ; Str[3] == “в” ; Str[4] == “е” ; Str[5] == “т” Изменение значения элемента строковой переменной приводит к его замене. В случае, если мы заменяем один элемент более длинной строкой, то её значение просто подставляется в заменяемую строку, начиная с выбранного положения. 1. 2. 3. 4. 5. Let Str := “Здесь что-то написано” ; ^ ^ ; 0 5 Let Str[5] := “ может быть ” ; Str == “Здесь может быть что-то написано” Массивы строк Массивы строк – обычный массив, может быть индексным, строковым или вещественным, произвольной размерности, элементу которого присвоено значение строковой переменной или строковой константы. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 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 19 Диапазоны элементов Диапазоны элементов доступны для массивов и строк. Для того чтобы задать диапазон элементов массива или строки, стоит написать их индексы через двоеточие: слева – стартовый индекс, справа – конечный. В случае с массивами вызов диапазона элементов создаёт их копию: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 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 В случае со строками, вызов диапазона создаст подстроку от стартового до конечного элемента. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 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 20 Глава 4. ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ Принципиально новое понятие в рамках скриптового языка Oblivion-а. Что такое функция? В программировании, функция – это именуемая часть программы, которая непосредственно не выполняется, но доступна из любой части программы, в том числе и для самой себя, которая может инициировать её выполнения посредствам обращения (вызова) с передачи ей начальных параметров (аргументов функции). По окончании выполнения функция возвращает результат (значение функции) вызвавшей её части программы. Функция, которая не предусматривает возвращение результатов (не имеет значения), называется процедурой. Вызов функцией самой себя называется рекурсией. Функция, которая в качестве аргументов принимает значение другой функции непосредственно, называется суперпозицией. Не столь важное понятие в программировании, но привожу для общего развития. Функция – очень удобный механизм разделения типичных, востребованных программных действий, делающий их доступным любому скрипту вашего плагина. Пользовательские функции Для того чтобы создать функцию, следует создать новый скрипт. Имя скрипта – это имя функции. На то, что этот скрипт есть функцией, указывает ключевое слово Function, которое используется в качестве типа блока с ключевым словом Begin. В скрипте функции может находиться только один блок Begin – End и только типа Function. Аргументы функции перечисляются в системных скобках, за ключевым словом Function, через запятую. Функция в OBSE может иметь до десяти аргументов, или не иметь их вовсе. Все аргументы функции – переменные, объявляются как в обычном скрипте. Значение функции определяется ключевым словом SetFunctionValue. Значением функции может служить как константа, так и переменная. Возврат из функции, имеющей или не имеющей значения, выполняется ключевым словом Return. Возврат из функции означает прекращение её выполнения. Помните, нельзя использовать Return в циклах! 1. 2. 3. 4. 5. 6. 7. 8. ScriptName FunctionName ; Имя функции type type … type type arg1 arg2 ; Первый аргумент функции ; Второй аргумент функции arg10 RetVal ; Десятый аргумент функции ; Значение функции 21 9. Begin Function {arg1, arg2, …, arg10} 10. ; … 11. SetFunctionValue RetVal ; Определение значения функции 12. Return ; Возврат из функции 13. End Для примера составим функцию, суммирующую два числа. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. ScriptName Sum float Num1 ; Первое число float Num2 ; Второе число Begin Function {Num1, Num2} Let Num1 += Num2 SetFunctionValue Num1 Return End ; Находим сумму чисел ; Задаём возвращаемое значение ; Возвращаем результат Для вызова пользовательской функции нужно использовать ключевое слово Call. 1. 2. 3. 4. 5. 6. 7. 8. ScriptName SomeScript float Var Begin GameMode Let Var := Call Sum 10 20 ; Var == 30 End Как я уже говорил ранее, не все функции обязательно должны иметь аргументы и/или возвращать значение. Давайте составим пример такой функции, она будет просто добавлять сто Септимов в карман игрока. 1. ScriptName ProcedureExample 2. 3. Begin Function {} 4. Player.AddItem Gold001 100 5. End В OBSE существует функция, которая позволяет определить скрипт (функция возвращает ссылку на него), из которого произошёл вызов функции, она называется GetCallingScript. 1. ScriptName GetCallingScriptExample 2. 3. Begin Function {} 4. If GetCallingScript == MyQuestScript 5. SetFunctionValue 1 6. Else 22 7. 8. 9. 10. End SetFunctionValue 0 EndIf Return Функция GetCallingScript ничего не возвращает, если вызов функции произошёл без помощи оператора Call или функция была использована как обработчик события. Обработчики событий Понятие заимствованное из .NET языков программирования. Обработчики событий являются функциями, которые автоматически вызываются программой, если происходит определённое событие. Все события в OBSE предопределены, с большинством из них вы должны быть уже знакомы, если ранее вам уже приходилось писать скрипты на родном скриптовом языке Oblivion-а: Имя события Аргумент события : Тип Первый аргумент Второй аргумент OnHit Цель:ref Атакующий:ref OnHitWith Цель:ref Оружие:ref OnMagicEffectH it Цель: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 Описание Происходит при нанесении атакующим урона цели Происходит при нанесении оружием урона цели Происходит при наложении магического эффекта на цель Происходит при надевании предмета целью Происходит при смерти цели Происходит при убийстве цели убийцей Происходит при нокаутировании цели Происходит при снятии предмета целью Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил взлом. Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил кражу. Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил нападение. Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил карманную кражу. 23 OnAlarm Murder Заметивший:ref Нарушитель:ref OnPackageChang e Цель:ref Пакет:form OnPackageStart Цель:ref Пакет:form OnPackageDone Цель:ref Пакет:form OnStartCombat Цель:ref Опонент:ref OnActivate Активатор:ref Цель:ref OnVampireFeed NONE OnSkillUp Код_навыка:int OnScriptedSkil lUp skillActorValue Code:int amount:int OnDrinkPotion Цель:ref Зелье:form OnEatIngredien t Цель:ref Ингридиент:form OnActorDrop Цель:ref Предмет:ref OnSpellCast Цель:ref Заклинание:form OnScrollCast Цель:ref свиток:form OnFallImpact Цель:ref OnMapMarkerAdd Маркер:ref OnHealthDamage Величина_повреж дения:float OnCreateSpell Заклинание:ref OnCreatePotion Зелье:ref Атакующий:ref Уникальное_ли:i nt Происходит, когда заметивший актёр поднимает тревогу, если нарушитель совершил убийство. Происходит при смене целью AI пакета Происходит при старте целью AI пакета Происходит при завершении целью AI пакета Происходит, когда цель нападает на оппонента Происходит, когда цель активирует активатор Происходит, когда вампир перестаёт питаться Происходит при повышении навыка игрока Происходит перед изменением навыка игрока скриптовой командой Происходит, когда цель принимает зелье Происходит, когда цель съедает ингредиент Происходит, когда цель выбрасывает/роняет предмет Происходит, когда цель произносит заклинание Происходит, когда цель читает заклинание со свитка Происходит, когда цель упадёт с опасной высоты, перед тем, как получить повреждение Происходит при добавлении маркера на карту игрока Происходит перед тем, как актёр получит повреждение. Если актёр свалится с высоты, то аргумент «Атакующий» будет иметь нулевое значение. Для определения актёра получившего повреждение стоит использовать функцию GetSelf. Происходит, когда игрок создаёт новое заклинание Происходит, когда игрок приготовит новое зелье. Второй аргумент будет равен 1, если игрок впервые приготовил такое 24 зелье, иначе он будет равен 0. OnEnchant Предмет:ref OnAttack Цель:ref OnBowAttack Цель:ref OnRelease Цель:ref OnBlock Цель:ref OnRecoil Цель:ref OnStagger Цель:ref OnDodge Цель:ref LoadGame SaveGame Имя_файла:strin g Имя_файла:strin g PostLoadGame Игра_загружена_ удачно:bool ExitGame NONE ExitToMainMenu NONE QQQ NONE OnNewGame NONE Происходит, когда игрок зачаровывает предмет. Происходит, когда цель начинает проигрывать анимацию атаки или наложения заклинания. Происходит, когда цель начинает проигрывать анимацию стрельбы из лука. Происходит, когда цель заканчивает проигрывать анимацию атаки, стрельбы или наложения заклятья. Происходит, когда цель начинает проигрывать анимацию блокирования ударов. Происходит, когда цель начинает проигрывать анимацию получения отдачи. Происходит, когда цель начинает проигрывать анимацию получения урона. Происходит, когда цель начинает проигрывать анимацию уклонения от атаки. Происходит при загрузке игры Происходит при сохранении игры Происходит после загрузки игры. Передаёт функцииобработчику события 1, если игра была загружена удачно, 0 - если нет Происходит при выходе из игры Происходит при выходе в главное меню Происходит при выходе из игры посредствам вызова консольной команды QQQ Происходит при старте новой игры Для того, чтобы задать обработчик события, существует функция SetEventHandler, её синтаксис таков: 25 1. (success:bool) SetEventHandler eventID:string functionScript:ref filter1:pair filter2:pair Где, eventID – имя события, одно из тех, которые наведены в таблице; functionScript – имя функция, которая будет использована для обработки события; filter1 – фильтр первого аргумента функции-обработчика; filter2 – фильтр второго аргумента функции-обработчика. Функция SetEventHandler возвращает единицу, если удалось назначить указанную функцию обработчиком события, или 0 – если нет. Аргументы filter1 и filter2 применяются для фильтрации аргументов, передаваемых функции-обработчику события. Если их не задавать, то обработчику события будут передаваться все аргументы, инициируемые определённым событием. Например, давайте составим функцию, которая будет использована для обработки события OnHit: 1. 2. 3. 4. 5. 6. 7. 8. ScriptName OnHitHandlerExample ref Target ref Attacker Begin Function {Target, Attacker} Print Attacker.GetName + " attacked " + Target.GetName End И инициализируем её как обработчик события: 1. ScriptName OnHitHandlerQuestScript 2. 3. Begin GameMode 4. SetEventHandler “OnHit” OnHitHandlerExample 5. End В таком случае, в ячейке, где разворачиваются игровые действия, на данный момент, любое событие OnHit инициализирует выполнение функции OnHitHandlerExample. Т.е. если любой NPC или Creature нанесёт урон любому другому NPC или Creature в ячейке, в которой находится игрок, будет вызвана функция OnHitHandlerExample, которой будут переданы ссылки атакуемого и атакующего. Если мы хотим, допустим, чтобы это событие выполнялось только когда атакуют игрока, следует установить фильтр первому аргументу, согласно описанию события OnHit. 1. ScriptName OnHitHandlerQuestScript 2. 3. Begin GameMode 26 4. 5. End SetEventHandler “OnHit” OnHitHandlerExample “ref”::Player.GetSelf Оператор :: создаёт пару “Ключ”::”Значение”. Ключ должен иметь значение типа соответствующего аргумента, согласно перечню событий. Независимо от того, что первый аргумент функции-обработчика теперь может принимать только одно значение, и мы заведомо знаем об этом, функцияобработчик всё равно должна иметь два аргумента. Если мы хотим избавиться от обработчика события, следует использовать функцию RemoveEventHandler. Её синтаксис аналогичен функции SetEventHandler. 1. ScriptName OnHitHandlerQuestScript 2. 3. Begin GameMode 4. RemoveEventHandler “OnHit” OnHitHandlerExample 5. End Если у нас есть несколько обработчиков события OnHit, то вызов функции RemoveEventHandler без фильтров приведёт к удалению их всех. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. ScriptName OnHitHandlerQuestScript Begin GameMode SetEventHandler “OnHit” OnHitHandlerExample SetEventHandler “OnHit” OnHitHandlerExample “ref”::Player.GetSelf ; ---------------------------------------------------------------------RemoveEventHandler “OnHit” OnHitHandlerExample ; Удалит все события OnHit ; ---------------------------------------------------------------------RemoveEventHandler “OnHit” OnHitHandlerExample “ref”::Player.GetSelf 11. ; Удалит события только с соответствующим фильтром 12. End С помощью функции GetCurrentEventName, вызванной из функцииобработчика события, можно узнать имя текущего события. Это полезно, если одна функция используется как обработчик нескольких событий. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 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 27 12. End 28 Глава 5. СПЕЦИАЛЬНЫЕ ФУНКЦИИ OBSE Функции для работы с массивами ar_Construct – создаёт массив. Синтаксис: (array_var) ar_Construct arrayType:string Где, arrayType – тип массива: Array – индексный массив Map – вещественный массив StringMap – ассоциативный массив ar_Size – возвращает количество элементов массива. Синтаксис: (size:int) ar_Size array:array_var Где, array – массив, размер которого стоит определить. ar_Dump – отладочная функция. Выводит в консоль все пары “Ключ”:”Значение” для выбранного массива. Синтаксис: (nothing) ar_Dump array:array_var Где, array – массив. ar_DumpID – отладочная функция. Делает то же самое, что и ar_Dump, за исключением того, что принимает ID массива вместо array_var . (nothing) ar_Dump array:array_var Где, array – массив. ar_Erase – удаляет элемент массива, диапазон элементов или все элементы массива. Возвращает количество удалённых элементов. (numRemoved:int) ar_Erase target:array index:arrayIndex (numRemoved:int) ar_Erase target:array range:slice Где, target – массив. index – индекс элемента массива, который стоит удалить. Если этот аргумент пропущен, то будут удалены все элементы массива. Индекс следующих за удалённым элементов будет смещён на единицу. range – стартовый индекс диапазона элементов, которые нужно удалить. slice – конечный индекс диапазона элементов, которые нужно удалить. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Let Arr := ar_Construct Array Let StrMp := ar_Construct StringMap Let Mp := ar_Construct Map While Tmp < 10 Let Arr[Tmp] := StrMp[“Elem” + $Tmp] := Mp[Tmp / 10] := Tmp Loop ; ar_Size Arr == ar_Size StrMp == ar_Size Mp == 10 ar_Erase Arr 5 ; ar_Size Arr == 9 ar_Erase StrMp “Elem3”:“Elem6” ; ar_Size StrMp == 6 ar_Erase Mp ; ar_Size Mp == 0 ar_Find – ищет значение в массиве. Возвращает ключ первого попавшегося элемента массива, значение которого соответствует искомому. Если искомому значению не соответствует ни один элемент массива, то возвращает пустую строку, если поиск происходил в ассоциативном массиве, или число -99999.0 – если в индексном или вещественном. (key:stringOrNumber) ar_Find value:stringOrNumberOrForm arrayToSearch:array inRange:range 29 Где, value – искомое значение. Может быть строкой, числом или формой. arrayToSearch – массив, в котором будет происходить поиск. inRange – диапазон элементов массива, среди которых будет происходить поиск. Если не указан, то поиск будет происходить среди всех элементов массива. ar_Sort – сортирует значения элементов массивов по возрастанию или по убыванию. Возвращает новый, отсортированный массив, связь «Ключ»::»Значение» не сохраняется. Внимание, тип элементов массива, который сортируется, должен быть одинаковым (строка, число, или объект), в противном случае функция возвращает пустой массив. Строки сортируются по алфавиту и без учёта регистра, номера - в номерном порядке, объекты - по ID. (sortedArray:Array) ar_Sort toSort:array sortDescending:bool Где, toSort – массив, который будет отсортирован. sortDescending – сортировать в обратном порядке. 1 – ДА, 0 – НЕТ. Указывать этот параметр не обязательно, по умолчанию сортировка происходит в прямом порядке. ar_SortAlpha – сортирует значения элементов массивов по алфавиту, по возрастанию или по убыванию. Возвращает новый, отсортированный массив, связь «Ключ»::»Значение» не сохраняется. В отличие от функции ar_Sort, не требует, чтобы значения элементов массива имели один тип. Если в массиве содержатся и численные и строковые значения, то численные значения преобразуются в строки, формы сортируются по имени, если такое имеется, иначе - по ID. (sortedArray:Array) ar_SortAlpha toSort:array sortDescending:bool Где, toSort – массив, который будет отсортирован. sortDescending – сортировать в обратном порядке. 1 – ДА, 0 – НЕТ. Указывать этот параметр не обязательно, по умолчанию сортировка происходит в прямом порядке. ar_Copy – создаёт точную копию массива. Если копируемый массив содержит другие массивы, то копия будет содержать указатели на те самые массивы. (copy:array) ar_Copy src:array Где, src – массив, который будет скопирован. ar_DeepCopy – то же самое, что и ar_Copy, за исключением того, что делает копии всех вложенных массивов. (copy:array) ar_DeepCopy src:array Где, src – массив, который будет скопирован. ar_First – возвращает ключ первого элемента массива. (key:arrayKey) ar_First src:array Где, src – массив. ar_Last – возвращает ключ последнего элемента массива. (key:arrayKey) ar_Last src:array Где, src – массив. ar_Next – возвращает ключ следующего элемента массива. (key:arrayKey) ar_Next src:array precedingKey:arrayKey Где, src – массив. precedingKey – ключ текущего элемента массива. ar_Prev – возвращает ключ предыдущего элемента массива. 30 (key:arrayKey) ar_Prev src:array prevKey:arrayKey Где, src – массив. precedingKey – ключ текущего элемента массива. ar_BadNumericIndex – возвращает неправильный ключ индексного или вещественного массива. Может быть использована для проверки значения функций, возвращающих ключ индексного или вещественного массивов. (badKey:int) ar_BadNumericIndex ar_BadStringIndex – то же самое, что и ar_BadNumericIndex для ассоциативных массивов. (badKey:string) ar_BadStringIndex ar_Keys – возвращает индексный массив, содержащий ключи элементов заданного массва. (keys:Array) ar_Keys src:array Где, src – массив. ar_HasKey – возвращает 1, если заданный массив имеет элемент с заданным ключом, иначе возвращает 0. (hasKey:bool) ar_HasKey src:array key:arrayKey Где, src – массив. key – ключ. ar_Null – возвращает пустой массив. Может быть использована для проверки значения функций возвращающих массивы, или для разрыва связи между элементом одного массива и другим массивом. (badKey:int) ar_BadNumericIndex ar_List – возвращает индексный массив из последовательности аргументов. Может принимать до двадцати аргументов произвольного типа. Рекомендуется разделять аргументы через запятые. (list:Array) ar_List element0:multi element1:multi ... element19:multi ar_Resize – изменяет размер указанного индексного массива. Если новый размер меньше текущего размера массива, то элементы с превышающими его индексами будут отброшены. Если новый размер, наоборот - больше, то новые элементы будут добавлены в конец массива. По умолчанию они имеют значение 0, но могут принимать и любое другое значение, определяемое необязательным параметром функции paddingValue. Возвращает единицу, если удалось изменить размер массива, ноль – если нет. (bResized:bool) ar_Resize array:Array newSize:int paddingValue:multi Где, array – массив. newSize – новый размер массива. paddingValue – значение, которое будет использовано для новых элементов массива. ar_Insert – добавляет один новый элемент с указанным индексом в указанный индексный массив. При этом существующие элементы будут сдвинуты на позицию вверх. Индекс нового элемента не может превышать текущий размер массива. Возвращает единицу, если удалось добавить новый элемент в массив, ноль – если нет. (bInserted:bool) ar_Insert array:Array index:int valueToInsert:multi Где, array – массив. index – индекс нового элемента. 31 valueToInsert – значение нового элемента. ar_InsertRange – добавляет диапазон элементов в указанный индексный массив начиная с указанного индекса. При этом существующие элементы будут сдвинуты на соответствующее количество позиций вверх. Индекс нового элемента не может превышать текущий размер массива. Возвращает единицу, если удалось добавить диапазон элементов в массив, ноль – если нет. (bInserted:bool) ar_Insert target:Array index:int rangeToInsert:Array Где, target – массив. index – индекс первого элемента диапазона в целевом массиве. rangeToInsert – индексный массив элементов, которые будут добавлены в целевой массив. ar_Range – возвращает индексный массив, состоящий из последовательности целых чисел, от начального до конечного числа, с заданным шагом. (range:Array) ar_Range start:int end:int step:int Где, start – стартовое число. end – конечное число. step – величина шага. 1. 2. 3. 4. Let Arr := ar_Range 5 10 2 ; Arr[0] == 5 ; Arr[1] == 7 ; Arr[2] == 9 ar_Map – возвращает вещественный или ассоциативный массив, состоящий из аргументов функции. Аргументы «Ключ»::»Значение», которых может быть до двадцати. (Map/StringMap) ar_Map key-value pair: key-value pair: key-value pair: entry_1:pair 1. Let Arr := ar_Map “Name”::”Alex” “Age”::20 2. ; Arr[“Name”] == “Alex” 3. ; Arr[“Age”] == 20 ar_Append – добавляет новый элемент в конец массива. (nothing) ar_Append array: toInsert: Где, array – массив. toInsert – значение. 32