Перейти к контенту
Malandrinus

Справочник по функциям и классам

Рекомендуемые сообщения

По классу CTime:

Универсалка для получения текущих игровых даты\времени (для вывода например на худ) от Artosa (ну и меня тоже - вместе как то на форуме СП забавлялись вышеприведенными методами и в итоге пришли к почти идентичным кодам :rolleyes: )

function Get_StringOnTimeOrDate(sType) --/  передаем аргументы из числа  "Y", "M", "D", h"", "m", "s", "ms" - по одной штуке - смотря какое значение  текущего игрового времени или даты  хотим получить- по умолчанию функция возвращает секунды.
    if sType then
    local tTime = {
      ["Y"]  = game.CTime.DateToYear,    --/ 2012
      ["M"]  = game.CTime.DateToToMonth, --/ 01/2012
      ["D"]  = game.CTime.DateToDay,     --/ 01/01/2012
      ["h"]  = game.CTime.TimeToHours,   --/ 23
      ["m"]  = game.CTime.TimeToMinutes, --/ 23:59
      ["s"]  = game.CTime.TimeToSeconds, --/ 23:59:59
      ["ms"] = game.CTime.TimeToMilisecs --/ 23:59:59:999
    }
        if sType == string.lower(sType) then --/ аргумент в нижнем регистре
        return game.get_game_time():timeToString(tTime[sType] or 0) --/> время
        end
     return game.get_game_time():dateToString(tTime[sType] or 0) --/> дата
    end
   return game.get_game_time():timeToString(game.CTime.TimeToSeconds) --/> по дефолту 23:59:59
end

 

Изменено пользователем malandrinus
Ссылка на комментарий
Постэффект - это набор модификаций изображения, выводимого на экран. Название "постэффект" означает буквально "послеобработка", т.е. финальная обработка отрисованного изображения. Это самый последний шаг в процессе отрисовки сцены. Набор доступных эффектов в данном движке включает: модификацию цвета (изменение яркости, убирание цветовой компоненты - gray, внесение цветовой тональности), внесение шума (noise), двоение изображения (duality), размытие (blur). Впрочем похоже, что размытие не работает.

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

Файлы постэффектов лежат в каталоге anims - том-же, что и анимации камеры, и имеют расширение "*.ppe". Они там лежат россыпью, в одном файле - один эффект, в отличие от, скажем, партиклов или анимаций цвета. Вообще непонятно, чем руководствовались разработчики, когда паковали те-же партиклы в общий контейнер, который потом дополнительно упакован в игровой архив.

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

add_pp_effector - запустить постэффект

remove_pp_effector - остановить постэффект

set_pp_effector_factor - изменить интенсивность запущенного эффекта

Относительно создания файлов постэффектов имеется статья на wiki.

 

Также имеется возможность создавать постэффекты со скриптовым управлением. Для этого имеется класс effector и несколько вспомогательных классов: effector_params, noise, duality, color.

Сначала формальное описание.

основной класс, требущий создания на его основе скриптового класса

class effector { 
    effector (int type, float time); // конструктор, время задаётся в секундах

    void start(); // запуск постэффекта
    void finish(); // остановка постэффекта
    bool process(effector_params*); // передача параметров
};

класс просто служит хранилищем для набора параметров постэффекта. Описывает мгновенное состояние постэффекта (с некоторой оговоркой для шума).

class effector_params {
    effector_params(); // создать объект с нулевыми настройками
    
    void assign(effector_params*); // копировать все настройки из аргумента к себе
    float blur; // степень размытия [0.0..1.0]
    float gray; // степень серости [0.0..1.0]
    duality* dual; // параметры двоения изображения
    noise* noise; // параметры шума
    color* color_add; // вроде как добавляет цвет после всех операций (делает изображение в целом красным, синим и т.д.)
    color* color_base; // общая яркость (вроде бы) 
    color* color_gray; // как-то влияет на способ преобразования в серое изображение
};

В теории цвета я полный чайник. Так что описание параметров, влияющих на цвет, не претендует ни на полноту ни на корректность. Почитайте wiki, экспериментируйте. Может кто сможет дополнить то, что здесь написано.

Класс duality отвечает за хранение параметров двоения изображения.

class duality {
    duality(); // создание объекта со значением h=0, v=0
    duality(float h, float v); // создание с явным указанием значений
    
    void set(float h, float v); // изменение значений
    float h; // отклонение изображения по горизонтали [0.0..1.0]
    float v; // отклонение по вертикали [0.0..1.0]
};

Не совсем ясно, в чём измеряется отклонение. Видимо в "экранах", и осмысленными являются достаточно маленькие значения, вроде 0.1.

 

Класс noise отвечает за хранение параметров шума.

Шум представляет собой искажение картинки, напоминающее мерцание экрана ЭЛТ с низким разрешением.

class noise {
    noise();
    noise(float i, float g, float f);

    void set(float i, float g, float f);
    float intensity; // яркость шума [0.0..1.0]
    float grain; // размер зерна [0.0..1.0]
    float fps; // частота смены
};

Класс color. Назначение и использование самоочевидно. Несмотря на вроде бы достаточно общий смысл, этот класс более нигде не используется.

class color {
    color ();
    color (float r, float g, float b);
    
    float b; [0.0..1.0]
    float g; [0.0..1.0]
    float r; [0.0..1.0]
    void set(float r, float g, float b);
};

 

 

