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

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


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

(изменено)

Ну тогда уж так:

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

level_name(level_id) -- возвращает имя уровня по его id

create_ammo(<имя секции патронов>, position, lvid, gvid, id, amount) - создание пачек с патронами

create(<имя секции объекта>, position, lvid, gvid, id) -- создание объектов в инвентаре

create(<имя секции объекта>, position, lvid, gvid) -- создание объектов на уровне

во всех функция создания объектов

position - объект класса vector. Не может быть nil, иначе вылет

lvid - level vertex id

gvid - game vertex id

id - это куда спавним. Если в инвентарь, то указываем id непися или ноль (для актора)

release(sobj, boolean) -- удаляет объект sobj. Зачем второй булевский аргумент - не известно. Во всех примерах он либо отсутствует, либо установлен в true

object(id) -- возвращает серверный объект по его id. Если не нашёл nil

story_object(sid) -- возвращает серверный объект по его story id. Если не нашёл nil

actor() -- возвращает серверный объект актора

set_switch_online(id, boolean) -- переводит в онлайн объект с идентификатором id

set_switch_offline(id, boolean) -- то же , но в онлайн

 

switch_distance() -- радиус переключения в оффлайн?

switch_distance(number) -- установка этого радиуса?

level_id() -- текущий номер уровня ?

object(string) -- получение объекта оп его имени. Не помню точно, что за имя.

object(number, boolean) - то же, что и object(number)? на что влияет второй аргумент?

valid_object_id(number) -- проверка наличия объекта по id?

alife():create( <индекс объекта в all.spawn> ) --

spawn_id(number) -- индекс объекта в all.spawn для объекта с заданным id ?

 

dont_has_info(const number&, string);

add_out_restriction(cse_alife_monster_abstract*, number);

set_interactive(number, boolean);

add_in_restriction(cse_alife_monster_abstract*, number);

function remove_in_restriction(cse_alife_monster_abstract*, number);

remove_out_restriction(cse_alife_monster_abstract*, number);

kill_entity(cse_alife_monster_abstract*, const number&, cse_alife_schedulable*);

kill_entity(alife_simulator*, cse_alife_monster_abstract*, const number&);

kill_entity(alife_simulator*, cse_alife_monster_abstract*);

has_info(const number&, string);

remove_all_restrictions(number, const enum RestrictionSpace::ERestrictorTypes&);

 

 

 

 

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

local sim = alife() -- получаем сам объект класса alife_simulator
local sactor = sim:actor() -- получаем серверный объект для актора

 

В придачу про класс vector.

vector - вспомогательный класс, содержащий три координаты, и позволяющий выполнять с ними различные манипуляции. Объекты класса vector являются аргументами многих функций и возвращаются многими функциями. См. например выше про метод create класса alife_simulator.

Отдельный объект класса вектор создаётся вызовом глобальной функции vector(). При создании имеет координаты [0,0,0].

v:set_length(number) - оставить направление, изменить длину

v:normalize(); - сделать длину единичной

 

v:magnitude() -- длина вектора v

v1:distance_to(v2) -- расстояние между v1 и v2

v1:distance_to_sqr(v2) -- квадрат расстояния между v1 и v2

v1:crossproduct(v2) -- векторное произведение. Возвращает вектор

v1:dotproduct(v2) -- скалярное произведение. Возвращает число

v:sub(number); -- вычесть из каждого компонента v число number

v1:sub(v2); -- вычесть почленно вектор v2 из v1 и поместить в v1

v1:sub(v2, v3) -- вычесть из v2 - v3 и поместить в v1

аналогично:

add - почленное сложение

mul - почленное умножение

div - почленное деление

max/min - почленный максимум/минимум

v1:clamp(v2) -- почленное обрезание первого вторым

average -- почленное среднее

invert -- почленная инверсия знака

 

v1:abs(v2); -- копирует почленно положительные компоненты в v1 из v2

lerp(v1, v2, number) -- линейная интерполяция между двумя векторами

-- если number больше 1, то выходит за них - экстраполяция

 

v1:reflect(v2, v3) - должно быть отражение одного вектора от плоскости, определяемой другим. Не проверял

v1:slide(v2, v3); - вероятно, проекция одного вектора на плоскость, определяемую другим. Не уверен

v1:distance_to_xz(v2) - учитывая, что плоскость XZ - это плоскость параллельная земле, то возможно это длина проекции на землю пути от точки до точки. Не проверял

 

v:getP() - зенитный угол в радианах (угол наклона над плоскостью XZ )

v:getH() - азимутальный угол в радианах (угол в плоскости XZ между проекцией вектора на эту плоскость и осью Z)

setHP - соответственно для установки этих значений

v1:align() - выравнивает вектор вдоль оси x или z и нормирует его. Значение по y игнорируется. В итоге всегда возвращается одно из четырёх значений ([1,0,0], [-1,0,0], [0,0,1], [0,0,-1])

v4:mad(v1,v2,v3) -- бешеная операция =) умножить почленно второе на третье и сложить с первым, т.е. v4 = v1 + v2*v3 (название операции mad == mul + add)

v1:inertion(v2, number) -- v1 = v2 + (v1-v2)*number (тоже почленно)

v1:similar(v2, number) -- сравнение векторов на равенство с погрешностью number. Выдаёт 1 или 0

 

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

local v1 = vector()
v1:set(1,2,3)
local v2 = vector():set(4,5,6)
local dist = v1:distance_to(v2)

 

 

З.Ы.:

не пробовал false ставить?Есть мнение,что может тогда обьект не удалится...
Не пробовал. Да и звучит не логично. Какой смысл в таком вызове?
Изменено пользователем malandrinus
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

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

 

Свой класс можно добавить так:

class "my_cool_class"

Здесь "my_cool_class" - имя моего нового класса. Записывается в кавычках, т.е. это строка.

 

Потом можно добавлять к этому классу свои методы:

 

-- это специальный метод - конструктор. Будет вызван при создании класса.
function my_cool_class:__init(num)
    self.my_param = num -- добавили в свой класс переменную
end

-- обычный метод
function my_cool_class:cool_method_of_my_class()
    get_console():execute("lua_bind_in_action_"..self.my_param)
end

-- деструктор, вызывается при сборке объекта сборщиком мусора. Аргументов не имеет (кроме скрытого self. об этом см. далее)
function my_cool_class:__finalize()
    get_console():execute("good_by_"..self.my_param)
end

 

Потом можно создавать экземпляры своего класса, вызывать их методы и пр.:

local obj1 = my_cool_class(1) -- здесь создаётся новый объект. При этом вызывается конструктор и ему передаются аргументы этого вызова (в данном случае число 1).
local obj2 = my_cool_class(4)
obj1:cool_method_of_my_class()
obj2:cool_method_of_my_class()

 

Как только объект становится ненужным (т.е. на него больше нет ссылок) он удаляется сборщиком мусора. При этом вызывается его метод __finalize

 

При чём здесь self.

self - это скрытый первый аргумент. Конструкция вида:

function class_name:fun_name(<список аргументов>)
end

на самом деле эквивалентна конструкции

function class_name.fun_name(self, <список аргументов>)
end

Обратите внимание на двоеточие в первом случае и точку во втором.

При вызове метода конструкция вида:

object_name:fun_name(<список аргументов>)

эквивалентна конструкции

object_name.fun_name(object_name, <список аргументов>)

Это означает, что в качестве self как правило передаётся сам же объект. "Как правило" - это потому, что можно в принципе передать что-то другое. Но лучше этого соглашения не нарушать.

 

Всё это здорово, но какая от этого польза? Ну сделал я свой класс, и что с ним делать?

 

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

 

Итак, кроме создания собственных классов технология Luabind позволяет создавать класы на основе уже существующих. Это реализовано в виде своеобразного наследования. Делается это с помощью следующей конструкции:

 

class "my_derived_class" (base_class)

 

Здесь:

"my_derived_class" как и раньше - это имя нового класса.

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

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

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

 

Итак после выполнения указанной конструкции появляется класс my_derived_class, который является копией класса base_class. У него есть в точности те же методы, что и у его базового класса. Пока от этого мало толку. Но теперь можно переопределить методы нашего класса, изменив таким образом его поведение. Делается это так же, как и ранее, но с некоторыми дополнениями.

 

-- конструктор
function my_derived_class:__init(num) super(num)
end
-- обычный метод
function my_cool_class:some_method(<список аргументов>)
    base_class.some_method(self, <список аргументов>)
end
-- деструктор
function my_cool_class:__finalize()
end

 

На что стОит обратить внимание.

Во-первых, конструкция super(num). Это вызов конструктора базового класса.

Во-вторых, для вызова метода базового класса из метода унаследованного используется другая конструкция base_class.some_method(self, ). Здесь надо использовать альтернативную форму вызова метода (с точкой и явным указанием первого аргумента self).

Наконец, метод __finalize() не требует вызова этого же метода для базового класса, поскольку тот будет вызван автоматически при удалении объекта сборщиком мусора.

 

Собственно и всё. Теперь можно пользоваться созданным классом: создавать объекты этого класса, вызывать их методы и пр. Здесь уже всё зависит от конкретных целей и требует знания конкретных классов.

 

  • Нравится 1
  • Полезно 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)
выносите продвижения в шапку плиз
Делать мегашапку пока рано, ещё даже одной страницы темы нет =)

а не мог бы ты дополнительно рассмотреть пару примеров собственно из сталкера?

С такими же детальными пояснениями.

На очереди object_binder. Там и рассмотрю.

 

А пока вот:

time_global() -- реальное время (в миллисекундах) с начала запуска программы

game.time() -- игровое время (в игровых миллисекундах) с начала игры (т.е. с начала прохождения игры)

level.get_time_days() -- день месяца по игровому времени

level.get_time_hours() -- час текущего игрового дня

level.get_time_minutes() -- минута текущего игрового часа

 

level.get_time_factor() -- возвращает отношение скорости течения игрового времени к скорости реального (game_time_speed / real_time_speed)

level.set_time_factor(number) -- устанавливает это отношение

 

game.get_game_time() -- возвращает игровое время в виде объекта класса CTime. Класс CTime я опишу подробно как-нибудь потом, а пока опишу только пару его методов.

конструктор. Вызывается через пространство имён game

game.CTime()

Дефолтовые значения

year, month, day = 1

hour, min, sek, ms = 0

CTime.set(year, month, day, hour, min, sek, ms) -- устанавливает все данные о времени

метод CTime.get описан так:

function get(number&, number&, number&, number&, number&, number&, number&);

что наводит на мысль о семи аргументах. однако с точки зрения Lua этот метод не принимает аргументов, зато возвращает семь значений. Пример использования обоих методов:

 

local t = game.CTime() -- создания объекта с дефолтовыми значнеиями
t:set(2009, 7, 11, 8, 11, 22, 333) -- установили все значения
local y,m,d,h,min,sec,ms = t:get() -- получили все значения

 

Класс CTime разобран в этом посте

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

Добил object_binder. Получилось многовато, но короче никак. Кроме того, пришлось разбить на две части. Для начала - некоторое введение в ситуацию со скриптовой моделью вообще. В основном трёп всякий =)


Факт первый. Сингловая игра построена на основе мультиплеерного движка. Мультиплеер подразумевает наличие сервера, к которому подключаются клиенты. Все данные (т.е. все объекты игры) существую в первую очередь на сервере, а у клиентов имеются копии этих данных. Естественным образом имеется разграничение обязанностей между клиентом и сервером: сервер отвечает в целом за создание, удаление, хранение объектов, а клиент - за всевозможные _игровые_ манипуляции с ними: отображение, проигрывание анимации и звуков, движение, убиение (но не удаление) и т.п.


Следующий факт. Существует такое понятие онлайн/оффлайн. Что это такое? Дело в том, что загрузить в память все объекты игрового мира сразу невозможно. Для решения этой проблемы конкретно здесь сделаны две вещи:
1. Игра разбита на уровни, и в каждый момент времени загружен только один уровень и, соответственно, загружены только объекты уровня (и давайте не будем обсуждать, как это делают в современных движках). Думаю, с этой идеей всё более или менее ясно.
2. Загружать весь уровень целиком также оказалось накладно. Поэтому в память загружаются только те объекты, которые находятся на некотором разумном расстоянии от игрока. Загрузку объекта называют переходом в онлайн, а выгрузку - переходом в оффлайн. Если конкретнее, то расстояние перехода в онлайн управляется двумя параметрами switch_distance и switch_factor секции alife из файла config\alife.ltx. Их обычные значения:
переход в оффлайн произойдёт на расстоянии switch_distance * (1 + switch_factor)
Теперь вспомним про нашу архитектуру. Нетрудно догадаться, что описанный процесс хорошо ложится на эту архитектуру. А именно: загрузка объекта (переход в онлайн) - это создание его клиентской части, а выгрузка (переход в оффлайн) - это удаление клиентского объекта. Серверный при этом остаётся постоянно. Отсюда и второе название серверного и клиентского объектов - оффлайновый и онлайновый соответственно.
Пошли дальше. Скриптовая модель (т.е. вся совокупность доступных классов) отражает эту архитектуру. Есть классы для серверных объектов, есть классы для клиентских. Есть вспомогательные классы общего назначения. Есть и ещё другие, но не будем забегать вперёд.
Пример серверных классов и функций:
- Часть функций объявленных в пространстве имён game и в глобальном пространстве имён (например alife()).
- Крупная иерархия классов, унаследованных от cse_abstract (который сам по себе ещё унаследован от нескольких классов, но в основном все значимые для скриптования серверные классы унаследованы от него)
Пример клиентских классов и функций:
- Все или почти все функции из пространства имён level
- Иерархия классов, порождённых от CGameObject
- Класс game_object (надеюсь, мы ещё разберём этого уродца)
О других пока не будем.
Важно понять, что в сингловой игре это разделение объектов на серверные и клиентские никуда не делось. Есть всё тот же сервер и есть клиент. Клиент разумеется всего один, и сервер - это разумеется этот-же компьютер. Объекты, которые раньше существовали по разные стороны сети, теперь находятся в одном адресном пространстве. Но их взаимодействие осталось прежним: при создании клиентский объект копирует себе нужные ему данные с серверной стороны, при удалении - сохраняет. В процессе жизни клиентского объекта он синхронизируется с серверным, в основном записывая в него свои данные. Используются ли при этом сетевые технологии, или путь передачи данных несколько оптимизирован для сингла - не известно.

Ещё немного лирики.
В голом мультиплее не так уж и много можно делать: бегать за артефактами и палить друг в друга. Совершенно очевидно, что иначе и быть не могло. Синхронизация всех копий клиентских объектов через сервер явно требует приличных затрат. Синхронизировать в совокупности около 30 игроков ещё возможно, но если наполнить уровень хоть вполовину так, как он наполнен в сингле, то явно ничего уже не выйдет.
Глядя на это или по каким иным причинам искусственный интеллект игры исходно создавался в расчёте исключительно на сингл. Вполне может быть, что ваяли его в спешке и просто не продумали архитектурно. Поэтому пресловутый A-Life вышел и не серверным и не клиентским. Он как бы размазан между этими частями. Так что совместное сафари за кабанчиками в мультиплее нам не светит.

К чему я всё это? К тому, что часть классов довольно сложно отнести к серверным или клиентским.

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

Несколько подробнее о клиентских объектах.
Для непосредственного управления клиентскими объектами существует два типа классов:
- классы, унаследованные от CGameObject. С ними есть одна проблема, большинство этих классов обладают весьма бедным интерфейсом и вообще говоря недоступны для программистов. Исключением являются три класса CHelicopter, CCar и hanging_lamp. До всех остальных попросту никак не добраться.
- Класс game_object, который используется для управления всеми онлайновыми игровыми объектами. Убогий уродец, страшный сон архитектора и головная боль скриптёров. Этот класс объединяет в себе все интерфейсы всех объектов, для управления которыми предназначен. В этом чуде около 300 (три сотни) методов! Вызов метода, не соответствующего типу объекта, может быть просто проигнорирован, а скорее всего приведёт к вылету, причём чаще всего без лога. И при этом никак и нигде не документировано, какие же методы относятся к каким типам объектов. Создать этого монстра - это был несомненно дьявольский план, который пришёл в головы творцов игры вероятно по большой укурке.
Что можно делать с game_object? В основном он позволяет получить разную информацию об онлайновом объекте, изменять некоторые его свойства, а также выполнять над объектом разные действия. Например, можно пнуть объект, проиграть анимацию, вылечить, убить (но не воскресить) и т.п.
Это всё хорошо, но помимо изменения свойств довольно часто надо решить следующую задачу: надо выполнить некое действие, когда с объектом происходит что-то конкретное, например объект переходит в онлайн/оффлайн, получил удар/повреждение, взял/выбросил предмет из инвентаря, наконец помер и т.п..

Как раз для этого у game_object есть методы set_callback, set_patrol_extrapolate_callback и set_enemy_callback, которые позволяют решить эту задачу. Но во-первых, некоторые события так не отследить, например переход в онлайн/оффлайн, а во-вторых, независимое использование этих методов не очень-то удобно. Не буду объяснять почему. Знатоки ООП поймут это сразу, а если не верите, попробуйте наладить мало-мальски структурированную обработку событий только этим методом и всё поймёте сами.
Посему эти методы как правило используются в совокупности с классом object_binder, о чём и пойдёт речь в следующей статье. См. следующую часть.

[alife]
    switch_distance     = 150
    switch_factor       = 0.1
переход в онлайн произойдёт на расстоянии switch_distance * (1 - switch_factor)


Данная статья посвящёна классу object_binder и, кроме того, является в каком-то смысле иллюстрацией к одной из предыдущих статей "Наследование от экспортированных классов".

