PDA

Просмотр полной версии : Уроки SDK. Поэтапно с начала.



Cansei
19.08.2013, 01:44
Подумал тут я, и решил начать цикл уроков по работе с SDK четвертой цивы, где объясню по возможности адекватно, что я сам в нём понял. Будет где-то пять уроков по добавлению новых возможностей через SDK. Ну это если я раньше не потеряю интереса.
Для начала что нужно для этих уроков.
Во первых нам потребуется все необходимое для компиляции SDK, описанное в этой статье (http://www.civfanatics.ru/threads/7424-%D0%9E%D1%81%D0%B2%D0%B0%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC-SDK-%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-SDK?p=425937&viewfull=1#post425937). Во вторых самые элементарные познания в программировании, что такое IF ELSE, функции и переменные. В третьих Notepad++ (http://notepad-plus-plus.org/), для удобного редактирования и WinMerge (http://winmerge.org/?lang=ru) для сравнения измененных файлов. Notepad++ нам потребуется уже на первом уроке, в то время как WinMerge лишь на следующих уроках.

Итак, установив Notepad++, открываем через него все файлы c расширением cpp и h из нашей папки с исходниками игры. Заодно сохраняем текущую сессию в Notepad++
это Файл ->Сохранить сессию, сохраняем под удобным названием, например Весь SDK, или all dll sources. В дальнейшем мы сможем через “Загрузить сессию” восстановить весь этот набор окон. Подобным образом в будущем можно будет работать с подходящим набором окон, не маясь со всем списком.

Урок 1. Основы работы с SDK, как добавить новую опцию игры. (http://www.civfanatics.ru/threads/10859-%D0%A3%D1%80%D0%BE%D0%BA%D0%B8-SDK-%D0%9F%D0%BE%D1%8D%D1%82%D0%B0%D0%BF%D0%BD%D0%BE-%D1%81-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%B0?p=427796&viewfull=1#post427796)
Урок 2. Копирование уже существующего булевского XML тега из CIV4UnitInfos.xml в CIV4PromotionInfos.xml. (Дать прокачкам те возможности, что ранее были только уникальными для юнитов) (http://www.civfanatics.ru/threads/10859-%D0%A3%D1%80%D0%BE%D0%BA%D0%B8-SDK-%D0%9F%D0%BE%D1%8D%D1%82%D0%B0%D0%BF%D0%BD%D0%BE-%D1%81-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%B0?p=427797&viewfull=1#post427797)



Спецурок от NeseryozniyVET, Cv и Cy файлы, как добавить параметр не относящийся к текущему инфо-классу. (http://www.civfanatics.ru/threads/10859-%D0%A3%D1%80%D0%BE%D0%BA%D0%B8-SDK-%D0%9F%D0%BE%D1%8D%D1%82%D0%B0%D0%BF%D0%BD%D0%BE-%D1%81-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%B0?p=427896&viewfull=1#post427896)
Спецурок от NeseryozniyVET. Как добавить активный элемент на экран. (http://www.civfanatics.ru/threads/10859-%D0%A3%D1%80%D0%BE%D0%BA%D0%B8-SDK-%D0%9F%D0%BE%D1%8D%D1%82%D0%B0%D0%BF%D0%BD%D0%BE-%D1%81-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%B0?p=429049&viewfull=1#post429049)
Спецурок SDK-технологии за авторством volod(отдельная тема). (http://www.civfanatics.ru/threads/7826-SDK-%D1%82%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D0%B8)
P.S. Если что-то не так и не работает, пишите. Как вернусь, проверю, исправлю.

Cansei
19.08.2013, 01:44
Урок 1. Основы работы с SDK, как добавить новую опцию игры.

В этом уроке мы займемся созданием новой игровой опции, из тех, что можно выбирать при создании новой игры. Вроде “Raging Barbarians”, той что повышает активность варваров. Мы сделаем её аналог для животных, назовём “Animal World”, она будет увеличивать количество животных в начале игры.

Первый шаг: Найти в папке Assets/XML/GameInfo/ файл CIV4GameOptionInfos.xml. Он то как раз и содержит список игровых опций. Работать нужно с версией из вашего мода разумеется.
Примечание: Для работы с xml можно использовать XML Marker, но я все равно настоятельно порекомендую Notepad++, он куда удобнее.
Находим в этом файле блок.

<GameOptionInfo>
<Type>GAMEOPTION_RAGING_BARBARIANS</Type>
<Description>TXT_KEY_GAME_OPTION_RAGING_BARBARIANS</Description>
<Help>TXT_KEY_GAME_OPTION_RAGING_BARBARIANS_HELP</Help>
<bDefault>0</bDefault>
<bVisible>1</bVisible>
</GameOptionInfo>
И создаем его копию ниже, с сохранением всех тегов вроде > </. Изменяем в копии RAGING_BARBARIANS на ANIMAL_WORLD. Сохраняем файл.
Пояснение:

<Type/> это собственно название опции для самой игры, оно нам и нужно прежде всего.
<Description/> это ссылка на текстовый файл с её названием, как мы его видим в игре. <Help/> это подсказка всплывающая при наведении на опцию курсора.
<bDefault> это включена ли опция по умолчанию.
<bVisible/> это судя по названию видна ли она в меню создания игры.

Предостережение: После изменения этого файла, пока мы не проведем все нужные изменения в самой игре, она перестанет запускаться.
Шаг второй: Начинаем поиск этого названия в исходниках игры. Собственно это основа подавляющей части SDK изменений. Нужно найти в этих самых исходниках, как прописывается в игре тот или иной элемент и прописать по аналогии. Большая часть работы с новыми элементами, это обычная рутина. (Автоматизировать бы её.)
Для этого в Notepad++ со всем открытыми исходными файлами начинаем поиск. Копируем в окно поиска GAMEOPTION_RAGING_BARBARIANS и нажимаем “Поиск во всех файлах”. Это к слову можно сделать и в Visual C++, просто в Notepad++ это опять же удобнее. Находим 8 результатов в разных файлах, но пока нам потребуются лишь три из них.
Шаг третий: Открываем нужную строку файла CyEnumsInterface.cpp, это можно сделать через окно поиска внизу. Видим там следующую строку.
.value("GAMEOPTION_RAGING_BARBARIANS", GAMEOPTION_RAGING_BARBARIANS)
С полным сохранением синтаксиса делаем её копию в том же самом порядке, что делали в XML файле. После чего меняем.
.value("GAMEOPTION_ANIMAL_WORLD", GAMEOPTION_ANIMAL_WORLD)
Проверяем нет ли отличий от соседей, сохраняем файл. Теперь наша опция считывается из XML.
Открываем CvEnums.h и под GAMEOPTION_RAGING_BARBARIANS, вписываем точно также с той же запятой GAMEOPTION_ANIMAL_WORLD. Сохраняем.
Теперь наша опция доступна в самом проекте игры.
Шаг четвертый. Вписывание в нужные функции. Самый пожалуй сложный и интересный этап работы с SDK, который требует думать и читать код. Ну и творческий заодно. Тут как раз и потребуются, хотя бы совсем элементарные, познания в программировании.
Открываем файл CvGame.cpp и смотрим там упоминания наших варваров. Первые два результата поиска относятся к функции void CvGame::createBarbarianCities() которая, как это понятно из названия создает варварские города. Но у животных нет городов, во всяком случае если это не цель вашего мода, так что сразу переходим к следующим строкам, что в функции void CvGame::createBarbarianUnits().


if (isOption(GAMEOPTION_RAGING_BARBARIANS))
{
iDivisor = std::max(1, (iDivisor / 2));
}
Эта обычная проверка на “истинно”/”ложно”. Если опция включена, то некая переменная цифрового целочисленного типа iDivisor делится на 2, при этом std::max выбирает наибольшее между iDivisor и 1. Ведь iDivisor может оказаться равен 0, а на 0 как известно делить нельзя. Всегда помните об этом, и делайте проверки в будущем, что если планируете деление на переменную, смотрите, чтобы она не была равна нулю.
По умолчанию iDivisor равен значению тайлов, указанному в CIV4HandicapInfo.xml на этот уровень сложности. В случае же если RAGING_BARBARIANS активна, он сам делится на 2. И вместо 40 незанятых тайлов на варвара в расчет идут уже 20.
Нам нужно сделать тоже самое, но для животных. Поднимаемся вверх по тексту функции и находим там вызов другой функции, нужной нам, это createAnimals().
Находим через поиск её содержание void CvGame::createAnimals(), вот это она. В случае с VS достаточно навести на неё курсор, чтобы узнать полное название её определения и двух щелчков, чтобы перейти на неё.
Читаем код функции и узнаем, что iDivisor в ней не используется, и что нужно его сделать самим. Для этого в списке переменных, под названием функции объявляем int iDivisor;
После чего, сразу перед определением числа нужных животных.


iNeededAnimals = ((pLoopArea->getNumUnownedTiles() / GC.getHandicapInfo(getHandicapType()).getUnownedTilesPerGameAnimal()) - pLoopArea->getUnitsPerPlayer(BARBARIAN_PLAYER));

Переводим значение

GC.getHandicapInfo(getHandicapType()).getUnownedTilesPerGameAnimal() в iDivisor.

iDivisor = GC.getHandicapInfo(getHandicapType()).getUnownedTilesPerGameAnimal();

И заменяем выражение.

iNeededAnimals = ((pLoopArea->getNumUnownedTiles() /iDivisor) - pLoopArea->getUnitsPerPlayer(BARBARIAN_PLAYER));
Теперь количество нужных животных изменяется в зависимости от введенной нами переменной.
Осталось внедрить нашу опцию. Сразу между строками iDivisor = и iNeededAnimals = вставляем

if (isOption(GAMEOPTION_ANIMAL_WORLD))
{
iDivisor = std::max(1, (iDivisor / 2));
}
Главное не запутаться со скобками. Не забудьте об этом.
Должно получится примерно так.

iDivisor = GC.getHandicapInfo(getHandicapType()).getUnownedTilesPerGameAnimal();
if (isOption(GAMEOPTION_ANIMAL_WORLD))
{
iDivisor = std::max(1, (iDivisor / 2));
}
iNeededAnimals = ((pLoopArea->getNumUnownedTiles() /iDivisor) - pLoopArea->getUnitsPerPlayer(BARBARIAN_PLAYER));

С поправкой на отступы разумеется.

Шаг пятый: Компилируем, если выдает ошибки смотрим какие и проверяем свою внимательность. Как правило в таких случаях это банально опечатки, где {} не так поставили, где ; забыли, или наоборот.

Запускаем игру. Если все сделано правильно, то при запуске должна появиться новая опция с названием. TXT_KEY_GAME_OPTION_ANIMAL_WORLD
При её выборе количество животных будет увеличено вдвое. Но не более того, если хотите чего-то ещё, смотрите код функции и добавляете проверку опцию, где вам потребуется.
Ах да, чтобы вместо TXT_KEY_GAME_OPTION_ANIMAL_WORLD у вам отображались нормальные слова вам потребуется создать текстовые записи в одном из файлов XML\Text\ Но об этом можно узнать в темах про редактирование XML.

Cansei
19.08.2013, 02:08
Сегодня будет перевод-пересказ первой части замечательного руководства An Idiots Guide to Editing the DLL (http://forums.civfanatics.com/showthread.php?t=314201). Руководство действительно хорошее, так что пересказывать буду во первых чтобы в одном месте все было. Во вторых оно с двумя ошибками из-за которых пример с деревушками нерабочий. Ошибки будут исправлены в пересказе, автор оригинала пару мелочей пропустил.
На этом уроке мы скопируем уже булевский тег <bNoBadGoodies> из CIV4UnitInfos.xml в CIV4PromotionInfos.xml. Тег этот отвечает за результаты получаемые из деревушек. По умолчанию идет у исследователей и скаутов. Если этот параметр включен жители деревушки не нападут на вас, как варвары.


Schema & XML

Начинаем мы с XML уровня, а конкретно с файла CIV4PromotionInfos.xml. Открываем его и видим в самой первой строке.

<Civ4PromotionInfos xmlns="x-schema:CIV4UnitSchema.xml">
А значит это, что он он построен по схеме под названием CIV4UnitSchema.xml. Идем туда и видим множество записей наподобие.


<ElementType name="_________" content="textOnly" dt:type="int"/>


<ElementType name="_________" content="textOnly" dt:type="boolean"/>

Они определяют содержание тегов, которые имеются в XML-файлах. Дополнения вроде dt:type="int" например обозначает, что содержимое тега может быть только целочисленным(2 12 34). С другой стороны dt:type="boolean" означает, что содержимое может быть только булевским (ИСТИНА/ЛОЖЬ или как обычно пишется 1 или 0).

Если же строка имеет вид без дополнения, это значит, что содержимое может быть только текстовым. Например PROMOTION_COMBAT1.


<ElementType name="_________" content="textOnly"/>

В некоторых, достаточно редких случаях она может иметь вид

<ElementType name="_________" content="eltOnly">
Это означает, что она содержит внутри себя набор других тегов, вместо как например.

<ElementType name="FeaturePassableTechs" content="eltOnly">

Это важно. Все эти <ElementType name="_______" присутствует в единственном экземпляре, для каждого уникального названия тега. Поэтому если добавляете новую строку проверяйте нет ли у неё дубликатов.

В таких строках имеются вложенные указания для тегов. Вот такие вот.


<element type="_______" minOccurs="0"/>

minOccurs="0" отвечает за минимальное число его использований в xml-файле.
Если стоит "0", то тег становится опциональным и его можно не вписывать в каждый объект в XML файле. Для мелких введений это очень удобно, но для крупного мода будет лучше продублировать их в каждом объекте файла, где-нибудь в удобном месте вам месте. Например в конце, или после выбранного вам тега. И вводить свои теги уже выстраивая их в алфавитном порядке.
В наборах тегов встречается и maxOccurs="" это ограничение максимального числа использования тега. Но пока нам это не потребуется, да и вообще используется не так часто.

Утка в зайце, яйцо в утке.
Отправимся в этот самый файл Schema где указаны правила для прокачек. Из файла CIV4PromotionInfos можно понять что каждая прокачка представляет собой набор тегов под названием PromotionInfo. Все они в свою очередь находятся в наборе тегов PromotionInfos, который в свою очередь входит в файл Civ4PromotionInfos.
Как мы видим в схеме.


<ElementType name="Civ4PromotionInfos" content="eltOnly">
<element type="PromotionInfos" minOccurs="0" maxOccurs="*"/>
</ElementType>

Идем глубже и глубже.


<ElementType name="PromotionInfos" content="eltOnly">
<element type="PromotionInfo" maxOccurs="*"/>
</ElementType>

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


<element type="iOrderPriority" minOccurs="0"/>

Ставим свои комментарии после неё. Комментарии в XML делаются вот так.
<!-- Cansei new tags(Название мода) 25/08/2013(Дата) Cansei(Автор) -->
Все что в скобках писать не надо, если что. Все остальное, что внутри <!-- --> на ваш выбор, мой пример просто образец.

У нас получается


<element type="iOrderPriority" minOccurs="0"/>
<!-- Cansei new tags 25/08/2013 Cansei -->
<element type="bNoBadGoodies" minOccurs="0"/>
<!-- Cansei new tags 25/08/2013 Cansei -->
</ElementType>

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

<ElementType name="bNoBadGoodies" content="textOnly" dt:type="boolean"/> у нас уже есть.
Переходим в Civ4PromotionInfos, где добавляем в конце нужной прокачки наш новый тег.


<Button>,Art/Interface/Buttons/Promotions/Combat3.dds,Art/Interface/Buttons/Promotions_Atlas.dds,2,6</Button>
<!-- Cansei new tags 25/08/2013 Cansei -->
<bNoBadGoodies>1</bNoBadGoodies>
<!-- Cansei new tags 25/08/2013 Cansei -->
</PromotionInfo>

Вверху вместо iOrderPriority значится <Button>, это потому что iOrderPriority пропущен, minOccurs="0" позволяет это.

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


CvInfos

Переходим к работе с SDK, первым на очереди будут файлы CvInfos.cpp и CvInfos.h
В них мы добавим возможность игре прочитать наш новый тег, чтобы она смогла с ним что-то сделать. Открываем файл CvInfos.cpp и начинаем поиск bNoBadGoodies. Находим семь совпадений.
Первым из них идет переменная m_bNoBadGoodies(false), что в функции CvUnitInfo::CvUnitInfo() В этой функции указываются значения переменной по умолчанию. Поэтому там где в XML не указано свое значение, используется именно это. Название переменной отличается от той, что в XML. Это сделано для удобства. Префикс m_b означает, что это переменная классовая, а не локальная а стало быть доступна и вне класса, а b, потому что она булевская. Ничего сложного тут нет, но о префиксах я ещё сделаю заметку, как нибудь.

Следующий результат поиска, это функция bool CvUnitInfo::isNoBadGoodies() const. Единственное что она делает, это возвращает текущее значение переменной m_bNoBadGoodies. CvUnitInfo:: перед именем функции означают, что эта функция принадлежит классу CvUnitInfo. Но об этом я ещё расскажу подробнее.


bool CvUnitInfo::isNoBadGoodies() const
{
return m_bNoBadGoodies;
}

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

stream->Read(&m_bNoBadGoodies);

stream->Write(m_bNoBadGoodies);

Эти две строки отвечают за запись переменной в файл сохранения и загрузку из него. Они должны быть размещены В ОДНОМ И ТОМ ЖЕ ПОРЯДКЕ ПО ОТНОШЕНИЮ К СОСЕДНИМ. Потому что иначе сделанное сохранения будет испорчено и загрузить его будет невозможно. Поэтому будьте очень внимательны, делая изменения в этих функциях. Это нетрудно, просто будьте внимательнее.

Ах да, не каждое изменение в XML должно прописываться в stream->Read и stream->Write. Они нужны прежде всего для игры с опцией "Lock Modified Assets”. Эта игровая опция следит за тем, чтобы текущие значения XML-тегов совпадали с теми, чтобы были в момент сохранения игры. Но мы все равно добавим их сюда, для порядка.
И последние два результата, это

pXML->GetChildXmlValByName(&m_bNoBadGoodies, "bNoBadGoodies");
В этой строке считывается значение переменной из XML. И значение m_bNoBadGoodies становится равно bNoBadGoodies. Имя тега bNoBadGoodies должно быть в точности таким же, как оно прописано в XML.

На этом с просмотром CvInfos.cpp все и мы переходим к CvInfos.h
Там у нас два поиск выдал два результата.
Первый из них.

bool isNoBadGoodies() const; // Exposed to Python

Это прототип функции возвращающей значение переменной. Рассматриваю подробнее.

bool означает, что возвращаемый результат булевский (вкл/выкл)
isNoBadGoodies это имя нашей функции, обязательно должно совпадать с её же именем из CvInfos.cpp где она и находится по сути.
() потому что это функция, не буду вдаваться в подробности.
const потому что она не должна ничего изменять, ставится для верности.
; Потому что прототип.
// Exposed to Python Ничего не делает, просто комментарий для удобства, поясняющий, что значение этой переменной можно передать в Python.

Ну и второй результат, это bool m_bNoBadGoodies; Собственно тут и заявляется что будет использоваться такая-то переменная для класса.

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

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// class : CvUnitInfo
//
// DESC:
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

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

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// class : CvPromotionInfo
//
// DESC:
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Начнет с .h файла. В удобное вам место копируем прототип нужной нам функции. Например после строки
bool isImmuneToFirstStrikes() const; // Exposed to Python


bool isImmuneToFirstStrikes() const; // Exposed to Python
//Cansei New tags 25.08.2013 Start
bool isNoBadGoodies() const; // Exposed to Python
//Cansei New tags 25.08.2013 End

Только то, что между закомментированными строками требуется скопировать. Ну и сами комментарии. Это очень удобно, вернее без них ОЧЕНЬ неудобно. Ставите свой ник, дату и задачу которую должен решить ваш код.

Далее скопируем объявление нашей переменной в набор переменных класса.


bool m_bImmuneToFirstStrikes;
//Cansei New tags 25.08.2013 Start
bool m_bNoBadGoodies; // Exposed to Python
//Cansei New tags 25.08.2013 End

Возвращаемся в .cpp
Вписываем в конструктор значение переменной по умолчанию. Так что получится.


m_bImmuneToFirstStrikes(false),
//Cansei New tags 25.08.2013 Start
m_bNoBadGoodies(false),
//Cansei New tags 25.08.2013 End

Копируем нужную нам функцию.


bool CvPromotionInfo::isImmuneToFirstStrikes() const
{
return m_bImmuneToFirstStrikes;
}

//Cansei New tags 25.08.2013 Start
bool CvPromotionInfo::isNoBadGoodies() const
{
return m_bNoBadGoodies;
}
//Cansei New tags 25.08.2013 End

Прописываем переменную на запись и чтения в файл сохранений.

stream->Read(&m_bImmuneToFirstStrikes);
//Cansei New tags 25.08.2013 Start
stream->Read(&m_bNoBadGoodies);
//Cansei New tags 25.08.2013 End


stream->Write(m_bImmuneToFirstStrikes);
//Cansei New tags 25.08.2013 Start
stream->Write(m_bNoBadGoodies);
//Cansei New tags 25.08.2013 End

Ну и завершаем копированием строки ответственной за считывание из XML.


pXML->GetChildXmlValByName(&m_bImmuneToFirstStrikes, "bImmuneToFirstStrikes");
//Cansei New tags 25.08.2013 Start
pXML->GetChildXmlValByName(&m_bNoBadGoodies, "bNoBadGoodies");
//Cansei New tags 25.08.2013 End

Компилируем и запускаем игру. Если мы все сделали правильно, dll успешно скомпилируется, а игра будет работать как надо, но все ещё без нашей новой прокачки. Игра теперь знает, что есть такая прокачка, но что с ней делать, она ещё не в курсе.


CvGameTextMgr

Теперь пришла пора научить игру отображать новую прокачку в подсказках при наведении на юнит и в Цивилопедии. Поиск по файлу CvGameTextMgr.cpp выдает нам:


if (GC.getUnitInfo(eUnit).isNoBadGoodies())
{
szBuffer.append(NEWLINE); szBuffer.append(gDLL->getText("TXT_KEY_UNIT_NO_BAD_GOODIES"));
}

Этот код отвечает, за подсказку при строительстве юнита и при просмотре Скаутов в Цивилопедии. Но для нас важно прежде всего то, что текст подсказки уже имеется и его не надо будет вводить заново.
В CvGameTextMgr.cpp несложно запутаться, поэтому сразу укажу нужные функции. И хорошо ещё, что нам редко потребуется лазить в CvGameTextMgr.h.

CvGameTextMgr::parsePromotionHelp(CvWStringBuffer &szBuffer, PromotionTypes ePromotion, const wchar* pcNewline).

Она отвечает за подсказки к прокачкам, как очевидно. Запрашивает информацию о прокачках и выводит её. ePromotion означет, что информация о прокачке берется из основной базы. Префикс e вообще означает, что речь идет о пока ещё теоретическом объекте. Например он используется при строительство нового юнита, или при просмотре Цивилопедии.
Поэтому добавляем по аналогии. Выбираем удобное место например.


if (GC.getPromotionInfo(ePromotion).getKamikazePercent() != 0)
{
szBuffer.append(pcNewline);
szBuffer.append(gDLL->getText("TXT_KEY_PROMOTION_KAMIKAZE_TEXT", GC.getPromotionInfo(ePromotion).getKamikazePercent()));
}
//Cansei New tags 25.08.2013 Start
if (GC.getPromotionInfo(ePromotion).isNoBadGoodies())
{
szBuffer.append(pcNewline);
szBuffer.append(gDLL->getText("TXT_KEY_UNIT_NO_BAD_GOODIES"));
}
//Cansei New tags 25.08.2013 End

Компилируем. Если все сделали правильно, теперь в цивилопедии для подсказки будет высвечиваться нужная подсказка. Обратите внимание, что вместо NEWLINE идет pcNewline. Принципиальной разницы между ними нет, но не забывайте следить за такими мелочами ориентируясь на соседние записи, меньше проблем будет.

Но нам нужно не только это, а ещё и чтобы при наведении на юнит всплывала эта же подсказка, не лазить же в описание каждой прокачки.
Идем в функцию отвечающую за эту подсказку.
void CvGameTextMgr::setUnitHelp(CvWStringBuffer &szString, const CvUnit* pUnit, bool bOneLine, bool bShort)
CvWStringBuffer &szString означает, что функция работает со строками.
const CvUnit* pUnit это константный указатель на текущий юнит. Для нас это значит что функция ничего не может случайно изменить в этом юните, для этого и стоит const. Про указатели в C++ почитайте в интернете. Для нас это значит, что выводится информация для конкретного реального юнита.
bool bOneLine если правда, то игра пытается вывести информацию о юните одной строкой, например меню F5.
bool bShort отвечает за сокрытие лишней информации при взгляде на вражеский юнит. Сделано не для секретности, а просто чтобы окно с информацией не загораживало то что надо.
Вставляем в эту функцию код в подходящее место, например по соседству от nukeRange().
//Cansei New tags 25.08.2013 Start

if (pUnit->isNoBadGoodies())
{
szString.append(NEWLINE); szString.append(gDLL->getText("TXT_KEY_UNIT_NO_BAD_GOODIES"));
}
//Cansei New tags 25.08.2013 End

if (pUnit->nukeRange() >= 0)
{
szString.append(NEWLINE);
szString.append(gDLL->getText("TXT_KEY_UNIT_CAN_NUKE"));
}

Внимание, обычно на этом этапе компилироваться игра перестает, ведь мы запрашиваем ещё не созданную функцию в CvUnit.cpp
Но нам повезло, ведь у нас уже есть такая функция. Мы же копируем уже существующий тег.

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


Функционал.

Открываем CvUnit.cpp и ищем NoBadGoodies.


bool CvUnit::isNoBadGoodies() const
{
return (m_pUnitInfo->isNoBadGoodies()) ;
}

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

Начинаем. В CvUnit.h находим

bool isNoBadGoodies() const;
//Cansei New tags 25.08.2013 Start
void changeNoBadGoodiesCount (int iChange);
//Cansei New tags 25.08.2013 End

А следом объявляем переменную в переменных класса, взяв за основу
int m_iKamikazePercent;

Получится у нас.


int m_iKamikazePercent;
//Cansei New tags 25.08.2013 Start
int m_iNoBadGoodiesCount;
//Cansei New tags 25.08.2013 End

Сохраняем и возвращаемся в CvUnit.cpp. Идем по следам и аналогии с m_iKamikazePercent;

В void CvUnit::reset устанавливаем значение по умолчанию.

m_iKamikazePercent = 0;
//Cansei New tags 25.08.2013 Start
m_iNoBadGoodiesCount = 0;
//Cansei New tags 25.08.2013 End

Меняем изначальную функцию и добавляем свою.


bool CvUnit::isNoBadGoodies() const
{
return (m_pUnitInfo->isNoBadGoodies() || m_iNoBadGoodiesCount > 0) ;
}
//Cansei New tags 25.08.2013 Start

void CvUnit::changeNoBadGoodiesCount(int iChange)
{
m_iNoBadGoodiesCount += iChange;
FAssert(m_iNoBadGoodiesCount >= 0);
}
//Cansei New tags 25.08.2013 End

Здесь мы делаем по аналогии с переменной m_iBlitzCount, то есть добавили проверку на то, что счетчик выше нуля. Добавили функцию на изменение счетчика. В которую ещё вписали и проверку FAssert(m_iNoBadGoodiesCount >= 0);

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

Далее в функции void CvUnit::setHasPromotion(PromotionTypes eIndex, bool bNewValue) мы добавляем.


changeKamikazePercent((GC.getPromotionInfo(eIndex).getKamikazePercent()) * iChange);
//Cansei New tags 25.08.2013 Start
changeNoBadGoodiesCount((GC.getPromotionInfo(eIndex).isNoBadGoodies()) ? iChange : 0);
//Cansei New tags 25.08.2013 End

Сиречь, если isNoBadGoodies() возвращает true и юнит и так не получает плохих результатов из деревень, то лишняя прокачка ничего не изменит. А если false, то она добавит ему это умение.

Далее прописываем переменную на сохранение и загрузку.


pStream->Read(&m_iKamikazePercent);
//Cansei New tags 25.08.2013 Start
pStream->Read(&m_iNoBadGoodiesCount);
//Cansei New tags 25.08.2013 End



pStream->Write(m_iKamikazePercent);
//Cansei New tags 25.08.2013 Start
pStream->Write(m_iNoBadGoodiesCount);
//Cansei New tags 25.08.2013 End

На этом с функционалом почти все.
Открываем CvUnitAI.cpp и находим функцию int CvUnitAI::AI_promotionValue(PromotionTypes ePromotion)
Там вписываем советы по текущей прокачке юнитам, которыми управляет AI.
Примерно вот так.

if (GC.getPromotionInfo(ePromotion).isLeader())
{
// Don't consume the leader as a regular promotion
return 0;
}
//Cansei New tags 25.08.2013 Start
if (GC.getPromotionInfo(ePromotion).isNoBadGoodies() && !isNoBadGoodies())
{
if (AI_getUnitAIType() == UNITAI_EXPLORE)
{
iValue += 50;
}
else
{
iValue += 5;
}


}
//Cansei New tags 25.08.2013 End

if (GC.getPromotionInfo(ePromotion).isBlitz())
{
if ((AI_getUnitAIType() == UNITAI_RESERVE && baseMoves() > 1) ||
AI_getUnitAIType() == UNITAI_PARADROP)
{


Теперь если перед юнитом встает вопрос какую прокачку выбрать и у него тем умения NoBadGoodies, то в случае если у юнита действует модель поведения исследователя - AI_getUnitAIType() == UNITAI_EXPLORE он получит +50 к значению этой прокачки для выбора. Иными словами почти гарантированно выберет именно её. Если же этот юнит следует иной модели поведения, он вряд ли станет брать эту прокачку, если он не дает дополнительных бонусов.
Примечание. Выбирая следующую прокачку AI оценивает каждую из них, после чего добавляет к каждой из них случайное число от 0 до 15. Таким образом он может выбрать прокачку с более низким уровнем ценности, если разница между ней и более высокой менее 15.
И напоследок, добавление в python.
Открываем CyInfoInterface1.cpp
Находим там

python::class_<CvPromotionInfo, python::bases<CvInfoBase> >("CvPromotionInfo")

Вписываем нашу прокачку между двумя уже существующими. Название в кавычках, показывает, как она будет известно в python.


.def("isLeader", &CvPromotionInfo::isLeader, "bool ()")
.def("isNoBadGoodies", &CvPromotionInfo::isNoBadGoodies, "bool ()")
.def("isBlitz", &CvPromotionInfo::isBlitz, "bool ()")

Ну и напоследок, как же работает прокачка.

В CvGameInterface имеется проверка, если она успешна при выборе скаута появляются синии круги на деревнях, ничего важного, но приятно.


if (pHeadSelectedUnit->isNoBadGoodies())
{
if (pLoopPlot->isRevealedGoody(pHeadSelectedUnit->getTeam()))
{
gDLL->getEngineIFace()->addColoredPlot(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), GC.getColorInfo((ColorTypes)GC.getInfoTypeForString("COLOR_HIGHLIGHT_TEXT")).getColor(), PLOT_STYLE_CIRCLE, PLOT_LANDSCAPE_LAYER_RECOMMENDED_PLOTS);
}
}

В CvPlayer же и происходит самое главное. Если у юнита есть наше умение, он не получит плохого результата от деревни.


if (GC.getGoodyInfo(eGoody).isBad())
{
if ((pUnit == NULL) || pUnit->isNoBadGoodies())
{
return false;
}
}

В CvPlayerAI же есть такая проверка.


case UNITAI_EXPLORE:
iValue += (iCombatValue / 2);
iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
if (GC.getUnitInfo(eUnit).isNoBadGoodies())
{
iValue += 100;
}
break;

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

Фух. На этом кажется все. Компилируем и смотрим результат.

P.S. На самом деле, как можно заметить если прочитать инструкцию, почти 90% описанной работы обычная рутина, которая почти для всех аналогичных случаев одинакова. И при наличие опыта и возможности перемещаться по собственным комментариям в моем случае это //Cansei New tags она одолевается минут за десять.
Можно ещё поискать варианты с автоматической вставкой нужного текста. Но это уже вопрос про текстовые редакторы.

Если что не так пишите. Изменю урок там где ошибся.

Cansei
19.08.2013, 02:09
reserved Урок 3

Cansei
19.08.2013, 02:13
reserved Урок 4

Cansei
19.08.2013, 02:15
reserved Урок 5

www555
19.08.2013, 12:00
:applau2: Вот этого я и ждал. Спасибо.

www555
19.08.2013, 12:02
Примечание:[/I] Для работы с xml можно использовать XML Marker, но я все равно настоятельно порекомендую Notepad++, он куда удобнее.
А для меня удобнее xml marker. А чем лучше Notepad++?

Cansei
19.08.2013, 12:36
:applau2: Вот этого я и ждал. Спасибо.

Да пожалуйста;)


А для меня удобнее xml marker. А чем лучше Notepad++?

Он хоть и не отображает теги потаблично, но сам по себе удобнее в обращении за счет огромного количества возможностей. Например в нём можно спрятать все строки и открывать их потом по блоку, или только нужный уровень кода. Или за счет поиска можно ввести <Class> и нажать "Найти все в текущем документе". После чего внизу в окне поиска можно будет уже ориентироваться по названиям юнитов, свободно прокручивая их и переходя на нужные блоки тегов.
И это лишь как пример. На деле можно куда большего добиться. А XML Marker, я им когда-то пользовался, как был корявый какой-то, так и остался, так ещё стал требовать заплатить 150 долларов. 150! Сто-пятьдесят долларов за то что и так не изменилось! Да они вконец обнаглели. А Notepad++ он бесплатный.
Ах да, забыл ссылку на скачивание в первый пост вставить, сейчас вставлю для порядка.

www555
19.08.2013, 21:13
Ссылка на скачивание xml marker (http://symbolclick.com/):king:

NeseryozniyVET
20.08.2013, 07:43
Маленькое дополнение к первому уроку.
Файлы SDK можно разделить на две группы: те, названия которых начинаются на Cv (Например CvUnit.cpp) и те, чьи названия начинаются на Cy (Например CyUnit.cpp).
Cv-файлы - это алгоритмы или перечисления (enum) самой игры. Cy-файлы - это функции или перечисления (enum) которые выводятся в питон. То есть, если новые функции не будут использоватся в питоне то и Cy-файлы трогать не надо.

Добавляемые параметры могут быть: булевые (начинаются на b (значения 0 или 1)), целочисленые (начинаются на i (значения от -2 миллиарда до 2 миллиарда)), дробные (начинаются на f (в модинге не применяются)), буквенные (названия представителей инфо-классов (папример UNIT_WARRIOR)) и различные масивы (список общих параметров (с ними гораздо больше работы))


Добавление нового параметра (делается по анологии с уже существующими параметрами) в игру через XML делается так.

Вариант 1: добавление параметра существующего инфо-класса (класс описание которого есть в файлах CvInfos.cpp и CvInfos.h. Характеристики этих класов какраз и меняются в XML фалах)
1) Открываем нужный XML файл (например CIV4BuildInfos.xml - строительные возможности рабочих) и добавляем новый параметр.
2) Открываем нужную XML схему (в нашем случае это CIV4UnitSchema.xml) и добавляем туда этот же параметр, если его еще там нету.
3) Открываем CvInfos.cpp и CvInfos.h ищем нужный инфо-класс (в нашем случае это CvBuildInfo) и добавляем в него наш параметр, функции работающие с эти параметром и новый алгоритм в функцию считывания из XML (в нашем случае это функция CvBuildInfo::read(CvXMLLoadUtility* pXML)).
4) Если собираемся пользоватся нашим параметром в питоне, то редактируем CyInfoInterface1.cpp, или CyInfoInterface2.cpp, или CyInfoInterface3.cpp (нужный фал ищем по названию инфо-класса (в нашем случае это CvBuildInfo)).
5) Все, новый параметр добавлен, осталось только сделать алгоритмы или функции работающие с новым параметром. Так как в нашем случае мы меняли строительные возможности рабочих, то, скорей всего, редактировать будем (CvUnit.cpp, CvUnit.h и, если будем использовать новые функции в питоне CyUnit.cpp, CyUnit.h, CyUnitInterface1.cpp.). Ну и, желательно, надо будет научить пользоваться новой возможностью ИИ - файл CvUnitAI.cpp.

Вариант 2: добавление параметра, который не относится к существующему инфо-классу.
1) Открываем GlobalDefines.xml или GlobalDefinesAlt.xml и добавляем новый параметр (например NEW_PARAMETR).
2) В SDK новый параметр будет вызыватся функцией GC.getDefineINT("NEW_PARAMETR"). Это очень медленная функция, поэтому если хотите чтоб с новым параметром расчеты происходили быстро, то надо сделать соответствующие изменения в файлах CvGlobals.cpp и CvGlobals.h на примере существующего параметра MOVE_DENOMINATOR. И уже заместь GC.getDefineINT("NEW_PARAMETR") использовать GC.getNEW_PARAMETR().

Наиболее часто используемые файлы:
CvCity - работа с городом
CvUnit - работа с юнитом
CvPlot - работа с тайлом (отдельная клетка на карте)
CvMap - работа с картой
CvGame - работа с игрой (например появление варваров и животных описывается в этом файле)
CvPlayer - работа с игроком
CvTeam - работа с командой (война объявляется не игроку, а команде. работа с технологиями в большенстве случаем происходит на уровне команды, а не игрока)

cfc-cfr
23.08.2013, 19:43
Ещё одна отличная инструкция!

NeseryozniyVET
24.08.2013, 11:28
Cansei, когда будет второй урок?

www555
24.08.2013, 12:17
Cansei, когда будет второй урок?
Да, Cansei, ждём второго урока:)