Создание своего скриптового постэффекта.

 

Ключевым моментом является создание своего скриптовго класса на основе effector. В созданном классе надо перегрузить конструктор и метод process. Как всегда, конструктор будет доступен в виде глобальной функции с именем класса. После создания экземпляра класса, надо запустить воспроизведение эффекта с помощью метода start. После этого движок начнёт периодически вызывать ваш перегруженный метод process для запроса текущих параметров воспроизведения. Аргумент метода - это указатель на объект класса effector_params. Его передаёт движок, а работа этого метода заключается в том, чтобы установить в этом объекте нужные значения, и, таким образом, изменить текущие параметры постэффекта.

 

class "TestPP" (effector)
function TestPP:__init() super(123,5)
    self.params = effector_params()
    
    self.base_amplitude        = color(0.2,0.15,0.0)
    self.gray_amplitude        = 0.5
    self.add_factor            = 0.1
    self.dual_amplitude        = 0.075
    self.gray_color            = color(0.33,0.33,0.33)
    self.noise_var            = noise(0,0,0)
    self.intensity            = 0.5
    self.power_factor        = fcolor(0.5,0.5,0.5,0.5)
    self.params.color_base    = color(0.5+self.base_amplitude.r*self.intensity,0.5+self.base_amplitude.g*self.inte
nsity,0.5+self.base_amplitude.b*self.intensity)
    self.params.color_gray    = color(self.gray_color.r,self.gray_color.g,self.gray_color.b)
    self.params.color_add     = color(self.power_factor.r*self.add_factor,self.power_factor.g*self.add_factor,self
.power_factor.b*self.add_factor)
    self.params.gray        = self.gray_amplitude*self.intensity
    self.params.dual        = duality(0.10, 0.0)
    self.params.noise        = self.noise_var
    self.params.blur        = 0.3
    effector.process(self, self.params)
    self.time = time_global()
    self:start()
end

function TestPP:process(pp)
    local dt = time_global() - self.time
    local a = 0.1
    self.params.dual = duality(a*math.sin(dt/1000.0), a*math.cos(dt/1000.0));
    effector.process(self, pp)
    pp:assign(self.params)
    return true
end

function TestPP:finalize()
end

 

Пример не претендует на осмысленность или оптимальность, просто показывает принцип работы. Для использования достаточно создать объект класса TestPP:

local pp = test_effector.TestPP()

 

В ТЧ имеется несколько достаточно подробных примеров применения этой системы. К примеру, это модули level_psy_antenna.script и postprocess.script.

level_psy_antenna.script - специализированная система эффектов и погоды на уровне с Выжигателем.

postprocess.script - универсальная система для воспроизведения файлов эффектов в виде ltx (см. wiki на предмет описания). В той статье утверждается, что это "декомпилированные" файлы эффектов. Но если посмотреть на билд 1935, то можно видеть, что там вообще отсутствуют постэффекты в бинарном виде и функции для их проигрывания. Т.е. вот эта скриптовая система - это исходная система постэффектов, и декомпилировать тогда было ещё нечего. Так что не "декомпилированные", а "доисторические".

 

В релизе ТЧ эти модули есть, и к ним имеются несколько ltx с описанием. Вот вызовы всех имеющихся, можете поиграться:

--local pp = postprocess.PostProcess (ini_file ("scripts\\proba.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\teleport.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\postprocess1.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\deadcity_wake.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\sincos.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\earthshake.ltx"))

--local pp = postprocess.PostProcess (ini_file ("scripts\\noise.ltx"))

В ЗП были удалены как модули, так и файлы ltx из каталога с анимациями. Но можно их вернуть. Лучше копировать из релиза ЧН, поскольку там внесены некоторые мелкие правки на предмет совместимости со стандартным Lua. Если это сделать, то система будет работать и под ЗП. Несмотря на то, что механизм рабочий, ни один из этих модулей ни в одной из релизных игр не используется.

 

 

Изменено пользователем malandrinus
 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Обновляю старый-престарый пост про alife_simulator. Теперь описаны и прокомментированы почти все функции, оформление приведено в порядок. Старый пост будет удалён.

[spoiler=alife_simulator]Класс нужен для выполнения самых базовых задач на серверной стороне: создание и удаление объектов, поиск объектов по идентификаторам, перевод в онлайн/оффлайн, проверка инфопорций. Из этого ряда странным образом выбивается получение номера текущего уровня.

Объект такого класса всего один и может быть получен глобальной функцией alife(). Используем примерно так:

