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

[SOC] Мелкие правки движка


Kondr48

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

добавление, например, на выстрел в Вашем примере - это всего лишь вызов в нужном месте что нибудь типа псевдокод вызова калбека на выстрел из класса оружия
auto source=this;
if (m_Owner==Actor()) 
{
 source=Actor();
}

source->callback(типкалбекахит)(кто_стрелял, из_чего, чем);
теперь в классе актора, или сталкера (да, нужен скриптовый класс биндера "script_binding= ", иначе у чего будет движок вызывать эти методы при их наличии?) регистрируем этот калбек через set_callback в одном вызове (ну и в одном очищаем) - чаще всего init/net_destroy. все, дальше обрабатываем так как надо. никаких load/save/update не требуется. 
еще один пример - реализация калбека на хит по объекту. движком генерируем вызов этого калбека из CGameObject (прародителя усех объектов в луа). в самих скриптах только в объектах (биндере), например, бочки делаем обработку этого калбека - и все, обрабатываются только бочки. 

И, да, само вот это вот "а на актора или еще куда

Само собой, иногда приходиться идти на хитрость - есть окно инвентаря - вот куда ему добавить перехват калбека , если по умолчанию, ни сам класс этого окна не экспортируется в игру (это полбеды), ни в игре нету дочернего класса от CUIInventoryWnd, чтобы на нем добавить обработчики (как это например сделано для главного меню). Но со вторым вариантом возиться долго и не интересно. проще всегда делать 
Actor()->callback(калбек_из_окна)(окно_каллбека, другие параметры)
и в биндере актора перехватить это калбек. все, чесно и прозрачно. Придумывать единый механизм, отличный от Actor() для таких целей - ну не знаю, не уверен что это окупиться. 

Кто уже довольно неплохо разгреб, в каких файлах функциях что лежит - может поделитесь

Поделиться чем? в движке примерно 4к файлов *.cpp. Сформулируйте вопрос... 
Изменено пользователем Kondr48
Немного подправил цитаты.
  • Нравится 1

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


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

Поговорим немного о диалогах в ТЧ 1.0007. Наверное, большинство знают что диалоги (как, например, и артикли в энциклопедии) являются объектами, которые загружаются один раз за всю сессию игры (на основании паттерна Singleton) в структуру SPhraseDialogData : CSharedResource. т.е. если мы разрабатываем диалог, то для проверки того что именно мы написали необходимо выйти и зайти в игру полностью. Само собой это сводит на нет полностью работу с диалогами, создаваемыми скриптами (через експорт функций класса CPhraseDialog). Но часто хочется оживить Зону и разнообразить диалоги. 

1) расширяем чуть чуть экспорт CPhraseDialog

Добавляем в экспорт CPhraseDialog функцию SetPriority для изменения приоритета нашего диалога. Теперь он выглядит вот так:

class_<CPhraseDialog>("CPhraseDialog")
		.def("AddPhrase",			&CPhraseDialog::AddPhrase_script )
		.def("SetPriority",			&CPhraseDialog::SetPriority),
 

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

 

 

2) В связи с тем что 80-90% диалогов все таки статические (им хватает для жизни preconditions) - добавляем параметр, который позволит управлять, обновлять ли диалог каждый раз, или только при первой загрузке

в структуру

struct SPhraseDialogData : CSharedResource

добавляем поле-признак, например, bool b_bForceReload. в класс CPhraseDialog два метода

public:
	void					SetDialogForceReload(bool value=false) {data()->b_bForceReload=value;}
	bool					GetDialogForceReload() {return data()->b_bForceReload;}
 

 

 

CPhraseDialog::load_shared после SetCaption, например, добавляем

SetDialogForceReload(pXML->ReadAttribInt(dialog_node, "force_reload", 0)==1? true:false);

 

 

т.е. при наличии в конфиге диалога параметра force_reload="1" он помечается как перезагружаемый  :)

<dialog id="test_dynamic" force_reload="1">
	<init_func>subtest.gen_dialog1</init_func>
</dialog> 

3) Добавляем в класс окна диалогов признак, который указывает, что окно в данный момент инициализируется. Это нужно в связи с тем что CPhraseDialogManager::AddAvailableDialog может вызываться из других мест, не только из инициализатора окна диалога. позволяет избежать, например, перестроения диалога, когда он уже начат с кем то из НПС.

в класс CUITalkWnd добавляем

protected:
    bool                m_bInitState;
public:
    bool GetInitState() const {return m_bInitState;}

и инициализацию m_bInitState=false в конструктор класса.

 

 

в методе void CUITalkWnd::InitTalkDialog() в начале пишем
m_bInitState=true;
и аналогично в конце
m_bInitState=false;
Теперь мы имеем признак того что CUITalkWnd инизиализирует графы диалогов.

 

4) Изменяем метод загрузки данных для конкретного диалога с использованием методов CSharedResource

void CPhraseDialog::Load(shared_str dialog_id)
{
	m_DialogId = dialog_id;
	bool need_load=inherited_shared::start_load_shared(m_DialogId); //делаем инициализацию 	SPhraseDialogData* при первом вызове. если синглтон не был создан ранее - он создается, иначе возвращается созданный
        //результат функции - это флаг, указывающий, требуется ли загрузка данных при первичной инициализации.
	if (need_load) //структура создается первый раз, ее надо просто загрузить, также таким образом можно избежать двойного вызова метода загрузки
		inherited_shared::load_shared(m_DialogId, nullptr);
	else if (GetDialogForceReload()) //структура уже создавалась и загружалась ранее
	{ //но установлен флаг для принудительной перезагрузки
		CUIGameSP* ui_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame());
		if (ui_sp && ui_sp->TalkMenu->GetInitState()) //перезагружаем данные только тогда, когда идет инициализация окна диалога
		{
			data()->SetLoad(false);//для структуры SPhraseDialogData устанавливаем флаг принудительной загрузки
			inherited_shared::load_shared(m_DialogId, nullptr); //обновляем данные
                        //inherited_shared::finish_load_shared вызывать не обязательно, так как load_shared уже выполняет его функционал.

		}
	}
} 

 

 

Все. теперь при каждом открытии окна диалога у Вас будет перезагружаться скрипт subtest.gen_dialog1, который Вам позволит генерировать случайные фразы, например, через math.random  :)

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

  • Спасибо 3

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


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

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

Мой пост можно воспринимать как иллюстрацию - как победить shared_data.Она используется не только для диалогов.

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

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


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

@Карлан, было бы неплохо, если бы Вы поделились частью исходников для изучения, как подобный механизм реализован у Вас, а именно CPhraseDialog::Load, CPhraseDialog::load_shared. xr_3da\xrGame\xml_str_id_loader.cpp тоже очень хотелось бы изучить :) 

Спасибо.

  • Спасибо 1
  • Согласен 1

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


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

В продолжении темы борьбы с диалогами - необходимо в выше приведенном xr_3da\xrGame\PhraseDialog.cpp void CPhraseDialog::Load(shared_str dialog_id)

заменить на такой:

void CPhraseDialog::Load(shared_str dialog_id)
{
	m_DialogId = dialog_id;
	bool need_load=inherited_shared::start_load_shared(m_DialogId); //начинаем загрузку
	if (need_load) //свеже созданное
		inherited_shared::load_shared(m_DialogId, nullptr);
	else if (GetDialogForceReload()) //уже создавалось раньше
	{
		CUIGameSP* ui_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame());
		if (ui_sp && ui_sp->TalkMenu->GetInitState()) //читать только если идет инициализация окна диалога. в других Load - загружать наново не надо
		{
			ITEM_DATA item_data = *id_to_index::GetById(m_DialogId); //перечитаем xml часть диалога
			std::string file_name=item_data._xml->m_xml_file_name;
			const size_t sidx = file_name.rfind('\\');
			if (std::string::npos != sidx)
				file_name=file_name.substr(sidx+1,file_name.length());
                        item_data._xml->ClearInternal(); //почистим внутреннюю структуру xml парсера
                        item_data._xml->Init(CONFIG_PATH, GAME_PATH, file_name.c_str());//заново запустим парсинг xml с диска
			data()->SetLoad(false);
			inherited_shared::load_shared(m_DialogId, nullptr); //перестроим граф диалогов на основании новой структуры xml
		}
	}
} 

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

 

 

Также , для меня чрезвычайно полезной оказалась немного переделанная функция дампа структуры xml, взятая с официальной документации TinyXML.

Добавляем в

в начало:

#include <locale>
#include <iostream>
#include <sstream>

и куда-нибудь, можно в конец:

#pragma region dump xml from tinyxml docs
const unsigned int NUM_INDENTS_PER_SPACE=2;

const char * getIndent( unsigned int numIndents )
{
	static const char * pINDENT="                                      + ";
	static const unsigned int LENGTH=strlen( pINDENT );
	unsigned int n=numIndents*NUM_INDENTS_PER_SPACE;
	if ( n > LENGTH ) n = LENGTH;
	return &pINDENT[ LENGTH-n ];
}

// same as getIndent but no "+" at the end
const char * getIndentAlt( unsigned int numIndents )
{
	static const char * pINDENT="                                        ";
	static const unsigned int LENGTH=strlen( pINDENT );
	unsigned int n=numIndents*NUM_INDENTS_PER_SPACE;
	if ( n > LENGTH ) n = LENGTH;
	return &pINDENT[ LENGTH-n ];
}

std::string dump_attributes(TiXmlElement* pElement, unsigned int indent)
{
	if ( !pElement ) return "";
	std::string result="";
	TiXmlAttribute* pAttrib=pElement->FirstAttribute();
//	const char* pIndent=getIndentAlt(indent);
	while (pAttrib)
	{
		std::stringstream ss;
		ss << pAttrib->Name() << ": value=["<<pAttrib->Value()<<"]";
		pAttrib=pAttrib->Next();
		result+=ss.str()+(result.length()>0? " " : "");
	}
	return result;	
}

void dumpTree(XML_NODE* startNode, unsigned int indent = 0)
{
	TiXmlNode* pChild;
	TiXmlText* pText;
	int t = startNode->Type();
	LPCSTR indentStr= getIndent(indent);
	switch ( t )
	{
	case TiXmlNode::DOCUMENT:
		Msg( "Document" );
		break;

	case TiXmlNode::ELEMENT:
		{
		std::string attrInfo=dump_attributes(startNode->ToElement(), indent+1);
		if (attrInfo.length()==0)
			Msg( "%sElement [%s]", indentStr,startNode->Value());
		else
			Msg( "%sElement [%s] attributes:(%s)", indentStr,startNode->Value() ,attrInfo.c_str());
		}
		break;

	case TiXmlNode::COMMENT:
		Msg( "%sComment: [%s]",indentStr, startNode->Value());
		break;

	case TiXmlNode::UNKNOWN:
		Msg( "%sUnknown" ,indentStr);
		break;

	case TiXmlNode::TEXT:
		pText = startNode->ToText();
		Msg( "%sText: [%s]", indentStr,pText->Value() );
		break;

	case TiXmlNode::DECLARATION:
		Msg( "%sDeclaration",indentStr );
		break;
	default:
		break;
	}
	//printf( "\n" );
	for ( pChild = startNode->FirstChild(); pChild != nullptr; pChild = pChild->NextSibling()) 
		dumpTree( pChild, indent+1 );
}

#pragma endregion 
void CXml::dump()
{
	Msg("--- start dump content for xml object based on %s ---",this->m_xml_file_name);
	dumpTree(GetRoot());
	Msg("--- end dump content for xml object based on %s ---",this->m_xml_file_name);
} 

 

 

 

В описание класса CXml  добавляем в

 

public:
   void dump();

 

 

Теперь, вызвав, например, в выше приведенном коде 

item_data._xml->dump();

в консоли мы увидим структуру распарсенного xml файла.

Изменено пользователем Kondr48
объединил посты
  • Спасибо 1

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


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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