Cansei
24.08.2013, 19:47
NeseryozniyVET Спасибо за урок. Сейчас добавлю ссылку на него в первый пост.

NeseryozniyVET,www555 Вот только что добрался до дома, был в тех местах где у меня нет интернета, но много дел по хозяйству. Впрочем там мне удалось реализовать такую штуку, как забывание тайлов карты, не виденных долгое время. Правда есть очень серьезные опасения за её скорость. Теперь осталось сделать лишь, чтобы после открытия нужной технологии, например "Бумаги", они не забывались.

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

cfc-cfr Спасибо. :)

cfc-cfr
24.08.2013, 22:01
cfc-cfr Спасибо. :)
Нет, не мне спасибо, спасибо всем, кто пишет что-то интересное.:)

Cansei
25.08.2013, 23:36
Добавил второй урок, намаялся с ним изрядно. Пишите работает или нет, нормально ли читается, не слишком ли расплывчато описано?

NeseryozniyVET
28.08.2013, 13:59
Вот нашел еще один хороший урок. Я его, кстате, в своем моде применил.
http://www.civfanatics.ru/threads/7826-SDK-технологии

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

Cansei
28.08.2013, 15:25
Вот нашел еще один хороший урок. Я его, кстате, в своем моде применил.
http://www.civfanatics.ru/threads/7826-SDK-технологии

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

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