local sim = alife() -- получаем сам объект класса alife_simulator
local sactor = sim:actor() -- получаем серверный объект для актора
class alife_simulator {
    float switch_distance(); // получить дистанцию переключения онлайн/оффлайн
    void switch_distance(float dist); // установить дистанцию. Не работает!
    int level_id(); // номер текущего уровня
    string level_name(int level_id) // возвращает имя уровня по его id
    cse_abstract* create(string <имя секции объекта>, vector* position, int level_vertex_id, int game_vertex_id) // создание объектов на уровне
    cse_abstract* create(string <имя секции объекта>, vector* position, int level_vertex_id, int game_vertex_id, int parent_id) // создание объектов в инвентаре
    cse_abstract* create_ammo(string <имя секции патронов>, vector* position, int level_vertex_id, int game_vertex_id, int parent_id, int amount) // создание пачек с патронами
    cse_alife_dynamic_object* create(int <индекс объекта в all.spawn> ) // создание объекта на основе секции в all.spawn
    int spawn_id(int id) // индекс секции в all.spawn для объекта с заданным id
    void release(cse_abstract* obj, bool); // удаление объекта.
    cse_alife_dynamic_object* object(int id, bool no_assert); // получение объекта по id. 
    cse_alife_dynamic_object* object(int id); // эквивалентно предыдущей функции с no_assert == true. 
    cse_alife_dynamic_object* object(string name); // получение объекта по имени. В ЗП нет!
    cse_alife_dynamic_object* story_object(int story_id); // получения объекта по сюжетному идентификатору
    cse_alife_creature_actor* actor(); // получение серверного объекта актора. Можно и просто получить его по id, равному 0
    bool valid_object_id(int id); // проверяет, что аргумент не равен 65535 (-1). На редкость бесполезная функция.
    void set_switch_online(int id, bool v); // устанавливает флажок flSwitchOnline, который определяет возможность перехода в онлайн
    void set_switch_offline(int id, bool v); // устанавливает флажок flSwitchOffline, который определяет возможность перехода в оффлайн
    void set_interactive(int id, bool v); // устанавливает флажок flInteractive
    bool has_info(int id, string info_portion); // проверяет наличие инфопоршена
    bool dont_has_info(int id, string info_portion); // проверяет отсутствие инфопоршена
    void add_out_restriction(cse_alife_monster_abstract* obj, int restrictor_id); // установка ограничения на выход
    void add_in_restriction(cse_alife_monster_abstract* obj, int restrictor_id); // установка ограничения на вход
    void remove_out_restriction(cse_alife_monster_abstract* obj, int restrictor_id); // снятие ограничения на выход
    void remove_in_restriction(cse_alife_monster_abstract* obj, int restrictor_id); // снятие ограничения на вход
    void remove_all_restrictions(int object_id, const enum RestrictionSpace::ERestrictorTypes type); // снятие всех ограничений либо на вход, либо на выход
    // Возможно, отвечают за убийство в оффлайне
    void kill_entity(cse_alife_monster_abstract* monster, int game_vertex_id, cse_alife_schedulable* obj);
    void kill_entity(cse_alife_monster_abstract* monster, int game_vertex_id); // эквивалентно первой функции с obj == 0
    void kill_entity(cse_alife_monster_abstract* monster); // эквивалентно второй с game_vertex_id, равным gvid монстра, переданного в первом аргументе
};

Комментарии к некоторым функциям:

 

void switch_distance(float dist);

функция для установки онлайн-радиуса. У меня всегда вызывает вылет. Впрочем, этот радиус можно изменить в файле конфигурации alife.ltx секции [alife]. Там параметр switch_distance. Там-же есть параметр гистерезиса перехода switch_factor.

 

string level_name(int level_id)

функция возвращает системное имя уровня, например "L01_Escape" для Кордона. В списке строк для многих уровней есть внятные имена. Их можно получить так:

local level_name = game.translate_string(string.lower(alife():level_name(level_id)))

 

Функция create с аргументом parent_id, позволяет создавать только инвентарные предметы. Аргумент amount функции create_ammo задаёт количество патронов в пачке. Пачка всегда создаётся одна. Номер секции в файле all.spawn для функции create с одним аргументом и spawn_id - это просто порядковый номер расположения объекта в all.spawn. Имеет тенденцию меняться после перепаковки с помощью acdc.

Функции create возвращают созданный объект. Однако он приведён к типу cse_abstract, и с ним возможны не все операции. Лучше после создания объекта получить объект заново, используя одну из функций поиска объектов.

 

С функцией release требуется осторожность. Некоторые объекты перед удалением надо переводить в оффлайн. Второй булевский аргумент этой функции игнорируется.

 

Функции set_switch_online, set_switch_offline и set_interactive позволяют менять соответствующие флажки из поля object_flags. Флажки flSwitchOnline и flSwitchOffline периодически проверяются для каждого объекта и определяют возможность перехода в онлайн или оффлайн соответственно. Если они оба true, то объект будет переходить в онлайн/оффлайн автоматически при пересечении онлайн-радиуса. Комбинации true/false и false/true соответственно форсируют одно из состояний вне зависимости от расстояния до актора. Естественно, это всё имеет смысл при нахождении объекта на одном уровне с актором.

Для некоторых объектов проверка осуществляется не этими флажками, а функциями can_switch_online и can_switch_offline, которые надо перегрузить в скриптовом серверном классе и, таким образом, управлять переключением. Собственно для остальных объектов управление также осуществляется этими функциями, просто они по дефолту возвращают значения этих флажков.

 

С флажком flInteractive я не экспериментировал, только выяснил, что функция interactive возвращает true, если установлены в 1 три флажка: flInteractive, flVisibleForAI и flUsefulForAI. Это имеет смысл для НПС, но какой именно - пока не выяснял.

 

В функциях для управления ограничениями надо передавать id рестрикторов. У рестрикторов имеется собственное поле типа, но, как мне показалось, в данных функциях собственный тип рестриктора игнорируется.

В функции remove_all_restrictions второй аргумент type определяет, какие именно ограничения надо удалить. Допустимые значения для этой функции: 4 - in, 5 - out.

Их всего шесть, что определяется вот таким перечислением:

