+ Ответить в теме
Страница 1 из 4 12 ... ПоследняяПоследняя
Показано с 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
    Вот этого я и ждал. Спасибо.

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

  9. #9
    Цитата Сообщение от www555 Посмотреть сообщение
    Вот этого я и ждал. Спасибо.
    Да пожалуйста

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

  10. #10

  11. #11
    Маленькое дополнение к первому уроку.
    Файлы 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 - работа с командой (война объявляется не игроку, а команде. работа с технологиями в большенстве случаем происходит на уровне команды, а не игрока)
    продвинь это сообщение в соцсеть:  
    Последний раз редактировалось NeseryozniyVET; 20.08.2013 в 08:01.
    Если новые технологии позволяют обходится без услуг простых людей - это прогресс, а если новые технологии позволяют обходится без услуг миллионеров и крупных компаний - это нарушение авторских прав.
    Мой мод

  12. #12
    Ещё одна отличная инструкция!
    продвинь это сообщение в соцсеть:  

  13. #13
    Cansei, когда будет второй урок?
    продвинь это сообщение в соцсеть:  
    Если новые технологии позволяют обходится без услуг простых людей - это прогресс, а если новые технологии позволяют обходится без услуг миллионеров и крупных компаний - это нарушение авторских прав.
    Мой мод

  14. #14
    Цитата Сообщение от NeseryozniyVET Посмотреть сообщение
    Cansei, когда будет второй урок?
    Да, Cansei, ждём второго урока

  15. #15
    NeseryozniyVET Спасибо за урок. Сейчас добавлю ссылку на него в первый пост.

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

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

    cfc-cfr Спасибо.
    продвинь это сообщение в соцсеть:  
    Последний раз редактировалось Cansei; 25.08.2013 в 21:16. Причина: опечатка

  16. #16
    Цитата Сообщение от Cansei Посмотреть сообщение
    cfc-cfr Спасибо.
    Нет, не мне спасибо, спасибо всем, кто пишет что-то интересное.
    продвинь это сообщение в соцсеть:  

  17. #17
    Добавил второй урок, намаялся с ним изрядно. Пишите работает или нет, нормально ли читается, не слишком ли расплывчато описано?
    продвинь это сообщение в соцсеть:  

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

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

  19. #19
    Цитата Сообщение от NeseryozniyVET Посмотреть сообщение
    Вот нашел еще один хороший урок. Я его, кстате, в своем моде применил.
    http://www.civfanatics.ru/threads/7826-SDK-технологии

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

  20. #20


    Как добавить активный элемент на экран.
    Для начала надо открыть файл нужного нам экрана (он находятся в папке 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 которые мы указывали при создании объектов.
    продвинь это сообщение в соцсеть:  
    Если новые технологии позволяют обходится без услуг простых людей - это прогресс, а если новые технологии позволяют обходится без услуг миллионеров и крупных компаний - это нарушение авторских прав.
    Мой мод

+ Ответить в теме
Страница 1 из 4 12 ... ПоследняяПоследняя

Похожие темы

  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