NeseryozniyVET
31.08.2013, 18:49
Как добавить активный элемент на экран.
Для начала надо открыть файл нужного нам экрана (он находятся в папке Assets\Python\Screens). Затем добавить объект с которым хотим взаимодействовать (addSlider - слайдер, setText - текст, setButtonGFC - кнопка плюс или минус, и т.д.).
Нужно иметь ввиду что для взаимодействия с объектом у этого объекта есть три параметра идущие друг за другом в функции создания оъекта: WidgetTypes (тип реакции объекта при взаимодействии), iData1 (число), iData2 (число) (например setButtonGFC("имя объекта", u"", "", X координата, Y координата, длинна, высота, WidgetTypes.WIDGET_GENERAL, -1, -1, ButtonStyles.BUTTON_STYLE_CITY_MINUS)).
Если хотим отделатся только питоном то достаточно указать WidgetTypes.WIDGET_GENERAL и написать алгоритм в функции def handleInput (self, inputClass) , которая находится в том же файле.
Но если питона не достаточно (напрмер сделать всплывающую подсказку в питоне у меня не получилось), то прийдется использовать SDK.
1) Открываем файлы CvEnums.h и CyEnumsInterface.cpp, ищем WidgetTypes и добавляем туда новый. Добавлять надо в конец списка перед , NUM_WIDGET_TYPES, .
2) Открываем файл CvDLLWidgetData.cpp
3) Ищем функцию void CvDLLWidgetData:: parseHelp(CvWStringBuffer &szBuffer, CvWidgetDataStruct &widgetDataStruct) - алгоритм подсказки при наведении курсора на объект. Добавляем туда наш widget и алгоритм подсказки (алгоритмы подсказок есть еще в файле CvGameTextMgr.cpp).
4) Ищем функцию bool CvDLLWidgetData:: executeAction( CvWidgetDataStruct &widgetDataStruct ) - клик по объекту. Добавляем туда наш widget и алгоритм действий.