enum RestrictionSpace::ERestrictorTypes {
    eDefaultRestrictorTypeNone, // 0
    eDefaultRestrictorTypeOut,  // 1
    eDefaultRestrictorTypeIn,   // 2
    eRestrictorTypeNone,        // 3
    eRestrictorTypeIn,          // 4
    eRestrictorTypeOut,         // 5
};

Можно обратить внимание, что в all.spawn никогда нет рестрикторов с типами 4 и 5. Не бывает также и типа 1.

В целом можно сделать ряд предположений:

- тип рестриктора 0 или 3 - это как бы "не рестриктор" вообще, и используются в основном для скриптовых зон, где движок ни за что не отвечает, а все проверки выполняются скриптами.

- только типы рестриктора 1 и 2 имеют смысл для движка и, по всей видимости, приводят к созданию зон "куда все не ходят"

- зоны "откуда всем ходу нет" очевидно лишены смысла, поэтому и не встречается в природе тип 1

- что касается типов 4 и 5, то это выходит не типы рестрикторов, а специальные константы для функции. Зачем их поместили в это-же перечисление, не знаю. Ещё раз, выглядит так, использовать можно рестрикторы с любым типом (поскольку, как я говорил, тип их не проверяется).

Обратите также внимание, что у класса game_object имеются функции, также отвечающие за управление ограничениями.

 

Назначение всех вариантов функции kill_entity неясно. Вроде бы отвечают за убийство в оффлайне, но у меня стабильно вызывали вылет.

 

 

 

Добавлено через 107 мин.:

Отредактировал шапку. Там теперь оглавление со ссылками на конкретные посты. Оглавление для удобства сделано не в хронологическом, а в тематическом порядке.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Содержание:

а) имена файлов\модулей игры - идут как строковые ключи в нижнем регистре(правило соблюдается для всех ключей вообще).

Значением ключа в данном случае является таблица содержащая(опять же как строковые ключи) все имена функций, скриптовых классов, констант и глобальных(локальные сюда не попадают) переменных модуля. Значения ключей - соответственно function, userdata, table, значение инициированных переменных - смотря что содержит файл.

б) имена движковых классов - тип значения =userdata (из lua недоступен для чтения - так что узнать список методов не получится).

в) имена Lua функций экспортированных в библиотеки игры(наконец то я узнал их точный список :rolleyes: ) - идут либо как название функции, либо как название пространства имен Lua - table, math, string - соответственно тип значения либо function, либо (во втором случае) таблица, в которой перечисляются уже сами функции экспортированные из данного пространства имен (это уже второй уровень итерации).

г) имена пространств имен игры и глобальных функций - типа device(), get_hud(), get_console(), time_global() и т.д.

д) имена глобальных скриптовых функций из файла _g.script.

 

Вроде все (хотя может чего и упустил - табличка все таки большая).

Заметил что в таблице присутствуют метод\ы (из экспортированных namespace - например из actor_stats), которые в lua_help не указаны.

 

Кстати: метатаблица _G содержит субтаблицу _g в которой практически(точно не сверял) все тоже самое, что и в таблице первого уровня, и при этом субтаблица _g содержит еще и саму себя.

 

На втором уровне итерации - (когда тип значения table) если пройтись по модулям, то получаем список имен функций и переменных модуля - как я уже выше указывал.

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

 

Код для получения распечатки _G (итерация двухуровневая):

pcall встроен для того, чтобы не вылетать на рабочий стол при итерации этой метатаблицы, так как в некоторых местах tostring'u попадаются неприемлемые для конвертации значения - nil или вызов функции. В этом случае в распечатке будет указано место ошибки и вместо значения строка "error_value".

 

 

function PrintG()
local res, value 
    for k, v in pairs(_G) do
    Console(tostring(k).." = "..tostring(v))  --Console() это моя "обертка" get_console()  - забыл ее заменить здесь
        if type(v)== "table" then
            get_console():execute("load ~ "..string.format("РАСПЕЧАТЫВАЕМ СУБТАБЛИЦУ: %s ", k))
            for key, val in pairs(_G[k]) do
            res, value = pcall(tostring, val)
                if not res then
                get_console():execute("load ~ ".."Ошибка преобразования значения: "..value)
                value="error_value"
            end
            get_console():execute("load ~ "..string.format("субтаблица: %s = %s", key, value))                
        end
        get_console():execute("load ~ ".."КОНЕЦ СУБТАБЛИЦЫ")
       end
    end 
end

 

Что хотелось бы узнать:

1) Как это можно использовать в игровых целях?

2) Как можно загрузить в эту таблицу модуль который туда еще не был кэширован (есть мнение, что пока к своему (не дефолтному) файлу(точнее к функции из него) не обратишься, он туда не кешируется, и следовательно автономный вызов функции из этого файла

function Func() body end

Func()

приводит к вылету ее не вызову так как в _G файла с этой функцией еще нет.

 

Ну и какой-нибудь дoполнительный теорбазис обо всем этом тоже не помешал бы.

Изменено пользователем Garry_Galler
Ссылка на комментарий
Что хотелось бы узнать:

1) Как это можно использовать в игровых целях?

Обычно порядок другой :) Есть игровая цель и думается: "Как это можно сделать?"

 