Один из важнейших классов в скриптовой модели сталкера - это object_binder. Он используется для того, чтобы присоединять к клиентским объектам собственные обработчики событий.
Для того, чтобы использовать object_binder надо:
1. Создать на его основе свой класс. Теорбазис смотри в статье "Наследование от экспортированных классов".
2. В нужном месте с помощью функций set_callback указать методы класса в качестве функций-обработчиков событий.
3. Указать этот класс в свойстве script_binding в секции объекта в файле *.ltx
После создания объекта на основе этой секции будет автоматически создан объект, написанного Вами класса, а его методы начнут вызываться по указанным событиям. Получается, что наш объект как-бы присоединился к клиентскому. Отсюда и его название "биндер" от слова bind (англ. присоединять, привязывать)

Теперь подробности. Класс object_binder - это экспортированный класс CScriptBinderObject.


class object_binder {
    game_object* object;

    object_binder(game_object*);

    void save(net_packet*);
    void update(number);
    void reload(string);
    void net_export(net_packet*);
    bool net_save_relevant();
    void load(reader*);
    void net_destroy();
    void reinit();
    void net_Relcase(game_object*);
    bool net_spawn(cse_alife_object*);
    void net_import(net_packet*);
};


Прежде, чем разобраться с коллбеками, разберём методы класса. В каком-то смысле - они тоже обработчики событий, поскольку вызываются в определённые моменты жизни объекта.
object_binder(game_object*) - конструктор. Хоть и показан на манер C++ как метод, но, как и для многих других классов, вызывается как отдельная глобальная функция. Аргумент - клиентский объект, к которому будем биндиться.

В каком-либо модуле, обычно в том-же, где и класс биндера, должна быть функция следующего вида:

function init(obj)
    local new_binder = my_binder(obj) -- создаём объект биндера
    obj:bind_object(new_binder) -- присобачиваем его к объекту
end

здесь my_binder - это новый класс биндера.
Теперь эту функцию надо указать в секции объекта. Допустим, модуль называется my_cool_binder.script. В этом случае в секции объекта будет такая запись:
script_binding = my_cool_binder.init
При создании объекта на основе этой секции будет вызвана эта функция, которая с помощью метода bind_object класа game_object присоединит наш биндер к свежесозданному объекту.


object - свойство на чтение. Указывает на объект типа game_object, к которому присоединён биндер. Обращаться к свойству надо так: self.object
net_spawn(cse_alife_object*) - вызывается при переходе объекта в онлайн. Аргумент - серверный объект для того клиентского, к которому присоединились. Метод должен вернуть логическое значение. true, если успешно, false, если что-либо не срослось, и в этом случае объект не будет создан, а в логе появится запись: Failed to spawn entity '<имя секции>'
net_destroy() - аналогично вызывается при уходе в оффлайн, т.е. при удалении данного объекта.
update(delta) - очень важный метод. Для большинства объектов этот метод вызывается постоянно, причём период его срабатывания зависит от расстояния до актора. Минимальный период составлял около 40 мс при нахождении предмета в инвентаре ГГ (если он там конечно может находиться). При удалении объекта от ГГ период растёт линейно вплоть до расстояния в 200 метров. На таком расстоянии период составляет одну секунду. После этого расстояния остаётся равным примерно одной секунде. Аргумент этого метода - целое число, которое в точности равно числу миллисекунд, прошедших с прошлого вызова. Правда в точности оно равно только если меньше 1000. Если выше, то оно просто равно 1000, при том, что реальное время может несколько отличаться (может быть больше на пару десятков миллисекунд). Вероятно, это позволяет точно просчитывать физику движения (в том случае, если я решил управлять ею из скрипта). Судя по комментариям в скриптах для некоторых объектов метод update не вызывается.
load(reader*) - метод для загрузки состояния объекта
save(net_packet*) - метод для сохранения состояния объекта
Здесь есть некоторый непонятки. Точно известно, что вызываются эти методы не для всех объектов. Как минимум вызываются для монстров, сталкеров и ГГ. А вот для, скажем, гранаты вызываться не хочет (ни load, ни save). C парностью вызова этих методов судя по всему такая ситуация. save вызывается при сохранении игры (если вообще вызывается), а load вызывается при создании биндера (как описано выше) только в том случае, если до этого был сделан хоть один save. Ну или иными словами, если есть чего загружать. А есть чего загружать, если до этого что-то сохранили.
reload(section) - вызывается при создании объекта. Аргумент - имя секции, на основе которой создан объект.
reinit() - ещё один метод, который вызывается в самом начале. Аргументов нет. Обычно, именно в этом методе устанавливаются колбеки.
net_export(net_packet*) - назначение неизвестно. примеров использования нет
net_import(net_packet*) - назначение неизвестно. примеров использования нет
net_save_relevant() - назначение неизвестно. Если его определяют, то как возвращающий всегда true
net_Relcase(game_object*) - назначение неизвестно. Один пример использования в bind_smart_terrain.script

Кроме указанных методов есть и ещё два, которые есть в любом скриптовом классе на основе luabind, __init и __finalize
__finalize обычно не используется, но для полноты картины стоит упомянуть.
__init - вызывается при создании объекта и получает те-же аргументы, что и конструктор. Фактически - это и есть конструктор (см. теорбазис)
__finalize - вызывается при удалении объекта сборщиком мусора
Кроме того, поскольку мы пишем свой класс, то кроме указанных методов ничто не мешает добавить и свои. См. дальше про коллбеки. Также можно добавить и свои свойства. Это можно сделать так.
self.my_property = 1 -- добавили своё свойство
Надо только помнить, что если не сохранить это свойство в save, то оно пропадёт при уходе объекта в оффлайн. Что делать при этом с теми объектами, у которых save и/или load не вызывается, непонятно.


1. Объект переходит в онлайн и вызывается функция init, прописанная в секции.
2. Там создаётся объект биндера, при этом вызывается конструктор __init
3. Биндер присоединяется к объекту и работа функции init заканчивается
4. Теперь пачкой вызываются методы биндера
reload
reinit
load - если вообще вызывается
netspawn
5. затем начинает вызываться update до тех пор, пока объект находится в пределах радиуса a-life.
6. при уходе объекта в оффлайн вызывается метод net_destroy (апдейты естественно до этого прекращаются)
7. наконец, несколько секунд спустя, объект удаляется окончательно сборщиком мусора. При этом вызывается __finalize

метод save, если вообще вызывается, вызывается при сохранении игры.
Для прочих методов не удалось выяснить ни назначение, ни момент вызова.


Теперь о коллбеках. Для того, чтобы с помощью функции set_callback и биндера сделать свой обработчик события надо:
1. Создать дополнительный метод в нашем классе биндера.
2. Указать этот метод как обработчик определённого события. Как правило, это делается в методе reinit.
function my_binder:reinit()
    object_binder.reinit(self)
    self.object:set_callback(callback.use_object, self.use_callback,   self)
end
function my_binder:use_callback(obj, who)
    --здесь выполняю действия, которые надо сделать при использовании объекта (нажатии на нём клавиши 'F')
end


Подробно разбирать функцию set_callback не будем. Только важные сейчас моменты:
- первый аргумент - один из членов перечисления callback.


C++ class callback {
const action_animation = 20;
const action_movement = 18;
const action_object = 23;
const action_particle = 22;
const action_sound = 21;
const action_watch = 19;
const actor_sleep = 24;
const article_info = 12;
const death = 8;
const helicopter_on_hit = 26;
const helicopter_on_point = 25;
const hit = 16;
const inventory_info = 11;
const inventory_pda = 10;
const level_border_enter = 7;
const level_border_exit = 6;
const map_location_added = 14;
const on_item_drop = 28;
const on_item_take = 27;
const patrol_path_in_point = 9;
const script_animation = 29;
const sound = 17;
const take_item_from_box = 33;
const task_state = 13;
const trade_perform_operation = 3;
const trade_sell_buy_item = 2;
const trade_start = 0;
const trade_stop = 1;
const trader_global_anim_request = 30;
const trader_head_anim_request = 31;
const trader_sound_end = 32;
const use_object = 15;
const zone_enter = 4;
const zone_exit = 5;
};


В общем видно, что можно сделать. Даже иногда понятно, для какого объекта применимо. Надо заметить, что для конкретного типа объектов будут работать только конкретный набор коллбеков. Например, точно известно, что для неписей работает death и hit. Для ГГ работает death, on_item_drop и on_item_take. Для ящиков - use_object. А вот для гранат похоже ничего не работает.
Списка работающих коллбеков для каждого типа объектов пока ещё никто не составил.

Теперь простой пример, который я сделал для ящика


function init(obj)
    local new_binder = my_binder(obj)
    obj:bind_object(new_binder)
end

class "my_binder" (object_binder)
function my_binder:__init(obj) super(obj)
    get_console():execute("my_binder:__init")
end

function my_binder:reload(section)
    get_console():execute("my_binder:reload")
    object_binder.reload(self, section)
end

function my_binder:reinit()
    get_console():execute("my_binder:reinit")
    object_binder.reinit(self)
    self.object:set_callback(callback.use_object, self.use_callback,   self)
end

function my_binder:update(delta)
    local actor_pos = db.actor:position()
    local obj_pos = self.object:position()
    local dist = actor_pos:distance_to(obj_pos)
    get_console():execute("my_binder:update_dist="..dist.."_delta="..delta)
    object_binder.update(self, delta)
end

function my_binder:net_spawn(data)
    get_console():execute("my_binder:net_spawn")
    return object_binder.net_spawn(self, data)
end

function my_binder:net_destroy()
    get_console():execute("my_binder:net_destroy")
    object_binder.net_destroy(self)
end

function my_binder:net_save_relevant()
    get_console():execute("my_binder:net_save_relevant")
    return true
end

function my_binder:save(packet)
    get_console():execute("my_binder:save")
    object_binder.save(self, packet)
end

function my_binder:load(reader)
    get_console():execute("my_binder:load")
    object_binder.load(self, reader)
end

function my_binder:use_callback(obj, who)
    get_console():execute("my_binder:use_callback")
end



кроме того в файле gamedata\config\misc\devices.ltx
добавил новую секцию

[inventory_box_my]:inventory_box
script_binding      = my_cool_binder.init


это будет ящик с визуалом ящика с динамитом с одним только отличием от стандартного - у него будет нестандартный наш биндер.


На очереди net_packet. Простой, но весьма важный класс.

net_packet - это один из вспомогательных классов. Представляет собой буфер размером ровно 8 кбайт (т.е. 8192 байта). Из буфера можно последовательно читать и записывать данные, используя методы класса. Имеется текущая позиция чтения и записи. Теперь подробности.


class net_packet {
    net_packet ();

    number w_tell();
    number r_tell();
    void w_begin(number);
    function r_begin(number&);
    function r_advance(number);
    function r_seek(number);
    number r_elapsed();
    bool r_eof();

    number r_u8();
    void w_u8(number);
    number r_s8();
    
    number r_u16();
    void w_u16(number);
    number r_s16();
    void w_s16(number);
    number r_u24();
    void w_u24(number);
    
    number r_u32();
    void w_u32(number);
    number r_s32();
    void w_s32(number);

    number r_u64();
    void w_u64(unsigned __int64);
    number r_s64();
    void w_s64(__int64);
    
    number r_float();
    function w_float(number);
    string r_stringZ();
    void w_stringZ(string);
    bool r_bool();
    void w_bool(bool);

    vector r_vec3();
    void w_vec3(const vector&);
    matrix r_matrix();
    void w_matrix(matrix&);

    vector r_sdir();
    void w_sdir(const vector&);
    vector r_dir();
    void w_dir(const vector&);
    
    number r_angle8();
    void w_angle8(number);
    number r_angle16();
    void w_angle16(number);

    function r_float_q8(number&, number, number);
    function w_float_q8(number, number, number);
    function r_float_q16(number&, number, number);
    function w_float_q16(number, number, number);

    function w_chunk_open8(number&);
    function w_chunk_open16(number&);
    function w_chunk_close16(number);
    function w_chunk_close8(number);

    ClientID r_clientID();
    void w_clientID(ClientID&);
};

Я немного изменил описание по сравнению с оригинальным из lua_help.script. Добавил типы возвращаемых значений, перегруппировал и убрал некоторую шелуху.

Более подробно о методах класса:
net_packet() - конструктор, вызывается в виде глобальной функции так:
local packet = net_packet()
созданный пакет по умолчанию имеет позиции чтения и записи установленные в 0
Не всегда надо создавать свой пакет. Часто приходится иметь дело с уже готовым пакетом (см. следующую статью об использовании нетпакетов)

w_tell() - возвращает текущую позицию записи
r_tell() - возвращает текущую позицию чтения
r_advance(shift) - смещает позицию чтения на shift байт. Смещение может быть отрицательным.
r_seek(pos) - устанавливает позицию чтения в pos
r_elapsed() - возвращает w_tell() - r_tell()
r_eof() - возвращает true, если r_tell() < w_tell(), иначе false

w_begin(number) - пишет двухбайтовое число в начало пакета и устанавливает позицию записи равной 2. Единственным другим способом начать запись с начала пакета - это создать новый пакет, у которого позиция записи установлена в 0.
r_begin(number&) - Непонятный метод. По аналогии с w_begin должен читать с начала пакета два байта и устанавливать позицию чтения в 2. Однако у меня приводит к вылету. Ну, в любом случае можно обойтись и без него.

Важный момент! Нет никакого способа узнать, что в процессе чтения или записи позиция чтения или записи вышла за пределы пакета. При чтении подразумевается, что прочитать можно столько, сколько записано. Для этого и есть функции r_elapsed() и r_eof(). А вот при записи никакой границы сверху нет, поэтому можно запросто записать больше, чем 8 кбайт. Ясно, что при этом произойдёт переполнение буфера с почти обязательным последующим вылетом игры. Так что надо самостоятельно следить за размером позиции записи и проверять, чтобы при последующей записи она не вышла бы за размеры пакета. Для этого надо знать, сколько мы запишем, ещё до того, как запишем. В особенности это важно для строк, которые имеют переменную длину. Поступаем примерно так:

if string.len(s) + 1 + packet:w_tell() > 8192 then
    -- здесь делаем что-то, например крашим игру и выводим сообщение, что не надо жадничать =)
end
packet:w_stringZ(s)

Обратите внимание на "+ 1" в вычислении новой позиции записи. Строки имеют дополнительный невидимый нулевой символ в конце, и их физическая длина на один больше, чем длина в символах, которую возвращает функция string.len().

Остальные методы предназначены собственно для чтения и записи. Описывать их все в подробностях нет смысла, только общий принцип. Читаем так:
local value = packet:r_XXX()
здесь XXX - это тип значения, которое читается. Значение читается из буфера начиная с текущей позиции чтения, а позиция чтения увеличивается на размер читаемого значения. В переменную value будет помещено значение того типа, которое прочитано.
аналогично пишем в буфер:
packet:w_XXX(value)
Значение пишется начиная с текущей позиции записи, а позиция записи увеличивается на размер типа XXX.
для целых типов:
s - знаковое значение
u - беззнаковое значение
8, 16, 24, 32, 64 - один, два, три, четыре, восемь байт соответственно
Обратите внимаение, что для типа s8 (знаковый байт) нет метода записи. Однако, вместо него без проблем можно использовать соответствующий метод для беззнакового типа w_u8.
float - число с плавающей запятой одинарной точности, 4 байта
stringZ - строка (размер равен длине + 1 байт)
bool - логическое значение (1 байт)
vec3 - объект типа vector - вектор из трёх float (12 байт)
matrix - объект типа matrix. Состоит из 4-х векторов (48 байт)
sdir - ? непонятно, на запись берёт вектор и пишет 6 байт
dir - ? аналогично, но пишет 2 байта
angle8 - ? берёт float и пишет 1 байт
angle16 - ? аналогично, но пишет 2 байта
четыре последних метода при чтении у меня вызывают вылет. зачем нужны, неизвестно.
ClientID - объект класа ClientID. Судя по всему, это надо для сетевой игры.
float_q8, float_q16 - пока непонятно
Назначение методов с w_chunk_ вообще непонятно, тем более, что для них отсутствуют соответствующие методы на чтение.


Собственно, законное использование нетпакетов - это буфер, в котором объект сохраняет своё состояние. Скорее всего это используется в первую очередь при передаче данных по сети. Потому и net_packet, т.е. буквально "сетевой пакет". Сначала объект записывает себя в пакет, затем он отправляется по сети. Похоже, однако, что используется не только при передаче по сети, но и для сохранения объектов вообще.
Теперь конкретнее.
1. Сохранение состояния серверного класса
На серверной стороне есть классы, унаследованные от cse_abstract. У них есть методы STATE_Read и STATE_Write.
метод STATE_Read вызывается при загрузке состояния объекта из сохранёнки, в нём данные читаются из переданного методу пакета. STATE_Write вызывается при сохранении объекта, в нём данные объекта сохраняются в пакет.
Если создать свой класс и перегрузить эти методы, то увидим такую картину:

class "se_my_server_class"    (<имя_базового_класса>)

function se_my_server_class:STATE_Write(packet)
    <имя_базового_класса>.STATE_Write(self, packet) -- базовый класс сохраняет свои данные
    -- здесь можно сохранить какие-то данные, в дополнение к данным базового класса
    packet:w_stringZ("моя строка")
end

function se_my_server_class:STATE_Read(packet, size)
    <имя_базового_класса>.STATE_Read(self, packet) -- здесь базовый класс читает своё состояние из пакета
    -- здесь можно прочитать состояние своего класса, которое было сохранено ранее
    local s = packet:r_stringZ() -- получим строку "моя строка"
end


2. Сохранение состояния клиентского класса
На клиентской стороне также имеется нечто подобное. Это реализуется методами биндера save и load. При создании биндера (см. мою статью насчёт класса object_binder) можно в этих методах что-то сохранить. Однако, нетпакет используется для сохранения в методе save. в этом он подобен методу STATE_Write серверного класса. А вот при загрузке почему-то вместо нетпакета передаётся поток на чтение (класс reader). Таким образом трюкачество, описанное в следующем пункте для клиентских объектов не выйдет.

