+ Ответить в теме
Показано с 1 по 20 из 70

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

Комбинированный просмотр

  1. #1

    Уроки SDK. Поэтапно с начала.

    Подумал тут я, и решил начать цикл уроков по работе с SDK четвертой цивы, где объясню по возможности адекватно, что я сам в нём понял. Будет где-то пять уроков по добавлению новых возможностей через SDK. Ну это если я раньше не потеряю интереса.
    Для начала что нужно для этих уроков.
    Во первых нам потребуется все необходимое для компиляции SDK, описанное в этой статье. Во вторых самые элементарные познания в программировании, что такое IF ELSE, функции и переменные. В третьих Notepad++, для удобного редактирования и WinMerge для сравнения измененных файлов. Notepad++ нам потребуется уже на первом уроке, в то время как WinMerge лишь на следующих уроках.

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

    Урок 1. Основы работы с SDK, как добавить новую опцию игры.
    Урок 2. Копирование уже существующего булевского XML тега из CIV4UnitInfos.xml в CIV4PromotionInfos.xml. (Дать прокачкам те возможности, что ранее были только уникальными для юнитов)



    Спецурок от NeseryozniyVET, Cv и Cy файлы, как добавить параметр не относящийся к текущему инфо-классу.
    Спецурок от NeseryozniyVET. Как добавить активный элемент на экран.
    Спецурок SDK-технологии за авторством volod(отдельная тема).
    P.S. Если что-то не так и не работает, пишите. Как вернусь, проверю, исправлю.
    продвинь это сообщение в соцсеть:  
    Последний раз редактировалось Cansei; 31.08.2013 в 19:01.

  2. #2
    Урок 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; 25.08.2013 в 22:36.

  3. #3

    Урок 2. Копирование уже существующего булевского XML тега из CIV4UnitInfos.xml в CIV4PromotionInfos.xml

    Сегодня будет перевод-пересказ первой части замечательного руководства An Idiots Guide to Editing the DLL. Руководство действительно хорошее, так что пересказывать буду во первых чтобы в одном месте все было. Во вторых оно с двумя ошибками из-за которых пример с деревушками нерабочий. Ошибки будут исправлены в пересказе, автор оригинала пару мелочей пропустил.
    На этом уроке мы скопируем уже булевский тег <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:arsePromotionHelp(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; 25.08.2013 в 23:32. Причина: Добавил урок

  4. #4
    reserved Урок 3
    продвинь это сообщение в соцсеть:  

  5. #5
    reserved Урок 4
    продвинь это сообщение в соцсеть:  

  6. #6
    reserved Урок 5
    продвинь это сообщение в соцсеть:  

  7. #7
    Цитата Сообщение от Cansei;427796[I
    Примечание:[/I] Для работы с xml можно использовать XML Marker, но я все равно настоятельно порекомендую Notepad++, он куда удобнее.
    А для меня удобнее xml marker. А чем лучше Notepad++?

  8. #8
    Цитата Сообщение от Cansei Посмотреть сообщение
    Шаг пятый: Компилируем ...
    Подскажите пожалуйста подробнее, как скомпилировать измененный файл срр, чтобы изменения были не только в самом файле, но и в игре?

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

  9. #9
    идейный враг всяких трансформероB

    Аватар для Snake_B


    Регистрация
    14.09.2007
    Адрес
    Донецк-Камчатка....
    Сообщений
    13,314
    Цитата Сообщение от keppurah Посмотреть сообщение
    Сохранил изменения, а при запуске игры параметр остался прежним.
    первый пост темы же..
    продвинь это сообщение в соцсеть:  

  10. #10
    Цитата Сообщение от Snake_B Посмотреть сообщение
    первый пост темы же..
    В первом посте не описано как скомпилировать измененный файл cpp.
    продвинь это сообщение в соцсеть:  

  11. #11
    идейный враг всяких трансформероB

    Аватар для Snake_B


    Регистрация
    14.09.2007
    Адрес
    Донецк-Камчатка....
    Сообщений
    13,314
    ну да.. вот здесь смотри: http://www.civfanatics.ru/threads/7424
    продвинь это сообщение в соцсеть:  

  12. #12
    Подскажите, пожалуйста, где прописываются доходы / убытки от морских блокад. Заранее спасибо.
    продвинь это сообщение в соцсеть:  
    Я не знаю, каким оружием будет вестись Третья Мировая Война, но в Четвертой будут сражаться палками и камнями. (с) Альберт Эйнштейн

    Сценарий исторического старта на Земле 1521 г. н. э. на 31 цивилизацию для мода RedForce

  13. #13
    Цитата Сообщение от Defender Посмотреть сообщение
    Подскажите, пожалуйста, где прописываются доходы / убытки от морских блокад. Заранее спасибо.
    Точно не в xml
    продвинь это сообщение в соцсеть:  
    Политик — это человек, который пожертвует вашей жизнью за свою родину.
    Цитата Сообщение от Ильф, Петров
    На третьем ходу выяснилось, что Остап играет восемнадцать испанских партий. На остальных досках васюкинцы применили хотя устаревшую, но верную защиту Филидора.

  14. #14
    Король Аватар для Хитрец


    Регистрация
    13.03.2015
    Адрес
    Ставрополь-Южнороссйский
    Сообщений
    1,584
    Тогда где?

    Понятно,но в каком месте СДК? Мне тоже интересно
    продвинь это сообщение в соцсеть:  
    Последний раз редактировалось Ostap352; 23.11.2015 в 22:29.

  15. #15


    Думаю, это функция collectBlockadeGold()
    продвинь это сообщение в соцсеть:  
    Последний раз редактировалось Ostap352; 23.11.2015 в 22:27.

  16. #16
    Король Аватар для Хитрец


    Регистрация
    13.03.2015
    Адрес
    Ставрополь-Южнороссйский
    Сообщений
    1,584
    Благодарстввую
    продвинь это сообщение в соцсеть:  

+ Ответить в теме

Похожие темы

  1. Тайлы с ресами позначены с начала игры?
    от Taras_UA в разделе Civ5 - Игровые вопросы
    Ответов: 1
    Новое: 05.10.2010, 18:18
  2. Тактические уроки двух последних войн Ирака для общевойскового командира
    от Гость в разделе Разговоры обо всём, кроме Цивилизации
    Ответов: 2
    Новое: 18.03.2009, 16:57
  3. Уроки французского
    от BuDDaH в разделе Палаты команды Монархия MTDG2
    Ответов: 5
    Новое: 01.02.2007, 14:21
  4. Уроки C++
    от vpadlo в разделе Civ4 - Модная Цивилизация
    Ответов: 13
    Новое: 21.01.2007, 00:58
  5. Глючит 1.61 рус: с начала игры остается 10 ходов!
    от Mityay в разделе Civ4 - Технические вопросы
    Ответов: 1
    Новое: 17.07.2006, 13:51

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
Рейтинг@Mail.ru

free counters