Для встроенных типов, я предпологаю в C4 ввести слово immutable, например:
immutable PEDHANDLE;
immutable VEHHANDLE;
А потом использовать PEDHANDLE в качестве типа.
полноценный typedef, скорее всего, не нужен.
Смысл такой конструкции в том, что у нас есть значение, с которым мы не можем сделать ничего внутри скрипта. Мы получаем его извне и можем передать дальше. Максимум - присвоить переменной такого же типа.
Заодно, это уберет кучу ошибок, когда хендл одного типа пытаются передать опкоду, который поддерживает хэндлы только другого типа.
Учитывая необъяснимую тягу скриптеров к меткам и переходам в SB, есть предложение выкинуть ее нафик
Хоть все и пользуются, все равно надо убрать... Где логика?
Все зависит от возможностей языка. Если язык позволяет обходиться без меток, поддержка их не нужна. Если нет - нужна. Все просто.
]]>И еще. Каким образом подходить к скриптовым константам (педтипам, номерам клавиш, номерам ооружия и проч.)? Тут 2 варианта - либо нативная поддержка (как с опкодами), либо определение во внешних подключаемых модулях (include)? Этакая стандартная библиотека...
Я за вариант 2 (инклюды). Их исправить можно, если что не нравится.
]]>теоретически, компиляторы си/паскаля тоже интерпретируют на асм только то, что ты пишешь...но написать на асме - получится гораздо короче/проще в большинстве случаев о_О
Это зависит и от асма. Если он содержит complex instruction set, то это представляет определенный простор для оптимизации. Взять тот же x86. Умножение регистра eax на 40 в общем случае выглядит так:
imul eax, 40
А оптимизировано по времени так (хотя я не думаю, что те кто пишет на ассемблере предпочтут этот вариант - он слишком коряво выглядит):
lea eax, [eax + 4*eax] shl eax, 3
Ну, а если в асме есть только одна инструкция для умножения, то тут особо не пооптимизируешь...
Хотя есть и более "жесткие" средства оптимизации. Но тут уж я предполагаю, что писаться будет удобо-компилируемый код. Например, вот в таком выражении:
extra_ints[base + i] = i / extra_ints[base + i];
Чтобы дважды не вычислялось base + i, его можно вынести в отдельное выражение:
int index = base + i; extra_ints[index] = i / extra_ints[index];
Хотя у меня есть определенные сомнения в будущей популярности си-подобного синтаксиса среди скриптеров.
Да уж. Не понимаю что такого страшного в сишном синтаксисе? Все равно среднестатистический гта-скриптер (как правило школьник 8-9 классов) будет с увлечением читать о таблицах переходов и о том, куда надо поставить нолик, а куда единичку, но будет страшно бояться разобраться в структуре switch-блока.:D
]]>asi и cleo, на сколько я понимаюд, различаются. asi - dllка, выдаёт команды непосредственно exe игры, а скрипты - скриптовому движку
]]>void (__thiscall *CPed__GiveWeapon)(CPed *, int weapon, int ammo) = (void (__thiscall *)(CPed *, int, int))(0x5E6080);
Это было во-первых. Во-вторых, нужно еще воткнуть эту ASI куда-то в процесс исполнения игры. Туда, куда воткнулась одна ASI, вторую уже не воткнешь, поэтому ASI библиотек, так мало, а CLEO скриптов так много.
А смысл перехода на этот язык - в общем повышении читабельности кода, в повышении защищенности.
Сколько здесь на форуме вопросов типа:
"Почему, какие бы координаты я ни поставил, я всегда появляюсь в центре карты?"
00A1: put_actor $PLAYER_ACTOR at 345 306 998
А все потому, что нет типизации. Да и от самого подхода к командам: опкод, операнды... - нужно отойти. За каждой командой нужно лезть в Opcode Search Tool, а за некоторыми вообще в SASCM.ini.
С именами гораздо удобнее: нажал ctrl+space - вывалился список всех команд, набрал первые буквы - получил свою команду.
@3Doomer -
По надежности высокоуровневый код будет всегда выигрывать у низкоуровневого. Ну а по оптимизации - ты получаешь только тот код, который пишешь. Каждая инструкция компилируется сама в себя. Есть ли разница, пишешь ты на низком уровне:
if 0AB0: 0x41// A then // ... end
или на высоком:
if (IS_KEY_PRESSED('A')) { // ... }
(Здесь ты кстати только сэкономишь на одной инструкции - 00D6: if 0).
ЗЫ: Забыл сказать, что комплексные условия, которые раньше образовывались с 00D6, теперь будут образовываться при помощи соответствующих переходов. То есть можно будет писать следующим образом:
if (PLAYER_DEFINED(0) && CHAR_HAS_WEAPON(PlayerChar, 28)) ...
Если первое подусловие ложно, то второе проверяеться не будет и вылета не последует.
]]>ИМХО лучше подучить несложный язык скриптинга, чем работать через интерпритатор с неизвестным КПД о_О
Что такое КПД? Скорость исполнения? или скорость компиляции? или еще что-то?
@Seemann -
C-подобный синтаксис выбран не из соображений понятности для новичков, а из соображений удобства реализации парсера и естественной соотносимости с низким уровнем. Вообще говоря, язык будет рассчитан явно не на новичков, а на опытных программистов или программеров-любителей, знакомых с языками типа C. Для них освоение такого скриптового языка - дело пары часов.
По поводу JIT-компиляции - предложение интересное, а главное, вполне реализуемое. Но это не то, чем я сейчас занимаюсь, так что вопрос отложим хотя бы до тех пор, пока не будет полностью рабочий и опробованный обычный компилятор.
Т.е. при внесении изменений в исходник не требовалась бы запускать SB и перекомпилировать скрипт.
Уточнюсь. cleo script compiler - это не компилятор из C-подобного языка в язык SB. Вообще, параллельно с компилятором идет выпиливание простенькой IDE к нему. Получится продукт, независимый от Sanny Builder'а (Хотя Sanny Builder будет удобно использовать в качестве декомпилятора и просмотра кода на низком уровне).
Ну а так, почему я решил этим заняться - просто проба сил. А почему компилятор для CLEO скриптов - да просто мне до жути надоело иметь дело с листингами скриптов на низком уровне.
______________________________________
А теперь кратко о языке.
[large]Типы данных[/large]
Имеются следующие типы данных:
int - 4-байтовое целое
float - вещественное одинарной точности
gxt - строка из 7 или менее символов
string - строка из 15 или менее символов (есть возможность использовать constant string длиной до 127 символов)
bool - логическое. Нельзя объявлять переменные этого типа - значения этого типа могут только возвращаться статическими функциями (опкодами) или пользовательскими функциями. Существуют 2 константных значения - true и false.
void - тип для функций, не возвращающих значения.
А также массивы: int[], float[], gxt[], string[].
[large]Объявление данных[/large]
Данные можно объявлять в теле функции в любом месте. Все, что объявлено между { и } действует только внутри этого блока и перегружает все одноименные символы из родительских блоков.
Объявление данных в глобальной области видимости в CLEO скриптах запрещено. Зато можно описать некоторые внешние символы из майна следующим образом:
[FixedVarPosition=2] extern int Player; [FixedVarPosition=3] extern int PlayerChar;
Естественно, все, что объявляется должно умещаться в набор 32 локальных переменных (в случае с миссиями - 1022) и при этом должно оставаться место для генерации компилятором временных переменных. Для обращения к таймерам 32@, 33@ используются предопределенные идентификаторы timer1, timer2.
[large]Операторы в математических инструкциях[/large]
Используются следующие операторы:
= (присваивание) +, += (сложение), -, -= (вычитание), *, -= (умножение), /, /= (деление), &, &=(поразрядное и), |, |= (поразрядное или), ^, ^= (поразрядное исключающее или) <<, >>, <<=, >>= (логические сдвиги) - (минус, унарный) sizeof (получение размера переменной / типа в байтах) ~ (поразрядное отрицание. унарный) %, %= (модуло) && (логическое и) || (логическое или) & (взятие ссылки на переменную, унарный) (int) (приведение float к int) (float) (приведение int к float) () (вызов функции или группировка операторов по первичности) == (тождественное равенство) >, <, >=, <=, != (операторы сравнения) ! (логическое отрицание, унарный)
Операторов постфиксного и префиксного инкремента/декремента нет и не будет. Оператора ?: также нет. Насчет других операторов - они имеют тот же приоритет и ассоциативность, что и в языке C.
Типы float <-> int приводятся автоматически, но лучше указывать приведение типов явно, чтобы не возникало неожиданных казусов.
Например, выражение 3 / 4 == 0, а выражение (float)3 / 4 == 0.75.
[large]Высокоуровневые конструкции[/large]
Используются следующие конструкции (кажется, все те же самые, что и в C): if, if-else, for, while, do-while, switch.
По поводу switch, здесь используется подход C# - неявный fall-through запрещен. Каждая ветвь исполнения должна оканчиваться либо на break, либо на goto case/ goto default.
ЗЫ: кто заинтересовался, спрашивайте, что непонятно и нужно пояснить.
]]>