Значения переменных widgetDataStruct.m_iData1 и widgetDataStruct.m_iData2 в функциях parseHelp и executeAction это наши iData1 и iData2 которые мы указывали при создании объектов.

Cansei
31.08.2013, 19:18
Спасибо, добавил в первый пост. Питоном займусь в ближайшее, относительно время. Так что пригодится весьма и весьма. Заодно хотел спросить.


В SDK новый параметр будет вызыватся функцией GC.getDefineINT("NEW_PARAMETR"). Это очень медленная функция, поэтому если хотите чтоб с новым параметром расчеты происходили быстро, то надо сделать соответствующие изменения в файлах CvGlobals.cpp и CvGlobals.h на примере существующего параметра MOVE_DENOMINATOR. И уже заместь GC.getDefineINT("NEW_PARAMETR") использовать GC.getNEW_PARAMETR().

А как удалось выяснить, что она очень медленная? Через установку таймера на выполнение функции? Я вчера CvPlot просматривал, так она там много где используется, например в расчете видимости тайлов. Если заменить её на GC.getNEW_PARAMETR() то это должно по идее ускорить игру, хоть и не намного.

Вообще, какие основные тормоза в SDK? Я знаю некоторые.
Callback'и в Питон, их и так большей частью ещё в RevDCM заблокировали, а я оставшиеся заблокировал, теперь даже расчет опыта юнита до следующего уровня у меня идет через SDK.
Pathfinding - в Caveman2Cosmos они свой сделали, с поддержкой многопоточности и без использования exe-файла, как я понял. Надо будет сделать по аналогии, но мне до это уровня ещё расти и учить.
Циклы через весь массив, для обнаружения нужной связи. Например через весь набор прокачек, имеется ли такая у юнита. Как бы ещё научить сохранять связи между ними, и автоматически пересчитывать при изменении XML. Эту проблему устранили в Realism Invictus.

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

