Q: Где можно ознакомиться со списком новых опкодов и значениями параметров?
A: http://cleo.sannybuilder.com/?lang=ru&id=2
Q: Правда ли, что можно добавлять новые скрипты и не начинать новую игру?
A: Да, это является основным нововведением CLEO 3. Подробнее о новом способе скриптинга написано здесь: http://cleo.sannybuilder.com/?lang=ru&id=3
Q: Как использовать в скрипте модель, которая обычно содержится в списке DEFINE OBJECT?
A: CLEO-скрипты содержатся в SCM-файлах без заголовка, т.е. являются аналогом внешних скриптов из файла script.img. Поэтому в них нет такого понятия как DEFINE OBJECT. Чтобы использовать такую модель, нужно указать вместо #имени ее глобальный ID (число).
Вы можете воспользоваться встроенной в SB функцией поиска ID модели (Ctrl+Alt+H).
--
Если вам потребуется в скрипте использовать глобальный ID модели из списка импортирумых моделей (DEFINE OBJECTS), вы можете прочитать его из адреса памяти игры, используя следующую формулу:
Например, чтобы прочитать глобальный ID модели INFO используйте следующий код:
в modelID будет записано 1239.
вот пример цикла, который выводит по очереди все ID моделей из стандартного майна
Q: Можно ли писать CLEO-скрипты в других редакторах?
A: Если вы по каким-то причинам не хотите или не можете использовать SB в качестве редактора скриптов, это не мешает вам писать скрипты для CLEO 3. Для этого вам нужно использовать первый способ написания скрипта, описанный здесь (извлечение скрипта из script.img).
Q: Для чего в начале некоторых скриптов стоит опкод 0000?
A: Это связано с некорректной обработкой скриптовым движком игры "нулевых переходов" (т.е. переходов на самое начало внешнего скрипта или миссии). Если скрипт представляет собой сплошной цикл, то этот опкод 0000 предназначен для создания минимального смещения от начала скрипта (эдакий буфер). Вместо 0000 можно использовать, например, wait 0.
--
Скриптовый движок SA имеет один неприятный баг, связанный с метками. Вы не можете осуществить переход из тела внешнего скрипта к его началу, т.е. сделать jump на самую первую метку.
Поэтому, если внешний скрипт представляет собой сплошной цикл, то нужно вставить какой-то опкод в начале, а затем метку для перехода:
Тот же баг проявляет себя и в миссиях, но там переход к самому началу почти никогда не требуется.
Q: Почему в CLEO-скриптах нельзя использовать глобальные переменные? Что тогда использовать?
A:
Когда мы компилируем main.scm вместе со всеми миссиями и скриптами, все глобальные переменные получают свой ID (порядковый номер). Часть из них получает ID всегда один и тот же (например, $PLAYER_CHAR компилируется как $2, $PLAYER_ACTOR - $3 и т.п.). Это справедливо для всех глобальных переменных, которые определены в CustomVariables.ini
Этот порядковый номер означает номер ячейки в памяти игры, где хранится значение переменной. Понятное дело, этот номер должен быть уникальным для каждой переменной. Иными словами, каждая переменная должна быть скомпилирована с уникальным ID.
Все остальные переменные компилируются следующих образом. Если имя переменной не найдено в CustomVariables.ini, то она компилируется с номером, который идет следом за наибольшей использованной переменной в скрипте. Звучит сложновато, поэтому пояcню на примере:
Простой скрипт из 2х строк:
$PLAYER_CHAR компилируется как $2. Значит, переменная $MYVAR, которая не содержится в CustomVariables.ini и имя которой не числовое, будет скомпилирована как $3. (на самом деле там будет не $3, потому что $3 зарезервировано для $PLAYER_ACTOR, поэтому $MYVAR получит первое свободное имя - $4
Другой пример:
Наибольшая переменная в скрипте теперь $100, поэтому $MYVAR станет $101.
Такой механизм позволяет присвоить каждой переменной свое уникальное имя.
В main.scm все новые переменные получают имена при компиляции начиная с $10948, т.к. $10947 наибольшая переменная, использованная в оригинальном main.scm.
Теперь вернемся к CLEO-скриптам. Каждый раз, когда мы компилируем новый скрипт, компилятор не знает ничегошеньки про состояние main.scm и сколько переменных в нем использовалось. Он именует переменные с нуля каждый раз. Это значит, что если ты сделаешь два скрипта, в которых будет:
в одном скрипте:
в другом скрипте:
то обе переменные будут скомпилированы как:
несмотря на то, что имена у них разные. И каждый раз компилятор будет в новом CLEO-скрипте именовать переменные, начиная с $4. Эффект, думаю, ясен. Переменные будут использовать одну и ту же ячейку памяти и менять значения друг друга. Если ты в одном скрипте запишешь актера в $MYVAR, а в другом в $MYVAR1 = 0, то когда первый скрипт обратится к $MYVAR, он прочитает не актера, а 0, т.к. обе переменные используют 4-ю ячейку памяти.
Другая ситуация - это выход за пределы памяти для глобальных переменных. Например, обычный stripped имеет размер памяти для глобальных переменных в 409 ячеек, поскольку наибольшая использованная переменная $ONMISSION это $409. Предположим, мы создали CLEO-скрипт:
Как будет скомпилирована переменная $MYVAR, если вы поняли, о чем я говорил выше ? Правильный ответ $411. $ONMISSION это $409, поэтому переменная должна бы стать $410, но этот ID уже зарезервирован для $Help_SprayCan_Shown (см. CustomVariables.ini). Поэтому первый свободный номер это $411.
Вернемся теперь к stripped. Размер памяти-то у него так и остался - 409 (если бы $MYVAR = 0 компилировалась в майне, то память была бы увеличена до 411). Поэтому когда CLEO-скрипт будет работать с переменной $MYVAR, ее значения будут читаться и писаться за пределами дозволенной области памяти. В худшем случае, такие переменные будут менять код самого main.scm (опкоды и параметры), что неминуемо повлечет вылет.
--
Глобальные переменные ($) вообще не рекомендуется использовать в CLEO-скриптах, не важно, каким способом их называть. Есть только 2 исключения: 1) это общераспространенные переменные типа $PLAYER_CHAR, $PLAYER_GROUP, $ONMISSION и 2) неиспользуемые переменные типа $30, $56. В любом случае к использованию глобальных переменных нужно подходить очень осторожно, иначе не минуемы различные глюки в игре.
Зато СВОБОДНО, БЕЗ ОГРАНИЧЕНИЙ можно пользоваться локальными переменными. Мало 32-х переменных? Есть масса способов как обойти ограничение (начиная от простой оптимизации, заканчивая использованием SCM-функций - 0AB1, 0AB2).
Если неудобно пользоваться локальными переменными из-за их имени (согласен, 1@ не всегда явно выражает смысл переменной), пользуйтесь конструкцией CONST..END Пара строк:
позволяет использовать в скрипте слово TRAIN_MODEL, которое на самом деле будет обозначать локальную переменную и компилироваться соответственно. Например, TRAIN_MODEL = 400 - это в переменную 1@ записали число 400. И все, больше никаких проблем из-за глобальных переменных.
Q: Можно ли в CLEO-скриптах использовать опкоды set_status_text и set_timer?
A: https://waterviper.forumtwilight.com/forum-f1/tema-t116.htm
Q: У меня есть код CLEO-скрипта, как получить из него .cs-файл?
A: Для этого понадобится программа Sanny Builder 3.03 и выше (найти можно на этом же сайте). Скачайте ее, установите. Запустите, зайдите в опции (F10), укажите путь к папке игры. Теперь нажмите Файл-Создать (Ctrl+N), копируйте весь скрипт в окно редактора (начиная со слова {$CLEO}) и сохраните полученный текстовый файл. После этого нажмите F7. Если все прошло успешно, в папке игры в папке CLEO должен появиться новый .cs-файл. Если возникли ошибки при компиляции, попытайтесь исправить их, следуя советам из справки (она на русском языке!).
---
З.Ы. См.: Кодинг в Sanny Builder 3 (на русском языке). Спасибо.
(thx to Seemann)
A: http://cleo.sannybuilder.com/?lang=ru&id=2
Q: Правда ли, что можно добавлять новые скрипты и не начинать новую игру?
A: Да, это является основным нововведением CLEO 3. Подробнее о новом способе скриптинга написано здесь: http://cleo.sannybuilder.com/?lang=ru&id=3
Q: Как использовать в скрипте модель, которая обычно содержится в списке DEFINE OBJECT?
A: CLEO-скрипты содержатся в SCM-файлах без заголовка, т.е. являются аналогом внешних скриптов из файла script.img. Поэтому в них нет такого понятия как DEFINE OBJECT. Чтобы использовать такую модель, нужно указать вместо #имени ее глобальный ID (число).
Вы можете воспользоваться встроенной в SB функцией поиска ID модели (Ctrl+Alt+H).
--
Если вам потребуется в скрипте использовать глобальный ID модели из списка импортирумых моделей (DEFINE OBJECTS), вы можете прочитать его из адреса памяти игры, используя следующую формулу:
- Code:
MA = -4982 + (ModelID * -7)
Например, чтобы прочитать глобальный ID модели INFO используйте следующий код:
- Code:
0@ = #INFO
0@ *= -7
0@ += -4982
0084: $modelID = &0(0@,1i)
в modelID будет записано 1239.
вот пример цикла, который выводит по очереди все ID моделей из стандартного майна
- Code:
03C4: set_status_text_to $modelID 0 'FEC_NMN'
// -4982 + (ModelID * -7)
for 1@ = -1 downto -388 // #INFO downto #SFCOPDR
0085: 0@ = 1@
0@ *= -7
0@ += -4982
0084: $modelID = &0(0@,1i)
wait 1000
end // for
Q: Можно ли писать CLEO-скрипты в других редакторах?
A: Если вы по каким-то причинам не хотите или не можете использовать SB в качестве редактора скриптов, это не мешает вам писать скрипты для CLEO 3. Для этого вам нужно использовать первый способ написания скрипта, описанный здесь (извлечение скрипта из script.img).
Q: Для чего в начале некоторых скриптов стоит опкод 0000?
A: Это связано с некорректной обработкой скриптовым движком игры "нулевых переходов" (т.е. переходов на самое начало внешнего скрипта или миссии). Если скрипт представляет собой сплошной цикл, то этот опкод 0000 предназначен для создания минимального смещения от начала скрипта (эдакий буфер). Вместо 0000 можно использовать, например, wait 0.
--
Скриптовый движок SA имеет один неприятный баг, связанный с метками. Вы не можете осуществить переход из тела внешнего скрипта к его началу, т.е. сделать jump на самую первую метку.
- Code:
DEFINE SCRIPT TEST_SCR AT @TEST_SCR
...
//-------------External script (TEST_SCR)---------------
:TEST_SCR
wait 0
jump @TEST_SCR // этот опкод вызовет ошибку
Поэтому, если внешний скрипт представляет собой сплошной цикл, то нужно вставить какой-то опкод в начале, а затем метку для перехода:
- Code:
DEFINE SCRIPT TEST_SCR AT @TEST_SCR
...
//-------------External script (TEST_SCR)---------------
:TEST_SCR
0000: // можно вставить 03A4
:TEST_SCR_1
wait 0
jump @TEST_SCR_1 // этот опкод будет работать.
Тот же баг проявляет себя и в миссиях, но там переход к самому началу почти никогда не требуется.
Q: Почему в CLEO-скриптах нельзя использовать глобальные переменные? Что тогда использовать?
A:
Когда мы компилируем main.scm вместе со всеми миссиями и скриптами, все глобальные переменные получают свой ID (порядковый номер). Часть из них получает ID всегда один и тот же (например, $PLAYER_CHAR компилируется как $2, $PLAYER_ACTOR - $3 и т.п.). Это справедливо для всех глобальных переменных, которые определены в CustomVariables.ini
Этот порядковый номер означает номер ячейки в памяти игры, где хранится значение переменной. Понятное дело, этот номер должен быть уникальным для каждой переменной. Иными словами, каждая переменная должна быть скомпилирована с уникальным ID.
Все остальные переменные компилируются следующих образом. Если имя переменной не найдено в CustomVariables.ini, то она компилируется с номером, который идет следом за наибольшей использованной переменной в скрипте. Звучит сложновато, поэтому пояcню на примере:
Простой скрипт из 2х строк:
- Code:
$PLAYER_CHAR = 1
$MYVAR = 1
$PLAYER_CHAR компилируется как $2. Значит, переменная $MYVAR, которая не содержится в CustomVariables.ini и имя которой не числовое, будет скомпилирована как $3. (на самом деле там будет не $3, потому что $3 зарезервировано для $PLAYER_ACTOR, поэтому $MYVAR получит первое свободное имя - $4
Другой пример:
- Code:
$PLAYER_CHAR = 1
$MYVAR = 1
$100 = 1
Наибольшая переменная в скрипте теперь $100, поэтому $MYVAR станет $101.
Такой механизм позволяет присвоить каждой переменной свое уникальное имя.
В main.scm все новые переменные получают имена при компиляции начиная с $10948, т.к. $10947 наибольшая переменная, использованная в оригинальном main.scm.
Теперь вернемся к CLEO-скриптам. Каждый раз, когда мы компилируем новый скрипт, компилятор не знает ничегошеньки про состояние main.scm и сколько переменных в нем использовалось. Он именует переменные с нуля каждый раз. Это значит, что если ты сделаешь два скрипта, в которых будет:
в одном скрипте:
- Code:
$MYVAR = 1
в другом скрипте:
- Code:
$MYVAR1 = 1
то обе переменные будут скомпилированы как:
- Code:
$4 = 1
несмотря на то, что имена у них разные. И каждый раз компилятор будет в новом CLEO-скрипте именовать переменные, начиная с $4. Эффект, думаю, ясен. Переменные будут использовать одну и ту же ячейку памяти и менять значения друг друга. Если ты в одном скрипте запишешь актера в $MYVAR, а в другом в $MYVAR1 = 0, то когда первый скрипт обратится к $MYVAR, он прочитает не актера, а 0, т.к. обе переменные используют 4-ю ячейку памяти.
Другая ситуация - это выход за пределы памяти для глобальных переменных. Например, обычный stripped имеет размер памяти для глобальных переменных в 409 ячеек, поскольку наибольшая использованная переменная $ONMISSION это $409. Предположим, мы создали CLEO-скрипт:
- Code:
$ONMISSION = 1
$MYVAR = 0
Как будет скомпилирована переменная $MYVAR, если вы поняли, о чем я говорил выше ? Правильный ответ $411. $ONMISSION это $409, поэтому переменная должна бы стать $410, но этот ID уже зарезервирован для $Help_SprayCan_Shown (см. CustomVariables.ini). Поэтому первый свободный номер это $411.
Вернемся теперь к stripped. Размер памяти-то у него так и остался - 409 (если бы $MYVAR = 0 компилировалась в майне, то память была бы увеличена до 411). Поэтому когда CLEO-скрипт будет работать с переменной $MYVAR, ее значения будут читаться и писаться за пределами дозволенной области памяти. В худшем случае, такие переменные будут менять код самого main.scm (опкоды и параметры), что неминуемо повлечет вылет.
--
Глобальные переменные ($) вообще не рекомендуется использовать в CLEO-скриптах, не важно, каким способом их называть. Есть только 2 исключения: 1) это общераспространенные переменные типа $PLAYER_CHAR, $PLAYER_GROUP, $ONMISSION и 2) неиспользуемые переменные типа $30, $56. В любом случае к использованию глобальных переменных нужно подходить очень осторожно, иначе не минуемы различные глюки в игре.
Зато СВОБОДНО, БЕЗ ОГРАНИЧЕНИЙ можно пользоваться локальными переменными. Мало 32-х переменных? Есть масса способов как обойти ограничение (начиная от простой оптимизации, заканчивая использованием SCM-функций - 0AB1, 0AB2).
Если неудобно пользоваться локальными переменными из-за их имени (согласен, 1@ не всегда явно выражает смысл переменной), пользуйтесь конструкцией CONST..END Пара строк:
- Code:
const
TRAIN_MODEL = 1@
end
позволяет использовать в скрипте слово TRAIN_MODEL, которое на самом деле будет обозначать локальную переменную и компилироваться соответственно. Например, TRAIN_MODEL = 400 - это в переменную 1@ записали число 400. И все, больше никаких проблем из-за глобальных переменных.
Q: Можно ли в CLEO-скриптах использовать опкоды set_status_text и set_timer?
A: https://waterviper.forumtwilight.com/forum-f1/tema-t116.htm
Q: У меня есть код CLEO-скрипта, как получить из него .cs-файл?
A: Для этого понадобится программа Sanny Builder 3.03 и выше (найти можно на этом же сайте). Скачайте ее, установите. Запустите, зайдите в опции (F10), укажите путь к папке игры. Теперь нажмите Файл-Создать (Ctrl+N), копируйте весь скрипт в окно редактора (начиная со слова {$CLEO}) и сохраните полученный текстовый файл. После этого нажмите F7. Если все прошло успешно, в папке игры в папке CLEO должен появиться новый .cs-файл. Если возникли ошибки при компиляции, попытайтесь исправить их, следуя советам из справки (она на русском языке!).
---
З.Ы. См.: Кодинг в Sanny Builder 3 (на русском языке). Спасибо.

(thx to Seemann)