PDA

Просмотр полной версии : SDK - технологии



volod
30.01.2010, 03:10
Доброго времени суток.

Опять же для начинающих и продолжающих - небольшой опус по работе с SDK. Поехали.

Цель задачи - добавление новых свойств технологиям (Опять же задача специфическая, но на её примере можно немножко рассмотреть чё такое SDK и с чем его едят).

Описания установки и сборки SDK приводить не буду, поскольку уже есть хорошая статья на эту тему в меморизе. Так же весьма вскользь задену тему добавления атрибутов в XML и их считывания в SDK - об этом весьма подробно написано в Добавление атрибутов в SDK (http://modiki.civfanatics.ru/index.php/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_XML_%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D0%BE%D0%B2 _%D0%B8_%D0%B8%D1%85_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B2_SDK)

В основном будем рассматривать непосредственно код.
Итак, в моём примере мне нужно было добавить процентные модификаторы коммерческой отдачи от освоения технологий, а так же открытие возможности построения городов при изучении некоторой технологии.
Добавляем два параметра в XML-схему (CIV4TechnologiesSchema.xml), один булевского типа:
bCanFound
, другой - структура коммерческой отдачи:
<CommerceModifiers>
<iCommerce/>
</CommerceModifiers>

Добавляем новые параметры всем технологиям, проставляем значения.
Открываем CvInfos.cpp, пишем в конструкторе класса CvTechInfo::CvTechInfo() :
инициализацию переменных:
m_bCanFound(false)
и
m_piCommerceModifiers(NULL)
сразу же добавим в деструктор CvTechInfo::~CvTechInfo()
корректное удаление массива
SAFE_DELETE_ARRAY(m_piCommerceModifiers);

далее пишет функции считывания новых параметров, булевский:
bool CvTechInfo::isCanFound() const
{
return m_bCanFound;
}
и, соответственно значение массива по индексу:
int CvTechInfo::getCommerceModifier(int i) const
{
FAssertMsg(i < NUM_COMMERCE_TYPES, "Index out of bounds");
FAssertMsg(i > -1, "Index out of bounds");
return m_piCommerceModifiers ? m_piCommerceModifiers[i] : -1;
}

и сам массив:
int* CvTechInfo::getCommerceModifiersArray() const
{
return m_piCommerceModifiers;
}

Описанные поля класса и функции доступа к ним не забываем добавить в описание класса в CvInfos.h.
В секции public:
DllExport bool isCanFound() const; // Exposed to Python
DllExport int getCommerceModifier(int i) const; // Exposed to Python
int* getCommerceModifiersArray() const;
В секции protected:
bool m_bCanFound;
int* m_piCommerceModifiers;
Теперь поправим функцию
void CvTechInfo::read(FDataStreamBase* stream)
добавив строки
stream->Read(&m_bCanFound);
и
SAFE_DELETE_ARRAY(m_piCommerceModifiers);
m_piCommerceModifiers = new int[NUM_COMMERCE_TYPES];
stream->Read(NUM_COMMERCE_TYPES, m_piCommerceModifiers);
а так же функцию
void CvTechInfo::write(FDataStreamBase* stream)
строками
stream->Write(m_bCanFound);
и
stream->Write(NUM_COMMERCE_TYPES, m_piCommerceModifiers);
Здесь, думаю, всё понятно. Чтение и запись наших параметров.
А теперь правим функцию чтения из XML
bool CvTechInfo::read(CvXMLLoadUtility* pXML)
добавляя строки
pXML->GetChildXmlValByName(&m_bCanFound, "bCanFound");
и
if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CommerceModifiers"))
{
pXML->SetCommerce(&m_piCommerceModifiers);
gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
}
else
{
pXML->InitList(&m_piCommerceModifiers, NUM_COMMERCE_TYPES);
}
В последнем примере поясню, если вдруг кто не поймёт, выполняемое по ветке else проводит базовую инициализацию массива, если запись этого тэга в XML не найдена.
Функция SetCommerce взята из стандартных, её стоит использовать, если вы определили внутренние тэги <CommerceModifiers>, хранящие сами изменения отдачи, как <iCommerce>. Иначе придётся писать свою функцию :)

На этом с инфосами всё, собираем длл-ку, подкладываем своему моду, запускаемся, тестим. Если всё написано правильно, игра успешно запустится. Новые параметры у нас есть, но пока они ещё ничего не делают. Займёмся теперь описанием функционала.
Здесь нужно понимать, что есть понятия "Игрок" и есть понятие "Команда". Исследование технологий является процессом, выполняемым командой. Флаг основания городов я добавил команде, а модификаторы коммерческой отдачи каждому игроку (вообще, их можно было добавить и команде, но мне для дальнейшей задумке требовалось приписывать их именно игроку. На текущем примере работать будет так же, как если бы было установлено команде).
Итак, открываем CvTeam.cpp и пишем в функции CvTeam::reset установку флага по дефолту
m_bCanFound = false;