3. Перепаковка серверных объектов с целью изменения их параметров
Предыдущие два варианта использования нетпакетов - это так сказать "законное" их использование. Но это не всё. Ничто не мешает вызывать методы STATE_Read и STATE_Write в произвольный момент времени, имитируя процесс сохранения и загрузки объекта. При этом можно сделать следующее:

local packet = net_packet() -- создаём пустой пакет
sobj:STATE_Write(packet) -- загрузили в наш пакет состояние серверного объекта
-- используя методы класса net_packet меняем нужные нам значения.
sobj:STATE_Read(packet, packet:w_tell()) -- записали в объект изменённое состояние обратно, имитируя процесс его загрузки


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


 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

Несколько уточнений:

Этот класс отвечает за движение неписей по путям. Информацию о путях берет только из алл.спавна. Переопределить это нельзя без ковыряния в движке, откуда следует невозможность задавать пути извне алл.спавна. Для решения этой задачи надо как-то обойти класс patrol.
Файл all.spawn можно дописывать, добавляя туда дополнительные точки переходов. Вопреки распространённому мнению, новую игру при этом начинать не надо.

 

index(string) - нет ни примеров использования, ни предположений, что это могло бы значить.
Вроде как это метод, обратный name. Т.е. по имени точки возвращает её номер.

 

Как обрабатываются связи между точками, до конца не ясно, могу только предположить, что для этого служит одна из этих команд:

patrol...

Но которая, и что она возвращает, и что требует в качестве аргументов - непонятно.

Это такие же конструкторы, как и patrol с одним только именем пути. Просто у них ещё есть дополнительные аргументы.

 

И, как мне думается, за само движение отвечает класс entity_action, которому передаётся объект класса move, у которого patrol - это аргумент. Короче, копать и копать ещё, но начало неплохое. Там глядишь, и разберёмся, как свои скриптовые схемы ваять.

 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)
Про новую игру при добавлении путей вроде никто и не говорил :unsure: Про новую игру говорят при добавлении новых объектов, изменении старых

Я добавил level_changer, доспавнил его функцией create(<номер>) и ничего мне за это не было =)

 

Если кому это надо, то добавлю вот что. Я делал тестовый объект на кордоне, и сначала попробовал добавить секцию в файл для кордона. Выяснил, сколько всего объектов, сделал секцию с номером, на один большим...

У меня заспавнился не тот. Начал выяснять, откуда он взялся, и понял, что это последний объект из последнего уровня (ящик какой-то). Тогда я просто добавил новый объект в конец последнего уровня, и все получилось.

Мораль, номер объекта из all.spawn - это просто его номер в порядке компиляции. Зачем acdc разбирает объекты по уровням, не знаю. Но на мой взгляд, после этого они перетасованы. Если каждый раз начинать игру заново, то это и без разницы. Но если мы хотим использовать пересобранный all.spawn не начиная игру заново, то видимо acdc не годится.

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

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

[spoiler=Описание класса render_device:]Класс render_device позволяет получить свойства экрана и камеры: разрешение, направление взгляда и т.п.

Объект такого класса всего один и возвращает его глобальная функция device():

local dev = device()

[spoiler=Описание класса из lua_help.script (с некоторыми моими дополнениями)]

class render_device {
    int height; // вертикальное разрешение
    int width; // горизонтальное разрешение
    float aspect_ratio; // отношение height/width
    vector cam_pos; // вектор положения камеры - точка, откуда глядим
    vector cam_dir; // единичный вектор направления взгляда
    float fov; // угол обзора в градусах
    vector cam_right; // единичный вектор, перпендикулярный направлению камеры и направленный вправо
    vector cam_top; // единичный вектор, перпендикулярный направлению камеры и направленный вверх
    float f_time_delta; // время между кадрами в с.
    int frame; // номер выведенного кадра
    int precache_frame; // число не выведенных кадров
    int time_delta; // время между кадрами в мс.

    int time_global(); // реальное время в мс.
    bool is_paused(); // состояние остановленности
    void pause(bool); // остановить/возобновить игру
};

Далее предполагаю, что объект типа render_device получен и записан в переменную dev.

Свойства (все только для чтения):

dev.height и dev.width - вертикальное и горизонтальное разрешение соответственно. Обращаю внимание, что независимо от настоящего разрешения экрана, элементы пользовательского интерфейса (статики и пр.) рисуются на экране в сетке 1024*768. Но при этом их размеры всё-таки указываются в реальных пикселях. Не запутались? Вот к примеру, если вы рисуете статик по координатам [512,384], то он нарисуется всегда в центре, независимо от разрешения, но его размеры при разрешении 1600*1200 будут в два раза меньше, чем при разрешении 800*600.

dev.fov - угол обзора актора в градусах. Это угол, который меряется из точки взгляда и определяется высотой экрана (как наименьшей из сторон). Дефолтовое значение этого угла составляет 67.5 градусов. Это число забито в код движка и мне встречались утилитки для его исправления. При масштабировании (с биноклем или прицелом) это число меняется. Собственно это можно использовать для определения входа в режим увеличения. Также это число можно использовать для вычисления проекции точки на экран.

dev.cam_pos и dev.cam_dir - положение камеры и направление взгляда. Это в точности та точка, откуда стреляем, и то направление, в котором стреляем. Есть отличие от методов position() и direction() класса game_object. position() - это положение точки на земле, а direction() определяет не направление взгляда, а направление модели актора (или непися). А направление это меняется рывками и не может служить для точного определения направления взгляда.

При проецировании точки на экран:

dev.cam_pos соответствует центру экрана (всегда точка 512*384)

dev.cam_dir направлен перпендикулярно экрану.

dev.cam_right совпадает с плоскостью экрана и направлен строго вправо

dev.cam_top совпадает с плоскостью экрана и направлен строго вверх

dev.frame - номер отрисованного кадра. Растёт постоянно. Можно определять FPS из скрипта. Зачем ещё может понадобиться - без понятия.

dev.precache_frame - не вполне понятное значение. Возможно, количество фреймов, готовых для вывода, но ещё не выведенных на экран. Могу здесь и ошибаться, но свойство это используется для определения готовности устройства рендеринга примерно так:

if dev.precache_frame >1 then <устройство не готово> end

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

dev.f_time_delta - число с плавающей запятой, время между кадрами в секундах, т.е. значение, обратное FPS

dev.time_delta - аналогично, но целое и измеренное в мс.

 

Функции:

dev:time_global() - возвращает в точности то-же значение, что и глобальная функция time_global(). В силу этого вероятно не имеет особенного смысла её использовать, хотя в скрипте _g.script имеются вот такие строчки:

if nil == time_global then
  time_global = function () return device():time_global() end
end

т.е. если такой глобальной функции нет, то она подменяется вот этой. Уж не знаю, для какой версии движка эта заплатка, но в текущей она не используется.

dev:pause(true/false) - позволяет программно выполнить паузу, как если бы вы нажали кнопку "Pause/Break".

dev:is_paused() - соответственно, возвращает состояние остановленности.

 

 

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

function point_projection(point)
    local dev = device()
    local scr_w = dev.width
    local scr_h = dev.height

    local fov2 = (dev.fov/2) * (math.pi/180)
    local scr_dist = 0.5 * scr_h / math.tan(fov2)

    local ppp = vector():sub(point, dev.cam_pos)
    local dp = dev.cam_dir:dotproduct(ppp)

    local x = 512 + dev.cam_right:dotproduct(ppp) * scr_dist / dp * (1024/scr_w)
    local y = 384 - dev.cam_top:dotproduct(ppp)   * scr_dist / dp * (768/scr_h)
    return x, y
end

Функция учитывает возможное разрешение экрана, режим зума и то, что камера может наклоняться. Можно использовать в различных целеуказателях.

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

На очереди класс FS. Если кому интересно, то внутри самого движка он имеет имя CLocatorAPI. Это класс для работы с файловой системой и файлами.

Экземпляр класса FS можно получить с помощью глобальной функции getFS()

local fs = getFS()

class FS {
    // флажки, используются в методах file_list_open и file_list_open_ex
    const FS_ListFiles = 1;
    const FS_ListFolders = 2;
    const FS_ClampExt = 4;
    const FS_RootOnly = 8;
    // константы используются в методе Sort класса FS_file_list_ex
    const FS_sort_by_name_up = 0;
    const FS_sort_by_name_down = 1;
    const FS_sort_by_size_up = 2;
    const FS_sort_by_size_down = 3;
    const FS_sort_by_modif_up = 4;
    const FS_sort_by_modif_down = 5;

    fs_file* exist(<абсолютный путь до файла>); // если файл с абсолютным путём существует, то возвращает объект типа fs_file, иначе nil
    fs_file* exist(<корень>, <путь относительно корня>); // аналогично, но для корень+относительный путь
    DWORD get_file_age(<абсолютный путь до файла>); // количество секунд, прошедших с 1 января 1970 года
    string get_file_age_str(<абсолютный путь до файла>); // строка со временем создания файла в виде: "Thu Sep 17 03:39:24 2009"
    int file_length(<абсолютный путь до файла>); // получить длину файла
    void file_copy(<абсолютный путь до файла 1>, <абсолютный путь до файла 2>); // копировать файл
    void file_rename(<абсолютный путь до файла 1>, <абсолютный путь до файла 2>, boolean); // третий логический аргумент определяет, будет ли файл замещать существующий
    void file_delete(<корень>, <путь относительно корня>);
    void file_delete(<абсолютный путь до файла>);

    function append_path(string, string, string, number); // не понятно, что делает
    string update_path(<корень>, <путь относительно корня>) // составляет абсолютный путь из корня и относительного пути
    void dir_delete(<абсолютный путь>, number); // назначение второго вргумента непонятно
    void dir_delete(<корень>, <путь относительно корня>, number); // аналогично
    FS_file_list* file_list_open(<корень>, number); // все три функции получают список файлов для указанной директории
    FS_file_list* file_list_open(<корень>, <маска>, number);
    FS_file_list_ex* file_list_open_ex(<корень>, number, <маска>);
    // во всех трёх функциях числовой аргумент - это набор флажков, составленных из FS_ListFiles, FS_ListFolders, FS_ClampExt и FS_RootOnly
    //их название говорит за себя, но эффект у меня был только от 
    //FS_ListFiles - получить список файлов
    //FS_ClampExt - убрать из имён файлов расширение
    bool path_exist(<корень>); // проверяет существование корня
    function get_path(string); // не работает

    reader* r_open(<корень>, <путь относительно корня>); // открыть файл на чтение
    reader* r_open(<абсолютный путь до файла>);// открыть файл на чтение
    void r_close(reader*&); // закрыть поток чтения. У меня всегда вызывало вылет
    IWriter* w_open(string, string); // не работает
    IWriter* w_open(string); // не работает
    void w_close(class IWriter*&); // не работает
};

Дополнительные классы:

class fs_file {
    int modif; // время создания в секундах, прошедших с 1 января 1970 года
    string name; // полный путь
    number ptr;
    int size_compressed; // пожатый размер (не совсем ясно, что имеется в виду. Возможно - не используемая сейчас фишка упаковки файлов в игровых архивах)
    int size_real; // реальный размер в байтах
    int vfs;
};

Объект типа fs_file возвращает метод exist. Если файл не найден, то возвращается nil. С одной стороны это можно использовать для проверки существования файла, а с другой, не отходя от кассы можно посмотреть свойства файла. Свойство name рекомендуется использовать в дальнейшем в тех методах FS, где требуется полный абсолютный путь к файлу. У меня случались вылеты, когда я прописывал путь руками.

class FS_file_list {
    void Free(); // надо вызвать после использования
    string GetAt(number); // получить имя файла в списке как строку
    int Size(); // количество файлов в списке
};

Объект типа FS_file_list возвращает метод file_list_open. Это класс, который хранит список файлов в виде обычного списка строк. По каким-то причинам после использования этого класса надо вызывать его метод Free.

class FS_file_list_ex {
    function Sort(number); // сортировать по признаку. Указывается одна из констант FS.FS_sort_by_xxx
    FS_item* GetAt(number); // получить информацию о файле в виде объекта типа FS_item.
    int Size(); // количество файлов в списке
};

Объект типа FS_file_list_ex возвращает метод file_list_open_ex. Обладает большими возможностями, чем FS_file_list. Позволяет сортировать список файлов и получать расширенную информацию о каждом в виде объекта типа FS_item.

class FS_item {
    string Modif(); // время изменения в виде: "Thu Sep 17 04:15:08 2009"
    string ModifDigitOnly(); // время изменения в виде: "17:09:2009 04:15"
    string NameShort(); // имя файла без пути (и без расширения, если использован флажок FS.FS_ClampExt)
    string NameFull(); // почему-то никакой разницы с NameShort
    int Size(); // размер в байтах
};

Дополнительная информация:

1. В тех методах класса FS, где указаны два аргумента <корень> и <путь относительно корня>, под корнем имеются в виду строки вида "$<имя_корня>$". Эти стандартные (для игры) пути в основном прописаны в файле fsgame.ltx, который расположен в каталоге установки игры. Причем один из таких путей, а именно "$fs_root$" там не прописан, поскольку игра и так его знает из реестра. Это и есть каталог установки игры. Почти все остальные являются от него производными. Есть ещё отдельный путь "$app_data_root$", который обычно прописан явно в системном профиле пользователя и является базовым для таких путей, как место сохранения, логи и скриншоты.

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

2. Класс FS позволяет обращаться к файлам, находящимся в игровых архивах.

3. Не любой файл удаётся открыть на чтение. Почему-то некоторые файлы при попытке открыть их вызывают бессловесный вылет. Надо пробовать.

4. Класс reader я здесь не описываю. В принципе там и так всё ясно, но можно почитать описание для net_packet. В части для чтения список методов очень похож.

5. Если на чтение из файла есть класс reader то вот на запись класса нет. Метод w_open возвращает не экспортированный класс IWriter, и у меня в любом случае приводил к вылетам. Так что с записью облом. Кстати, два метода класса FS - get_path и append_path судя по всему тоже возвращают экземпляры не экспортированного класса (FS_Path) и тоже приводят (у меня по крайнем мере) к вылетам.

 

 

Ещё один класс, имеющий отношение к файловой системе - это CSavedGameWrapper. С помощью этого класса можно получить информацию о сохранённой игре

class CSavedGameWrapper {
    CSavedGameWrapper (<имя сохранёнки без расширения>);

    string level_name(); -- системное имя уровня
    int level_id(); -- номер уровня
    CTime* game_time(); -- игровое время
    number actor_health(); -- здоровье игрока
};

local flist = getFS():file_list_open_ex("$game_saves$",bit_or(bit_or(FS.FS_ListFiles,FS.FS_RootOnly), FS.FS_ClampExt) , "*.sav")
local f_cnt = flist:Size()
local con = get_console()
if f_cnt > 0 then
    local file = flist:GetAt(0)
    con:execute("FullName="..file:NameFull())
    local sg = CSavedGameWrapper(file:NameFull())
    
    local y,m,d,h,min,sec,ms = sg:game_time():get()
    local level_name = sg:level_name()
    con:execute("level_name="..level_name)
    local level_id = sg:level_id()
    con:execute("level_id="..level_id)
    local actor_health = sg:actor_health()
    con:execute("actor_health="..actor_health)
end

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Попробуем приступить к game_object. Это класс для доступа к онлайновым (клиентским) объектам. Причём один класс является интерфейсом для совершенно разных объектов. Разработчики не придумали ничего лучше, как взять и объединить в одном классе все интерфейсы всех клиентских объектов: актора, сталкеров, монстров, физических объектов, автомобилей и лампочек, вообще всех. Более странного и вообще говоря уродливого объектно-ориентированного дизайна я ещё не видел. Во-первых, класс вышел совершенно необозримым - три сотни методов! Во-вторых, вызов не подходящего метода для произвольно взятого объекта приводит к совершенно непредсказуемым результатам. В лучшем случае не будет ничего, а чаще всего - будет вылет, причём обычно без лога. Наконец, описание этого класса в lua_help совершенно невнятное (как впрочем и всех остальных классов): типы возвращаемых значений опущены, типы входных аргументов указаны не всегда, а о назначении большинства методов можно только гадать. Предлагаю несколько более внятное описание. Источником информации служил в первую очередь документ из билда 1935, где описаны (весьма лаконично) многие методы этого класса. Во вторую очередь использована отладочная информация из мультиплеерных билдов, что позволило довольно точно восстановить типы аргументов и возвращаемых значений. Для некоторых функций имеются описания, полученные либо мной лично либо подсмотренные на форумах. Так что предлагаю рассматривать всё это как плод коллективного творчества.

Пара замечаний:

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

2. Вследствие большого объёма причёсывать сил не осталось, так что выкладываю как есть. В некоторых местах осталась информация стороннего характера - сравнение с ранними билдами, внутренние названия функций и некоторые мои собственные домыслы.

3. Я перетасовал список методов с целью их осмысленной группировки "по родству". Очевидно, что мог и заблуждаться насчёт "родства" методов, так что смотрите в оба.

4. Поскольку описание не закончено, то предлагаю всем поучаствовать в завершении этого этапа.

 

class game_object {

 

const dialog_pda_msg = 0;

const info_pda_msg = 1;

const no_pda_msg = 2;

 

 

// enum MovementManager::EPathType {

// ePathTypeGamePath,

// ePathTypeLevelPath,

// ePathTypePatrolPath,

// ePathTypeNoPath,

// ePathTypeDummy,

// };

const game_path = 0;

const level_path = 1;

const patrol_path = 2;

const no_path = 3;

const dummy = -1;

 

//

const friend = 0;

const neutral = 1;

const enemy = 2;

 

//

const relation_kill = 0;

const relation_attack = 1;

const relation_fight_help_human = 2;

const relation_fight_help_monster = 4;

//

// enum ScriptEntity::EActionType {

// eActionTypeMovement,

// eActionTypeWatch,

// eActionTypeAnimation,

// eActionTypeSound,

// eActionTypeParticle,

// eActionTypeObject,

// eActionTypeCount,

// }

 

const movement = 0;

const watch = 1; ????????????

const animation = 2;

const sound = 3;

const particle = 4;

const object = 5;

const action_type_count = 6;

//

Члены класса:

//visible – видимость -- нет такого

//enabled – доступность -- нет такого

//satiety – сытость -- нет такого

//circumspection – осторожность -- нет такого

---------------------------------- свойства --------------------------------------------

property health; // здоровье 0..1

property morale; // мораль

property power; // сила

property psy_health;

property radiation; // радиация

// все свойства на запись работают не так, как на чтение. При записи значение имеет смысл

// изменения соответствующего свойства. Т.е. если записать 0.1 в health, то это увеличит его на 0.1

---------------------------------- методы ----------------------------------------------

// самые основные

vector position() – получить позицию

int id() -- уникальный идентификатор объекта. 1..65534, 0 - актор, 65535 - ничей, имеет специальное значение

int clsid() –- идентификатор класса (один из членов класса clsid)

int story_id() -- сюжетный идентификатор. Также уникальный, если есть. Если нет, то -1

string section() -– секция в system.ltx

string profile_name() -- профиль (тот, что в XML)

string name() -- имя. Это системное имя, обычно уникальное. Является таким же идентификатором, как и id

ini_file *spawn_ini() -- возвращает указатель на custom data

int game_vertex_id() -- номер текущей вершины глобального графа,

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

int level_vertex_id() -- номер текущей вершины локального графа уровня,

-- который используется только для навигации клиентских (онлайновых) объектов

vector direction() -- направление

int parent() –- кому принадлежит. Для предметов в инвентаре и в ящиках. Если никому, то -1

float mass() - масса

float accuracy() -- точность (вроде бы сталкеров)

int cost() - стоимость

float condition() -- состояние предмета

void set_condition(float) -- установить состояние предмета

void kill(game_object*) -- убить, аргумент - кто убийца

int death_time() – время смерти, если помер

float get_bleeding() -- кровотечение. Для изменения функции нет, но можно использовать отрицательный хит

 

vector center() -- центр физической оболочки

 

float fov() //угол зрения неписей

void set_fov(float) //установить угол зрения неписей

float range() – максимальная видимость

void set_range(float)

// функции перемещения и установки направления для актора. Можно использовать для телепорта актора в пределах уровня

void set_actor_position(vector);

void set_actor_direction(number);

// функции для работы со слотами. Слоты есть и у сталкеров, не только у актора

int active_slot(); // текущий активный слот

game_object* item_in_slot(number) // объект в слоте

void activate_slot(number); // выбрать слот текущим

game_object* active_item(); // объект в текущем активном слоте

 

int object_count() -- количество объектов в инвентаре

game_object* object(string) -- возвращает объект из инвентаря по секции

game_object* object(number) -- возвращает объект из инвентаря по индексу. Индекс в диапазоне от 0 до object_count()-1

-- такой код эквивалентен использованию приведённой далее функции iterate_inventory()

    local cnt = st:object_count()
    for i=0,cnt-1 do
        local item = st:object(i)
        ...
    end

bool is_inv_box_empty() -- для инвентаря ящика

// функции перебора предметов в инвентаре

void iterate_inventory(function* iterator, game_object* obj) -- перебрать предметы, принадлежащие NPC

-- перебирает все предметы, независимо от возможности продажи. Работает для монстров

-- iterator - это функция вида:

    function iterate_inventory_action(obj, item)
    --  здесь:
    --   obj - объект, переданный ранее в функцию iterate_inventory
    --   item - текущий предмет
    end

 

void inventory_for_each(function *iterator) -- перебрать предметы в инвентаре

-- работает только для актора и сталкеров. Не работает для монстров (при наличии предмета у монстра - вылет).

-- iterator - это функия вида:

    function inventory_for_each_action(item, npc)
    --  здесь:
    --   npc - сталкер или актор, для которого перебираем
    --   item - текущий предмет
    end

// Есть несколько отличий от функции iterate_inventory.

// Перебираются не все предметы. Для актора пропускаются предметы в невидимых слотах: нож, бинокль, болт, фонарик и ПДА. Кроме того, для актора определён порядок перебора предметов: сначала перебираются предметы в рюкзаке, потом на поясе, потом в слотах (только те, что видны). На этом основан алгоритм определения предметов на поясе (в слотах для артов). Спасибо Колмогору за дополнение!

// Для сталкеров inventory_for_each возвращает список предметов, которые они выставляют на продажу. По крайней мере у меня так всегда выходит. Пояса у сталкеров нет вообще. Слоты есть, но предметы из слотов в список, перебираемый inventory_for_each, не попадают.

// обе указанных функции не работают для ящиков. Вылета не дают, но и не перебирают. Единственным известным мне способом определения списка предметов в ящике является полный перебор объектов и сравнение их parent с id ящика.

 

int get_ammo_total() – функция вызывается для оружия. Если ствол не в инвентаре, то возвращает количество патронов в обойме. Если в инвентаре, то возвращает суммарное количество доступных для этого ствола патронов как в его обойме, так и в инвентаре хозяина.

int get_ammo_in_magazine() -– для стволов. Вернуть количество патронов в магазине

void set_ammo_elapsed(int) -- установить число патронов в магазине. Патроны берутся из ниоткуда, настройки ствола по максимальной вместимости магазина игнорируются, т.е. можно к примеру в ПМ зарядить 100 патронов

//

void unload_magazine() -- разрядить магазин. Точнее установить число патронов в магазине равным нулю. Патроны при этом пропадают. Это эквивалентно вызову set_ammo_elapsed(0)

 

void change_team(int, int, int);

int team() – команда

int squad() – отряд

int group() – группа

 

// состояние

float max_health() – максимальное здоровье

bool critically_wounded();

bool alive() –- живой или неживой

bool wounded() -- состояние ранености

void wounded(boolean) -- изменить состояние ранености

//

enum MovementManager::EPathType path_type()

void set_path_type(enum MovementManager::EPathType)

enum DetailPathManager::EDetailPathType detail_path_type()

void set_detail_path_type(enum DetailPathManager::EDetailPathType)

//

enum MonsterSpace::EMentalState mental_state()

enum MonsterSpace::EMentalState target_mental_state()

void set_mental_state(enum MonsterSpace::EMentalState);

//

enum MonsterSpace::EBodyState body_state()

enum MonsterSpace::EBodyState target_body_state()

void set_body_state(enum MonsterSpace::EBodyState);

//

enum MonsterSpace::EMovementType movement_type()

enum MonsterSpace::EMovementType target_movement_type()

void set_movement_type(enum MonsterSpace::EMovementType);

//movement

void movement_enabled(boolean); // enable_movement

bool movement_enabled();

//

void bind_object(object_binder*) -- установить биндер

object_binder* binded_object() -- вернуть текущий биндер

//

// возвращает специальные объекты

CCar* get_car()

CHelicopter* get_helicopter()

hanging_lamp* get_hanging_lamp()

// только для аномалий

float get_anomaly_power();

void set_anomaly_power(float);

void enable_anomaly();

void disable_anomaly();

//

bool weapon_strapped() -- только сталкеры. Показывает, что оружие висит за спиной (если есть)

bool weapon_unstrapped() -- только сталкеры. Значение, обратное weapon_strapped.

--Хотя логично было бы в случае отсутствия оружия показывать false

-- управление оружием выполняется через метод set_item

void restore_weapon() -- только для актора. Достать оружие из активного слота

void hide_weapon() -- только для актора. Прячет оружие.

// функции с memory в имени имеют отношение к "памяти" неписей. Можно получать

// разную информацию о том, что видел, слышал непись и т.п.

int memory_time(const game_object&);

vector memory_position(const game_object&);

// возвращают таблицу из объектов – можно итерировать как по таблице LUA.

<таблица объектов not_yet_visible_object> not_yet_visible_objects() -– объекты из памяти, которые видны или собираются стать видны.

<таблица объектов CVisibleObject> memory_visible_objects() –- объекты из памяти, которые видны или были когда-то видны.

<таблица объектов CSoundObject> memory_sound_objects() -– объекты из памяти, которые слышны или были когда-то слышны.

<таблица объектов CHitObject> memory_hit_objects() -– объекты из памяти, которые наносят или наносили повреждения.

void enable_memory_object(game_object*, bool enable) -- активизировать/деактивизировать объект из памяти

// почему аргументом стоит game_object? непонятно... вот старое описание из 1935

//void enable_memory_object(memory_object&, bool enable) – активизировать/деактивизировать объект из памяти

//

void set_trader_global_anim(string);

void set_trader_sound(string, string);

void set_trader_head_anim(string);

//

game_object* best_item() –- возвращает объект

game_object* best_enemy() -– возвращает объект

danger_object* best_danger();

game_object* best_weapon() -- получить от объекта его лучшее оружие (только для сталкеров)

 

//best_hit() – возвращает hit_memory_object -- теперь такого нет

//best_sound() – возвращает snd_type.object -- теперь такого нет

 

//

void info_add(string);

void info_clear();

bool has_info(string);

bool dont_has_info(string);

CTime* get_info_time(string);

 

bool give_info_portion(string);

bool disable_info_portion(string);

//

void set_tip_text(string) – установка строки-подсказки, которая будет показана при наведении на предмет курсором

void set_tip_text_default() – возвращение строки-подсказки по умолчанию

// анимация

int animation_count()

int animation_slot()

void clear_animations();

void add_animation(string);

void add_animation(string, boolean);

//

void play_sound(int);

void play_sound(int, int);

void play_sound(int, int, int);

void play_sound(int, int, int, int);

void play_sound(int, int, int, int, int);

void play_sound(int, int, int, int, int, int);

SoundInfo get_sound_info();

void remove_sound(int);

void active_sound_count();

int active_sound_count(boolean);

void set_sound_mask(int);

void add_sound(string, int, enum ESoundTypes, int, int, int);

void add_sound(string, int, enum ESoundTypes, int, int, int, string);

string sound_voice_prefix()

string sound_prefix()

void sound_prefix(string);

void external_sound_start(string);

void external_sound_stop();

//money

int money() -- сколько денег

void transfer_money(int <сколько>, game_object* <кому>) -- передать деньги

void give_money(int) -- дать денег

//

void transfer_item(game_object* <что>, game_object* <кому>) -- передать предмет

//

void enable_attachable_item(boolean) -- активирует батоны, гитару, радио, фонарики и пр.

bool attachable_item_enabled()

//

bool marked_dropped(game_object*);

void mark_item_dropped(game_object*);

void drop_item(game_object*);

void drop_item_and_teleport(game_object*, vector);

 

game_object* get_current_outfit() -- броня

float get_current_outfit_protection(int);

 

int character_reputation();

// void set_character_reputation(<int 0 to 100>); -- не определена (была в 1935)

void change_character_reputation(number);

int rank() -- получить ранг сталкера

int character_rank();

void set_character_rank(<int 0 to 100>);

string character_name(); -- внятно читаемое имя "Бес", "Волк", "Вася Пупкин"

string character_community(); -- группировка

void set_character_community(string, number, number); -- сменить группировку

enum ALife::ERelationType relation(game_object*) -– отношение в объекту (friend,neutral,enemy,dummy)

void set_relation(enum ALife::ERelationType <relation>, game_object* <who>) –- установка отношения

flags32 get_actor_relation_flags()

void set_actor_relation_flags(flags32)

int goodwill(game_object*) -- текущая благосклонность

void set_goodwill(<goodwill> int, game_object* <who>) -- установка благосклонности к кому-либо

void change_goodwill(int, game_object* <who>)

int general_goodwill(game_object*);

 

void jump(const vector&, float);

void explode(number) -– взорвать объект (гранаты, канистры, бочки и пр.)

 

 

vector bone_position(string <bone_name>) -– возвращает позицию кости.

void eat(game_object*); -- зажевать хавчик

//

holder* get_current_holder();

holder* get_holder_class(); // CHolderCustom* CScriptGameObject::get_custom_holder();

//

physics_shell* get_physics_shell() -- получить физическую оболочку. Можно работать с отдельными костями и сочленениями.

vector get_current_direction();

//hit

MonsterHitInfo get_monster_hit_info()

string who_hit_section_name() -– возвращает имя секции того типа объектов, нанесших последний хит

string who_hit_name() -– возвращает имя объекта, нанесшего последний хит

void hit(hit*) -– нанести хит

 

vector head_orientation()

 

game_object* get_enemy()

//path

bool path_completed()

int location_on_path(float, vector*);

//patrol

void set_patrol_path(string, enum PatrolPathManager::EPatrolStartType, enum PatrolPathManager::EPatrolRouteType, boolean);

void patrol_path_make_inactual();

string patrol() –- получить текущий патрульный путь // char* CScriptGameObject::GetPatrolPathName();

//

void set_patrol_extrapolate_callback();

void set_patrol_extrapolate_callback(const function<boolean>&);

void set_patrol_extrapolate_callback(const function<boolean>&, object);

//sight -- устанавливает направление зрения неписей

CSightParams sight_params()

// куда смотрит

void set_sight(enum SightManager::ESightType, const vector*, number);

void set_sight(enum SightManager::ESightType, boolean, boolean);

void set_sight(enum SightManager::ESightType, const vector&, boolean);

void set_sight(enum SightManager::ESightType, const vector*);

// на кого смотрит

void set_sight(game_object*);

void set_sight(game_object*, boolean);

void set_sight(game_object*, boolean, boolean);

void set_sight(game_object*, boolean, boolean, boolean);

//

float extrapolate_length()

void extrapolate_length(float)

void play_cycle(string)

void play_cycle(string, boolean);

//

void set_start_dialog(string <dialog_id>) – установка приветственного диалога NPC

???? get_start_dialog() – текущий диалог (функция точно ничего не возвращает, вероятно, вызывает диалог при определённых условиях)

void restore_default_start_dialog() – восстановление диалога, заданного в профиле персонажа

//

int get_enemy_strength()

//

void show_condition(ini_file*, string);

void sell_condition(ini_file*, string);

void sell_condition(float, float);

void buy_condition(ini_file*, string);

void buy_condition(float, float);

//

void buy_supplies(ini_file*, string);

// для кровососа

void set_alien_control(boolean);

void set_manual_invisibility(boolean);

void set_invisible(boolean);

//

bool inside(const vector&, float)

bool inside(const vector&)

 

//function debug_planner(const action_planner*); -- нет такой функции

void set_const_force(const vector& <направление>, float <импульс>, int <время действия ????>);

//

void skip_transfer_enemy(boolean);

//

bool see(game_object* target) -– проверить, виден ли target тому, для кого вызываем метод

bool see(string “section_name”) -– проверить, видит ли заданный класс, в кавычках – имя секции из system.ltx

// это вроде как установка предмета, который непись держит в руках, хотя могу и ошибаться. Есть какая-то разница с attachable предметами

// я видел, как непись одновременно держит ствол и один из attachable предмет (рация, водка и пр.)

void set_item(enum MonsterSpace::EObjectAction);

void set_item(enum MonsterSpace::EObjectAction, game_object*);

void set_item(enum MonsterSpace::EObjectAction, game_object*, int);

void set_item(enum MonsterSpace::EObjectAction, game_object*, int, int);

//

void set_home(

string <путь патрулирования>,

float minr, -- какой-то радиус

float maxr, -- ещё какой-то радиус

boolean <агрессия>) -- вероятно задаёт агрессию

void remove_home() -- удаляет ограничение

-- комментарии к set_home/remove_home

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

// restrictions

bool active_zone_contact(int);

//

string out_restrictions() -- даёт список имён рестрикторов через запятую или пустую строку. Хотя название функции и переводится "ограничения", всё равно непонятно, может ли вообще быть больше одного рестриктора.

string in_restrictions() -- эта функция у меня вызывает вылет и для сталкеров и для монстров

string base_out_restrictions() -- исходные рестрикторы. Если убрать или добавить рестрикторы, то так можно узнать исходные значения.

string base_in_restrictions() -- вылета не вызывает, но у меня возвращает непустую строку только для монстров. В строке - список аномалий. Рассуждая от противного - если out_restrictions - список рестрикторов "где надо быть", то in_restrictions похоже на список мест, куда "ходить не надо".

void remove_restrictions(string <out_restrictions_names>, string <in_restrictions_names>) -- вероятно убрать рестрикторы, заданные списком

void remove_all_restrictions() -- убрать все рестрикторы

void add_restrictions(string <out_restrictions_names>, string <in_restrictions_names>) -- аналогично, добавить рестрикторы списками

//

int action_count() -– возвращает количество action-ов в очереди

entity_action* action_by_index(<action_index>) -– получает по индексу action

entity_action* action() // GetCurrentAction –- получить текущую команду

void command(const entity_action*, boolean) -– отдать команду

void reset_action_queue();

//

void set_queue_size(int <queue_size>) – установить размер очереди при стрельбе из автоматического оружия

//

void berserk(); -- вопреки некоторым предположениям - это не наследие "Oblivion Lost", поскольку функция новая, а в 1935 её не было. Однако её назначение не вполне понятно.

//

bool is_body_turning()

//script

//void CScriptGameObject::SetNonscriptUsable(bool, );

void set_nonscript_usable(<true/false>) – разрешение/запрещение стандартных (нескриптовых)

-- действий над объектом (взять объект в интерфейс, говорить с персонажем и т.д.)

//char* CScriptGameObject::GetScriptControlName();

string get_script_name() –- получить имя скрипта

//bool CScriptGameObject::GetScriptControl();

bool get_script() -– получить скриптовый режим

//void CScriptGameObject::SetScriptControl(bool, char*, );

void script(boolean, <script_name>) -– установить в скриптовый режим // SetScriptControl(bool, char*, );

//bool CScriptGameObject::can_script_capture();

bool can_script_capture()

//для зомби

void fake_death_stand_up();

void fake_death_fall_down();

//

void set_fastcall(function *fun, obj) -- установка "быстрого" обработчика

-- fun - это функция, принимающая один аргумент

    bool function fun(obj)
    --   здесь obj - это объект, переданный ранее в функцию set_fastcall
    -- return <true/false> -- как только возвращает true, обработчик снимается
    end

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

-- вызывается до тех пор, пока не вернёт true.

-- отличия два:

-- первое, вызывается с периодом около 17-18 мс., что более чем в два раза чаще, чем update в самом лучшем случае

-- (напоминаю, что update вызывается с периодом около 40 мс. при нахождении предмета в

-- инвентаре актора или для самого актора)

-- второе, период вызова не зависит от расстояния до актора, как это происходит в случае с update.

//

void give_game_news( -- выдать на экран сообщение

string, -- само сообщение. Может содержать некоторые форматирующие элементы

string, --текстура, из которой берется аватара "отправителя"

Frect, --координаты и размеры вырезаемого из текстуры изображения

int, --начало показа сообщения с текущего момента (в миллисекундах)

int --длительность показа сообщения (в миллисекундах)

)

//talk

void give_talk_message(string, string, Frect, string); // AddIconedTalkMessage

//talk

void enable_talk();

bool is_talk_enabled();

void disable_talk();

void stop_talk();

bool is_talking();

//trade

void enable_trade();

bool is_trade_enabled();

void disable_trade();

 

Во время разговора:

switch_to_trade() -- (для актора) переключение из режима диалога в режим торговли

switch_to_talk() -- (для актора) переключение из режима торговли в режим диалога

-----

run_talk_dialog(game_object* <to_who>) – (для актора) принудительный запуск диалога с актера с NPC, to_who – NPC персонаж

//point

void set_start_point(int);

void set_previous_point(int);

int get_current_point_index() -- только сталкеры. похоже на номер текущей точки патрулирования

//task (для актора)

//Состояние подцели

//<objective_state> = {task.fail, task.in_progress, task.completed, task.dummy}

void give_task(CGameTask*, int, boolean);

void set_task_state(enum ETaskState, string, int) -- если такого задания у актера нет, то выдаст ошибку

enum ETaskState get_task_state(string, int) -- если такого задания у актера нет, то вернется task.dummy

//

void set_callback(enum GameObject::ECallbackType, const function<void>&);

void set_callback(enum GameObject::ECallbackType, const function<void>&, object);

void set_callback(enum GameObject::ECallbackType);

//

game_object* get_corpse();

 

void set_dest_level_vertex_id(int) -- установить вертекс уровня, куда надо идти.

cover_point* best_cover(

vector <self_position>,

vector <enemy_position>,

float <radius>,

float <min_enemy_distance>,

float <max_enemy_distance> ) – возвращает лучшую точку прикрытия от врага

 

cover_point* safe_cover(const vector&, float, float);

 

int vertex_in_direction(number, vector, number) ;

// если судить по описанию из 1935 возвращает два значения, возможно второе - булевское

int accessible_nearest(const vector&, vector&) // u32,result accessible_nearest(const Fvector &position, Fvector &result)

bool accessible(const vector&);

bool accessible(level_vertex_id);

//

void make_object_visible_somewhen(game_object*);

//vision

void enable_vision(boolean);

bool vision_enabled()

 

//управляют порогами страбатывания

void set_sound_threshold(float);

void restore_sound_threshold();

//

void set_default_panic_threshold();

void set_custom_panic_threshold(float);

//

float visibility_threshold() -– получить пороговое значение.

// Если not_yet_visible_object::value имеет значение >= этого, то объект считается видимым.

void ignore_monster_threshold(float);

float ignore_monster_threshold()

void restore_ignore_monster_threshold();

//

float max_ignore_monster_distance()

void max_ignore_monster_distance(float);

void restore_max_ignore_monster_distance();

 

 

float level_vertex_light(const number&) -- начиная с ЧН исчезла

//

void set_enemy_callback();

void set_enemy_callback(const function<boolean>&);

void set_enemy_callback(const function<boolean>&, object);

//

void set_desired_direction()

void set_desired_direction(vector*)

void set_desired_position();

void set_desired_position(vector*);

//

action_planner* motivation_action_manager(game_object*);

};

 

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

[spoiler=Регистрация скриптовых классов с помощью object_factory]Класс object_factory предназначен для регистрации пользовательских скриптовых классов. Конструктора у этого класса нет, нет и функции, с помощью которой можно его получить (по крайней мере я не знаю такой). Экземпляр этого класса доступен исключительно в качестве аргумента специальных функций, которые вызываются движком в определённое время. Функции эти прописаны в config\script.ltx в параметре class_registrators в секции common.

Например там прописана class_registrator.register. Эта функция вызывается в самом начале один раз ещё при старте программы.

В функцию register движок передаёт один аргумент типа object_factory. Здесь как раз и можно зарегистрировать свой скриптовый класс.

У объекта object_factory два метода. Один принимает четыре аргумента строкового типа и предназначен для регистрации связки серверный-клиентский классы + идентификатор. Второй метод принимает три аргумента строкового типа и предназначен для регистрации чисто клиентских классов.

class object_factory {
    // регистрация связки клиентский класс / серверный класс
    void register(
        <клиентский_класс>, <скриптовый_серверный_класс>, 
        <clsid_для конфигов>, <clsid_для_скриптов>); 
    // регистрация чисто клиентских классов
    void register(
        <скриптовый_клиентский_класс>, 
        <clsid_для конфигов>, <clsid_для_скриптов>); 
};

Чтобы объяснить, зачем нужен этот класс требуется немного лирики. Допустим, мы хотим создать некий объект (вертолёт к примеру). Игровые объекты вообще создаются с помощью метода create класса alife_simulator (см. ранее статью про этот класс). Первым и главным аргументов этого метода является имя секции в system.ltx. Даже если объект создаётся по индексу из all.spawn всё равно имя секции неявно присутствует (прописана в самом all.spawn). Что такое секция? Это просто именованный набор параметров, это все знают. Однако вопрос, как движок определяет, какой именно класс создаётся для конкретно этой секции? Есть мнение, что для этого там есть параметр class. Например, в секции вертолёта этот параметр имеет такое значение:

class = C_HLCP_S

Как теперь можно представить себе последовательность создания объекта:

1. В функцию create передаётся имя секции (например для вертолёта имя секции будет "helicopter")

2. Движок читает секцию и находит там параметр class (для вертолёта имеет значение C_HLCP_S). Теперь известно, объект какого класса надо создать.

3. Движок создаёт объект нужного класса и инициализирует его значениями, полученными из секции. Если набор параметров не соответствует созданному классу (т.е. входит в противоречие с clsid), то будет ошибка при создании.

После создания объекта ему назначается id и ссылка на него передаётся как результат работы функции create.

4. Всё это происходит на серверной стороне. Ещё один момент - это какой клиентский объект создать при переходе объекта в онлайн. Эта информация также должна быть связана с соответствующим clsid.

 

Если теперь проверить идентификатор класса свежесозданного объекта с помощью метода серверного класса cobj:clsid(), то мы получим некое число. Это число соответствует одной из констант класса clsid (для вертолёта это clsid.helicopter). С другой стороны это же число можно получить, прочитав значение параметра class из секции с помощью метода r_clsid() класса ini_file. Конкретное число не важно. Более того, вредно использовать конкретное число, поскольку оно может измениться при добавлении новых классов. Надо использовать вот эти идентификаторы.

 

Вроде как всё понятно. В движке имеется набор зарегистрированных сочетаний <серверный класс>/<клиентский класс>. Каждому такому сочетанию присвоен уникальный номер - идентификатор класса. Этот номер в секции файла system.ltx указывается через строковый идентификатор, а в коде ему же соответствует один из членов класса clsid. Набор этих сочетаний имеется в движке исходно, даже если ничего не делать.

 

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

1. сохранение на серверной стороне дополнительных данных. Это происходит примерно так же, как сохранение в биндере.

2. можно дополнить или изменить некоторые аспекты поведения серверного класса. Например, можно изменить правила перехода в онлайн/оффлайн.

Пример реализации такого серверного класса (для того же вертолёта, в этом случае он создаётся на основе класса cse_alife_helicopter) можно посмотреть в файле se_car.script (он там правда весьма короткий и ненаглядный). Имя скриптового класса - se_heli. Имя клиентского класса для вертолёта - CHelicopter. Это один из классов, унаследованных от CGameObject. Создавать на их основе свои скриптовые нельзя.

 

Теперь можно посмотреть пример применения этого класса в модуле class_registrator.script. Выглядит это так (с сокращениями):

function cs_register(factory,client_object_class,server_object_class,clsid,script_clsid)
    factory:register    (client_object_class,server_object_class,clsid,script_clsid)
end

function c_register(factory,client_object_class,clsid,script_clsid)
    if (editor() == false) then
        factory:register(client_object_class,clsid,script_clsid)
    end
end
function register(object_factory) 
    c_register(object_factory, "ui_main_menu.main_menu", "MAIN_MNU", "main_menu")
    ...
    cs_register    (object_factory, "CHelicopter", "se_car.se_heli", "C_HLCP_S", "script_heli")
    ...
end

Здесь видим, что методы класса вызываются опосредованно с помощью двух вспомогательных функций:

c_register - для чисто клиентских классов

cs_register - для связки клиентский/серверный

Сделано это скорее всего для наглядности, и никто не мешает в функции register непосредственно использовать методы object_factory.

 

Здесь мы видим, что скриптовый серверный класс se_car.se_heli, связывается с клиентским классом CHelicopter, для указания этой связки в секциях system.ltx задаётся строковый идентификатор "C_HLCP_S" , а для регистрации его в классе clsid ему назначается имя script_heli.

 

Собственно и всё. Эта регистрация происходит в самом начале, ещё до запуска главного меню. После выполнения модуля класс clsid прирастает новыми константами, а при создании соответствующих объектов получают управление соответствующие зарегистрированные скриптовые классы.

 

Что касается варианта для регистрации чисто клиентских скриптовых классов. Единственный пример использования класса object_factory таким образом показан в коде выше. Это регистрация класса для главного меню. Окна, ясное дело, существуют только на клиентской стороне. Кроме того, идентификатор для меню используется уже знакомый системе - он вшит в движок. Можно ли использовать это как-либо иначе - неизвестно.

 

Несколько ремарок:

1. Идентификаторы для создаваемой связки могут быть любыми. Но если они равны существующим значениям, то регистрируемая связка заменяет существующую.

2. Естественно не получится комбинировать клиентский и серверный классы в произвольных сочетаниях. Они друг другу должны соответствовать. Кроме того, должен быть соответствующий экспортированный клиентский класс, а они имеются далеко не для всех игровых объектов. Например, для актора нет (появился только в CS). Учитывая это и изучив содержимое class_registrator.script, можно сделать вывод, что ресурс применения объекта object_factory практически исчерпан, поскольку почти все объекты, производные от CGameObject там уже задействованы. Впрочем, можно создавать какие-либо хитрые специализации уже знакомых объектов.

 

Изменено пользователем malandrinus
  • Нравится 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

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

<пространство имён>.<имя функции>(<список аргументов>)

Пространства имён - это всего лишь способ сгруппировать функции по некоторому признаку. Хотя надо заметить, что логика группировки функций в сталкере иной раз не поддаётся объяснению.

Всего есть несколько пространств имён: безымянное (т.е. функции их него вызываются просто по именам как обычные глобальные функции), game, level, relation_registry, actor_stats. В ЧН (и ЗП) появилось пространство имён main_menu, кроме того есть незначительные изменения в остальных: некоторые функции исчезли, некоторые добавлены. Изменения однако незначительны.

Сейчас рассматриваем только три основных пространства имён (безымянное, game, level) и только для ТЧ.

 

-- функции, которые не работают или вызывают вылеты или непонятно назначение

function log(string) -- видимо для дебаговой версии

function error_log(string) -- видимо для дебаговой версии

function flush() -- видимо для дебаговой версии

bool app_ready() -- готовность чего-то к чему-то =)

-- пока игра не загружена, у меня возвращает false, после загрузки - true

bool editor() -- под редактором??? Это то, чего нам как ушей не видать???

int script_server_object_version() -- число, знать бы что означает

function verify_if_thread_is_running()

function xrRender_test_r2_hw()

------------- получение некоторых глобальных объектов --------------------------------------

alife_simulator* alife() -- см. описание alife_simulator

render_device* device() -- см. описание render_device

CGameGraph* game_graph() -- получение глобального графа игры

CConsole* get_console() -- получение объекта для управления консолью. Это и так все знают

CUIGameCustom* get_hud() -- получение объекта для управления худом.

-- Можно добавлять к худу свои окошки и элементы управления. Так делают всякие индикаторы.

FS* getFS() -- файловые операции. см. описание FS

string command_line() -- командная строка, включая полный путь к исполняемому файлу

string user_name() -- сетевое имя игрока (вроде бы имя активного пользователя в системе)

cef_storage* ef_storage() -- хз что такое

-------------- работа с файлами конфигурации

ini_file* create_ini_file(string) -- создаёт из строки объект типа ini_file в памяти

ini_file* game_ini() -- открытый файл "config\game.ltx" и все его вложения

ini_file* system_ini() -- открытый файл "config\system.ltx" и все его вложения

------------ побитовые операции --------------------------------------

function bit_and(number, number)

function bit_not(number)

function bit_or(number, number)

function bit_xor(number, number)

function dik_to_bind(number) -- я подозреваю, что преобразует константу

-- из набора DIK_keys в константу из набора key_bindings

DWORD GetARGB(number, number, number, number) -- формирует 32-х разрядное целое из

-- 4-х отдельных байт. для задания цвета. Где используется - точно не известно.

------------ всякий мелкий утиль -----------------

void prefetch(string <имя модуля>) -- вызывает загрузку и прекомпиляцию модуля.

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

int time_global() -- реальное время (в миллисекундах) с начала запуска программы

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

void buy_condition(ini_file*, string <имя секции>) -- прочитать настройки торговца

-- на покупку из файла конфигурации и определённой секции.

-- Только непонятно, для кого читаются эти настройки

void buy_condition(float, float) -- непонятная функция.

void sell_condition(ini_file*, string <имя секции>) -- аналогично на продажу

void sell_condition(float, float) -- непонятная функция

void show_condition(ini_file*, string <имя секции>) -- совсем непонятно

--

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

-- в ссылку на производный для получения доступа к методам производного класса

-- за деталями надо обращаться в описание соответствущих иерархий.

-- надеюсь, руки до этого дойдут

action_planner* cast_action_to_planner(action_base*)

action_base* cast_planner_to_action(action_planner*)

cse_alife_creature_abstract* cast_alife_object_to_creature(cse_alife_object*)

cse_alife_trader_abstract* cast_alife_object_to_trader_abstract(cse_alife_object*)

--------------------- получение встроенных шрифтов ---------------------

CGameFont* GetFontDI()

CGameFont* GetFontGraffiti19Russian()

CGameFont* GetFontGraffiti22Russian()

CGameFont* GetFontGraffiti32Russian()

CGameFont* GetFontGraffiti50Russian()

CGameFont* GetFontLetterica16Russian()

CGameFont* GetFontLetterica18Russian()

CGameFont* GetFontLetterica25()

CGameFont* GetFontMedium()

CGameFont* GetFontSmall()

--------- некая информация о текстурах. В ЗП эти функции убраны

CGameFont* GetTextureInfo(string, string)

CGameFont* GetTextureName(string)

CGameFont* GetTextureRect(string)

[spoiler=Пространство имён game]CTime* get_game_time() -- возвращает игровое время в виде объекта класса CTime

void start_tutorial(string) -- запускает туториал, зарегистрированный

-- в файле ui\game_tutorials.xml. В частности, на туториалах сделаны сны

bool has_active_tutorial() -- запущен ли туториал

int time() -- игровое время (в игровых миллисекундах) с начала игры (т.е. с начала прохождения игры)

string translate_string(string) -- получает вменяемую строку по её строковому идентификатору

-- из одного из xml файлов, прописанных в файле \config\localization.ltx в секции

-- [string_table] в параметре files

-- если там такой строки нет, то возвращает свой аргумент - исходную строку

[spoiler=Пространство имён level]-- функции неопределённые или вызывающие непонятные вылеты

//function check_object(game_object*)-- выглядит хитро. Как бы поле level.check_object есть

-- и тип его "function"

-- однако значение этого поля nil и вызов его невозможен. Т.е. такой функции на самом деле нет

-- Видимо была в отладочной версии

function debug_actor() -- аналогично check_object. Нет такой функции

function debug_object(string) -- аналогично check_object. Нет такой функции

function physics_world() -- в ТЧ - вылет

function environment() -- в ТЧ - вылет

---------------- вылета не вызывают, но назначение неясно ---------------------------

int game_id() -- возвращает некое число (у меня 1). Вероятно для мультиплея.

client_spawn_manager* client_spawn_manager() -- возвращает некий объект,

-- с помощью которого можно ставить некие коллбеки на спаун. В игре нигде не используется

---------------- информация об уровне в целом, управление уровнем в целом -----------

bool present() -- наличие уровня. Так можно определять, что игра загружена.

-- Т.е. если мы в главном меню и игра не загружена, то функция возвращает false

string name() -- имя текущего уровня, точнее его идентификатор

Fbox* get_bounding_volume() -- даёт некий параллелепипед, видимо охватывающий уровень

------------------ коллбеки ----------------------------------------

void add_call(const function<boolean>&, const function<void>&)

-- устанавливаются два коллбека:

-- первый. Аргументов нет, вызывается периодически непосредственно после апдейта актора

-- должен возвращать false до тех пор, пока не решит прекратить работу.

-- второй. Аргументов нет, вызывается один раз сразу после последнего вызова

-- первого коллбека (после того, как тот вернёт true)

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

-- что коллбек ставится на актора

void remove_call(const function<boolean>&, const function<void>&) -- по идее должно убирать коллбеки, но как-то не работает

void add_call(object, const function<boolean>&, const function<void>&) -- аналогично для

-- произвольного объекта, но теперь в качестве первого аргумента колбека при его вызове

-- передаётся тот объект, на который его ставили

void remove_call(object, const function<boolean>&, const function<void>&)

--

void add_call(object, string, string) -- непонятно, как работает. Пытался передать имя функции,

-- но это не сработало.

void remove_call(object, string, string)

--

void remove_calls_for_object(object)

---------------- эффекторы ---------------------------------------------------

float add_cam_effector(

string <имя анимации>, -- имя файла *.anm, адресуемого от папки anims

int <идентификатор>, -- произвольное целое число, которое можно использовать для удаления

boolean <зациклить>, -- проигрывать бесконечно

string <коллбек на окончание>) -- имя функции, которая выполнится по окончании

-- действия эффекта. Функция не принимает аргументов и не возвращает значений.

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

-- Функция возвращает некое число, для каждого файла анимации своё. Зачем нужно - не знаю.

float add_cam_effector2(string, number, boolean, string) -- в целом тоже самое,

-- что и предыдущая функция. Видимая разница в том, что предыдущая смещала позицию камеры

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

void remove_cam_effector(number <идентификатор>) -- убрать эффектор с ранее заданным номером

--

void add_pp_effector(

string <имя постэффекта>, -- имя файла *.ppe, адресуемого от папки anims

int <произвольный идентификатор>, -- для дальнейшего удаления

boolean <зациклить>) -- проигрывать бесконечно

void set_pp_effector_factor(

int <идентификатор>, -- число, ранее заданное при установке эффекта

float <интенсивность>) -- (0, 1) -- интенсивность эффекта

void set_pp_effector_factor(

int <идентификатор>,

float <интенсивность>,

number <скорость изменения>) -- не до конца ясно, но вроде как скорость перехода

-- от текущего состояния к заданному. По какому принципу считается время перехода - непонятно

void remove_pp_effector(int <идентификатор>) -- убрать эффект

--

-- следующие две функции практически не используются. Задействованы незначительно только в ЗП

void add_complex_effector(

string <секция>, -- имя секции в system.ltx, с описанием эффекта

int <идентификатор>)

void remove_complex_effector(int <идентификатор>) -- отмена эффектора

----------------------- функции времени --------------------------------

int get_time_days() -- день месяца по игровому времени

int get_time_hours() -- час текущего игрового дня

int get_time_minutes() -- минута текущего игрового часа

float get_time_factor() -- возвращает отношение скорости течения игрового времени к скорости реального (game_time_speed / real_time_speed)

void set_time_factor(number) -- устанавливает это отношение. Фактор времени в целом

-- не сказывается на физике мира. Т.е. скорость движения объектов остаётся естественной.

-- Пули летят с той же скоростью, что и раньше. Люди и монстры бегают как и раньше.

-- На чём точно сказывается фактор времени - это на смене дня и ночи. Установив его

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

------------------ погода ----------------------------------------------------

string get_weather() -- получить идентификатор текущей погоды

void set_weather(string <weather>, boolean <now>) -- устанавливает погоду. Идентификатор погоды

-- должен присутствовать в файле config\weathers\environment.ltx в сеции [weathers] иначе вылет

-- второй параметр отвечает за немедленную смену погоды

-- если true - меняем прямо сейчас

-- если false - то непонятно. У меня также менялось сразу.

void set_weather_fx(string) -- устанавливает погодный эффект,

-- описанный в файле weathers\environment.ltx в секции [weather_effects]. Это всякие молнии,

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

bool is_wfx_playing() -- соответственно проигрывается ли сейчас погодный эффект

float rain_factor() -- степень дождливости. Зависит от выбранной погоды. Меняется видимо автоматически

-------------------------- HUD и интерфейс ---------------------------------------

function start_stop_menu(CUIDialogWnd*, boolean) -- в ЗП убрана

-- Эти функции практически не используются. Открытие окон делается через объект худа

function add_dialog_to_render(CUIDialogWnd*)

function remove_dialog_to_render(CUIDialogWnd*)

CUIWindow* main_input_receiver() -- Возвращает открытое окно(инвентаря, торговли, ПДА, диалога)

-- С ее помощью к стандартным окнам можно добавить новые элементы. В ЗП отсутствует!

-- (спасибо Колмогору за дополнение)

--

void show_indicators() -- показывает индикаторы и прицел

void hide_indicators() -- прячет индикаторы и прицел

void disable_input() -- блокирует мышь и клавиатуру

void enable_input() -- разблокирует мышь и клавиатуру

float get_snd_volume() -- уровень звука (0,1) с ползунками в опциях не связано

void set_snd_volume(number) -- установить уровень звука (0,1)

ESingleGameDifficulty get_game_difficulty() -- сложность игры, с ползунками в опциях не связано

void set_game_difficulty(enum ESingleGameDifficulty) -- установить сложность игры

-- Перечисление ESingleGameDifficulty экспортировано как класс game_difficulty

-- Его описание

C++ class game_difficulty {

const novice = 0;

const stalker = 1;

const veteran = 2;

const master = 3;

}

-- т.е. вызываем функцию так: level.set_game_difficulty(game_difficulty.veteran)

---------------------- работа с разными объектами на уровне --------------

game_object* object_by_id(number) -- клиентский объект по его id (или nil,

-- если объект не в онлайне или не существует)

void spawn_phantom(const vector&) -- создаёт фантомного монстра в указанной точке (как на радаре)

-- фантомный мостр начинает бежать к актору и добежав - исчезает

function patrol_path_exists(string)

function vertex_in_direction(number, vector, number)

vector vertex_position(number) -- положение левел-вертекса по его номеру

-- на каждом уровне свои вертексы. Нумерация на разных уровнях может

-- (и вообще говоря обязательно будет) пересекаться

function cover_in_direction(number, const vector&)

-------------------- управление картой -----------------------------------

void map_add_object_spot( -- установить метку

int <id>, -- идентификатор объекта

string <тип метки>, -- имя типа метки, зарегистрированное в файле config\ui\map_spots.xml и всех, что в него включены инклюдами

string <надпись>) -- надпись

 

void map_add_object_spot_ser(int <id>, string <тип метки>, string <надпись>) -- разница

-- между этой и предыдущей функцией в том, что вторая ставит метку постоянно,

-- или иными словами на серверный объект (потому и _ser в конце). При вызове вы сначала не заметите разницы,

-- поскольку и та и другая функция поставят метку как на объект на текущем уровне, так и на любом другом.

-- Однако после сохранения и загрузки метки, поставленные второй функцией останутся.

-- Очевидно, каждая из функций полезна в своём случае: постоянные метки нужно ставить на тайники и квесты,

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

 

void map_change_spot_hint(int <id>, string <тип метки>, string <надпись>) -- сменить надпись на метке

bool map_has_object_spot(int <id>, string <тип метки>) -- проверить наличие метки

void map_remove_object_spot(int <id>, string <тип метки>) -- удалить метку

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

function iterate_sounds(string, number, function<void>)

function iterate_sounds(string, number, object, function<void>)

function prefetch_sound(string)

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Чуть дополнил описание game_object (см. пост ранее). Продублирую изменения в этом посте

[spoiler=дополнения к описанию game_object]

int get_ammo_total() – функция вызывается для оружия. Если ствол не в инвентаре, то возвращает количество патронов в обойме. Если в инвентаре, то возвращает суммарное количество доступных для этого ствола патронов как в его обойме, так и в том-же инвентаре.

void unload_magazine() -- разрядить магазин. Точнее установить число патронов в магазине равным нулю. Патроны при этом пропадают. Это эквивалентно вызову set_ammo_elapsed(0)

set_ammo_elapsed(int) -- установить число патронов в магазине. Патроны берутся из ниоткуда, настройки ствола по максимальной вместимости магазина игнорируются, т.е. можно к примеру в ПМ зарядить 100 патронов.

 

bool weapon_strapped() -- только сталкеры. Показывает, что оружие висит за спиной (если есть)

bool weapon_unstrapped() -- только сталкеры. Значение, обратное weapon_strapped.

--Хотя логично было бы в случае отсутствия оружия показывать false

-- управление оружием выполняется через метод set_item

void restore_weapon() -- только для актора. Достать оружие из активного слота

void hide_weapon() -- только для актора. Прячет оружие.

 

string out_restrictions() -- даёт список имён рестрикторов через запятую или пустую строку. Хотя название функции и переводится "ограничения", всё равно непонятно, может ли вообще быть больше одного рестриктора.

string base_out_restrictions() -- исходные рестрикторы.

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

string in_restrictions() -- эта функция у меня вызывает вылет и для сталкеров и для монстров

string base_in_restrictions() -- вылета не вызывает, но у меня возвращает непустую строку только для монстров. В строке - список аномалий. Рассуждая от противного - если out_restrictions - список рестрикторов "где надо быть", то in_restrictions похоже на список мест, куда "ходить не надо".

void remove_restrictions(string <out_restrictions_names>, string <in_restrictions_names>) -- вероятно убрать рестрикторы, заданные списком

void set_home(

string <путь патрулирования>,

float minr, -- какой-то радиус

float maxr, -- ещё какой-то радиус

boolean <агрессия>) -- вероятно задаёт агрессию

void remove_home() -- удаляет ограничение

-- комментарии к set_home/remove_home

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

 

int get_current_point_index() -- только сталкеры. похоже на номер текущей точки патрулирования

 

void set_dest_level_vertex_id(int) -- только сталкеры. вертекс уровня, куда надо идти.

 

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

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

void give_task(
    CGameTask* <задание>, -- подготовленный объект задания, см. далее
    int <время до истечения>, -- время от начального момента до истечения срока задания в ms
    boolean <проверять на наличие такого>) -- если true и задание с таким идентификатором уже есть, то не будет добавлено

Комментарии:


1. Нетрудно посчитать, что в 32-х разрядное целое влазит миллисекунд примерно на месяц. Соответственно - это и будет максимально возможным временем для задания.
2. Третий параметр вероятно имеет смысл использовать для разных категорий заданий. Одноразовые квестовые должны иметь параметр true, а повторяющиеся однотипные - false

Выдаём задания так:
1. Сначала надо подготовить объект типа CGameTask. Самый простой способ - воспользоваться уже готовыми заданиями, описанными в xml файлах. Для этого у класса CGameTask есть метод load
local new_task = CGameTask()
new_task:load("gar_car_graveyard_quest") -- задание "Предложить помощь Бесу"
2. Собственно выдаём задание
db.actor:give_task(new_task, 3600*1000, true) -- срок выполнения 1 час, если уже выдано, то второй раз не получим

Для получения статуса отдельной подзадачи задания есть метод класса get_task_state.
enum ETaskState get_task_state(string <идентификатор задания>, int <номер подзадачи>)
перечисление ETaskState экспортировано в виде класса task (см. далее)
Пример использования
local task_status = db.actor:get_task_state("gar_car_graveyard_quest", 0)

Для установки статуса отдельной позадачи есть метод set_task_state:
void set_task_state(enum ETaskState <статус>, string <идентификатор задания>, int <номер подзадачи>) -- если такого задания у актера нет, то выдаст ошибку

Это всё было для уже готовых заданий. Если надо лепить задания скриптами, то необходимо использовать все возможности класса CGameTask. Далее я привожу описание классов CGameTask, вспомогательного класса SGameTaskObjective и task. Если кому это на самом деле надо, этого описания должно быть достаточно.


C++ class CGameTask {
    CGameTask() -- конструктор. Как и для большинства классов, конструктор - это просто глобальная функция с именем класса, которая возвращает свежесозданный объект.

    void load(string) -- загрузить конфигурацию задачи из файла config\gameplay\game_tasks.xml
    -- этот файл содержит несколько инклюдов, так что фактически нужное описание может
    -- находиться и не в нём
    string get_id() -- даёт строку-идентификатор задачи. Это атрибут id из тега game_task в XML
    -- если не был выполнен метод load или не была добавлена хоть одна подзадача, то вернёт nil
    -- если вручную добавили подзадачу, то вернёт "user_task"
    SGameTaskObjective get_objective(number) -- получить параметры подзадачи по номеру
    -- каждая подзадача в xml-файле описывается отдельным тегом objective
    -- можно добавлять свои с помощью add_objective
    void add_objective(SGameTaskObjective*) -- добавить подзадачу
    int get_objectives_cnt() -- число подзадач
    string get_title() -- содержимое тега title или значение, установленное ранее set_title,
    -- или nil, если никак не инициализировали
    void set_title(string) -- установить заголовок задачи (внятное имя)
};


Класс [b]SGameTaskObjective[/b] описывает одну подзадачу
C++ class SGameTaskObjective {
    SGameTaskObjective (CGameTask*, number) -- загрузить подзадачу из конкретной задачи
    -- поскольку конструктора без аргументов нет, то выходит так, что сделать свою подзадачу
    -- полностью с нуля не получится. С другой стороны ничто не мешает взять из любой задачи
    -- произвольную подзадачу и изуродовать её по своему вкусу

    int get_idx() -- номер подзадачи в составе общей задачи
    int get_state() -- состояние подзадачи. Возвращает один из элементов перечисления task (см. далее)
    void set_description(string) -- установка имени подзадачи (точнее идентификатора, который
    -- ещё может быть сконвертирован в строку через механизм локализации)
    string get_description() -- даёт значение тега <text> или ранее установленное функцией set_description
    -- при выдаче задания будет тренькающий звук и на экране появится имя первой подзадачи
    -- (а не имя общей задачи, как можно было бы подумать). При этом, если в таблице строк есть
    -- строка с соответствующим идентификатором, то будет выдана именно эта строка
    -- если такой строки нет, то будет выдано исходное значение (иными словами все строки перед выводом
    -- проходят конвертацию с помощью функции game.translate_string())
    void set_article_key(string) -- не знаю что такое
    void set_article_id(string <article_id>) -- установка статьи в PDA для подзадачи.
    -- эквивалентно значению тега <article>
    -- Будет отображаться в правой части PDA при просмотре информации о задании
    -- article_id должен быть зарегистрированной статьёй
    void set_object_id(number) -- вроде как story_id целевого объекта, определяет координаты метки
    void set_map_hint(string) -- надпись на метке (не проверял)
    void set_map_location(string) -- иконка (не проверял)
    -- с помощью указанных ниже функций можно добавлять предусловия и действия на успех и провал задач
    -- можно добавлять несколько
    -- инфопорция предусловия успешного завершение задачи, соответствует тегу "infoportion_complete"
    void add_complete_info(string); //
    -- скриптовая функция предусловия успешного завершение задачи, соответствует тегу "function_complete"
    void add_complete_func(string);
    -- инфопорция, выдаваяемая при успешном завершении задачи, соответствует тегу "infoportion_set_complete"
    void add_on_complete_info(string);
    -- скриптовая функция, выполняемая при успешном завершении задачи, соответствует тегу "function_call_complete"
    void add_on_complete_func(string);
    -- инфопорция предусловия провала задачи, соответствует тегу "infoportion_fail"
    void add_fail_info(string);
    -- скриптовая функция предусловия провала задачи, соответствует тегу "function_fail"
    void add_fail_func(string);
    -- инфопорция, выдаваяемая при провале задачи, соответствует тегу "infoportion_set_fail"
    void add_on_fail_info(string);
    -- скриптовая функция, выполняемая при провале задачи, соответствует тегу "function_call_fail"
    void add_on_fail_func(string);
};


C++ class task {
    const fail = 0;
    const in_progress = 1;
    const completed = 2;
    const task_dummy = -1;
};


Начиная с ЧН система заданий несколько изменилась. Исчезли подзадачи и соответственно перестал быть нужным класс SGameTaskObjective. Его методы по-сути просто перешли в состав класса CGameTask. На мой взгляд всё стало проще, и если Вы поняли, как это работает в ТЧ, то экстраполировать это на ЧН и ЗП труда не составит. Изменено пользователем Murarius
  • Спасибо 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

lekzd,

можно попытаться по разнице между position и center. Это типа по наклону туловища =) Но там тоже нестабильные значения.

Вообще-то по скорости не так уж и плохо работает. Если учесть, что апдейты идут нерегулярно и вычислять скорость по настоящему, т.е. учитывая параметр delta, то точность повышается. Также вроде как лучше вычислять скорость только с учётом координат xz, т.е. игнорировать вертикальную компоненту. Кроме того, можно ввести фильтрацию шума - усреднение по нескольким точкам. У меня усреднение по трём дало достаточно различимую картину: стоит, идёт, бежит. Но иногда таки бывают выбросы. По пяти точкам - различие практически идеальное. Хотя и появляется некоторая микроскопическая задержка, связанная с размазыванием переходов.

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

 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Введение

Скрытый текст