NeseryozniyVET
31.08.2013, 20:03
1) Потому что GC.getNEW_PARAMETR() возвращает число, хранящееся в переменной. А GC.getDefineINT("NEW_PARAMETR") сначала ищет по названию нужную переменную, а только потом возвращает число.
Когда я начинал осваивать SDK, то сделал чтоб юниты по диагоналям передвигались не так далеко как по вертикалям и горизонталям, и сделал я это через GC.getDefineINT("NEW_PARAMETR"). Игроки, которые играли в мой мод, сразу стали жаловатся что ИИ стал дольше думать. Когда я сделал все через GC.getNEW_PARAMETR(), жалобы исчезли.

2) Расчет лучшего тайла для города делается в конце каждого хода игроков. Я сделал так чтоб этот алгоритм выполнялся перед ходом поселенца. ИИ запоминает что выбрал лучший тайл, и если не произошли события, которые могут повлиять на выбор, то алгоритм не выполняется.
Возмжность апгредить юнита в другого проверяется через рекурсивную функцию для каждого юнита каждый ход. Я этот алгоритм вызываю при запуске мода для каждого юнита и сохраняю результат в единственный битовый масив. Этат алгоритм настолько медленный, что мой мод стал долго запускаться. Поэтому я этот алгоритм сделал многопоточным. При запуске моего мода можно посмотреть загрузку процессора, если загрузились все ядра - значит выполняется этот алгоритм.
Многие расчеты я тоже делаю при запуске мода или игры, и уже в самой игре использую заместь алгоритмов заранее вычесленый результат.
Есть еще метод замены ветвлений (if else) на математические выражения (современные процессоры не любят ветвления).
Например
if (x > 0) {y += x;}
заменяется на
y += x * (x > 0);.
Правда, код после этого делается плохо читаемым, поэтому оптимизацию ветвлений я всегда сопровождаю коментариями оригинального кода
y += x * (x > 0);//if (x > 0) {y += x;}.

А в каком файле (и название функции) находится альтернатива Pathfinding в моде Caveman2Cosmos? Хочу себе такое зделать.

Cansei
31.08.2013, 21:05
Спасибо, заменю ради интереса GC.getDefineINT("PLOT_VISIBILITY_RANGE") на GC.getPLOT_VISIBILITY_RANGE().



Расчет лучшего тайла для города делается в конце каждого хода игроков.

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


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

А это кажется в RevDCM побеждено, туда ведь был включен Sanguo Mod Performance в котором с этой проблемой боролись.


Есть еще метод замены ветвлений (if else) на математические выражения (современные процессоры не любят ветвления).

Про такое даже не слышал, запомню на будущее, ещё пригодится.


А в каком файле (и название функции) находится альтернатива Pathfinding в моде Caveman2Cosmos? Хочу себе такое зделать.
CvPathGenerator.cpp и CvPathGenerator.h если правильно помню.

Cansei
31.08.2013, 22:20
Ах, да, оставлю ссылку здесь, чтобы не забылась. Как раз в тему. А пока попробую разобраться, что такое профайлер и как им пользоваться на примере Civ4.

http://forums.civfanatics.com/showthread.php?t=487573

Cansei
01.09.2013, 00:55
Буду писать новыми постами, все равно уроки по ссылкам в первом посте рассредоточены, а под новые есть зарезервированное место.

Побаловался с этими профайлерами, с Very Sleepy, LTProf и вот сейчас CodeAnalysist.

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


http://i021.radikal.ru/1308/b6/3b519f314ea6.jpg (http://radikal.ru/fp/0f6dee1366194ee6971e9570be2d6d97)

http://s003.radikal.ru/i203/1308/4d/f80fc570bcfd.jpg (http://radikal.ru/fp/0b9ca5d56a4d4fe9a9b0111f4098f12a)

Вот скрин из http://s017.radikal.ru/i435/1308/4c/3d8bf61e9f72t.jpg (http://radikal.ru/fp/7b2350227d8f44f4b49fe4a4df7f2f35)

Very Sleepy

Что смущает, из того что мне понятно. Функция initCvPythonExtensions она жрет просто какое-то дико неприличное количество ресурсов, это как я понимаю использование игрой питона?

Далее уже по доступному CvGameCoreDLL.dll. Неожиданный результат по функциям.
shuffle - ещё понятно, хотя надо посмотреть, что она именно делает.
CvCity::getTeam - уже странно, но все таки.
getUnit - ладно, это самое понятное.
CvPlayerAI::AI_maxGoldPerTurnTrade - а вот это как? Как он умудряется так золото свое считать.
CvPlayerRecord::getMinutesPlayed - понятно, но что-то больно много этот подсчет минут себе требует.
CvPlayer::getAdvancedStartVisibilityCost - а это-то ему зачем. Игру начинал нормальную, безо всяких там AdvancedStart.

В общем смотреть и смотреть, учиться да учиться.

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

Немного изменил её, дабы calculateGoldRate() высчитывался лишь один раз.


int CvPlayerAI::AI_maxGoldPerTurnTrade(PlayerTypes ePlayer) const
{
int iMaxGoldPerTurn;
int GoldRate = calculateGoldRate();//Cansei 1.09.2013

FAssert(ePlayer != getID());

if (isHuman() || (GET_PLAYER(ePlayer).getTeam() == getTeam()))
{
// iMaxGoldPerTurn = (calculateGoldRate() + (getGold() / GC.getDefineINT("PEACE_TREATY_LENGTH")));//Cansei 1.09.2013
iMaxGoldPerTurn = (GoldRate + (getGold() / 10));
}
else
{
iMaxGoldPerTurn = getTotalPopulation();

iMaxGoldPerTurn *= GC.getLeaderHeadInfo(getPersonalityType()).getMaxGoldPerTurnTradePercent();
iMaxGoldPerTurn /= 100;

iMaxGoldPerTurn += std::min(0, getGoldPerTurnByPlayer(ePlayer));
}

// return std::max(0, std::min(iMaxGoldPerTurn, calculateGoldRate()));//Cansei 1.09.2013
return std::max(0, std::min(iMaxGoldPerTurn, GoldRate));
}

Функция немного присмирела, но все равно остается неприлично прожорливой. Буду смотреть дальше, вглубь calculateGoldRate().

0x2eb90a0 CvPlayerAI::AI_maxGoldPerTurnTrade 2.25

0x2eb90a0 CvPlayerAI::AI_maxGoldPerTurnTrade 2.80

0x2eb90a0 CvPlayerAI::AI_maxGoldPerTurnTrade 1.96

NeseryozniyVET
01.09.2013, 16:54
Попробуй заменить условные переходы на математические выражения вот так.

замени
iMaxGoldPerTurn += std::min(0, getGoldPerTurnByPlayer(ePlayer));
на
int iGoldPerTurnByPlayer = getGoldPerTurnByPlayer(ePlayer);
iMaxGoldPerTurn += iGoldPerTurnByPlayer * (iGoldPerTurnByPlayer < 0);//iMaxGoldPerTurn += std::min(0, getGoldPerTurnByPlayer(ePlayer));

замени
return std::max(0, std::min(iMaxGoldPerTurn, GoldRate));
на
int iDelta = iMaxGoldPerTurn - GoldRate;
int iReturn = iMaxGoldPerTurn - iDelta * (iDelta > 0);//iReturn = std::min(iMaxGoldPerTurn, GoldRate)
iReturn *= (iReturn > 0);//iReturn = std::max(0, iReturn);
return iReturn;

Мне самому интересно насколько такая замена эффективней.


А эти програмки анализируют время затраченое на каждую функцию ?


У себя в моде я оптимизировал CvCity::getTeam(). Она по твоим скриншотам 4,73% жрет


TeamTypes CvCity::getTeam() const
{
//VET Optimization - begin /
//return GET_PLAYER(getOwnerINLINE()).getTeam();
return m_eTeam;
//VET Optimization - end /

}
Может ее еще и инлайновой сделать ?

Cansei
01.09.2013, 18:15
Попробуй заменить условные переходы на математические выражения вот так.
Мне самому интересно насколько такая замена эффективней.


Заменю на будущее, но пока эффекта от такой замены не ожидается, потому что все куда хитрее.

По направлению calculateGoldRate() нашел мало чего интересного, разве что громоздкую формулу расчета инфляции, да callback в питон на запрос ExtraUnitCosts(или как их там), функцию оставил, а коллбек прибил, ибо нефиг. Заменил его на считывание переменной напрямую из SDK. Потом я вообще заменил calculateGoldRate() на getGold()/10/ Ну чтобы выдавал количество золота в казне/10 ходов. Да вот только пользы мало было.

Начал смотреть, обнаружил что вызывается AI_maxGoldPerTurnTrade преимущественно из питона. И стоит там привидение из класса CvPlayer к CvPlayerAI через dynamic_cast. А вот это походу жрет куда больше, чем вычисления расходов. Заодно как один из источников вызовов обнаружил Civ4Alert это из RevDCM, когда включен, каждый ход сообщает об изменения в торговле AI. Вырубил, но толку все равно мало.



int CyPlayer::AI_maxGoldPerTurnTrade(int iPlayer)
{
CvPlayerAI* pPlayer = dynamic_cast<CvPlayerAI*>(m_pPlayer);
if (pPlayer)
{
return (pPlayer->AI_maxGoldPerTurnTrade((PlayerTypes)iPlayer));
}
return 0;
}

Думаю проще будет перенести эту функцию в CvPlayer. Ну это потом. Программки эти как раз и анализируют использование процессора. Причем на саму CvGameCoreDLL приходится в лучшем случае 16-17% загрузки двухядерника. Обычно меньше. Проверяю на автоплее, ставлю его на 50-100 ходов, на поздних стадиях на 20. И сворачиваю игру, как правило. Ресурсов больше идет на exe-файл, причем в том около 70% из него идет на питон. Если игру держать свернутой подольше, то тут уже CvGameCoreDLL вырывается вперед.

Пытался понять в чем дело, думал, :wacko: Потом обнаружил, что Very Sleepy ещё может и анализировать стеки вызовов. Причем как вызывающие выбранную функцию, так и вызываемые ею.
http://i017.radikal.ru/1309/a6/4b34f40123ee.jpg (http://radikal.ru/fp/89030ebb56e64b7f942eeb1e0f2d1a6e)

Похоже что большая часть вызовов из AI_get_Greeting, немного поменьше из shuffle. Основная проблема стало быть в AI_get_Greeting.

С вызовами из AI_maxGoldPerTurnTrade ещё веселее. Как она умудряется так их вызывать, понятия не имею.

http://s018.radikal.ru/i514/1309/3d/5df7287aa63a.jpg (http://radikal.ru/fp/c1e9e34c2f674d57b00c97c3f6e83861)

Буду думать, скорее всего перекомпилирую в debug и буду смотреть по точкам останова. Заодно ещё обнаружил, что CvTeam::getCompletedSpaceshipProjects жрет от 1% до 1.5%. И это притом, что игра у меня только один раз до Ренессанса добиралась.

Cansei
01.09.2013, 18:24
Попробуй заменить условные переходы на математические выражения вот так.

У себя в моде я оптимизировал CvCity::getTeam(). Она по твоим скриншотам 4,73% жрет

Может ее еще и инлайновой сделать ?

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

Cansei
01.09.2013, 19:57
Отошел покопаться в другом направлении, а именно CvRandom::get Внешне непримечательная, но как показывает Very Sleepy, на её внутренние вызовы приходится 25% всей работы. Причем она обращается к .exe файлу, а тот в свою очередь вызывает кучу внешних в том числе и те, что наиболее ресурсоемкие. Вызывается она чаще всего через питон.

http://s017.radikal.ru/i432/1309/cb/847db44606d9t.jpg (http://radikal.ru/fp/5ac2aa5c7ed842ca942413607aadb601)

http://s020.radikal.ru/i718/1309/01/4290ed3e62cet.jpg (http://radikal.ru/fp/22e56a71f5374268b9132c5ff68c12aa)

Отвечает она, судя по названию за рандом. Выглядит вот так.


unsigned short CvRandom::get(unsigned short usNum, const TCHAR* pszLog)
{
if (pszLog != NULL)
{
if (GC.getLogging() && GC.getRandLogging())
{
if (GC.getGameINLINE().getTurnSlice() > 0)
{
TCHAR szOut[1024];
sprintf(szOut, "Rand = %d on %d (%s)\n", getSeed(), GC.getGameINLINE().getTurnSlice(), pszLog);
gDLL->messageControlLog(szOut);
}
}
}

m_ulRandomSeed = ((RANDOM_A * m_ulRandomSeed) + RANDOM_C);

unsigned short us = ((unsigned short)((((m_ulRandomSeed >> RANDOM_SHIFT) & MAX_UNSIGNED_SHORT) * ((unsigned long)usNum)) / (MAX_UNSIGNED_SHORT + 1)));

return us;
}


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