2) Как можно загрузить в эту таблицу модуль который туда еще не был кэширован (есть мнение, что пока к своему (не дефолтному) файлу(точнее к функции из него) не обратишься, он туда не кешируется, и следовательно автономный вызов функции из этого файла

function Func() body end

Func()

приводит к вылету, так как в _G его еще нет.

Если у тебя модуль выполняется - значит он уже загружен и вылета не будет.

Вообще с подгрузкой не понял - пишешь модуль_который_не_закеширован.функция() и движок сам загружает файл модуль_который_не_закеширован.script.

 

Ну и какой-нибудь дoполнительный теорбазис обо всем этом тоже не помешал бы.

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

Ссылка на комментарий
Обычно порядок другой smile.gif Есть игровая цель и думается: "Как это можно сделать?"

Обычно я так и делаю :)

 

Про кеширование: я имел ввиду вот что -

берем функцию - например такую -

function TestMessage()

news_manager.send_tip(db.actor, "Функция вызвана автономно", 0, "default", 180000)

end

и вставляем ее в файл в котором есть функции вызываемые из апдейта.

И пишем под функцией ее автономный вызов

TestMessage()

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

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

К тому же при попытке обратиться к незагруженному в таблицу _G файлу из xml тегов (это уже пишу на основе чужого опыта) может произойти вылет.

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

Ссылка на комментарий

можно, например, написать скрипт, который при старте игры будет перебирать файлы в папке scripts и у нужных вызывать функцию init

или же просто прописать вызов функции из start_game_callback

 

Насчет вызова xml тегов не скажу - не знаю. Знаю что было что-то похожее в вызове функций из файлов логики. Но там причина была совершенно в другом - в скрипте обрабатывающем логику было жестко прописано, что функции должны вызываться из xr_effects.script. В АМК это было исправлено и можно было вызывать функции из любого файла.

А скорее всего в файле была ошибка и он просто не загружался :unsure: Хотя, если есть реальный пример, было бы интересно посмотреть

Ссылка на комментарий
прописать вызов функции из start_game_callback

Но это уже не будет автономным вызовом функции. Вызов то во внешний файл можно поставить куда угодно.

----------------------------------------------------------------------------------------

По поводу вылетов при обращении к своему файлу из xml -тегов:

как я уже отметил - это не мой опыт - но опыт человека многим известного - Artosa(разработчика мультимода Симбион).

Вот что он писал по этому поводу:

То, что вызов функции из скрипта, имеющего синтаксические ошибки невозможен - это собственно и так понятно. Скрипт не считывается в метатаблицу _G/db и обращение к отсутствующей таблице -> nil.

А вот с фактом того, что первичное обращение к 'самописному' скрипту порой приводит к аналогичному результату (nil), заметил еще в давние времена, когда начинал ковыряться в кодах игры.

Причем практически 100% это заметно при вызове из диалогов (xml-ек) 'своих' функций из 'своих' скриптов.

Предполазаю, что при косвенных вызовах (через xml-тэги или аналогичное) идет обращение НЕ к скрипту, а к метатаблице _G (или даже db!) и если файл скрипта еще не был кеширован в нее - результат ->nil.

--------------------------------------------------------------------------------------

И еще - можно чуток поподробнее о функции init :rolleyes: ?

Заметил, что во всех биндерах есть функция init() или bind() и указывается она в конфиг секциях объектов которые биндятся.

Какую функцию ты имел ввиду?

---------------------------------------------------------------------------------------------

malandrinus

Спасибо за указание на prefetch() - будем тестить.

-------------------------------------------------------------

Все работает - один минус - вызов prefetch() нужно куда-то ставить :wacko: - автономность вызова функций опять же убивается.

Изменено пользователем Garry_Galler
Ссылка на комментарий

Garry_Galler,

2) Как можно загрузить в эту таблицу модуль который туда еще не был кэширован (есть мнение, что пока к своему (не дефолтному) файлу(точнее к функции из него) не обратишься, он туда не кешируется, и следовательно автономный вызов функции из этого файла

Глобальная функция prefetch(<имя модуля>)

Имя модуля указывается без расширения ".script"

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
И еще - можно чуток поподробнее о функции init :rolleyes: ?

Заметил, что во всех биндерах есть функция init() или bind() и указывается она в конфиг секциях объектов которые биндятся.

Какую функцию ты имел ввиду?

Это обычная функция, можно и по-другому назвать. К биндерам отношения не имеет. Просто чтобы эту функцию можно было вызвать. prefetch в этом отношении может даже и лучше

 

Непонятно зачем тебе автономность нужна? Можешь привести пример?

В сталкере нет никакого механизма автономности - вызовы функций должны быть где-то прописаны.

Есть файл script.ltx, в нем прописыван class_registrator.script, из него подгружаются серверные и клиентские классы

Из конфигов секций объектов подгружаются скрипты биндеров.

Из них подгружаются по мере необходимости остальные скрипты

 

Можешь сделать такой же механизм. Заведи ltx и прописывай туда скрипты, которые надо автономно запускать, ну и скрипт напиши, обрабатывающий этот ltx и вызывающий prefetch для прописанных скриптов.

 

---------

По поводу вызова функций из xml: проверил на ЗП - прописал в прекондишен свою функцию в своем скрипте - все нормально загружается и вызывается, хотя мой скрипт на момент вызова функции закеширован не был

Изменено пользователем Kolmogor
Ссылка на комментарий

Garry_Galler,

вызов prefetch() нужно куда-то ставить :wacko: - автономность вызова функций опять же убивается.

Если сильно надо, поставь все необходимые вызовы prefetch в начале модуля _g.script.

 

Kolmogor,

По поводу вызова функций из xml: проверил на ЗП - прописал в прекондишен свою функцию в своем скрипте - все нормально загружается и вызывается, хотя мой скрипт на момент вызова функции закеширован не был

Я никогда не понимал особенного смысла в этой функции, но по мотивам данного обсуждения могу предположить, что это вроде заплатки. Возможно, в ЗП выловили все такие ситуации, и необходимость в заплатке отпала.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Kolmogor

Смысл всей этой затеи с автономностью - делать свои разработки как можно менее зависимыми от дефолтных файлов(совместимость модов будет более простая) - в плане вызовов своих функций. Например на данный момент для одной своей разработки я полностью избавился от необходимости использовать bind_stalker как для апдейта, так и в плане некоторых каллбеков - переустановил их на эктора заново в своем биндере.

 

malandrinus

Если сильно надо, поставь все необходимые вызовы prefetch в начале модуля _g.script.

Так тут дело не в том куда поставить - вариантов то масса - а в том именно, что нАдо кУда То ставить :rolleyes: - а это значит опять задейстовать\править дефолтные файлы.

Изменено пользователем Garry_Galler
Ссылка на комментарий

Распишу немного то, что по работе с физикой объектов освоил:

Для начала требуется получить управление над физической оболочкой объекта для чего существует уже описанный в справочнике метод get_physics_shell().

Применяем метод к клиентской юзердате объекта.

Далее можно обратиться к методам нижеследующих классов для нужных нам действий.

[spoiler=class physics_shell ]class physics_shell {

get_joints_number() --получить число сочленений у объекта, если они конечно есть. Чаще есть только элементы и кости.

is_breaking_blocked() -- возможно проверка на заблокированность разрушаемости объекта .

У меня всегда возвращал false - имеется ввиду после применения метода block_breaking().

get_element_by_bone_id(number) -- получить элемент по числовому идентификатору кости.

get_linear_vel(vector&)-

is_breakable() -- разрушаем ли объект. У меня метод всегда возвращал false как для объектов класса O_PHYS_S так и для P_DSTRBL

get_elements_number() -- получить число элементов у объекта. У ящиков и контейнеров элементов одна штука.

unblock_breaking() -- убрать блокировку ?

get_joint_by_bone_name(string) --получить сочленение по строковому идентификатору кости.

get_element_by_order(number) -- получить элемент по порядковому номеру.

Объекты с которым я работал имели только один элемент и его порядковый номер был соответственно 0.

get_element_by_bone_name(string) -- получить элемент по строковому идентификатору кости.

apply_force(number, number, number) -- применить импульс к объекту по трем координатным осям - x, y, z.

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

get_angular_vel(vector&)

block_breaking() -- полагал что блокирует разрушение объектов класса P_DSTRBL - однако тест не принес желаемого эффекта.

get_joint_by_order(number) -- получить сочленение по порядковому номеру.

get_joint_by_bone_id(number) -- видимо получить сочленение по числовому идентификатору кости.

};

 

 

Методы класса physics_element следует применять к предварительно полученному объекту элемента физической оболочки предмета.

[spoiler= class physics_element]class physics_element {

get_density() -- получить плотность объекта. Весьма любопытные цифирьки дает - к реальной плотности материалов(железо\дерево\ткань\картон и т.д) отношения не имеют. Тестил на разного рода ящиках и контейнерах.

get_mass() -- получить массу . Значение совпадает с тем что возвращает метод mass() для клиентского объекта.

is_fixed() -- проверка зафиксирован ли элемент.

is_breakable()-- видимо проверка является ли объект разрушаемым - у меня на на объектах класса P_DSTRBL метод возвращал всегда false.

--Для справки - объекты класса O_BRKBL физ. оболочки не имеют - поэтому, хотя метод по названию вроде как для них, на самом деле неприменим к этому классу.

get_volume() -- получить физический объем объекта. По результатам теста вполне вменяемые цифры - объекты мелкой вместимости дают маленькие значения, крупной - большие.

get_linear_vel(vector&)-

fix() --зафиксировать элемент физической оболочки(фактически сделать сам объект привязанным к точке). Никто не использует - а зря. Я этот метод для фиксации своих схронов применил, а также мин-растяжек.

get_angular_vel(vector&)

apply_force(number, number, number)- - применить импульс к элементу объекта по трем координатным осям - x, y, z.

release_fixed() -- удалить фиксацию элемента. Побочный эффект - объект переходит в состояние невесомости. Очень прикольно надо сказать выходит.

global_transform(physics_element*) -- не дало никакого эффекта. Возможно неправильно применял.

};

 

 

 

Пример фиксации объекта - хоть в воздухе, хоть на земле. Фиксация сохраняется и после сейв\лоада. Но у мелких объектов фиксация после сейва\лоада слабая - и может легко разрушаться от приложения к нему силы. До перезагрузки - фиксируется намертво.

[spoiler=фиксация]

function ObjectFixed(id)
    if id then 
    local obj = level.object_by_id(id)
        if obj then
        local pshell = obj:get_physics_shell()
            if  pshell then 
            local element = pshell:get_element_by_order(0)
                if element then 
                element:fix()
                end
            end
        end
    end                
end

 

 

Кстати насчет метода script_server_object_version()

вот для чего он используется:

local ss_ver = script_server_object_version()
local IS_SOC = (ss_ver <= 7) -- although anything earlier then 1.0004 won't work
local IS_CS = (ss_ver > 7 and ss_ver < 12) -- although anything earlier then 1.5.0.4 won't work
local IS_COP = (ss_ver >= 12) --(ss_ver == 12)

комментарии думаю излишни.

 

Kirag

Спасибо. Я так и думал что как то не так их применял. Теперь более менее понятно.

Но вот только что они дают интересного(линеарное и угловое ускорение?).

И как работать с полученными значениями?

Изменено пользователем Garry_Galler
  • Нравится 1
Ссылка на комментарий

Garry_Galler,

 

Методы get_linear_vel и get_angular_vel рабочие (для всего шелла - проверено, для отдельного элемента - наверняка то же самое), применяются они так:

 

-- ps - ранее полученная физ. оболочка объекта
lvel = vector()
ps:get_linear_vel(lvel)
avel = vector()
ps:get_angular_vel(avel)
-- дальше с переменными lvel и avel делается все, что нужно

 

 

По поводу практического применения - оба метода используются в нашей с malandrinus-ом "Гравипушке". Линейная скорость - для контроля положения захваченного объекта - как и куда нужно пнуть объект, чтобы он остался в фокусе захвата, обе скорости - для определения факта соударения брошенного объекта - оба вектора при ударе должны заметно измениться.

Изменено пользователем Kirag

Мои работы:

Ночные прицелы + смена ножевого слота

AI вертолетов + ПЗРК

Soul Cube

 

Работаю только с ТЧ. С ковырянием ЧН/ЗП не связываюсь ни в какой форме. Совсем.

Ссылка на комментарий

Garry_Galler,

Но вот только что они дают интересного(линеарное и угловое ускорение?).

И как работать с полученными значениями?

Это вектор скорости и угловой скорости соответственно. Вектор угловой скорости направлен по оси вращения, а его значение = количеству радианов в секунду.

 

 

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus,

Занялся недавно скриптовыми диалогами - на практике.

В общем думаю стоит добавить в твои посты про них следующий момент:

При использовании метода AddAction(string) функция, которая вызывается им, принимает вот такой набор аргументов:

func(oSpeaker1, oSpeaker2, sDlgId, idPhrase)

первые два аргумента думаю всем понятны - юзердата собеседников, sDlgId - строковое имя диалога, и самый важный для меня аргумент - idPhrase - айди фразы (строковое или числовое - в 1.0004 используются числовые идентификаторы).

Не зная всего этого - потратил лишнее время на изобретение системы отслеживания активированных фраз в диалоге - но все оказалось гораздо проще. ;)