При первом взгляде на lua_help.script возникает впечатление "дофига всего - объять нереально". Особенно это касается больших иерархий классов. Наиболее "развесистыми" являются три: оконные классы для создания пользовательского интерфейса и модификации элементов худа, серверные классы и клиентские классы для серверной и клиентской частей игровых объектов соответственно.
Про оконные классы поговорим в другой раз, а сейчас займёмся серверными и клиентскими классами. Что потребуется:
Первое. Необходимо представлять себе что такое классы и с чем их едят. Основное понятие, которое надо понимать - это наследование классов. Неплохо бы понимать идею базового класса и интерфейса.
Второе. Необходимо достаточно хорошо понимать природу разделения классов на серверные и клиентские. Можете почитать мой трёп здесь в статье про object_binder.
Третье. Вероятно имеет смысл почитать мою статью о реализации классов в сталкере.
Четвёртое. Между серверными и клиентскими классами есть взаимосвязь. Кое что я попробую описать, кое-что уже описывал в статье о классе object_factory. Но это не полная информация.
Пятое. Я вынужден ввести некоторые искусственные соглашения при описании классов. Это необходимо для сокращения объёма дублирующей информации. Внимательно прочитайте это вступление!
Шестое. Информация приводится для Зова Припяти. Впрочем, отличия от ЧН и ТЧ минимальны. Я постарался пометить отличающиеся места, но где-то мог и не сделать этого. Смотрите внимательней!
Наконец. Данное описание - это не то чтобы описание в полном смысле этого слова. Я не в состоянии описать в одиночку более сотни классов и несколько сотен методов и свойств. Это скорее попытка привести в некоторую систему помойку из lua_help, убрать смысловой шум и создать основу для дальнейшего полного описания. Я постарался дать по возможности точную информацию о типах аргументов и возвращаемых значений функций, но назначение их не описано.


Итак. Все игровые сущности (сталкеры, монстры, предметы и пр.) в общем случае существуют одновременно на двух сторонах - на сервере и на клиенте. На каждой стороне для этого есть соответствующие классы. На серверной - это классы, имя которых начинается на "cse". С некоторыми поправками можно считать, что все серверные классы происходят от cse_abstract. На клиентской стороне есть соответствующая иерархия классов. Можно считать, что все они происходят от CGameObject. На клиентской стороне есть ещё и game_object, который также является интерфейсом к клиентским объектам, причём основным интерфейсом. Его описание уже начато, но оно далеко от завершения.

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

Начнём с серверных.
Можно обратить внимание, что имется некоторое количество базовых классов и интерфейсов, которые вообще не содержат никаких методов или свойств. Наследование от них не даёт конкретной информации о свойствах серверного класса, но парадоксальным образом может дать некоторые догадки о применимости методов game_object. Вот список этих базовых интерфейсов:

Базовые интерфейсы

Скрытый текст

// все приведённые интерфейсы не содержат ни методов ни свойств, поэтому
// приводится только их список и отношение их наследования
// в принципе, можно погадать и на тему
ipure_alife_load_object
ipure_alife_save_object
ipure_alife_load_save_object : ipure_alife_load_object,ipure_alife_save_object
ipure_server_object : ipure_alife_load_save_object
cpure_server_object : ipure_server_object
// все вообще классы наследованы от cpure_server_object,
// его можно описать, как "объект, который умеет сохраняться и восстанавливать своё состояние"
ipure_schedulable_object
cse_alife_schedulable : ipure_schedulable_object
// класс cse_alife_schedulable - это вроде как объекты, с которыми работает планировщик
cse_visual // для этих объектов нужна модель. Их "видно"
cse_motion // эти объекты умеют двигаться
cse_ph_skeleton // у этих есть кости
cse_shape // вроде как объекты, с которыми связана некоторая область пространства: аномалии, смарты и т.п.
cse_alife_group_abstract // без понятия, зачем нужен
cse_alife_inventory_item // объекты, которые можно взять в инвентарь
// этот интерфейс является некоторым исключением, поскольку содержит методы
// судя по использованию отвечает за возможность торговать: актор и сталкеры
C++ class cse_alife_trader_abstract {
    string profile_name();
    int reputation();
    int rank();
    int community() const;
};

 

 

Несколько предваряющих комментариев к дальнейшему материалу. 

Скрытый текст

1. Почти каждый из описанных классов имеет конструктор, который по традиции языка C++ имеет имя, совпадающее с именем класса, и не возвращает никакого значения. У каждого объекта из иерархии cse_abstractвсего один конструктор, который всегда принимает один аргумент - строку. Очевидно, что аргумент - это имя секции из system.ltx. Как и для остальных классов, экспортированных с помощью технологии Luabind, это означает, что имеется глобальная функция с именем класса, которая принимает такие же аргументы, как и конструктор, и возвращает объект класса. И такие функции на самом деле есть для каждого серверного класса. Однако попытка их вызвать у меня лично приводит к неизменному вылету. Так и должно быть! Серверные объекты создаются опосредованно, с помощью вызова метода create класса alife_simulator. См. эту статью.
Итак, конструктор всегда один, имеет определённое имя и один и тот-же аргумент, да и толку от него нет. Кроме того, его наличие в составе класса - вообще изрядная условность, поскольку он не внутри класса, а снаружи. Так что в качестве дополнительного соглашения, позволяющего экономить пространство форума, я не буду приводить эти конструкторы в описании классов.
Если класс не добавляет ничего к базовому, то ради экономии места его пишу по сокращённому варианту - одной строкой с указанием только его имени и базовых классов.

2. Серверные классы - это классы экспортированные таким образом, что на их основе можно создавать свои скриптовые классы. Опять же см. эту статью. Примеры таких скриптовых классов в игре можно найти в файлах, которые в основном начинаются с "se_"se_artefact.scriptse_item.script и т.д.


Вся иерархия серверных классов

Скрытый текст

class cse_abstract : cpure_server_object {
    vector* angle; // начиная с ЗП
    int id;
    int parent_id;
    vector* position;
    int script_version;
   
    void UPDATE_Read(net_packet&);
    void STATE_Read(net_packet&, number);
    string name(const cse_abstract*);
    void UPDATE_Write(net_packet&);
    void STATE_Write(net_packet&);
    int clsid() const;
    ini_file* spawn_ini();
    string section_name();
   
    cse_abstract* init(); //
    // FillProps и OnEvent начиная с ЧН убраны
    void FillProps(string, class xr_vector<class PropItem *,class xalloc<class PropItem *> >&);
    void OnEvent(net_packet&, number, number, ClientID);
};

От класса cse_abstract унаследованы ВСЕ остальные классы. Иногда примешиваются дополнительные интерфейсы.

Список классов унаследованных от cse_abstract

Скрытый текст

class cse_alife_graph_point : cse_abstract // так выглядит сокращённое описание, класс ничего не добавляет к родительскому
class cse_spectator : cse_abstract
class cse_temporary : cse_abstract
class cse_alife_object_climable : cse_shape,cse_abstract



class CSE_AbstractVisual : cse_visual,cse_abstract {
    string getStartupAnimation(); // добавлен всего один метод.
};


class cse_alife_object : cse_abstract {
    int m_game_vertex_id;
    int m_level_vertex_id;
    int m_story_id;
    bool online;
   
    bool used_ai_locations() const;
    ??void?? use_ai_locations(boolean); // начиная с ЗП
    bool can_save() const;
    bool can_switch_online() const;
    void can_switch_online(boolean);
    bool interactive() const;
    bool visible_for_map() const;
    void visible_for_map(boolean);
    bool can_switch_offline() const;
    void can_switch_offline(boolean);
    bool move_offline() const;
    void move_offline(boolean);
};

От класса cse_alife_object унаследован всего один класс cse_alife_dynamic_object, так что нет смысла прятать его под спойлер.



class cse_alife_dynamic_object : cse_alife_object {
    void switch_offline();
    void switch_online();
    bool keep_saved_data_anyway() const; // либо synchronize_location, либо redundant
    void on_spawn();
    void on_before_register();
    void on_register();
    void on_unregister();
};

Список классов, унаследованных от cse_alife_dynamic_object

Скрытый текст

class cse_alife_space_restrictor : cse_alife_dynamic_object,cse_shape

Список классов, унаследованных от cse_alife_space_restrictor

Скрытый текст

class cse_alife_level_changer : cse_alife_space_restrictor
class cse_alife_team_base_zone : cse_alife_space_restrictor





class cse_alife_smart_zone : cse_alife_space_restrictor,cse_alife_schedulable {
    float detect_probability();
    void smart_touch(cse_alife_monster_abstract*);
    void unregister_npc(cse_alife_monster_abstract*);
    void update(); // явно идёт от cse_alife_schedulable
    function register_npc(cse_alife_monster_abstract*);
    float suitable(cse_alife_monster_abstract*) const;
    CALifeSmartTerrainTask* task(cse_alife_monster_abstract*);
    bool enabled(cse_alife_monster_abstract*) const;
};

 




class cse_alife_online_offline_group : cse_alife_dynamic_object,cse_alife_schedulable {
    // все методы класса экспортированы только начиная с ЗП
    void update();
    void register_member(number);
    void unregister_member(number);
    function clear_location_types();
    function get_current_task();
    function commander_id();
    function squad_members() const;
    function force_change_position(vector);
    function add_location_type(string);
    ??int?? npc_count() const;
};



class cse_smart_cover : cse_alife_dynamic_object {
    function description() const;
    function set_available_loopholes(object);
};

class cse_custom_zone : cse_alife_dynamic_object,cse_shape

 

Список классов, унаследованных от cse_custom_zone

Скрытый текст

class cse_torrid_zone : cse_custom_zone,cse_motion
class cse_anomalous_zone : cse_custom_zone
class cse_zone_visual : cse_anomalous_zone,cse_visual

class cse_alife_dynamic_object_visual : cse_alife_dynamic_object,cse_visual

 

Список классов, унаследованных от cse_alife_dynamic_object_visual

Скрытый текст

class cse_alife_car : cse_alife_dynamic_object_visual,cse_ph_skeleton
class cse_alife_helicopter : cse_alife_dynamic_object_visual,cse_motion,cse_ph_skeleton

// класс cse_alife_inventory_box появился в ЗП
class cse_alife_inventory_box : cse_alife_dynamic_object_visual
class cse_alife_mounted_weapon : cse_alife_dynamic_object_visual
class cse_alife_object_breakable : cse_alife_dynamic_object_visual
class cse_alife_object_hanging_lamp : cse_alife_dynamic_object_visual,cse_ph_skeleton
class cse_alife_object_physic : cse_alife_dynamic_object_visual,cse_ph_skeleton
class cse_alife_object_projector : cse_alife_dynamic_object_visual
class cse_alife_ph_skeleton_object : cse_alife_dynamic_object_visual,cse_ph_skeleton
class cse_alife_trader : cse_alife_dynamic_object_visual,cse_alife_trader_abstract





class cse_alife_creature_abstract : cse_alife_dynamic_object_visual {
    int team;
    int group;
    int squad;
   
    void on_death(cse_abstract*);
    bool alive() const;
    int g_team();
    int g_group();
    int g_squad();
    float health() const;
    // o_torso появилась начиная с ЧН
    Rotation o_torso();
};

Список классов, унаследованных от cse_alife_creature_abstract

Скрытый текст

class cse_alife_creature_actor : cse_alife_creature_abstract,cse_alife_trader_abstract,cse_ph_skeleton
class cse_alife_creature_crow : cse_alife_creature_abstract // начиная с ЧН
class cse_alife_creature_phantom : cse_alife_creature_abstract // начиная с ЧН






class cse_alife_monster_abstract : cse_alife_creature_abstract,cse_alife_schedulable {
    // начиная с ЗП
    int group_id;
    // начиная с ЧН
    int m_smart_terrain_id;
   
    function kill(); // начиная с ЗП
    void update();
    function force_set_goodwill(number, number); // начиная с ЗП
    function clear_smart_terrain();
    float travel_speed(); // начиная с ЧН
    void travel_speed(float); // начиная с ЧН
    function smart_terrain_task_deactivate();
    function smart_terrain_task_activate();
    float current_level_travel_speed(); // начиная с ЧН
    void current_level_travel_speed(float); // начиная с ЧН
    CALifeMonsterBrain* brain();
    bool has_detector(); // начиная с ЗП
    int smart_terrain_id(); // всегда тоже самое, что и m_smart_terrain_id
    int rank();
};

Список классов, унаследованных от cse_alife_monster_abstract

Скрытый текст

class cse_alife_monster_rat : cse_alife_monster_abstract,cse_alife_inventory_item
class cse_alife_monster_zombie : cse_alife_monster_abstract
class cse_alife_monster_base : cse_alife_monster_abstract,cse_ph_skeleton
class cse_alife_psydog_phantom : cse_alife_monster_base
class cse_alife_human_stalker : cse_alife_human_abstract,cse_ph_skeleton







class cse_alife_human_abstract : cse_alife_trader_abstract,cse_alife_monster_abstract {
    // rank из интерфейса cse_alife_trader_abstract конфликтует с такой же из cse_alife_monster_abstract
    // непонятно, какая из них "побеждает" и непонятно, к чему тогда относится вот эта
    void set_rank(int); //
};

 





class cse_alife_item : cse_alife_dynamic_object_visual,cse_alife_inventory_item {
    bool bfUseful();
};

Список классов, унаследованных от cse_alife_item

Скрытый текст

class cse_alife_item_ammo : cse_alife_item
class cse_alife_item_artefact : cse_alife_item
class cse_alife_item_bolt : cse_alife_item
class cse_alife_item_custom_outfit : cse_alife_item
class cse_alife_item_detector : cse_alife_item
class cse_alife_item_document : cse_alife_item
class cse_alife_item_explosive : cse_alife_item
class cse_alife_item_grenade : cse_alife_item
class cse_alife_item_pda : cse_alife_item
class cse_alife_item_torch : cse_alife_item






class cse_alife_item_weapon : cse_alife_item {
    // clone_addons появилась в ЗП
    function clone_addons(cse_alife_item_weapon*);
};

// иерархия наследования от cse_alife_item_weapon простая, так что без спойлера
// класс cse_alife_item_weapon_auto_shotgun появился в ЗП
class cse_alife_item_weapon_auto_shotgun : cse_alife_item_weapon
class cse_alife_item_weapon_magazined : cse_alife_item_weapon
class cse_alife_item_weapon_magazined_w_gl : cse_alife_item_weapon_magazined
class cse_alife_item_weapon_shotgun : cse_alife_item_weapon

 

 

 

 

 

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Полагаю, красивая картинка никому не помешает. Вот вам диаграмма серверных классов с высоты птичьего полёта =) Обращаю внимание на нотацию записи: большая пустая стрелка означает отношение обобщения (или наследования иными словами) и направлена от унаследованного класса к базовому. Включение абстрактного интерфейса обозначается стрелкой с кружочком и указанием, какой интерфейс наследуется. Это сокращённая форма записи, позволяющая избавиться от нагромождения линий и лучше понять основную линию наследования.

1b2c4918.png

 

С серверными классами пока всё. Некоторые выводы по данному этапу.

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

2. Между ТЧ, ЧН и ЗП не так уж и много отличий. Хотя отличия имеются: добавлено несколько новых классов, изрядно добавлено новых методов в существующих классах и пара методов исчезла.

3. Я не описывал ни назначение классов, ни назначение их методов. В некоторых местах (в основном для новых методов в ЧН и ЗП) не удалось пока получить информации о параметрах и возвращаемых значениях. Наверняка где-то упустил продокументировать различие между играми.

 

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

 

Продолжение следует...

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

7.9,

всё верно, только не 40 млн., а 4 мрд., точнее максимальное 32-х разрядное беззнаковое целое:

4294967295 что в миллисекундах составляет примерно 1193 часа, т.е. 49 дней.

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

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

Разбираем систему клиентских классов.

Сначала немного лирики.

Скрытый текст

Итак, на клиентской стороне есть своя иерархия классов, отвечающих за онлайновые (т.е. клиентские) объекты. Все они унаследованы от CGameObject. Дерево наследования там не столь сложное, как для серверных классов. Тем не менее для каждого (почти) типа игровых объектов есть свой класс, и при первом взгляде всё смотрится вполне прилично. Но это только на первый взгляд. При более внимательном рассмотрении выясняется, что классы хоть и есть, но добраться до произвольного клиентского объекта невозможно. Т.е. нет никаких функций, с помощью которых можно было бы получить произвольный клиентский объект, производный от CGameObject. Соответственно, нет и возможности вызывать их методы. Кроме того, эти классы не предназначены для создания на их основе скриптовых классов. Таким образом, все общие пути для использования объектов этого класса закрыты. Соответственно, основным и почти единственным интерфейсом к объектам на клиентской стороне остаётся game_object
Однако не всё так трагично =) Есть исключения. Если в классе добавлены какие-либо методы сверх основного набора из CGameObject, то для получения объектов такого класса в game_object имеются специальные методы. В ТЧ исходно всего три таких класса: CCarCHelicopter и hanging_lamp. Соответственно в game_object есть методы:
get_car() - для CCar
get_helicopter() - для CHelicopter
get_hanging_lamp() - для hanging_lamp
В ЧН методы добавлены в классы CPhysicObjectCArtefactCZoneCampfire и CAI_Bloodsucker. Соответственно появились и новые методы в game_objectget_physics_object()get_artefact() и get_campfire(). А вот для кровососа почему-то функции нет.
К этому могу добавить, что посмотрев на состав методов CGameObject, можно увидеть их сходство с набором методов object_binder. Таким образом скорее всего методы CGameObject опосредованно доступны через биндер. Для тех, что недоступны, вроде как имеются некие аналоги в game_object, и даже для работы с визуалом похоже начиная с ЗП появились функции (но это неподтверждённая информация).

Наконец надо заметить, что главным использованием системы этих классов является не прямое использование, а опосредованное - через механизм регистрации скриптовых серверных классов. Я об этом рассказывал 
здесь, рекомендую почитать. Возможность регистрации своего скриптового серверного класса определяется наличием соответствующего ему экспортированного клиентского. Достаточно, чтобы он просто был. И вот как раз здесь имеется приличный прорыв: по сравнению с ТЧ в ЧН и ЗП добавлено много новых классов - теперь охвачены почти все игровые объекты (почти, но таки не все).
Теперь можно делать скриптовые серверные классы для ящиков, PDA, аддонов и аномалий. В первую очередь это означает, что теперь можно без проблем хранить серверное состояние этих объектов, что существенно облегчает создание модов, где эти объекты используются в оффлайне. Также добавлен актор, что означает возможность сделать серверного актора. Как-то это используется в игре.