#include "CvGameCoreDLL.h"
#include "CvRandom.h"
# include <boost/python/overloads.hpp>
using namespace boost::python;

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(CvRandom_get_overloads, CvRandom::get, 2, 2)

//
// published python interface for CvRandom
//
void CyRandomPythonInterface()
{
OutputDebugString("Python Extension Module - CyRandomPythonInterface\n");

python::class_<CvRandom>("CyRandom")
.def("get", &CvRandom::get, CvRandom_get_overloads( args("usNum", "pszLog"), "returns a random number"))
.def("init", &CvRandom::init, "void (unsigned long int ulSeed)")
;
}


P.S.

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

; Enable rand event logging
RandLog = 1

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

; Enable rand event logging
RandLog = 0

Cansei
02.09.2013, 14:38
У себя в моде я оптимизировал CvCity::getTeam(). Она по твоим скриншотам 4,73% жрет


TeamTypes CvCity::getTeam() const
{
//VET Optimization - begin /
//return GET_PLAYER(getOwnerINLINE()).getTeam();
return m_eTeam;
//VET Optimization - end /

}
Может ее еще и инлайновой сделать ?

Сделал по аналогии, добавил функции


TeamTypes CvCity::getTeam() const
{
return m_eCityTeam;
}

void CvCity::updateTeamType()
{
m_eCityTeam = GET_PLAYER(getOwnerINLINE()).getTeam();
}

updateTeamType() прописал в init, liberate и read. Так чтобы не сохранялась, а вычислялась при загрузке сейва. Вроде все идет, но не оставляет ощущение, что что-то забыл. Может ещё updateTeamType() нужен, какие вообще способы существуют для смены владельца города? Захват, дипломатия и освобождение?
При захвате, как понимаю старый полностью уничтожается и создаётся новый, как его копия?


После изменения, функция CvCity::getTeam() пропала из топа, но на верхние места вылезла другая CvCity::getOwner.
CvTeam::getCompletedSpaceshipProjects вконец обнаглела, причем в половине случаев вызывает сама себя.

Cansei
02.09.2013, 14:59
Похоже найден виновник многих несуразностей. Это функция CvUnit::getLayerAnimationPaths()

Выглядит она вот так.


void CvUnit::getLayerAnimationPaths(std::vector<AnimationPathTypes>& aAnimationPaths) const
{
for (int i=0; i < GC.getNumPromotionInfos(); ++i)
{
PromotionTypes ePromotion = (PromotionTypes) i;
if (isHasPromotion(ePromotion))
{
AnimationPathTypes eAnimationPath = (AnimationPathTypes) GC.getPromotionInfo(ePromotion).getLayerAnimationPath();
if(eAnimationPath != ANIMATIONPATH_NONE)
{
aAnimationPaths.push_back(eAnimationPath);
}
}
}
}

Вызывает уйму всякой муры, включая как саму себя, так и самые неожиданные вещи. Например CvCity::getY, причем обходится даже без CvCity::getX

NeseryozniyVET
02.09.2013, 16:23
Похоже найден виновник многих несуразностей. Это функция CvUnit::getLayerAnimationPaths(Ну во-первых GC.getNumPromotionInfos() нужно вывести за пределы цикла
int iNumProm = GC.getNumPromotionInfos();
for (int i=0; i < iNumProm; ++i)

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


int iNumProm = m_iNumPromotions;//m_iNumPromotions - новая переменная у каждого юнита.
PromotionTypes* peLoopProms = m_paePromotions;//m_paePromotions - новый масив взамен старого
for (int i=iNumProm; i != 0; --i, ++peLoopProms)
{
AnimationPathTypes eAnimationPath = (AnimationPathTypes) GC.getPromotionInfo(peLoopProms[0]).getLayerAnimationPath();
if(eAnimationPath != ANIMATIONPATH_NONE)
{
aAnimationPaths.push_back(eAnimationPath);
}
}Эффект будет пропорционален количеству прокачек в моде.

Cansei
02.09.2013, 16:31
А во-вторых можно заменить булевый масив наличия прокачек у юнита, на масив существующих прокачек у юнита (и переделывать его при получении или потери прокачки) и переменную количества этих прокачек. Тогда код будет выглядеть так.

Спасибо, как-то так я и думал. Но при таких условиях по идее резко возрастет количество потребляемой памяти и время на запись/сохранение. Я вот думаю, как бы ввести int массив прокачек типа вектор, содержащие индекс прокачки в GC.getNumPromotionInfos() Но вот как конкретно это реализовать пока не знаю. Я C++ начал изучать только в июне, и то по книжкам, так что реального опыта в этом маловато.


Похоже найден виновник многих несуразностей. Это функция CvUnit::getLayerAnimationPaths

Там проблема в другом, там функция возвращает ссылку на пустой вектор, потому как LayerAnimationPaths отвечает за изменение внешнего вида юнита при получении прокачки. А таких прокачек не только в игре, но и в большинстве модов нет. Я специально смотрел в Master of Mana и Caveman2Space. Таких прокачек там не нашел. Я пробовал изменить функцию, убрав из неё ссылку на вектор. Но игра теперь не идет дальше главного меню. Буду думать дальше.

NeseryozniyVET
02.09.2013, 17:23
Ну так предложеный мною метод и есть что-то типа вектора только быстрее и без лишних функций и переменных. Размер масива равен количеству прокачек у юнита, если прокачек нет - то просто нулевой указатель. В моем примере я предлагал масив PromotionTypes (32 бита), но если в моде количество прокачек не превышает число 255, то можно и unsigned char (8 бит) использовать или unsigned short (16 бит), если прокачек больше 255 и меньше 64 тисяч.

Cansei
02.09.2013, 17:59
Ну так предложеный мною метод и есть что-то типа вектора только быстрее и без лишних функций и переменных. Размер масива равен количеству прокачек у юнита, если прокачек нет - то просто нулевой указатель. В моем примере я предлагал масив PromotionTypes (32 бита), но если в моде количество прокачек не превышает число 255, то можно и unsigned char (8 бит) использовать или unsigned short (16 бит), если прокачек больше 255 и меньше 64 тисяч.

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

NeseryozniyVET
02.09.2013, 20:52
updateTeamType() прописал в init, liberate и read.В liberate прописывать не надо. Там идет вызов acquireCity, та вызывает initCity, а та init.

Cansei
02.09.2013, 21:25
В liberate прописывать не надо. Там идет вызов acquireCity, та вызывает initCity, а та init.

Спасибо. Уберу. То есть достаточно только в init прописать? Ну и в read, если не сохранять. А этот TeamTypes для города, оно вообще много памяти требует? Как полноценный объект класса CvTeam?

С LayerAnimationPaths долго маялся, разбирался с AnimationPaths. В итоге понял, что перемудрил и банально закомментил все лишнее. До меня дошло, что вызовы идут из самой функции, а не из exe. В итоге в списках этой функции теперь вообще не замечено. А раньше она со всеми вызовами жрала почти 5%. Все равно она даже в модах походу не используется. Тут может быть ошибка была, что вектор не был определен в функции, ну там std::vector<AnimationPathTypes> aAnimationPaths?


void CvUnit::getLayerAnimationPaths(std::vector<AnimationPathTypes>& aAnimationPaths) const
{
// for (int i=0; i < GC.getNumPromotionInfos(); ++i)
// {
// PromotionTypes ePromotion = (PromotionTypes) i;
// if (isHasPromotion(ePromotion))
// {
// AnimationPathTypes eAnimationPath = (AnimationPathTypes) GC.getPromotionInfo(ePromotion).getLayerAnimationPath();
// if(eAnimationPath != ANIMATIONPATH_NONE)
// {
// aAnimationPaths.push_back(eAnimationPath);
// }
// }
// }
}

Теперь вот новый враг CvCity::getOwner функция дерзкая больно, надо присмирить. Причем большая часть её вызовов почему-то идет из старой знакомой чертовки CvPlayerAI::AI_maxGoldPerTurnTrade. А ещё есть весьма наглая GetLocaleTime судя по названию она из питона? Почти полтора процента времени на неё уходит. Перенести бы это в dll.

Cansei
03.09.2013, 15:38
Продолжаю углубляться в особенности исполнения функций. Специально прогонял через эти программки обычную версию, без модов. Так как играл без автоплея, то результат сильно отличался. Но вот проблемные функции по прежнему на месте. Тот же LayerAnimationPaths был замечен не раз, к тому же ещё вчера нашел в теме мода Planetfall случай когда она вообще приводила к вылету докапываясь до настроек карты. Что интересно на ихфанатиках вообще почти не заметил упоминания этой функции, походу либо они и не знают, что она странная, либо эти программки что-то не так показывают. Но судя по упоминанию в теме Planetfall и тому что обе программы показывают примерно одни и те же результаты, похоже где-то здесь и вправду подвох, найти бы ещё где.
Я пока нашел три основные функции где точно виден подвох. Это за исключением уже побежденной LayerAnimationPaths , CvPlayerAI::AI_getGreeting и многократно упоминаемая CvPlayerAI::AI_maxGoldPerTurnTrade. Есть ещё около десятка других, но они не так заметны. CvPlayerAI::AI_getGreeting к примеру не только многократно вызывает сама себя, но и CvPlot::isLake зачем вообще функции выбирающей тип приветствия при диалоге с AI знать является ли тайл озером?

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

DiploCommentTypes CvPlayerAI::AI_getGreeting(PlayerTypes ePlayer) const
{
TeamTypes eWorstEnemy;

if (GET_PLAYER(ePlayer).getTeam() != getTeam())
{
eWorstEnemy = GET_TEAM(getTeam()).AI_getWorstEnemy();

if ((eWorstEnemy != NO_TEAM) && (eWorstEnemy != GET_PLAYER(ePlayer).getTeam()) && GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isHasMet(eWorstEnemy) && (GC.getASyncRand().get(4) == 0))
{
if (GET_PLAYER(ePlayer).AI_hasTradedWithTeam(eWorstEnemy) && !atWar(GET_PLAYER(ePlayer).getTeam(), eWorstEnemy))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_WORST_ENEMY_TRADING");
}
else
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_WORST_ENEMY");
}
}
else if ((getNumNukeUnits() > 0) && (GC.getASyncRand().get(4) == 0))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_NUKES");
}
else if ((GET_PLAYER(ePlayer).getPower() < getPower()) && AI_getAttitude(ePlayer) < ATTITUDE_PLEASED && (GC.getASyncRand().get(4) == 0))
{
return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_UNIT_BRAG");
}
}