Функция, которая вызывается методом AddPrecondition(string) принимает немного другой набор аргументов:

precondition(oSpeaker1, oSpeaker2, sDlgId, idParent, idPhrase)

как видно тут добавился еще один - idParent, который возвращает айди родительской фразы, то есть фразы идущей перед той, в которой данный прекондишен находится.

При динамическом формировании фраз (например как у меня через цикл) - когда число фраз заранее неизвестно - значение аргумента idPhrase просто незаменимо.

Без этого я например просто не смог бы передать нужные данные для каждой конкретной фразы.

Ну правда я немного схитрил и использовал give_talk_message для некоторых ответных фраз NPC - просто с иконкой фразы красивше смотрелись (и иконка в тему).

Вот. Пока все, что выяснил интересного. Работает мой диалог отлично - так что тебе большое спасибо за раскрытие темы.

Изменено пользователем Garry_Galler
Ссылка на комментарий

[spoiler=Работа с консолью. Класс CConsole]Довольно известный и широко используемый класс. Наиболее широкое его использование - вывод в консоль строк - это, вообще говоря, использование его не по назначению =) Подробнее смотри здесь. Есть и другие способы применения.

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

 

[spoiler=Описание класса CConsole]

class CConsole {
    void show(); // показать консоль
    void hide(); // скрыть консоль
    void execute_script(string <имя файла конфигурации>); // загрузить файл конфигурации
    void execute(string <команда консоли>); // выполнить команду консоли
    void execute_deferred(string); // начиная с ЗП. Возможно, выполнить в отдельном потоке.
    // функции для получения значения какого-либо параметра консоли
    // можно воспринимать их как специфические функции чтения специальных переменных
    // установка этих переменных в этом случае выполняется с помощью execute
    string get_string(string <имя параметра>); // получить значение строкового параметра (если применимо)
    bool   get_bool(string <имя параметра>); // получить значение параметра в виде true/false (если применимо)
    float  get_float(string <имя параметра>); // получить значение параметра в виде числа с плавающей запятой (если применимо)
    int    get_integer(string <имя параметра>); // получить значение параметра в виде целого значения (если применимо)
    string get_token(string <имя параметра>); // Who the hell know what that means?
};

 

Комментарии к функциям get_xxx. Это весьма недооцененное средство для доступа к параметрам конфигурации игры, среди которых есть и параметры управления худом, такие как отображение оружия, прицела, тип рендера и пр. Что получает get_token я не знаю.

execute_script несмотря на многообещающее название всего лишь грузит файл конфигурации (такой как user.ltx).

Примеры использования:

local con = get_console()
con:execute("некое_сообщение") -- вывод в консоль строки
con:execute("flush") -- записать лог на диск (то, что было записано до этого момента, не пропадёт после обрушения)
con:execute("hud_crosshair off") -- убрать прицел с экрана
local hud_crhr = con:get_bool("hud_crosshair") -- узнать, включён ли прицел (выдаст логическое значение)
local hud_crhr = con:get_integer("hud_crosshair") -- узнать, включён ли прицел (выдаст значение 1 или 0)

 

 

Добавлено через 77 мин.:

Garry_Galler

стоит добавить в твои посты про них следующий момент:

Там это всё есть в первой части. В части про скриптовые диалоги я просто ссылаюсь на ранее описанные теги. А зачем тебе потребовались скриптовые диалоги? Они же не дают практически никаких преимуществ по сравнению с XML. Я об этом писал. Они скриптовые, но ни разу не динамические. А вся динамичность в равной степени доступна как при скриптовом создании, так и при использовании XML.

 

Кстати, в описании тега action у меня была ошибка. Заметил и исправил благодаря твоему посту. Спасибо!

  • Полезно 1
 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus

Там это всё есть в первой части

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

Если продублировать и там - тогда думаю многим будет яснее. :rolleyes:

Они же не дают практически никаких преимуществ по сравнению с XML.

Ну как это не дают преимуществ - еще как дают.

Я их применил для динамического создания диалога (у одного NPC ) о продаже кодов к закодированным схронам.

Фразы во первых формируются в цикле - что в xml сделать нельзя - то есть их число заранее неизвестно и определяется длиной таблички, которая хранит коды. В момент инициализации диалога специальная функция у меня формирует временную(существующую только до закрытия диалога) табличку со всеми нужными(и свежими данными), а далее в каждую новую фразу подставляются табличные данные. И хотя сами фразы однотипные по структуре - наполнение у всех разное. Что опять же нельзя сделать из xml, где наполнение предустановлено заранее.

Ну и на каждую фразу ГГ стоит скриптовый экшен, который выполняет нужное действие ориентируясь на номер фразы.

Также использованы скриптовые прекондишены. Диалог динамически меняет - не знаю как правильно сказать - граф не граф - в общем свой состав - каждая активированная фраза о покупке тут же исчезает из диалога(так как больше не нужна), при новой инициации диалога он строится уже в новом составе(с новым числом фраз) и с обновленными данными. В случае, если данных нет - кончились - выводится фраза NPC типа "на сегодня предложений больше нет".

Диалог у меня конечно маленький (и развилок всего пара штук)- но работает как надо.

Изменено пользователем Garry_Galler
Ссылка на комментарий

Garry_Galler,

 

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

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

Самое важное следствие из сказанного - динамически строить граф фраз в зависимости от ситуации не получится. Более того, надо тщательно следить за тем, чтобы ненароком не построить граф фраз иначе, чем в прошлый раз. В противном случае можно нарваться на абсолютно непредсказуемые ошибки, которые зависят от таких вещей, как запустили ли вы программу только что или это уже второй и далее сейв и с каким из NPC вы заговорили раньше и пр. в этом роде. Это будет невозможно отловить и отладить.

 

 

Если ты не делаешь ничего этому противоречащего, то на здоровье. Я же сам в своё время нажёгся. Думал сделать диалог, полностью перестраиваемый при каждом начале разговора. Сильно обломался, когда понял, что в этом плане скриптовые диалоги ничего не дают.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus,

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

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

Во первых, если сделать перед началом диалога сейв, потом поговорить с NPC и купить у него всю инфу(то есть активировать все имеющиеся фразы, которые по использованию больше не выводятся - вместо них будет выводиться развилочная фраза "На сегодня ничего предложить не могу."), то после того как снова - не выходя из игры - загрузить тот же сейв перед началом диалога - получаем такую ситуацию - NPC говорит, что инфы НЕТ! То есть получается, что диалог заново - с нуля - действительно как ты и писал не выстраивается\не инициализируется. Вчера я этот момент даже не проверил.

А во вторых странная путаница выходит время от время с номерами фраз и ключами временной таблицы(они же номера фраз) хранящей инфу для фраз - в некоторых случаях они перестают совпадать. И я получаю сравнение числа с nil.

В общем требуется серьезный разбор полетов . Поможешь?

-----------------------------------------------------------------------

Путаницу с ключами вылечил.

Теперь все вроде работает без ошибок - хотя плохо, конечно, что диалог не желает обновляться вторично. У меня так происходит даже если были активированы не все фразы - при загрузке сейва сделанного по итогам диалога, все равно получаю только фразу-концовку "На сегодня ничего предложить не могу.", хотя еще несколько фраз не были использованы. Но если выйти из игры, зайти снова и загрузить этот сейв - тогда появляются все неиспользованные фразы.

Исправил и этот момент - надо было просто сохранять табличку с данными для фраз - она ведь у меня с прекондишенами для фраз связана, а так как она не сохранялась, то прекондишены после перезагрузки возвращали false для вывода фраз. Теперь в этом плане все норм.

 

А действительно никак нельзя переинициализировать диалог вручную - принудительно?

Ведь граф диалога куда то записывается при инициализации диалога...а если перезаписать...если конечно знать что и как...

 

----------------------------------------------------------------------------

Кстати, а что полезного дает строковое айди диалога - кроме того, что оно используется при выводе в лог в случае ошибки при построении диалога?

Изменено пользователем Garry_Galler
Ссылка на комментарий

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

Комментарии могут оставлять только зарегистрированные пользователи

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

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

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    Ни один зарегистрированный пользователь не просматривает эту страницу.

AMK-Team.ru

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