Довольно лирики. Далее представлена система клиентских классов. Используются те-же соглашения, что и раньше: конструктор не показан, если класс не добавляет своих методов, то показан одной строкой.

 

Иерархия клиентских классов

Сперва базовые интерфейсы. Их не так много, как у серверных. 

Скрытый текст



class DLL_Pure {
    DLL_Pure* _construct();
};

Как будет видно далее, на самом деле самым базовым классом является DLL_Pure. Похоже, что именно этот класс является необходимым для механизма регистрации скриптовых классов. Есть ещё классы, не относящиеся к игровым объектам, но унаследованные от DLL_Pure, которые тоже можно использовать с классом object_factory.
ICollidable
IRenderable
ISheduled
Непосредственно от DLL_Pure наследованы всего три класса.
class ce_smart_zone : DLL_Pure
class ce_script_zone : DLL_Pure


class CGameObject : DLL_Pure,ISheduled,ICollidable,IRenderable {
    ??string?? Visual() const;
    function getEnabled() const;
    void net_Export(net_packet&);
    void net_Import(net_packet&);
    function getVisible() const;
    int net_Spawn(cse_abstract*);
    bool use(CGameObject*);
};

 

 

Все остальные классы имеют своим предком CGameObject

Классы, производные от CGameObject

Скрытый текст

 

CActor // начиная с ЧН
CAI_Stalker
CAI_Trader
// монстры


class CAI_Bloodsucker : CGameObject {
    function force_visibility_state(number);
};

CAI_Boar
CAI_Dog
CAI_Flesh
CAI_PseudoDog
CBurer
CCat
CChimera
CController
CFracture
CPoltergeist
CPseudoGigant
CPsyDog
CPsyDogPhantom
CSnork
CTushkano
CZombie

//съедаемые предметы
CAntirad
CBottleItem
CFoodItem
CMedkit

//устройства
CTorch
CSimpleDetector -- класс есть, но не документирован в lua_help
CAdvancedDetector -- класс есть, но не документирован в lua_help
CEliteDetector -- класс есть, но не документирован в lua_help
CScientificDetector -- класс есть, но не документирован в lua_help
//аддоны
CGrenadeLauncher
CScope
CSilencer

//разные объекты

hanging_lamp

Скрытый текст


class hanging_lamp : CGameObject {
    hanging_lamp ();
    void turn_on();
    void turn_off();
};

 

CCar

Скрытый текст


class holder {
    bool engaged();
    void Action(int, int);
    void SetParam(int, vector);
};
class CCar : CGameObject,holder {
    // CCarWeapon::__unnamed
    const eWpnDesiredDir = 1;
    const eWpnDesiredPos = 2;
    const eWpnActivate = 3;
    const eWpnFire = 4;
    const eWpnAutoFire = 5;
    const eWpnToDefaultDir = 6;
   
    CCar ();
   
    float GetfHealth() const;
    float SetfHealth(float);
    vector CurrentVel();
    bool IsObjectVisible(game_object*);
    int ExplodeTime();
    void SetExplodeTime(int);
    bool HasWeapon();
    float FireDirDiff();
    void CarExplode();
    bool CanHit();
};

 

CHelicopter

Скрытый текст


class CHelicopter : CGameObject {
    //EHeliState
    const eAlive = 0;
    const eDead = 1;
    //EHeliBodyState
    const eBodyByPath = 0;
    const eBodyToPoint = 1;
    //EHeliHuntState
    const eEnemyNone = 0;
    const eEnemyPoint = 1;
    const eEnemyEntity = 2;
    //EHeilMovementState
    const eMovNone = 0;
    const eMovToPoint = 1;
    const eMovPatrolPath = 2;
    const eMovRoundPath = 3;
    const eMovLanding = 4;
    const eMovTakeOff = 5;
   
    bool m_dead;
    bool m_exploded;
    bool m_flame_started;
    bool m_light_started;
    float m_max_mgun_dist;
    float m_max_rocket_dist;
    float m_min_mgun_dist;
    float m_min_rocket_dist;
    bool m_syncronize_rocket;
    int m_time_between_rocket_attack;
    bool m_use_mgun_on_attack;
    bool m_use_rocket_on_attack;
   
    CHelicopter ();
   
    void Die(); // красиво падать, потом взорваться
    void Explode(); // сразу взорваться
   
    float GetfHealth() const;
    float SetfHealth(float);
   
    EHeliHuntState GetHuntState();
    EHeliBodyState GetBodyState();
    EHeilMovementState GetMovementState();
    ??EHeliState?? GetState();
   
    void ClearEnemy(); // ни в кого не стрелять
    function SetEnemy(game_object*); // в кого стрелять
    function SetEnemy(vector*); // куда стрелять
   
   
    float GetCurrVelocity();
    vector GetCurrVelocityVec();
    float GetMaxVelocity();
    void SetMaxVelocity(float);
   
    float GetDistanceToDestPosition();
    float GetOnPointRangeDist();
    float GetRealAltitude(); // абсолютная высота
    float GetSafeAltitude();
    float GetSpeedInDestPoint(number);
    void SetSpeedInDestPoint(float);
    void SetBarrelDirTolerance(float);
    function SetLinearAcc(number, number);
   
    void GoPatrolByPatrolPath(string, int);
    void GoPatrolByRoundPath(vector, float, boolean);
    bool isVisible(game_object*);
    void LookAtPoint(vector, boolean); // развернуться носом в точку
    void SetDestPosition(vector*); // куда лететь
    bool UseFireTrail();
    void UseFireTrail(boolean);
    void SetFireTrailLength(float);
    void SetOnPointRangeDist(float);
    void StartFlame(); // начать гореть
    void TurnEngineSound(boolean); // звук двигателя
    void TurnLighting(boolean); // включить прожектор
};

 

CPhysicObject

Скрытый текст


class CPhysicObject : CGameObject {
    function anim_time_get();
    function anim_time_set(number);
    function play_bones_sound();
    function run_anim_back();
    function run_anim_forward();
    function set_door_ignore_dynamics();
    function stop_anim();
    function stop_bones_sound();
    function unset_door_ignore_dynamics();
};

 

CDestroyablePhysicsObject
CExplosiveItem
CWeaponAmmo

CInventoryBox
CPda

//Гранаты
CF1
CRGD5

//оружие
CWeaponAK74
CWeaponAutomaticShotgun
CWeaponBinoculars
CWeaponBM16
CWeaponFN2000
CWeaponFORT
CWeaponGroza
CWeaponHPSA
CWeaponKnife
CWeaponLR300
CWeaponPM
CWeaponRG6
CWeaponRPG7
CWeaponShotgun
CWeaponSVD
CWeaponSVU
CWeaponUSP45
CWeaponVal
CWeaponVintorez
CWeaponWalther

//аномалии
CMincer
CMosquitoBald
CHairsZone
CRadioactiveZone
CTorridZone

// Разные области


class CZoneCampfire : CGameObject {
    function is_on();
    function turn_on();
    function turn_off();
};

CLevelChanger
CSpaceRestrictor
smart_cover_object

//Шкурки
CStalkerOutfit
CHelmet -- класс есть, но не документирован в lua_help

Артефакты

Скрытый текст


class CArtefact : CGameObject {
    function SwitchVisibility(boolean);
    function FollowByPath(string, number, vector);
    function GetAfRank() const;
};

Производные от CArtefact
CBastArtefact
CBlackDrops
CBlackGraviArtefact
CDummyArtefact
CElectricBall
CFadedBall
CGalantineArtefact
CGraviArtefact
CMercuryBall
CRustyHairArtefact
CThornArtefact
CZudaArtefact

 

 

Рисовать картинку с иерархией наследования смысла нет, поскольку и так всё довольно очевидно. За исключением малого числа классов остальные к CGameObject ничего не добавляют. Соответственно и описывать нечего. Названия классов более или менее говорят о том, для каких объектов они предназначены. Где мог, указал точные сигнатуры функций (CCar, CHelicopter и hanging_lamp). Для новых классов и методов по сравнению с ТЧ такой информации к сожалению нет.

Несколько рассуждений на тему взаимодействия серверного и клиентского объектов. Попробуем рассуждать логически. Есть сервер, есть клиент. Это в общем случае разные компьютеры. Каким может быть взаимодействие объектов, находящихся по разную сторону сети? Могут ли они, к примеру, вызывать методы друг друга, или непосредственно читать данные своего "напарника"? Очевидно нет. Моё мнение такое, что их взаимодействие сводится в основном к посылке данных друг другу. При этом, данные посылаются "пачкой", т.е. обо всём состоянии сразу. То, что я наблюдал, выглядит так: при переходе в онлайн создаётся клиентский объект, серверный его инициализирует, посылая ему при создании нетпакет. Этот момент можно отловить в биндере клиентского класса. Все дальнейшие движения происходят от клиентского объекта к серверному. Это означает, что периодически клиентский затирает данные серверного. Точные моменты, когда это происходит, периодичность, а также механизм передачи данных мне лично понять пока не удалось. По крайней мере на серверной стороне ничего не видно, просто тихо меняются данные, и понять, когда это происходит, крайне непросто.
Однако некоторые выводы таки можно сделать. Если взаимодействие классов примерно такое, как я здесь предположил, то совместимость классов определяется совместимостью бинарных данных, которые они друг другу посылают. Ну в общем классы должны друг другу соответствовать, хотя, как далее будет видно, это правило на практике (очень редко) нарушается. Если не забыли ещё, то зарегистрированные пары классов задаются идентификаторами класса. В файлах конфигурации (в секциях объектов) этим идентификаторам соответствует параметр class. В скриптах - это один из членов clsid. В движке уже зарегистрированы соответствующие пары классов, и вы можете добавлять свои пары с помощью класса object_factory. Я это всё описывал 
здесь.

Далее я привожу таблицу соответствия классов. Для полноты картины в ней указаны классы, которые не экспортированы в Lua. Если класс не экспортирован, то это означает, что вам не удастся зарегистрировать на его основе свою пару классов. Однако это не мешает (теоретически) создавать объекты этого класса при условии, что имеется и известен его идентификатор.
Приведённая таблица собрана из разных источников, в том числе основана на примерах регистрации классов из игры. Видно, что иногда клиентский класс использует в качестве "напарника" серверный, который является базовым для своего "родного" серверного. Видимо, это технически в норме вещей и так можно поступать и нам. Из этого ряда выбивается CZombie, для которого вроде бы родным должен быть серверный класс cse_alife_monster_zombie, а регистрируют его с cse_alife_monster_base. Это вроде как нарушает вышеописанную логику, поскольку cse_alife_monster_base не является базовым для cse_alife_monster_zombie. С другой стороны, он так зарегистрирован в самом движке. Возможно cse_alife_monster_zombie попросту не задействован и никакого отношения к CZombie на самом деле не имеет. 


Таблица соответствия серверных и клиентских классов

Скрытый текст

  Серверный класс                      |  Клиентский класс
---------------------------------------+-------------------------------------------------
cse_abstract                           |  
cse_alife_graph_point                  |  
cse_spectator                          |  CSpectator -- не экспортированный класс
cse_temporary                          |  CExplosiveRocket -- не экспортированный класс
cse_alife_object_climable              |  CClimableObject -- не экспортированный класс
CSE_AbstractVisual                     |  
cse_alife_object                       |  
cse_alife_dynamic_object               |  
cse_smart_cover                        |  smart_cover_object
cse_alife_online_offline_group         |  
cse_custom_zone                        |  CCustomZone -- не экспортированный класс
cse_alife_space_restrictor             |  ce_smart_zone -- cse_alife_space_restrictor - это базовый 
                                       |  для cse_alife_smart_zone
                                       |  видимо так можно делать
                                       |  CSpaceRestrictor
                                       |  ce_script_zone
cse_alife_dynamic_object_visual        |  CScriptObject -- не экспортированный класс
cse_alife_creature_abstract            |  
cse_alife_inventory_box                |  CInventoryBox
cse_alife_mounted_weapon               |  
cse_alife_object_breakable             |  CBreakableObject -- не экспортированный класс
cse_alife_object_projector             |  CProjector -- не экспортированный класс
cse_alife_item                         |  CAntirad
                                       |  CBottleItem
                                       |  CFoodItem
                                       |  CMedkit
                                       |  CGrenadeLauncher
                                       |  CScope
                                       |  CSilencer
                                       |  CInventoryItemObject -- не экспортированный класс
                                       |  CMPPlayersBag -- не экспортированный класс
                                       |  CFlare -- не экспортированный класс
cse_alife_trader                       |  CAI_Trader
cse_alife_helicopter                   |  CHelicopter
cse_alife_car                          |  CCar
cse_alife_object_hanging_lamp          |  hanging_lamp
cse_alife_object_physic                |  CDestroyablePhysicsObject
                                       |  CPhysicObject
cse_alife_ph_skeleton_object           |  CPhysicsSkeletonObject -- не экспортированный класс
cse_alife_creature_crow                |  CAI_Crow -- не экспортированный класс
cse_alife_creature_phantom             |  CPhantom -- не экспортированный класс
cse_alife_creature_actor               |  CActor
cse_alife_monster_abstract             |  
cse_alife_monster_zombie               |  CZombie -- вроде должен быть здесь
cse_alife_monster_rat                  |  CAI_Rat -- не экспортированный класс
cse_alife_monster_base                 |  CAI_Bloodsucker
                                       |  CAI_Boar
                                       |  CAI_Dog
                                       |  CAI_Flesh
                                       |  CAI_PseudoDog
                                       |  CBurer
                                       |  CCat
                                       |  CChimera
                                       |  CController
                                       |  CFracture
                                       |  CPoltergeist
                                       |  CPseudoGigant
                                       |  CPsyDog
                                       |  CPsyDogPhantom
                                       |  CSnork
                                       |  CTushkano
                                       |  CZombie -- это нарушает логику. 
                                       |  cse_alife_monster_base - не базовый для cse_alife_monster_zombie
cse_alife_psydog_phantom               |  CPsyDogPhantom // должно быть так, вместо 
                                       |  этого записан в cse_alife_monster_base
cse_alife_human_abstract               |  
cse_alife_human_stalker                |  CAI_Stalker
cse_alife_item_ammo                    |  CWeaponAmmo
cse_alife_item_artefact                |  CElectricBall
                                       |  CBastArtefact
                                       |  CBlackDrops
                                       |  CBlackGraviArtefact
                                       |  CDummyArtefact
                                       |  CFadedBall
                                       |  CGalantineArtefact
                                       |  CGraviArtefact
                                       |  CMercuryBall
                                       |  CRustyHairArtefact
                                       |  CThornArtefact
                                       |  CZudaArtefact
                                       |  CtaGameArtefact -- не экспортированный класс
cse_alife_item_bolt                    |  CBolt -- не экспортированный класс
cse_alife_item_custom_outfit           |  CStalkerOutfit
                                       |  CScientificOutfit -- не экспортированный класс
                                       |  CMilitaryOutfit -- не экспортированный класс
                                       |  CExoOutfit -- не экспортированный класс
cse_alife_item_helmet                  |  CHelmet
cse_alife_item_detector                |  CAdvancedDetector
                                       |  CEliteDetector
                                       |  CScientificDetector
                                       |  CSimpleDetector
cse_alife_item_document                |  CInfoDocument -- не экспортированный класс
cse_alife_item_explosive               |  CExplosiveItem
cse_alife_item_grenade                 |  CF1
                                       |  CRGD5
cse_alife_item_pda                     |  CPda
cse_alife_item_torch                   |  CTorch
cse_alife_item_weapon                  |  CWeaponKnife
cse_alife_item_weapon_auto_shotgun     |  CWeaponAutomaticShotgun
cse_alife_item_weapon_magazined        |  CWeaponBinoculars
                                       |  CWeaponHPSA
                                       |  CWeaponLR300
                                       |  CWeaponMagazined
                                       |  CWeaponPM
                                       |  CWeaponRPG7
                                       |  CWeaponSVD
                                       |  CWeaponSVU
                                       |  CWeaponUSP45
                                       |  CWeaponVal
                                       |  CWeaponVintorez
                                       |  CWeaponWalther
                                       |  CWeaponMagazined -- не экспортированный класс
                                       |  CWeaponFORT -- не экспортированный класс
cse_alife_item_weapon_shotgun          |  CWeaponBM16
                                       |  CWeaponRG6
                                       |  CWeaponShotgun
cse_alife_item_weapon_magazined_w_gl   |  CWeaponAK74
                                       |  CWeaponGroza
                                       |  CWeaponFN2000
                                       |  CWeaponMagazinedWGrenade -- не экспортированный класс
cse_alife_level_changer                |  CLevelChanger
cse_alife_team_base_zone               |  CTeamBaseZone -- не экспортированный класс
cse_alife_smart_zone                   |  ce_smart_zone
cse_anomalous_zone                     |  CMincer
                                       |  CMosquitoBald
                                       |  CRadioactiveZone
                                       |  CNoGravityZone -- не экспортированный класс
                                       |  CZoneCampfire
cse_zone_visual                        |  CHairsZone
cse_torrid_zone                        |  CTorridZone
CSE_ALifeStationaryMgun                |  CWeaponStatMgun -- не экспортированный класс
-- серверный также не экспортирован    |

 

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
(изменено)

dimos,

Это «Отбойник» - автоматический дробовик из ЗП. Секция wpn_protecta в файле configs\weapons\w_protecta.ltx.

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

W_GLAUNC - это вообще никак не поможет, поскольку это объект подствольника, а все аддоны - это просто инвентарные предметы. Они не стреляют, стреляют стволы. При надевании аддона сам объект аддона исчезает, а у ствола просто устанавливается соответствующий флажок.

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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

AMK-Team.ru

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