Почему именно в reset? Потому что эта функция вызывается в функции инициализации CvTeam::init и, фактически, является инициализирующей функцией.

Добавляем функции доступа к новому параметру:
bool CvTeam::isCanFound() const
{
return m_bCanFound;
}

void CvTeam::setCanFound(bool bNewValue)
{
if (isCanFound() != bNewValue)
{
m_bCanFound = bNewValue;
}
}

Прописываем саму переменную m_bCanFound и функции доступа к ней в хидере CvTeam.h (не забываем, что ф-ии доступа нужно ложить в секцию public, чтобы открыть их для питона, если понадобится в дальнейшей в нём)
Теперь пропишем считывание и установку параметров при исследовании технологии. Находим функцию:
void CvTeam::processTech(TechTypes eTech, int iChange)
и среди прочих выставляемых параметров добавляем блок возможности освоения городов:
if (GC.getTechInfo(eTech).isCanFound())
{
setCanFound(true);
}
А с блоком модификации коммерческой отдачи немножко интереснее. Можно сделать ещё один цикл по типам, но я предпочёл вставить в уже существующий (всё-таки не нужно создавать лишних проходов жрущих лишнее время :) )
Находим
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
...
И выставляем внутри свой блок. Должно быть так:
for (iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
{
if (GC.getTechInfo(eTech).isCommerceFlexible(iI))
{
changeCommerceFlexibleCount(((CommerceTypes)iI), iChange);
}
for (iJ = 0; iJ < MAX_PLAYERS; iJ++){
if (GET_PLAYER((PlayerTypes)iJ).getTeam() == getID()){
GET_PLAYER((PlayerTypes)iJ).changeCommerceRateModifier(((CommerceTypes)iI), (GC.getTechInfo(eTech).getCommerceModifier(iI) * iChange));
}
}
}
То есть, после установки параметра CommerceFlexible мы модифицируем параметр коммерческого модификатора всех игроков команды.
Собираем длл, запускаемся, тестируем. Если всё описано верно, должны работать указанные в XML модификаторы коммерческой отдачи при открытии технологий. А вот города всё ещё можно строить, независимо от технологий. Потому что мы только считали значение, но ещё никак его не используем.
Поехали. Команды юнитов у нас описаны в CvUnit.cpp
Сперва для удобства записи сделаем функцию в CvPlayer.cpp
bool CvPlayer::isCanFound() const
{
return GET_TEAM(getTeam()).isCanFound();
}
И не забываем добавить её в public-секцию описания класса в CvPlayer.h
Далее открываем CvUnit.cpp и добавляем в функцию bool CvUnit::canFound(const CvPlot* pPlot, bool bTestVisible) const
ещё одну проверку. Вместо
if (!(isFound())
пишем
if (!(isFound() && (GET_TEAM(getTeam()).isCanFound()))
это условие будет проверять, есть ли у нашей команды возможность строить города, которая задаётся открытием технологий.
Теперь ограничим поведение юнитов АИ в файле CvUnitAI.cpp
В функции
void CvUnitAI::AI_settleMove()
меняем условие if (canFound(plot()))
на if (GET_PLAYER(getOwnerINLINE()).isCanFound() && canFound(plot()))
Эта проверка производится там в трёх блоках, нужно поправить все три.

Здесь сделаю небольшое отступление. Сперва я пробовал добавить проверку в саму функцию ::canFound плейера. Но при этом АИ начинает думать, что он не может строится на конкретной проверяемой клетке и начинает оценивать следующую. В результате получается очень сильное подвисание АИ. Добавив проверку в описанном выше месте, мы блокируем поиск АИ места для строительства, если он в принципе ещё не может строится.

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

Возвращаемся к нашей теме. Теперь новые параметры работают, но хотелось бы ещё и видеть их описание. Во-первых, в подсказке к самим технологиям, во-вторых, отразить на экране города из чего собственно состоит коммерческая отдача.
Текстовые подсказки описываются в CvGameTextMgr.cpp.
Нас будет интересовать функция описания технологии:
void CvGameTextMgr::setTechHelp(CvWStringBuffer &szBuffer, TechTypes eTech, bool bCivilopediaText, bool bPlayerContext, bool bStrategyText, bool bTreeInfo, TechTypes eFromTech)
Среди выводы прочих флагов добавляем вызов
buildCanFoundString(szBuffer, eTech, true, bPlayerContext);
Сама функция формирования строки выглядит так:
void CvGameTextMgr::buildCanFoundString(CvWStringBuffer &szBuffer, TechTypes eTech, bool bList, bool bPlayerContext)
{
if (GC.getTechInfo(eTech).isCanFound() && (!bPlayerContext || !(GET_TEAM(GC.getGameINLINE().getActiveTeam()).isCanFound())))
{
if (bList)
{
szBuffer.append(NEWLINE);
}
szBuffer.append(gDLL->getText("TXT_KEY_MISC_ENABLES_FOUND"));
}
}
Отображаемый текст нужно положить в описание тэга TXT_KEY_MISC_ENABLES_FOUND в CIV4GameTextInfos.xml
Не забываем добавить функцию в хидер CvGameTextMgr.h

Возвращаемся к функции описания технологии и среди прочих блоков описания добавляем
int aiCommerces[NUM_COMMERCE_TYPES];
for (iI = 0; iI < NUM_COMMERCE_TYPES; ++iI)
{
aiCommerces[iI] = GC.getTechInfo(eTech).getCommerceModifier(iI);
}
setCommerceChangeHelp(szBuffer, L", ", L"", L"", aiCommerces, true, false);
То есть, читаем модификаторы ком.отдачи из описания технологии и формируем стандартной функцией строку с внятным описанием.
Примечание: если предпоследним параметром в setCommerceChangeHelp передать false, то проценты рисоваться не будут, то есть будет показано количественное приращение. Разумеется, чтобы и логически приращивались не проценты, а абсолютные величины, нужно при освоении технологии вызывать не changeCommerceRateModifier, а changeCommerceRate.

Теперь добавим описание на экран города. Находим функцию
void CvGameTextMgr::setCommerceHelp(CvWStringBuffer &szBuffer, CvCity& city, CommerceTypes eCommerceType)
и добавляем туда в то место, где хотим видеть информацию о модификаторах от технологий (у меня перед мод. от институтов) блок:

int iTechMod = 0;
for (int i = 0; i < GC.getNumTechInfos(); i++)
{
if (GET_TEAM(owner.getTeam()).isHasTech((TechTypes)i))
{
iTechMod += GC.getTechInfo((TechTypes)i).getCommerceModifier(eCommerceType);
}
}
if (0 != iTechMod)
{
szBuffer.append(gDLL->getText("TXT_KEY_MISC_HELP_YIELD_TECH", iTechMod, info.getChar()));
szBuffer.append(NEWLINE);
iModifier += iTechMod;
}
Не забываем положить внятный текст в CIV4GameTextInfos.xml для ключа TXT_KEY_MISC_HELP_YIELD_TECH.

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

Надеюсь, описанный пример поможет начинающим разработчикам лучше ориентироваться в SDK :)

С уважением, Волод.

Snake_B
30.01.2010, 03:28
<div class='quotetop'>Цитата(Волод * 30.1.2010, 3:08) 323566</div>

http://modiki.civfanatics.ru/index.php/%D0...0%B5_%D0%B2_SDK (http://modiki.civfanatics.ru/index.php/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_XML_%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D0%BE%D0%B2 _%D0%B8_%D0%B8%D1%85_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B2_SDK)[/b]

Ссылки лучше делать так... (http://modiki.civfanatics.ru/index.php/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BD%D0%BE%D0%B2%D1%8B%D1%85_XML_%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D0%BE%D0%B2 _%D0%B8_%D0%B8%D1%85_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B2_SDK)

<div class='quotetop'>Цитата(Волод * 30.1.2010, 3:08) 323566</div>

{
return m_bCanFound;
}
[/b]

а код так...
[code]
{

Хальк Юсдаль
30.01.2010, 04:15
А название файлов и тому подобное, лучше выделять жирным шрифтом.

Открываем CvInfos.cpp, пишем в конструкторе класса CvTechInfo::CvTechInfo() :
инициализацию переменных:

А вообще спасибо огромное за статью, эх побольше бы таких было бы. Модостроение бы тогда намного дальше продвинулось.

Snake_B
30.01.2010, 12:10
<div class='quotetop'>Цитата(Хальк Юсдаль * 30.1.2010, 4:13) 323570</div>

А вообще спасибо огромное за статью, эх побольше бы таких было бы. Модостроение бы тогда намного дальше продвинулось.
[/b]

не дальше, а шире... :-p

kabjans
30.01.2010, 12:48
Очень интересная статья. Большое спасибо. :applau2:

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

Заранее большое спасибо.

volod
30.01.2010, 13:09
Модифицированные файлы прилагаю.
Но кроме описанных в статье изменений там есть и другие :) Например, добавлена процентная коммерческая отдача от проектов, которая ещё не описывается в текстах подсказок, а так же добавлена миссия "Разбить стоянку" для сеттлеров, которая пока увеличивает базовый научный рост на 10% при старте и уменьшает его на 10% при прекращении, а анимируется, как укрепление.

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

Поскольку размер загружаемых файлов ограничен, выложу в архиве.

С уважением, Волод.

NeseryozniyVET
31.01.2010, 18:42
Вставлю я и свои 5 копеек.
Хочу заметить что это только теория, на практике не проверенная !!!
Волод упустил одну маленькую деталь: новые фишки не будут показыватся на дереве технологий. Я покажу как это исправить.
Открываем файл Assets\Python\Screens\CvTechChooser.py и находим
def placeTechs (self).
Внутри этой функции ищем цикл
for i in range(gc.getNumTechInfos()):
В нем после строчек
fX = X_START
пишем[code]if gc.getTechInfo(i).isCanFound():

volod
31.01.2010, 20:26
Заморочиться, наверное, стоит, но потом, когда времени больше будет.
А сейчас работает всё и так ) На дереве технологий прекрасно всё рисуется.

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

С уважением, Волод.

NeseryozniyVET
31.01.2010, 21:03
<div class='quotetop'>Цитата(Волод * 31.1.2010, 18:24) 323705</div>
Там просто многие функции сперва вызывают питоновский вариант, и если питон сделает свою работу и вернёт им нужный флаг, начинают делать свой. Я не стал заморачиваться с питоном, а напрямую внёс изменения в сдк.[/b]А где именно?

volod
31.01.2010, 21:14
Класс CvGameTextMgr, функция setCommerceChangeHelp для показа на городском экране и setTechHelp для показа во всех окнах технологий.

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

NeseryozniyVET
31.01.2010, 21:21
<div class='quotetop'>Цитата(Волод * 31.1.2010, 19:12) 323711</div>
Класс CvGameTextMgr, функция setCommerceChangeHelp для показа на городском экране и setTechHelp для показа во всех окнах технологий.

Как в первом посте описывал - добавил строчку, в которую выводится текст о том, что технология позволяет основывать города. И добавил стандартный блок с показателями коммерческих изменений к выходной строке.[/b]Так это, вроде, только подсказка, когда курсор на теху наводишь.
А я про показ кнопок на панельке технологии.

volod
31.01.2010, 21:41
<div class='quotetop'>Цитата(NeseryozniyVET * 31.1.2010, 20:19) 323712</div>

Так это, вроде, только подсказка, когда курсор на теху наводишь.
А я про показ кнопок на панельке технологии.
[/b]

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

Blacksun
04.02.2010, 14:12
А можно поподробнее, чего мы ищем ? (Ведь АИ и так умеет строить города.). И еще. Функции для АИ и для челов, совершенно разные. Поэтому надо вставлять коды и для АИ.

NeseryozniyVET
04.02.2010, 17:53
<div class='quotetop'>Цитата(Blacksun * 4.2.2010, 12:10) 323983</div>
И еще. Функции для АИ и для челов, совершенно разные. Поэтому надо вставлять коды и для АИ.[/b]Тоесть, в данном примре если человек выучит теху то получит бонус, а если ИИ выучти то толку не будет ?

Blacksun
05.02.2010, 14:55
<div class='quotetop'>Цитата(NeseryozniyVET * 4.2.2010, 16:51) 324020</div>

<div class='quotetop'>Цитата(Blacksun * 4.2.2010, 12:10) 323983
И еще. Функции для АИ и для челов, совершенно разные. Поэтому надо вставлять коды и для АИ.[/b]Тоесть, в данном примре если человек выучит теху то получит бонус, а если ИИ выучти то толку не будет ?
[/b][/quote]

Дело не в этом. Чел будет стремиться ее учить, потому что получит бонус. А АИ, не будет знать бонуса и поэтому эту теху может учить не в первую очередь.

volod
05.02.2010, 15:19
<div class='quotetop'>Цитата</div>

Дело не в этом. Чел будет стремиться ее учить, потому что получит бонус. А АИ, не будет знать бонуса и поэтому эту теху может учить не в первую очередь.
[/b]

АИ будет изучать технологии согласно заданной в её описании схеме. В том числе, опираясь на "ароматы" и числовые значения ценности для АИ.

Конечно, мысль о том чтобы поправить код, выбирающий теху для АИ, тоже правильная. Но его поведение в данном случае прекрасно рулится настройками XML, без вмешательства в код.

С уважением, Волод.

Blacksun
06.02.2010, 13:53
Может для данного случая это не так актуально, но, когда мне надо было делать так, чтобы АИ делал тераформинг, приходилось исправлять код для АИ рабочего. (Это так, на будущее) :bye: