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

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


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

Garry_Galler,

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

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

Нельзя.

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

Это хранилище инициализированных диалогов не принадлежит какой-либо конкретной сохранённой игре. Время его жизни - сеанс работы программы.

Относительно графа диалога. Я разумею под ним полный граф, включающий все возможные фразы (игнорируя предусловия). Представьте себе лабиринт с дверями. В какие-то двери войти можно, в какие-то нельзя. Это предусловия. А сам лабиринт - это граф. Граф менять нельзя, а предусловия можно.

 

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

Мы же его указываем в профиле персонажа. Для этого и нужен, чтобы ссылаться на диалог. Или я не понял вопроса?

 

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

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

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

 

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

malandrinus

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

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

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

Garry_Galler,

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

В указанных функциях их можно использовать тогда, когда одна функция используется для нескольких диалогов. Типа оптимизация. Есть некая функция в одном диалоге и в другом. Очень похожи, но отличаются на пару строк. Объединяем в одну, а разницу реализуем в зависимости от имени диалога. Как-то так.

 

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

Но проблема бывает в том, что фишка могла быть сделана для какого-то билда, который давно ушёл в историю, и примеров использования не сыскать, и не актуально это давным-давно. А мы тут сидим, умные разговоры ведём =)

 

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

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

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

 

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

Вот это дело!

 

Теперь осталось только ui_events. :)

Кстати, а какая константа обозначает коллбек флажка (checkbutton'а) на снятие/установку. Пробовал CHECK_BUTTON_SET, CHECK_BUTTON_RESET, PROPERTIES_CLICKED - не работают. :( И как сохранить значение флажка?)

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

IQDDD,

насчёт колбеков сейчас точно не могу сказать. Либо что-то не то делаешь, либо они не работают. Можешь ещё попробовать для чекбокса "кнопочные" события типа BUTTON_CLICKED. Насчёт сохранения, пошарь методы для CUICheckButton двумя постами выше =)

 

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

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

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

 

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

Да я понимаю, что есть check:GetCheck(). но разрабы сохраняют в оригинальных скриптах через некий класс COptionsManager() (см. ui_mm_opt_main)

BUTTON_CLICKED работает. :)

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

IQDDD,

разрабы сохраняют в оригинальных скриптах через некий класс COptionsManager()

Этот класс по-моему не имеет прямого отношения к состоянию отдельно взятого контрола. Что-то для работы с настройками.

 

P.S.: Я может вопроса не понял. Сохранить - что означает? Сохранить до следующей игры?

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

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

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

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

 

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

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

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

В связи с внезапным окончанием бета версии Visio красивая картинка с иерархией оконных классов откладывается =)

 

Так что сейчас разберём общие вопросы, т.е.

Скрытый текст

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

 

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

 

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

 

Начнём с игрового режима и для начала определимся с терминологией.

В этом режиме игрок видит перед собой трёхмерную картинку происходящего и так называемый худ (HUD = heads-up display, ведет происхождение от интерфейса военных самолетов, при котором информация отображается на стекле обзора, что позволяет не опускать голову на приборы, т.е. голова остается поднятой (англ. "head up")). Причём, элементами худа являются в том числе руки и предмет, который актор крутит в руках. Эти объекты по своей природе трёхмерны и в этом движке на них влиять нельзя (а было бы неплохо на самом-то деле управлять, скажем, изображением на экране детектора, который находится в руках у актора в ЧН и ЗП). Так что в дальнейшем мы будем говорить о худе, имея в виду только плоские элементы интерфейса: индикаторы здоровья, оружия, слотов, миникарта и прочее в этом роде. Если точнее, будем называть худом ту прозрачную плоскость на которой все эти элементы расположены.

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

Для управления худом имеется класс CUIGameCustom.

Подобно некоторым другим классам существует всего один глобальный объект такого класса и получить его можно с помощью глобальной функции get_hud() (см. описание пространств имён).

Скрытый текст
class CUIGameCustom {
    void AddCustomMessage(string id, float x, float y, float font_sz, CGameFont *font, unsigned short alignment, DWORD color); // только ТЧ
    void AddCustomMessage(string id, float x, float y, float font_sz, CGameFont *font, unsigned short alignment, DWORD color, float flicker); // только ТЧ
    void CustomMessageOut(string id, string msg, DWORD color); // только ТЧ
    void RemoveCustomMessage(string id); // только ТЧ
    
    SDrawStaticStruct* AddCustomStatic(string id, bool single_instance);
    SDrawStaticStruct* GetCustomStatic(string id);
    void RemoveCustomStatic(string id);
    void AddDialogToRender(CUIWindow* wnd);
    void RemoveDialogToRender(CUIWindow* wnd);
    
    void HidePdaMenu();  // только ЗП
    void HideActorMenu();  // только ЗП
    void show_messages();  // только ЗП
    void hide_messages();  // только ЗП
};

 

 