return (DiploCommentTypes)GC.getInfoTypeForString("AI_DIPLOCOMMENT_GREETINGS");
}

Snake_B
04.09.2013, 13:29
установил теме статус важная...

to Cansei
хотелось бы все таки, чтобы выкладывались исходники... а то много чего интересного делали, потом по каким-нибудь причинам пропадали...
и все наработки вместе с ними...

Cansei
04.09.2013, 14:56
to Cansei
хотелось бы все таки, чтобы выкладывались исходники... а то много чего интересного делали, потом по каким-нибудь причинам пропадали...
и все наработки вместе с ними...

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

NeseryozniyVET
05.09.2013, 01:23
А этот TeamTypes для города, оно вообще много памяти требует? Как полноценный объект класса CvTeam? 32 бита (4 байта) на город. Если построишь миллион городов, то игра будет из-за TeamTypes жрать аж на 4 мегабайта больше :-)

Cansei
05.09.2013, 01:39
32 бита (4 байта) на город. Если построишь миллион городов, то игра будет из-за TeamTypes жрать аж на 4 мегабайта больше :-)

Спасибо, до меня только позавчера дошло, что эти Types это же перечисления, те же int по сути. До этого я почему-то считал их разновидностью объектов.

Cansei
06.09.2013, 15:01
Хе-хе. Натравил на исходники цивы Cppcheck, ищет вот сейчас. Нашел кучу предупреждений и три (пока три) ошибки с delete созданного как new[].

Исправил, посмотрю что он найдет дальше.

Cansei
07.09.2013, 00:33
С неправильным удалением указателей разобрался. Их там всего пять и было. Но и то весьма неприятно. Ну и ещё штук 20 утечек памяти в CvInfos. Но там они некритичны, завтра с ними все равно разберусь для порядка. Из предупреждений в основном на опечатки с FAssert. Запускал профилирование с дебаг версией, насколько же приятно смотреть на реальное распределение ресурсов.
Тут бы отдельную темку выделить, "Оптимизация Civ4", а то к урокам это имеет немного отдаленное отношение.
http://s018.radikal.ru/i519/1309/31/300c9b0353b3.jpg (http://radikal.ru/fp/561178937aaf4660a4774f04634f296a)

Snake_B
07.09.2013, 01:46
Тут бы отдельную темку выделить, "Оптимизация Civ4", а то к урокам это имеет немного отдаленное отношение.

дык... создавай...
для тех кто не в курсе... разговор про ошибки в оригинальном dll или модифицированном?

Cansei
07.09.2013, 04:27
дык... создавай...
для тех кто не в курсе... разговор про ошибки в оригинальном dll или модифицированном?

Создам, через некоторое время.
А ошибки, которые с указателями, это ещё из оригинального dll, которое BTS 3.19. Проверка исходников через Cppcheck мне их и выдала.

Cansei
07.09.2013, 04:27
дык... создавай...
для тех кто не в курсе... разговор про ошибки в оригинальном dll или модифицированном?

Создам, через некоторое время.
А ошибки, которые с указателями, это ещё из оригинального dll, которое BTS 3.19. Проверка исходников через Cppcheck мне их и выдала.

Defender
18.11.2015, 05:30
Подскажите, пожалуйста, где прописываются доходы / убытки от морских блокад. Заранее спасибо.

Ostap352
18.11.2015, 15:38
Подскажите, пожалуйста, где прописываются доходы / убытки от морских блокад. Заранее спасибо.
Точно не в xml

Хитрец
18.11.2015, 18:39
:huh:Тогда где?

Понятно,но в каком месте СДК? Мне тоже интересно

Не могу придумать никнейм
23.11.2015, 22:08
В SDK

Думаю, это функция collectBlockadeGold()

Хитрец
24.11.2015, 09:02
Благодарстввую

keppurah
08.01.2016, 23:05
Шаг пятый: Компилируем ...


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

Например, в моде "RFC Dawn of Civilization" с помощью блокнота я изменил файл CvRhyes.cpp (точнее параметр StartingYear для одной из цивилизаций). Сохранил изменения, а при запуске игры параметр остался прежним.

Snake_B
09.01.2016, 02:50
Сохранил изменения, а при запуске игры параметр остался прежним.

первый пост темы же..

keppurah
09.01.2016, 17:32
первый пост темы же..

В первом посте не описано как скомпилировать измененный файл cpp.

Snake_B
09.01.2016, 20:14
ну да.. вот здесь смотри: http://www.civfanatics.ru/threads/7424

keppurah
09.01.2016, 20:35
ну да.. вот здесь смотри: http://www.civfanatics.ru/threads/7424

Не получается скачать 3 библиотеки (msvcrt.lib, msvcrtd.lib, и msvcprt.lib).

А можно скомпилировать изменения проще, без установки всех этих программ и настроек?

Snake_B
09.01.2016, 20:48
А можно скомпилировать изменения проще, без установки всех этих программ и настроек?

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

п.с у меня вроде начинает скачивать.. у тебя когда не получается?

keppurah
09.01.2016, 20:50
Перезалейте пожалуйста файлы msvcrt.lib, msvcrtd.lib, и msvcprt.lib, а то по ссылкам они не доступны.

Snake_B
09.01.2016, 20:58
http://www.redforce.civfanatics.ru/sdk/lib.rar

keppurah
09.01.2016, 21:12
Благодарю за файлы!

В моде "RFC Dawn of Civilization" отсутствует файл CvGameCoreDLL.vcproj. Как правильно внести изменения в этот мод в таком случае?

Snake_B
09.01.2016, 21:13
ну, это я уже не знаю.. может быть он там стандартный используется..

keppurah
09.01.2016, 21:14
В этом моде есть файлы: CvGameCoreDLL.suo, CvGameCoreDLL.sln, CvGameCoreDLL.h, CvGameCoreDLL.cpp.

keppurah
09.01.2016, 21:29
ну, это я уже не знаю.. может быть он там стандартный используется..

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

Кто знает, как изменить модификацию без файла CvGameCoreDLL.vcproj? При этом есть файлы CvGameCoreDLL.suo, CvGameCoreDLL.sln, CvGameCoreDLL.h, CvGameCoreDLL.cpp.

keppurah
09.01.2016, 21:51
Похоже что модификацию "RFC Dawn of Civilization" создавали с помощью программы Microsoft Visual Studio 2013 или старше, судя по наличию файла CvGameCoreDLL.suo вместо CvGameCoreDLL.vcproj.

Подскажите пожалуйста как мне изменить и скомпилировать эту модификацию?

keppurah
10.01.2016, 14:32
Я обратился к разработчику модификации, и он посоветовал создать проект для исходного кода мода и скомпилировать его.

Нужна помощь зала :rolleyes:, как с помощью программы CodeBlocks создать свой проект, при этом использовать исходный код мода "RFC DoC", чтобы из его модификации ничего не пропало и потом работало?

keppurah
10.01.2016, 15:28
Шаг VII. Бессмертный наркоман.

Можете начинать компилировать. Запускаете Microsoft Visual C++ 2008 Express, открываете файл CvGameCoreDLL.sln и выбираете в панельке вверху Release, там ещё Win32 рядом стоит.

При попытке открыть файл CvGameCoreDLL.sln мода "RFC DoC", файл не открывается по причине:
---------------------------
Visual C ++ 2008 Express Edition
---------------------------
Выбранный файл является решением, но был создан в более новой версии данного приложения, и не может быть открыт.
---------------------------
ОК
---------------------------

Вопрос, возможно уже появился 3й способ скомпилировать модификацию? Или кто-то может поделится советом и свежим дистрибьютивом?

keppurah
10.01.2016, 20:28
Подскажите пожалуйста как изменить и успешно скомпилировать чужой мод RFC DoC с помощью программы Visual Studio 2010 (т.к. автор мода скомпилировал файл CvGameCoreDLL.sln в этой версии)?

Snake_B
10.01.2016, 20:37
ну, не надо вот в десяти темах писать.. если молчат, значит нет никого, кто знает.. как появятся, ответят..