1) Предисловие Помимо прочего, на форуме я предложил прикреплять триггеры к объектам, а не объекты к триггерам, как это обычно делают. В другом проекте (неигровом) это было идеальным решением и отлично сработало, но насколько это применимо к Diablo, я не задумывался. Это выгодно, когда каждый объект делает что-то своё, реагируя на многие события, затрагивающие его. Diablo к таким случаям совсем не относится. Так что продолжаем вносить предложения и совместно выявлять их недостатки и избытки (известная личность мне подсказывает, что это называется "мозговой штурм"). Кстати, я ещё побаиваюсь, что всё это уже придумано до меня, и читать это будет пустой тратой времени. 2) Техническая сторона Повторяю: не пытайтесь связать нижеследующий текст с моим сообщением на форуме. Запуск триггеров: Чтобы почём зря не гонять компьютер по поводу выполнения триггеров (мало ли чего захочется моддеру), можно делить триггеры на группы. Какой триггер в какую группу - решает моддер с помощью скрипта. Работает так: вот, например, ударили монстра. ДО ТОГО, КАК ПРИМЕНЯЮТСЯ ПОСЛЕДСТВИЯ, эти последствия рассчитываются (нанесённый урон, факт попадания, факт блокировки, ...), данные заносятся во временный массив, и немедленно начинается поиск триггеров. Допустим, мы решили, что 8 групп на триггер типа GetHit хватит на всех. Берём значение однобайтового поля "TrigGetHit" монстра и пробегаемся по его битам. Если находим бит "1", переходим к поочерёдному запуску триггеров в соответствующей группе. И лишь когда выполнены все триггеры во всех заявленных группах, применяем последствия. Так эти последствия можно менять средствами скрипта до того, как о них будет известно: ... Бой с убером, здоровья остаётся один удар против одного удара, глад успевает первым опустить топор на ногу убера и... ПРОМАХ!!! И вылезает сообщение: "А ведь мог бы и попасть :b"... А триггер такой: находится в одной из вызываемых групп GetHit для убера; прекращает своё выполнение, если таймер заморозки этого триггера не истёк; также выходит, если засчитан промах; выходит, если нанесённый урон меньше текущего здоровья убера; выходит, если случайное целое число из диапазона 0..1 равно 0; меняет "не промах" на "промах"; выводит сообщение о состоявшемся былинном отказе; запускает таймер заморозки триггера на 3 секунды; выходит. С момента начала запуска триггера программа занимается исключительно триггером. Триггер завершается при очистке стека вызовов или при вызове скриптом соответствующей функции. Паузы организуются с помощью таймеров. Триггеры предлагаю организовать в виде связанного списка, содержащего точки входа в общий для всех триггеров интерпретируемый код. Если покажется полезным, можно добавить идентификатор и/или флаг включенности триггера. И ещё кое-что: нужно добавить возможность управлять порядком следования триггеров в группе. И ещё кое-что: отдельно, вне групп, должны быть "системные" триггеры, отвечающие, например, за генерацию уровня. Данные: Как написано выше, при срабатывании события перед проверкой групп триггеров происходит запись предварительных результатов события в буфер, чтобы потом их можно было изменить (например, чтобы можно было случайно ударить топором монстра, находящегося тремя этажами ниже). Если триггер что-то изменит, следующие триггеры будут работать с изменёнными данными. Достаточно одного общего буфера для всех событий, тк пока используются данные одного события, другие события не обрабатываются. Локальные переменные не нужны. Для глобальных переменных используем два массива с размерами, указанными в заголовке файла скомпилированного скрипта. Почему именно два - потом объясню. Нужен ещё массив для объектов, созданных в редакторе и участвующих в скрипте. Один массив. Для каждого объекта нужно предоставить возможность цеплять по два массива к этому объекту. Тб два поля для массивов и два поля для количества элементов в них. Память под эти массивы отхапывается по приказу скрипта. Почему именно два массива - сейчас объясню. Итак, почему два массива. Игру надо будет сохранять. И если числа можно записывать в файл напрямую, то с объектами проблема - в массивы очень желательно записывать указатели на объекты. Поэтому числа сохраняем напрямую, а для каждого элемента массива объектов нужно предварительно найти тип и номер объекта в игре. К объектам также относятся переменные с текстовым значением. Файл скрипта: Всё хранится в одном файле. В заголовке должны быть прописаны размеры вышеописанных массивов и стека вызовов, строковые постоянные, точки входа для триггеров к "системным" событиям, ну и ещё что-нибудь. Далее идёт код. Точки входа отсчитываются от начала кода. (Здесь должен быть синоним слова "код") интерпретируется так: Берём сколько нужно байт для получения номера действия, из массива по этому номеру берём указатель на обработчик действия (даааа, вот работёнка будет программистам проекта - составлять таблицу этих указателей). Далее в коде следуют параметры, которые могут быть непосредственными значениями, строковыми константами (смещение с начала таблицы, см. ниже), объектными константами (индекс в массиве указателей, см. выше), переменными (индекс переменной в массиве) или функциями (номер функции, указатель на обработчик получаем аналогично). У функции тоже бывают параметры. Итак: обработчик действия/функции получает управление и начинает хватать параметры из следующих байтов кода. Если встречает функцию, передаёт управление ей, (где-то здесь была рекурсия), вторая функция возвращает значение, а первая после этого продолжает собирать параметры. Как только все параметры собраны, начинается их обработка. То есть результат компиляции этого: action(a,b,func1(5,c,func2(),d),"abc",^leoric) устроен так: action v.a v.b f.func1 i.5 v.c f.func2 v.d s.0 o.18 где v., f., i., s. и o. - признак того, чем является аргумент (переменная, функция, непосредственное значение, строковая константа, объектная константа). Имеется в виду, что строка "abc" стоит первой в таблице строк, а указатель на Леорика - девятнадцатый в массиве. Логичная последовательность байтов получилась? В общем, при такой организации и код интерпретируется не слоупоком, и компилятор писать не особо сложно. Можно ещё приколоться и сделать так, чтобы функция могла возвращать несколько значений (тб у одной и той же функции могут быть два аргумента подряд, возвращающие координаты точки, а может вместо них быть одна функция, сразу возвращающая обе координаты). Но при этом придётся добавить стек параметров. В том самом "неигровом проекте" я именно так и сделал. После кода идёт таблица строковых констант. 3) Конкретности Дополнения к геймплею: Если вдруг ещё никто не додумался, можно ввести объекты, активируемые оружием. Здесь надо решить, делать событие "объект активирован ударом" или оставить эту идею для воплощения средствами скрипта. Можно не связывать проходы друг с другом, а делать проходы односторонними. Можно, например, организовать "волшебную лестницу", ведущую себя по принципу не 2<->1, а 1->2->3->1. В Lamentation Sword был такой портал. События: Таймер истёк (c) Известная личность Начал атаку Атака прервана Наносит урон (втч воздуху) Готов к следующему действию Начал использовать магию Акт использования магии прерван Закончил использование магии ... И это ещё не всё. Триггеру нужно передавать настолько же подробную информацию при каждом событии. Что, куда, откуда, где, сколько... Действия: Изменить точку назначения межуровневого прохода Имитировать удар с заданными уроном, типом, точностью, ... ) Приложения О моём сообщении на форуме: Там был представлен код, который с вероятностью 1/16 делает сундук пустой ловушкой, бросающей фаербол на открывшего. Если фаербол попадает в стенку, всем игрокам даётся сообщение, что вам, мол, ничего не сделалось, а открывшему сундук - что на тебя, мол, никто не обиделся. 5) Послесловие Число 4 мне не нравится. Поэтому пусть здесь будет 5 разделов. Жаль только, что это не оригинальная шутка...