Этот класс даёт два метода для размещения на худе своих элементов.

 

Метод первый и исторически самый распространённый. Используются функции AddCustomStatic, RemoveCustomStatic и GetCustomStatic.

Теперь по порядку:

  • Имеется файл xml с описаниями создаваемых элементов. Это файл configs\ui\ui_custom_msgs.xml, имя которого прописано прямо в движке.
  • Необходимо внести в этот файл описание своего элемента и дать ему имя. Смотрите там, как это делается. Вкратце, создаётся новый узел XML с атрибутами и подузлами, содержащими все необходимые описания. Имя тега и будет именем нашего элемента. Можно использовать включаемые файлы (с помощью #include <имя файла>), чтобы минимально менять оригинальный файл.
  • теперь во время выполнения получаем объект худа так

    local hud = get_hud()

    и создаём наш элемент так

    local st = hud:AddCustomStatic(<имя элемента>, true)

при этом создаётся окно типа CUIStatic по описанию из XML и автоматически размещается на худе. Возвращаемый объект имеет тип SDrawStaticStruct. Его описание

class SDrawStaticStruct {
    float m_endTime;
    CUIStatic* wnd();
};
 

Здесь нам в основном интересен метод wnd, который возвращает указатель на созданный CUIStatic. Внимательно изучите возможности этого контрола, они довольно немаленькие.

Удалить элемент можно так

hud:RemoveCustomStatic(<имя элемента>)

получить в любой момент можно так

local st = hud:GetCustomStatic(<имя элемента>)

хотя можно и просто запомнить ссылку на элемент, полученную при создании.

Собственно всё. С помощью описания в XML можно изобразить что-то простое, но если вам нужна динамика или сложные элементы, то ничто не мешает разместить на созданном окне дополнительные дочерние окна с помощью AttachChild. При этом можно сделать описание в XML минимальным, фактически определив только координаты окна и более ничего, а всё, что требуется, сделать позже с помощью дочерних окон. Данный способ почти всем неплох, но у него есть один маленький недостаток. Все созданные таким образом элементы будут расположены под стандартными элементами худа. А иногда нужно создать элемент, который перекрывает стандартные индикаторы и диалоги.

 

Для этого есть второй метод с использованием функций AddDialogToRender и RemoveDialogToRender. Последовательность работы такая:

  • создаём любым методом окно любого типа

    wnd = <любое окно>

  • размещаем его на худе так

    get_hud():AddDialogToRender(wnd)

  • когда захотим, убираем с худа так

    get_hud():RemoveDialogToRender(wnd)

Важное замечание: После помещения окна на худ таким способом обязательно надо как-то сохранить ссылку на объект окна вплоть до его удаления с худа.

Если потерять ссылку, то сборщик мусора удалит окно, и игра рухнет.

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

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

 

Теперь о диалоговом режиме. Здесь всё вертится вокруг класса CUIScriptWnd. Основная идея заключается в создании на основе этого класса своего скриптового. Не буду в подробностях это описывать (может потом как-нибудь). Есть масса примеров, то же главное меню сделано именно так (файл ui_main_menu.script). В качестве наиболее простого и наглядного примера рекомендую ui_numpad.script. Это - диалоговое окно с панелью ввода кода двери.

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

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

 

В ТЧ

Допустим у вас есть скриптовый класс CMyDialog, сделанный на основе CUIScriptWnd. Сначала вы создаёте объект такого класса

local dlg = CMyDialog() -- конструктор может быть и с параметрами, поскольку вы его сами написали

затем запускаем диалог

level.start_stop_menu(dlg, true/false)

или так

dlg:GetHolder():start_stop_menu(dlg, true/false)

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

 

ЧН я не рассматриваю, но там вроде похоже на ТЧ.

 

В ЗП всё ещё проще, у самого диалога есть нужные методы ShowDialog и HideDialog.

Сначала как обычно создаём объект диалога

local dlg = CMyDialog()

затем запускаем его

dlg:ShowDialog(true/false) -- аргумент как и раньше позволяет скрыть индикаторы

закрываем диалог

dlg:HideDialog()

 

Вот и всё. Остальные подробности про диалоги смотри в примерах.

 

 

В неохваченном осталась тема о создании окон на основе XML описания. Для этого есть класс CScriptXmlInit. Он сам по себе несложен, но к нему надо ещё и описывать форматы XML файлов для разных окон, а к этому я сейчас морально не готов =)

Также есть ещё достаточно сложная тема о модификации стандартных окон: торговли, PDA и прочего. Здесь на помощь может прийти функция level.main_input_receiver(), которая возвращает текущее активное окно. Приемы работы в этом случае достаточно нетривиальны. Кроме того, в ЗП этой функции нет. Может как-нибудь и дойдут руки...

 

Изменено пользователем Kirgudu
  • Спасибо 1
 

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

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

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

 

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

В ТЧ и ЧН были глобальные функции GetTextureInfo, GetTextureName и GetTextureRect. Это значит по имени текстуры получить ее файл (вроде как это еще полотном называют) и координаты прямоугольника. Напоминаю, что в таком понимании текстура это то, что обычно указывается в XML описаниях разных окошек. Там как правило прописывается имя, которое ссылается на отдельное описание (в другом XML файле), где уже и задается имя файла и координаты прямоугольника. Эти функции как раз и позволяют получить эту информацию. Особой пользы от этих функций не было, поскольку не было ни одной функции, которая возвращала бы имя текстуры.

И вот в ЧН такая функция появилась! Для онлайновых объектов сталкеров стало возможно получить иконку персонажа с помощью функции character_icon. Это очень нужная функция, поскольку дает информацию о реальном профиле сталкера. Так и что вы думаете! В ЗП разрабы взяли и убрали функции получения информации о текстурах. И какой теперь толк от той функции получения иконки? XML прочитать нельзя, а иначе логическое имя ни о чем не говорит. Нет слов, одни междометья.

 

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

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

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

 

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

DimOriN,

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

 

А что за задача стоит?

 

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

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

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

 

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

Проблема понимания сего класса в следующем

 

local game_difficulty = reader:r_u8()

затем следом

local stored_input_time = reader:r_u8()

 

вот не укладывается у меня в голове - как так 2 разных переменных с разными значениями равны reader:r_u8()

надо для "пробы" вскрытия файлов *.sav

Untitled-1.png

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

Возник вопрос: Как правильно присоединить окно класса CuiScriptWnd к инвентарю через функцию level.main_input_receiver()?

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

Ссылка на комментарий
Проблема понимания сего класса в следующем

 

local game_difficulty = reader:r_u8()

затем следом

local stored_input_time = reader:r_u8()

 

вот не укладывается у меня в голове - как так 2 разных переменных с разными значениями равны reader:r_u8()

нет-пакеты же ты понимаешь? или нет? :)

Функция r_u8() читает один байт и передвигает текущую позицию на один байт вперед.Повторный вызов r_u8() прочитает следующий байт. Все точно также как с обычными файлами

 

надо для "пробы" вскрытия файлов *.sav

Там все просто - пожаты обычным сталкеровским методом - разжимаешь - там чанки с разным содержимым:

ид чанка:

0x0 - хранится версия alife

0x1 - идет ссылка на all.spаwn, может еще что

0x2 - нет-пакеты спавн-объектов

0x5 - 8! байт время, текущий тайм-фактор, нормал таймфактор

0x9 - инфопоршни, отношение между ГГ и группировками, статьи в пда, лог в пда, метки на карте, задания, статистика

 

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

Возник вопрос: Как правильно присоединить окно класса CuiScriptWnd к инвентарю через функцию level.main_input_receiver()?

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

Алгоритм простой:

- ждешь пока откроется инвентарь, ловишь ui_inventory в info_callback

- получаешь его окно через level.main_input_receiver()

- аттачишь свое окно AttachChild

 

Лучше скажи, что именно и как именно не получалось.

Возможно стоит перейти в ковырялку

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

DimOriN,

local game_difficulty = reader:r_u8()

local stored_input_time = reader:r_u8()

как так 2 разных переменных с разными значениями равны reader:r_u8()

у reader есть некий указатель внутри, который смещается при каждом чтении. Всякий раз читает новое значение и смещается. При следующем чтении читается уже другое значение. Всё-таки почитай про net_packet. Там очень похоже.

 

Real Wolf,

Как правильно присоединить окно класса CuiScriptWnd к инвентарю через функцию level.main_input_receiver()?

Хлопотное это дело. Потом, не уверен, что стоит пытаться присоединять к одному диалогу другой. Лучше отдельные контролы. Безвылетную технологию для этого я описывал здесь.

 

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

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

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

 

Ссылка на комментарий
Потом, не уверен, что стоит пытаться присоединять к одному диалогу другой. Лучше отдельные контролы.

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

Ссылка на комментарий
Лучше скажи, что именно и как именно не получалось.

Возможно стоит перейти в ковырялку

 

Нужно к инвентарю приаттачить кнопку, при нажатии, которой происходит действие, соответственно. Вот только у класса кнопки нет действия на нажатие(по крайней мере я не нашёл), поэтому начал делать через класс CuiScriptWnd, вот только он остаётся активным, а инвентарь нет, или инвентарь работает и кнопка, но нет иконок в инвентаре и слотах, иконка ГГ в костюме работает. Описания тоже.

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

Kolmogor,

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

 

Real Wolf,

таки прочитай тот старый пост, что я привёл. Там именно твоя проблема и решалась.

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

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

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

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

 

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

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

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

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

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

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

Войти

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

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

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

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