Case study: switch mods

MatzeHH

Warlord
Joined
Jan 8, 2006
Messages
210
Location
Germany
Hello World!

Here’s an approach to switch combined mods on and off.
I have combined many mods and wanted to switch single parts. I want a screen at the beginning of the game where I can choose the parts for this game.
First, I tried to enhance the game options, but referring to this bug that way seems to be impossible.
So, I made a new full set of options to switch my mods on an off.
I placed comments to the source code. "Mod Chooser Start" and "Mod Chooser End" declare start and end of my changes. This is the same for the XML files, Python files an c++ files.

You can download the whole set of files here:
Edit: files removed due to code rewrite.

There is no CvGameCoreDLL.dll in the archive, that wouldn't make sense because there is just sample code in it. You will see that in the following text.

The attachments show the result. Picture 1 and 2 show it with the sample code and picture 3 and 4 show it in a real environment.
 

Attachments

  • ModOptionsScreen.jpg
    ModOptionsScreen.jpg
    103.5 KB · Views: 347
  • ShowOptions.jpg
    ShowOptions.jpg
    166.5 KB · Views: 337
  • ModOptionsScreen_real.jpg
    ModOptionsScreen_real.jpg
    121 KB · Views: 361
  • ShowOptions_real.jpg
    ShowOptions_real.jpg
    191.8 KB · Views: 380
Step 1: XML

We need 3 new XML files and 1 changed file.
The 3 new files are:

\\Assets\XML\gameinfo\CIV4ModOptionInfos.xml
This file holds a list of the options and some references to the next 2 files.

\\Assets\XML\Text\ModChooserText.xml
Here are the shown names of the new options.

\\Assets\XML\Text\ModChooserMisc.xml
Here are the help texts for the tool tips on our new options.

The changed file is
\\Assets\XML\gameinfo\CIV4GameInfoSchema.xml

1.1) CIV4ModOptionInfos.xml

Code:
<Civ4ModOptionInfos xmlns="x-schema:CIV4GameInfoSchema.xml">
	<ModOptionInfos>
		<ModOptionInfo>
			<Type>MODOPTION_A</Type>
			<Description>TXT_KEY_MOD_OPTION_A</Description>
			<Help>TXT_KEY_MOD_OPTION_A_HELP</Help>
			<bDefault>1</bDefault>
		</ModOptionInfo>
		<ModOptionInfo>
			<Type>MODOPTION_B</Type>
			<Description>TXT_KEY_MOD_OPTION_B</Description>
			<Help>TXT_KEY_MOD_OPTION_B_HELP</Help>
			<bDefault>1</bDefault>
		</ModOptionInfo>
		<ModOptionInfo>
			<Type>MODOPTION_C</Type>
			<Description>TXT_KEY_MOD_OPTION_C</Description>
			<Help>TXT_KEY_MOD_OPTION_C_HELP</Help>
			<bDefault>1</bDefault>
		</ModOptionInfo>
	</ModOptionInfos>
</Civ4ModOptionInfos>

1.1.1) <Type>
Here is the internal name for the option. This name must reflect in the SDK, but

we will see this later.

1.1.2) <Description>
A link to the text for the name of the mod. See ModChooserText.xml

1.1.3) <Help>
A link to the text for the description / tool tip of the mod. See

ModChooserMisc.xml

1.1.4) <bDefault>
Here is the default value for this option. It may be 1 or 0. Later, we will see

that this value normally isn't used.

If we would read this XML file, we had errors, because the tags </ModOptionInfo>,

</ModOptionInfos>, </Civ4ModOptionInfos> are not yet defined. We have to add them

to CIV4GameInfoSchema.xml

1.2) CIV4GameInfoSchema.xml
1.2.1) Element </Civ4ModOptionInfos>
Looking at the file we find a section which holds some upper elements:

Code:
...
	<ElementType name="Civ4GameOptionInfos" content="eltOnly">
		<element type="GameOptionInfos" minOccurs="0" maxOccurs="1"/>
	</ElementType>
	<ElementType name="Civ4MPOptionInfos" content="eltOnly">
		<element type="MPOptionInfos" minOccurs="0" maxOccurs="1"/>
	</ElementType>
...

Let's add our new element:
Code:
...
	<ElementType name="Civ4GameOptionInfos" content="eltOnly">
		<element type="GameOptionInfos" minOccurs="0" maxOccurs="1"/>
	</ElementType>
	<!-- Mod Chooser Start-->
	<ElementType name="Civ4ModOptionInfos" content="eltOnly">
		<element type="ModOptionInfos" minOccurs="0" maxOccurs="1"/>
	</ElementType>
	<!-- Mod Chooser End-->
	<ElementType name="Civ4MPOptionInfos" content="eltOnly">
		<element type="MPOptionInfos" minOccurs="0" maxOccurs="1"/>
	</ElementType>
...

This means that we have this new tag, which is not needed for XML files but can

occur maximal one time.

1.2.2) Element</ModOptionInfos>
Similar to </Civ4ModOptionInfos> we need the next lower section.

Code:
...
	<ElementType name="GameOptionInfos" content="eltOnly">
		<element type="GameOptionInfo" maxOccurs="*"/>
	</ElementType>
	<!-- Mod Chooser Start-->	
	<ElementType name="ModOptionInfos" content="eltOnly">
		<element type="ModOptionInfo" maxOccurs="*"/>
	</ElementType>
	<!-- Mod Chooser End-->
...


1.2.3) Element </ModOptionInfo>
This element holds all the information for one option. It has 3 more elements:

Type, Description, Help and bDefault

Code:
...
	<ElementType name="GameOptionInfo" content="eltOnly">
		<element type="Type"/>
		<element type="Description"/>
		<element type="Help"/>
		<element type="bDefault"/>
	</ElementType>
	<!-- Mod Chooser Start-->
	<ElementType name="ModOptionInfo" content="eltOnly">
		<element type="Type"/>
		<element type="Description"/>
		<element type="Help"/>
		<element type="bDefault"/>
	</ElementType>	
	<!-- Mod Chooser End-->
...

1.3) ModChooserText.xml
We need some names for our option which can be shown during game on screen. So I

made a file which holds names in different languages.

Code:
<Civ4GameText xmlns="http://www.firaxis.com">
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_A</Tag>
		<English>Name for Mod Option A</English>
		<French>Name for Mod Option A</French>
		<German>Name for Mod Option A</German>
		<Italian>Name for Mod Option A</Italian>
		<Spanish>Name for Mod Option A</Spanish>
	</TEXT>
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_B</Tag>
		<English>Name for Mod Option B</English>
		<French>Name for Mod Option B</French>
		<German>Name for Mod Option B</German>
		<Italian>Name for Mod Option B</Italian>
		<Spanish>Name for Mod Option B</Spanish>
	</TEXT>
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_C</Tag>
		<English>Name for Mod Option C</English>
		<French>Name for Mod Option C</French>
		<German>Name for Mod Option C</German>
		<Italian>Name for Mod Option C</Italian>
		<Spanish>Name for Mod Option C</Spanish>
	</TEXT>
</Civ4GameText>

<Tag> has to hold the same names as given in <Description> in

CIV4ModOptionInfos.xml

1.4) ModChooserMisc.xml
Additional to a name it would be nice to have some longer description / tool tip

for one option.
Code:
<Civ4GameText xmlns="http://www.firaxis.com">
	<!--Please place any new text entries into this file-->
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_A_HELP</Tag>
		<English>Tooltip for Mod Option A</English>
		<French>Tooltip for Mod Option A</French>
		<German>Tooltip for Mod Option A</German>
		<Italian>Tooltip for Mod Option A</Italian>
		<Spanish>Tooltip for Mod Option A</Spanish>
	</TEXT>
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_B_HELP</Tag>
		<English>Tooltip for Mod Option B</English>
		<French>Tooltip for Mod Option B</French>
		<German>Tooltip for Mod Option B</German>
		<Italian>Tooltip for Mod Option B</Italian>
		<Spanish>Tooltip for Mod Option B</Spanish>
	</TEXT>
	<TEXT>
		<Tag>TXT_KEY_MOD_OPTION_C_HELP</Tag>
		<English>Tooltip for Mod Option C</English>
		<French>Tooltip for Mod Option C</French>
		<German>Tooltip for Mod Option C</German>
		<Italian>Tooltip for Mod Option C</Italian>
		<Spanish>Tooltip for Mod Option C</Spanish>
	</TEXT>
</Civ4GameText>

<Tag> has to hold the same names as given in <Help> in CIV4ModOptionInfos.xml


Now, we have some XML files holding a new set of options. But the options doesn't do a thing yet. The files are not even read by the game.

Matze
 
Step 2: SDK changes
Now the tricky part.

2.1) CvEnums.h
I add a new enum in order to have internal names for the options. Othewise we would have to cope with numbers which would not be very useful.
Code:
//Mod Chooser Start
enum DllExport ModOptionTypes					// Exposed to Python
{
	NO_MOD_OPTION = -1,

	MODOPTION_A,
	MODOPTION_B,
	MODOPTION_C,
	
	NUM_MODOPTION_TYPES
};
//Mod Chooser End

Recognize 3 things:
1) The enum is exposed to python. We will need this later.
2) The elements have the same names as in the XML files.
3) NUM_MODOPTION_TYPES has the number of options. This is useful for loops and arrays.

2.2) CvGame.h
We need a variable which holds the state of our new options.
In "class CvGame" in the section "protected" I added an array m_abModOptions:
Code:
...
	int m_aiTeamScore[MAX_TEAMS];						// Ordered by team ID...

	//Mod Chooser Start
	bool m_abModOptions[NUM_MODOPTION_TYPES];
	//Mod Chooser End
	
	int* m_paiUnitCreatedCount;
...
Here we see the first use of NUM_MODOPTION_TYPES. The array is declared with a size referring to the number of our options.

Also, in CvGame.h in the public section I declare one method to get the state of a specific option and a method to set the state.
Code:
...
	DllExport void setOption(GameOptionTypes eIndex, bool bEnabled);

	//Mod Chooser Start
	DllExport bool isModOption(ModOptionTypes eIndex) const;														// Exposed to Python
	DllExport void setModOption(ModOptionTypes eIndex, bool bEnabled);											// Exposed to Python
	//Mod Chooser End

	DllExport bool isMPOption(MultiplayerOptionTypes eIndex) const;												// Exposed to Python
...

Both methods are exposed to python. We will need this later.
The methods are defined in CvGame.cpp. I will explain this file later.

2.3) CvXMLLoadUtilitySet.cpp
Bevor we can fill our array we have to read the XML files. We do this in CvXMLLoadUtility::LoadPostMenuGlobals()
Code:
...
	// load the new FXml variable with the Civ4TutorialInfos.xml file
	bLoaded = LoadCivXml(m_pFXml, "Misc/Civ4TutorialInfos.xml");
	if (!bLoaded)
	{
		char	szMessage[1024];
		sprintf( szMessage, "LoadXML call failed for Misc/Civ4TutorialInfos.xml. \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Load Error");
	}
	if (bLoaded && Validate())
	{
		SetGlobalClassInfo(&GC.getTutorialInfo(), "Civ4TutorialInfos/TutorialInfo", &GC.getNumTutorialInfos());
	}

	//Mod Chooser Start
	bLoaded = LoadCivXml(m_pFXml, "GameInfo/CIV4ModOptionInfos.xml");
	if (!bLoaded)
	{
		char	szMessage[1024];
		sprintf( szMessage, "LoadXML call failed for GameInfo/CIV4ModOptionInfos.xml. \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Load Error");
	}
	if (bLoaded && Validate())
	{
		int iEnumVal = NUM_MODOPTION_TYPES;
		SetGlobalClassInfo(&GC.getModOptionInfo(), "Civ4ModOptionInfos/ModOptionInfos/ModOptionInfo", &iEnumVal, true);
	}
	//Mod Chooser End

	SAFE_DELETE_ARRAY(pszDefaultUnits);
...

Now we have our values in the memory, but we need to have a function to get them.

2.4) CvInfos.h
Let's make a new class for the options. Each instance of the class holds the set of all values of one option. In our exmaple it's just the default value.
Code:
...
//Mod Chooser Start
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//  class : CvModOptionInfo
//
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class CvModOptionInfo :
	public CvInfoBase
{
public:
	DllExport CvModOptionInfo();
	DllExport virtual ~CvModOptionInfo();

	DllExport bool getDefault() const;

	DllExport bool read(CvXMLLoadUtility* pXML);

private:
	bool m_bDefault;

};
...

This class is derived from CvInfoBase.
The definition of the class can be found in CvInfos.cpp.

2.5) CvInfos.cpp
The declared methods have to be defined.
Code:
...
//Mod Chooser Start
//////////////////////////////////////////////////////////////////////////
//
//	CvModOptionInfo			
//	Mod options and their default values
//
//
CvModOptionInfo::CvModOptionInfo() :
m_bDefault(false)
{
}

CvModOptionInfo::~CvModOptionInfo()
{
}

bool CvModOptionInfo::getDefault() const 
{ 
	return m_bDefault;
}

bool CvModOptionInfo::read(CvXMLLoadUtility* pXML)
{
	if (!CvInfoBase::read(pXML))
	{
		return false;
	}

	pXML->GetChildXmlValByName(&m_bDefault, "bDefault");
	
	return true;
}
//Mod Chooser End
...

The method "read" gets the default value from the XML file and the method getDefault() returns this value.

2.6) CvGlobals.h
After defining the class CvModOptionInfo we need an instance of this class. I do that in CvGlobals.h
First, we need to make our class known to the file:
Code:
...
class CvGameOptionInfo;

//Mod Chooser Start
class CvModOptionInfo;
//Mod Chooser End

class CvMPOptionInfo;

This is beacuse the file containing the class hasn't been read yet. We just say: there is a class called CvModOptionInfo.

And now, we declare the instance of CvModOptionInfo
Code:
...	int m_iNumGameOptionInfos;

	//Mod Chooser Start
	CvModOptionInfo* m_paModOptionInfos;
	int m_iNumModOptionInfos;
	//Mod Chooser End
		
	CvMPOptionInfo* m_paMPOptionInfos;
...

I do this in the protected section of CvGlobals.
Beside the instance of CvModOptionInfo I also declare a variable for the number of options.

Now, we have to expose those variables in the public section of CvGlobals to the world.

Code:
...
	DllExport	CvGameOptionInfo& getGameOptionInfo(GameOptionTypes eGameOptionNum);

	//Mod Chooser Start
	DllExport int& getNumModOptionInfos();
	CvModOptionInfo*& getModOptionInfo();
	DllExport	CvModOptionInfo& getModOptionInfo(ModOptionTypes eModOptionNum);
	//Mod Chooser End
	
	DllExport int& getNumMPOptionInfos();
...

2.7) CvGlobals.cpp
We have to define the new methods of section 2.6

Code:
...
int& CvGlobals::getNumGameOptionInfos()
{
	m_iNumGameOptionInfos = NUM_GAMEOPTION_TYPES;
	return m_iNumGameOptionInfos;
}

//Mod Chooser Start
int& CvGlobals::getNumModOptionInfos()
{
	m_iNumModOptionInfos = NUM_MODOPTION_TYPES;
	return m_iNumModOptionInfos;
}

CvModOptionInfo*& CvGlobals::getModOptionInfo()
{
	return m_paModOptionInfos;
}

CvModOptionInfo& CvGlobals::getModOptionInfo(ModOptionTypes eModOptionNum)
{
	FAssert(eModOptionNum >= 0);
	FAssert(eModOptionNum < GC.getNumModOptionInfos());
	return m_paModOptionInfos[eModOptionNum];
}
//Mod Chooser End

CvGameOptionInfo*& CvGlobals::getGameOptionInfo()
{
	return m_paGameOptionInfos;
}
...

getNumModOptionInfos() just returns NUM_MODOPTION_TYPES from our enum.
getModOptionInfo() returns the whole array m_paModOptionInfos with all CvModOptionInfo objects.
getModOptionInfo(ModOptionTypes eModOptionNum) returns one specific object of the type CvModOptionInfo.

2.8) CvGame.cpp
Now we can use all our new functions.
In CvGame::init(HandicapTypes eHandicap) we get the default values from the XML file to initialize the array m_abModOptions
Code:
...
	if (isOption(GAMEOPTION_LOCK_MODS))
	{
		if (isGameMultiPlayer())
		{
			setOption(GAMEOPTION_LOCK_MODS, false);
		}
		else
		{
			static const int iPasswordSize = 8;
			char szRandomPassword[iPasswordSize];
			for (int i = 0; i < iPasswordSize-1; i++)
			{
				szRandomPassword[i] = getSorenRandNum(128, NULL);
			}
			szRandomPassword[iPasswordSize-1] = 0;

			GC.getInitCore().setAdminPassword(szRandomPassword);
		}
	}
	//Mod Chooser Start
	for (int i = 0; i < NUM_MODOPTION_TYPES; ++i)
	{
		m_abModOptions[(ModOptionTypes)i] = GC.getModOptionInfo((ModOptionTypes)i).getDefault();
	}
	//Mod Chooser End
...

The command GC.getModOptionInfo((ModOptionTypes)i).getDefault() calls the method getModOptionInfo in CvGlobals.cpp which returns the
CvModOptionInfo object. On this object the method getDefault() is called which returns the value from the XML file.
Finally, we now have filled the array declared in CvGame.h in section 2.2

In CvGame::reset(HandicapTypes eHandicap, bool bConstructorCall) I reset all values to false:
Code:
...
	for (iI = 0; iI < MAX_TEAMS; iI++)
	{
		m_aiRankTeam[iI] = 0;
		m_aiTeamRank[iI] = 0;
		m_aiTeamScore[iI] = 0;
	}

	//Mod Chooser Start
	for (iI = 0; iI < NUM_MODOPTION_TYPES; ++iI)
	{
		m_abModOptions[iI] = false;
	}
	//Mod Chooser End
...
It's just for resetting the game.

There are two more methods in CvGame to be declared:
Code:
...
void CvGame::setOption(GameOptionTypes eIndex, bool bEnabled)
{
	GC.getInitCore().setOption(eIndex, bEnabled);
}

//Mod Chooser Start
bool CvGame::isModOption(ModOptionTypes eIndex) const
{
	FASSERT_BOUNDS(0, NUM_MODOPTION_TYPES, eIndex, "CvGame::isModOption");
	return m_abModOptions[eIndex];
}

void CvGame::setModOption(ModOptionTypes eIndex, bool bEnabled)
{
	FASSERT_BOUNDS(0, NUM_MODOPTION_TYPES, eIndex, "CvGame::setModOption");
	m_abModOptions[eIndex] = bEnabled;
}
//Mod Chooser End
...

These methods are what we will use later to determine wether an option is true or false and to set an option.

Two more changes to deal with savegames:
In CvGame::read(FDataStreamBase* pStream):
Code:
...
	if (isOption(GAMEOPTION_NEW_RANDOM_SEED))
	{
		if (!isNetworkMultiPlayer())
		{
			m_sorenRand.reseed(timeGetTime());
		}
	}
	
	//Mod Chooser Start
	pStream->Read(NUM_MODOPTION_TYPES, m_abModOptions);
	//Mod Chooser End
}
...
This reads the variable m_abModOptions from savegames.
And in CvGame::write(FDataStreamBase* pStream):
Code:
...
	pStream->Write(m_iNumSessions);
	
	//Mod Chooser Start
	pStream->Write(NUM_MODOPTION_TYPES, m_abModOptions);
	//Mod Chooser End
}
...
This writes the variable to savegames.

2.9) CvXMLLoadUtilityInit.cpp
One more change here in CvXMLLoadUtility::CleanUpGlobalVariables()
Code:
...
	SAFE_DELETE_ARRAY(GC.getGameOptionInfo());
	
	//Mod Chooser Start
	SAFE_DELETE_ARRAY(GC.getModOptionInfo());
	//Mod Chooser End
	
	SAFE_DELETE_ARRAY(GC.getMPOptionInfo());
...

The next sections show how to expose all we have done to python.
2.10) CyEnumsInterface.cpp
First, I want to have my names of the enum to be exposed to python. I do that in CyEnumsPythonInterface()
Code:
...
//Mod Chooser Start
	python::enum_<ModOptionTypes>("ModOptionTypes")
		.value("NO_MOD_OPTION", NO_MOD_OPTION)
		.value("MODOPTION_A", MODOPTION_A)
		.value("MODOPTION_B", MODOPTION_B)
		.value("MODOPTION_C", MODOPTION_C)
		.value("NUM_MODOPTION_TYPES", NUM_MODOPTION_TYPES)
		;
//Mod Chooser Start
}
...

Now we can use ModOptionTypes.MODOPTION_X in python.

2.11) CyGame.h
I define two method to read and write the options. I do this in the public section of CyGame.
Code:
...
	bool isOption(int /*GameOptionTypes*/ eIndex);
	
	//Mod Chooser Start
	bool isModOption(int /*ModOptionTypes*/ eIndex);
	void setModOption(int /*ModOptionTypes*/ eIndex, bool bEnabled);
	//Mod Chooser End
	
	bool isMPOption(int /*MultiplayerOptionTypes*/ eIndex);
...

2.12) CyGame.cpp
The declared methods have to be defined.
Code:
...
bool CyGame::isOption(int /*GameOptionTypes*/ eIndex)
{
	return m_pGame ? m_pGame->isOption((GameOptionTypes)eIndex) : -1;
}

//Mod Chooser Start
bool CyGame::isModOption(int /*ModOptionTypes*/ eIndex)
{
	return m_pGame ? m_pGame->isModOption((ModOptionTypes)eIndex) : -1;
}

void CyGame::setModOption(int /*ModOptionTypes*/ eIndex, bool bEnabled)
{
	m_pGame->setModOption((ModOptionTypes)eIndex, bEnabled);
}
//Mod Chooser End

bool CyGame::isMPOption(int /*MultiplayerOptionTypes*/ eIndex)
{
	return m_pGame ? m_pGame->isMPOption((MultiplayerOptionTypes)eIndex) : -1;
}
...

isModOption(int /*ModOptionTypes*/ eIndex) calls the method isModOption of CvGame and returns the result.
setModOption(int /*ModOptionTypes*/ eIndex, bool bEnabled) calls setModOption of CvGame and sets the specific option to the value given by bEnabled.

2.13) CyGameInterface.cpp
The two methods of the former section have to be exposed to python. In CyGamePythonInterface() I added:
Code:
...
		.def("isOption", &CyGame::isOption, "bool (eIndex) - returns whether Game Option is valid")
		
		//Mod Chooser Start
		.def("isModOption", &CyGame::isModOption, "bool (eIndex) - returns whether Mod Option is valid")
		.def("setModOption", &CyGame::setModOption, "(eIndex, bEnabled) - sets Mod Option")
		//Mod Chooser End
		
		.def("isMPOption", &CyGame::isMPOption, "bool (eIndex) - returns whether MP Option is valid")
...
Now, the methods are usable in python.

2.14) CyGlobalContext.h
Beside the state of an option we also need the name and the description from the XML file. In the public section ov CyGlobalContext I added:
Code:
...
	CvInfoBase* getGameOptionInfo(int i) const;
	
	//Mod Chooser Start
	CvInfoBase* getModOptionInfo(int i) const;
	//Mod Chooser End
	
	CvInfoBase* getMPOptionInfo(int i) const;
...

And also for the number of options:
Code:
...
	int getNumGameOptionInfos() const { return GC.getNumGameOptionInfos(); }
	
	//Mod Chooser Start
	int getNumModOptionInfos() const { return GC.getNumModOptionInfos(); }
	//Mod Chooser End
	
	int getNumMPOptionInfos() const { return GC.getNumMPOptionInfos(); }
...

2.15) CyGlobalContext.cpp
The method getModOptionInfo(int i) has to be defined:
Code:
...
CvInfoBase* CyGlobalContext::getGameOptionInfo(int i) const
{
	return (i>=0 && i<GC.getNumGameOptionInfos()) ? &GC.getGameOptionInfo((GameOptionTypes)i) : NULL;
}

//Mod Chooser Start
CvInfoBase* CyGlobalContext::getModOptionInfo(int i) const
{
	return (i>=0 && i<GC.getNumModOptionInfos()) ? &GC.getModOptionInfo((ModOptionTypes)i) : NULL;
}
//Mod Chooser End

CvInfoBase* CyGlobalContext::getMPOptionInfo(int i) const
{
	return (i>=0 && i<GC.getNumMPOptionInfos()) ? &GC.getMPOptionInfo((MultiplayerOptionTypes)i) : NULL;
}
...

2.16) CyGlobalContextInterface3.cpp
Now, the two methods also have to be exposed to python.
Code:
...
		.def("getGameOptionInfo", &CyGlobalContext::getGameOptionInfo, python::return_value_policy<python::reference_existing_object>(), "GameOptionInfo () - Returns Info object")

		//Mod Chooser Start
		.def("getNumModOptionInfos", &CyGlobalContext::getNumModOptionInfos, "int () - Returns NumModOptionInfos")
		.def("getModOptionInfo", &CyGlobalContext::getModOptionInfo, python::return_value_policy<python::reference_existing_object>(), "ModOptionInfo () - Returns Info object")
		//Mod Chooser End	
				
		.def("getNumMPOptionInfos", &CyGlobalContext::getNumMPOptionInfos, "int () - Returns NumMPOptionInfos")
...
The methods are now usable in python.

2.17) CyInfoInterface3.cpp
One more method to expose to python. I want the default value of an option to be present in python.
Code:
...
	python::class_<CvGameOptionInfo, python::bases<CvInfoBase> >("CvGameOptionInfo")
		.def("getDefault", &CvGameOptionInfo::getDefault, "bool ()")
		;

	//Mod Chooser Start
	python::class_<CvModOptionInfo, python::bases<CvInfoBase> >("CvModOptionInfo")
		.def("getDefault", &CvModOptionInfo::getDefault, "bool ()")
		;
	//Mod Chooser End
	
	python::class_<CvMPOptionInfo, python::bases<CvInfoBase> >("CvMPOptionInfo")
		.def("getDefault", &CvMPOptionInfo::getDefault, "bool ()")
		;
...

Well, I have made some additional changes to Replays used in the Hall of Fame. Section 2.18-2.22 explains that.
2.18) CvReplayInfo.h
In the public section:
Code:
...
	DllExport bool isGameOption(GameOptionTypes eOption) const;
	
	//Mod Chooser Start
	DllExport bool isModOption(ModOptionTypes eOption) const;
	//Mod Chooser End
	
	DllExport bool isVictoryCondition(VictoryTypes eVictory) const;
...
This returns the state of our options.

Code:
...
	std::vector<GameOptionTypes> m_listGameOptions;
	
	//Mod Chooser Start
	std::vector<ModOptionTypes> m_listModOptions;
	//Mod Chooser End
	
	std::vector<VictoryTypes> m_listVictoryTypes;
...
This vector holds the options.

2.19) CvReplayInfo.cpp
In CvReplayInfo::createInfo(PlayerTypes ePlayer):
Code:
...
		m_listGameOptions.clear();
		for (int i = 0; i < NUM_GAMEOPTION_TYPES; i++)
		{
			GameOptionTypes eOption = (GameOptionTypes)i;
			if (game.isOption(eOption))
			{
				m_listGameOptions.push_back(eOption);
			}
		}

		//Mod Chooser Start
		m_listModOptions.clear();
		for (int i = 0; i < NUM_MODOPTION_TYPES; i++)
		{
			ModOptionTypes eOption = (ModOptionTypes)i;
			if (game.isModOption(eOption))
			{
				m_listModOptions.push_back(eOption);
			}
		}
		//Mod Chooser End

		m_listVictoryTypes.clear();
...
This reads the options from the game and stores them in m_listModOptions.

Code:
...
bool CvReplayInfo::isGameOption(GameOptionTypes eOption) const
{
	for (uint i = 0; i < m_listGameOptions.size(); i++)
	{
		if (m_listGameOptions[i] == eOption)
		{
			return true;
		}
	}
	return false;
}

//Mod Chooser Start
bool CvReplayInfo::isModOption(ModOptionTypes eOption) const
{
	for (uint i = 0; i < m_listModOptions.size(); i++)
	{
		if (m_listModOptions[i] == eOption)
		{
			return true;
		}
	}
	return false;
}
//Mod Chooser End
...
Definition isModOption. Returns the state of the option given by eOption.

In CvReplayInfo::read(FDataStreamBase& stream):
Code:
...
		for (int i = 0; i < iNumTypes; i++)
		{
			stream.Read(&iType);
			m_listGameOptions.push_back((GameOptionTypes)iType);
		}
		
		//Mod Chooser Start
		stream.Read(&iNumTypes);
		for (int i = 0; i < iNumTypes; i++)
		{
			stream.Read(&iType);
			m_listModOptions.push_back((ModOptionTypes)iType);
		}
		//Mod Chooser End
...
Reads the set of options from the replay file.

In CvReplayInfo::write(FDataStreamBase& stream):
Code:
...
	for (uint i = 0; i < m_listGameOptions.size(); i++)
	{
		stream.Write((int)m_listGameOptions[i]);
	}
	
	//Mod Chooser Start
	stream.Write((int)m_listModOptions.size());
	for (uint i = 0; i < m_listModOptions.size(); i++)
	{
		stream.Write((int)m_listModOptions[i]);
	}
	//Mod Chooser End
...
Stores the set of options in the replay file.

2.20) CyReplayInfo.h
We need to expose the method defined above to python.
So, I added to the public section:
Code:
...
	bool isGameOption(int iOption) const;
	
	//Mod Chooser Start
	bool isModOption(int iOption) const;
	//Mod Chooser End
	
	bool isVictoryCondition(int iVictory) const;
...

2.21) CyReplayInfo.cpp
Definition of the above method:
Code:
...
bool CyReplayInfo::isGameOption(int iOption) const
{
	if (m_pHoF)
	{
		return m_pHoF->isGameOption((GameOptionTypes)iOption);
	}
	return false;
}

//Mod Chooser Start
bool CyReplayInfo::isModOption(int iOption) const
{
	if (m_pHoF)
	{
		return m_pHoF->isModOption((ModOptionTypes)iOption);
	}
	return false;
}
//Mod Chooser End
...

2.22) CyHallOfFameInterface.cpp
Finally, we have to expose that method to python. In python::class_<CyReplayInfo>("CyReplayInfo"):
Code:
...
		.def("isGameOption", &CyReplayInfo::isGameOption, "bool (int iOption)")
		
		//Mod Chooser Start
		.def("isModOption", &CyReplayInfo::isModOption, "bool (int iOption)")
		//Mod Chooser End
		
		.def("isVictoryCondition", &CyReplayInfo::isVictoryCondition, "bool (int iVictory)")
...

Well, that's all with the SDK.

That's all? No, not really. You have to USE the options. A simple example:
Imagine you want to use Borders over water.
RogerBacon changed CvCity::doPlotCulture(bool bUpdate).
Original:
Code:
...
if pLoopPlot->isPotentialCityWorkForArea(area()))
{
	pLoopPlot->changeCulture(getOwnerINLINE(), (((getCultureLevel() - iCultureRange) * 20) + getCommerceRate(COMMERCE_CULTURE) + 1), (bUpdate || !(pLoopPlot->isOwned())));
}

Change:
Code:
...
if (true)
{
	pLoopPlot->changeCulture(getOwnerINLINE(), (((getCultureLevel() - iCultureRange) * 20) + getCommerceRate(COMMERCE_CULTURE) + 1), (bUpdate || !(pLoopPlot->isOwned())));
}
...

To make this switchable, you have to modify the IF-clause:
Code:
...
if (pLoopPlot->isPotentialCityWorkForArea(area()) || GC.getGameINLINE().isModOption(MODOPTION_BORDERS_OVER_WATER))
{
	pLoopPlot->changeCulture(getOwnerINLINE(), (((getCultureLevel() - iCultureRange) * 20) + getCommerceRate(COMMERCE_CULTURE) + 1), (bUpdate || !(pLoopPlot->isOwned())));
}
...
Assuming there is a mod option called "MODOPTION_BORDERS_OVER_WATER" defined in CvEnums.h and, off cause, in the XML file.

You can use GC.getGameINLINE().isModOption(MODOPTION_X) wherever you want to determine wether an option is true or false referring to the default value in the XML file.
This is also possible in python. The call is:
gc.getGame().isModOption(ModOptionTypes.MODOPTION_X)
For getting the name of the mod defined in the XML file:
gc.getModOptionInfo().getDescription(ModOptionTypes.MODOPTION_X)
And for the tool tip:
gc.getModOptionInfo(ModOptionTypes.MODOPTION_X).getHelp()

Matze
 
Step 3: Python
I want some things to be done with Python:
- Showing the activated mod options in the options screen which is shown after pressing F8
- Showing a screen on game start which lets me choose the options I want to use in this game
- Saving my decicions to a file in order to have them back for the next game. This is useful when you want to switch more than 2 or 3 options. In my case, I have 26 mods implemented which I want to switch.

I have some Python files changed:
\\Assets\Python\Screens\CvVictoryScreen.py
\\Assets\Python\Screens\CvScreenEnums.py
\\Assets\Python\EntryPoints\CvScreensInterface.py
\\Assets\Python\CvCustomEventManager.py (well, I asume everybody who deals with mods has this file)
\\Assets\Python\EntryPoints\CvEventInterface.py

And I have some new files:
\\Assets\Python\Screens\CvModOptionsScreen.py
\\Assets\Python\EntryPoints\CvModOptionsScreenCallbackInterface.py
\\Assets\Python\EntryPoints\CvPath.py


3.1) CvVictoryScreen.py
In def showGameSettingsScreen(self):
Code:
...
		for i in range(GameOptionTypes.NUM_GAMEOPTION_TYPES):
			if gc.getGame().isOption(i):
				screen.appendListBoxString(szOptionsTable, gc.getGameOptionInfo(i).getDescription(), WidgetTypes.WIDGET_GENERAL, -1, -1, CvUtil.FONT_LEFT_JUSTIFY)
		#Mod Chooser Start		
		for iLoopModOptions in range(ModOptionTypes.NUM_MODOPTION_TYPES):
			if gc.getGame().isModOption(iLoopModOptions):
				screen.appendListBoxString(szOptionsTable, gc.getModOptionInfo(iLoopModOptions).getDescription(), WidgetTypes.WIDGET_GENERAL, -1, -1, CvUtil.FONT_LEFT_JUSTIFY)
		#Mod Chooser End
...
The used mod options are shown right below the normal game options. Goal No. 1 achieved!

3.2) CvScreenEnums.py
We want a new screen, so first I list it in the screen enums:
Code:
...
UN_SCREEN = 26
# Mod Chooser Start
MOD_OPTIONS_SCREEN = 27
# Mod Chooser End
...

3.3) CvScreensInterface.py
Import the class CvModOptionsScreen which I explain later:
Code:
...
import CvDebugInfoScreen
#import CvDiplomacy

# Mod Chooser Start
import CvModOptionsScreen
# Mod Chooser End

import CvUtil
...

Have a call for the screen:
Code:
...
victoryScreen = CvVictoryScreen.CvVictoryScreen(VICTORY_SCREEN)
def showVictoryScreen():
	victoryScreen.interfaceScreen()
	
# Mod Chooser Start
modoptionsscreen = CvModOptionsScreen.CvModOptionsScreen()
def showModOptionsScreen():
	modoptionsscreen.interfaceScreen()
# Mod Chooser End
...

And add it to the handle map:
Code:
...
DEBUG_INFO_SCREEN : debugInfoScreen,

# add new screens here
# Mod Chooser Start
MOD_OPTIONS_SCREEN : modoptionsscreen,
# Mod Chooser End
...

We could now use the command CvScreensInterface.showModOptionsScreen() to show the screen. But we have to implement the screen first.

3.4) CvModOptionsScreen.py
I don't want to post the whole file here. Mainly it's a copy of CvOptionsScreen.py.
The important code is in def drawModOptionsTab(self):
Code:
...
i = 0
for iOptionLoop in range(ModOptionTypes.NUM_MODOPTION_TYPES):
	szOptionDesc = gc.getModOptionInfo(iOptionLoop).getDescription()
	szHelp = gc.getModOptionInfo(iOptionLoop).getHelp()
	szCallbackFunction = "handleModOptionsClicked"
	szWidgetName = "GameOptionCheckBox_" + str(iOptionLoop)
	bOptionOn = gc.getGame().isModOption(iOptionLoop)
	if ((i+1) <= (ModOptionTypes.NUM_MODOPTION_TYPES+1)/2):
		vbox = "GameVBox1"
	else: 
		vbox = "GameVBox2"
	tab.attachCheckBox(vbox, szWidgetName, szOptionDesc, self.callbackIFace, szCallbackFunction, szWidgetName, bOptionOn)
	tab.setToolTip(szWidgetName, szHelp)
	i += 1
...
I have a loop over all options getting state, description and help for every option and displaying it.
Recognize, that "handleModOptionsClicked" as a callback function is attached to the checkboxes next to the name of an option.

3.5) CvModOptionsScreenCallbackInterface.py
Again, this is mainly a copy of CvOptionsScreenCallbackInterface.py, I post only the important things:
Code:
...
def handleModOptionsClicked ( argsList ): 
	"Handles checkbox clicked input"
	bValue, szName = argsList
	
	iModOption = int(szName[szName.find("_")+1:])
	gc.getGame().setModOption(iModOption, bValue)
	return 1
...
Well, this just means, if you click on a checkbox and change its state, this state is passed to the game. This is where we really set the option.

There is another important part in handleModExitButtonInput, but I will explain this later.

The command CvScreensInterface.showModOptionsScreen() works now. We just have to find a useful place to call.

3.6) CvEventInterface.py
Replace
Code:
...
normalEventManager = CvEventManager.CvEventManager()
...
with
Code:
...
normalEventManager = CvCustomEventManager.CvCustomEventManager() #Mod Chooser
...
in order to use the custom event manager

3.7) CvCustomEventManager.py
This is simple, call the function in onGameStart(self, argsList)
Some things to watch:
Code:
...
if (gc.getGame().getGameTurnYear() == gc.getDefineINT("START_YEAR")):
	if gc.getGame().getActivePlayer() == 0:
		player = gc.getPlayer(0)
		if (player.isAlive() and player.isHuman()):
			CvScreensInterface.showModOptionsScreen()
...
I call the screen only in some special conditions:
1. The year has to be the start year of the game. This prevents popping up the screen after loading a savegame
2. Show the screen only to player 0. This is for multiplayer games, where player 0 is the host. I didn't test this function. I don't know, if this results in sync errors.
3. Show the screen only to an alive human player.

Goal No. 2 achieved, we have a screen to choose the options I want to use this game.

Now Goal No. 3: saving the options to an ini file and loading it.
Python has a built-in config parser I will use. Also, we need some file operation libraries
Code:
...
import ConfigParser
import os
import os.path
import CvPath
...
I will explain CvPath later.

Next, I enhance the sequence in onGameStart(self, argsList):
Code:
...
def onGameStart(self, argsList):
	self.parent.onGameStart(self, argsList)
	if (gc.getGame().getGameTurnYear() == gc.getDefineINT("START_YEAR")):
		if gc.getGame().getActivePlayer() == 0:
			player = gc.getPlayer(0)
			if (player.isAlive() and player.isHuman()):
				ini_file = os.path.join(CvPath.installActiveModDir, "Mod Chooser.ini")
				ModConfig = ConfigParser.ConfigParser()
				ModConfig.read(ini_file)
				g_ModOptions = ModConfig.get("CONFIG", "ModOptions")
				for iOptionLoop in range(ModOptionTypes.NUM_MODOPTION_TYPES):
					sValue = g_ModOptions[iOptionLoop:iOptionLoop+1]
					if (sValue == "1"):
						bValue = true
					else:
						bValue = false
					gc.getGame().setModOption(iOptionLoop, bValue)		
				CvScreensInterface.showModOptionsScreen()
...
Before calling the screen I get the path to the current mod and add the filename "Mod Chooser.ini". Then I read this file and store it in ModConfig. In g_ModOptions I store the value of "ModOptions" in the section "CONFIG".

The Mod Chooser.ini has the following syntax:
Code:
...
[CONFIG]
modoptions = 111
...
Each 1 or 0 represents on option and says if it's true or false.

I do a loop and with gc.getGame().setModOption(iOptionLoop, bValue) I set each option to the value in the ini file. After that, the screen is called.

To save the options after choosing we have to look at CvModOptionsScreenCallbackInterface.py again

3.8) CvModOptionsScreenCallbackInterface.py reprise
We have also to import the config parser and the file operation libraries in this file.
Code:
...
import ConfigParser
import os
import os.path
import CvPath
...

In handleModExitButtonInput ( argsList ) we save the file:
Code:
def handleModExitButtonInput ( argsList ):
	"Exits the screen"
	szName = argsList
	sValue = ""
	
	for iOptionLoop in range(ModOptionTypes.NUM_MODOPTION_TYPES):
		if (gc.getGame().isModOption(iOptionLoop)):
			sValue = sValue + "1"
		else:
			sValue = sValue + "0"
			
	ini_file = os.path.join(CvPath.installActiveModDir, "Mod Chooser.ini")
	ModConfig = ConfigParser.ConfigParser()
	ModConfig.read(ini_file)
	ModConfig.set("CONFIG", "ModOptions", sValue)
	
	ini_file = file(os.path.join(CvPath.installActiveModDir, "Mod Chooser.ini"), "w")
	ModConfig.write(ini_file)
	ini_file.close() 

	getTabControl().destroy()
	
	return 1

In the loop, the value for the ini file is calculated.
Then, the file is read, the value is set and finally, the file is saved.

3.9) CvPath.py
This file is from the Civilization IV Alerts mod.

Well, that's all, enjoy.

Matze
 
MatzeHH said:
Nobody interested?

Matze
This is definitely a very interesting thing you've done here but I think people are a bit intimidated by the length of the explanation :)

What I'm curious about is (and I apologize if this is addressed in the explanation but I just didn't have time to read it all), will this method switch the various mods on the fly or do you have to restart the game for it to take effect?

I realize of course that for some mods it's impossible not to have to restart the game - if you turned off something like the mercenaries mod, you'll have to start a new game anyway. However, for less "intrusive" smaller mods, like maybe displaying the other civs' attitude on the civ list, or using different foreign advisor - can these be turned off and on w/o restarting the game, or at least only by having to reload it?
 
Rabbit said:
This is definitely a very interesting thing you've done here but I think people are a bit intimidated by the length of the explanation :)

Maybe a little long, but every piece of code is neccessary.

What I'm curious about is (and I apologize if this is addressed in the explanation but I just didn't have time to read it all), will this method switch the various mods on the fly or do you have to restart the game for it to take effect?

It would be possible to switch during a game if you display the options screen on some keyboard event. The array holding the options will then take the new options you choose and the get-functions will return the new options.
Remember how to implement a mod:
In SDK:
Code:
if GC.getGameINLINE().isModOption(MODOPTION_X)
{
[Mod code]
}
else
{
[Original code]
}

In Python:
Code:
if GC.getGame().isModOption(ModOptionTypes.MODOPTION_X):
	[Mod code]
else:
	[Original code]

So, whenever the game reaches this code, isModOption(...) returns the value from the array. It doesn't matter if the value changed during gameplay.

In my code, the screen is only shown on game start. I don't want the player to change the options during gameplay because some mods can really behave funny if you deactivate them in a running game.

Matze
 
Over at the Civ4 Core Community Project SimCutie has done a similar type of Mod switching enhancment but I belive his is focused on the Python layer and init files and bypasses the XML layer. Am I correct that your system keeps the Options on the Savegame file and they will be preserved when re-loading the game. I think you two could do some interesting colaboration in this area possibly combining the best of both systems.

I recomend you sign up with us over at the CCCP as you definatly know what your doing and we could use as many developers as we can get incorporating their work into our Mod which is intended to be a highly configurable universal base for all SDK modding. Were using a SourceForge CVS to organize our work and you may have noticed the latest release version containing SimCuties Option system and Event manager.

Our public thread is here http://forums.civfanatics.com/showthread.php?t=167004

Sign up grab the latest build and start a thread on our forum concerning your work and any future mods your planning.
 
Impaler[WrG] said:
...snip ...I recomend you sign up with us over at the CCCP... snip...
CCCP?! :eek: That's a different kind of organization. ;) :D
 
Impaler[WrG] said:
Over at the Civ4 Core Community Project SimCutie has done a similar type of Mod switching enhancment but I belive his is focused on the Python layer and init files and bypasses the XML layer.

Maybe, I haven't read the thread.

Am I correct that your system keeps the Options on the Savegame file and they will be preserved when re-loading the game.

Yes, that's true. All options are kept in the savegame.

I think you two could do some interesting colaboration in this area possibly combining the best of both systems.

I recomend you sign up with us over at the CCCP as you definatly know what your doing and we could use as many developers as we can get incorporating their work into our Mod which is intended to be a highly configurable universal base for all SDK modding. Were using a SourceForge CVS to organize our work and you may have noticed the latest release version containing SimCuties Option system and Event manager.

Our public thread is here http://forums.civfanatics.com/showthread.php?t=167004

Sign up grab the latest build and start a thread on our forum concerning your work and any future mods your planning.

Unfortunately, I don't have the time for that. I'm working on another big mod with the german community.

Matze
 
Phew, awesome!

The link to the ModChooser.rar doesn't work.

Do you already have a finished mod that is using this method or are the screens just 'fake shots'? I'd really like to have modpacks that are fully configurable, but it looks like so much work (additional to the normal effort of combining mod) that I don't expect more than a handful of people to tackle such a project. For them, however, this guide will be invaluable.
 
Concerning multiplayer compatibility:

I refer to this code:

Code:
...
if (gc.getGame().getGameTurnYear() == gc.getDefineINT("START_YEAR")):
	if gc.getGame().getActivePlayer() == 0:
		player = gc.getPlayer(0)
		if (player.isAlive() and player.isHuman()):
			CvScreensInterface.showModOptionsScreen()
...

I think this code correctly shows the screen to the host in multiplayer games. What I would like to know is whether the selected options will be correctly passed to all participants of the game (obviously, this is not a problem in Hotseat games). If you have thought of it, how did you manage to do it?
 
Teg_Navanis said:
Phew, awesome!

The link to the ModChooser.rar doesn't work.

I'm very sorry, I accidently deleted the file. Now, it works.

Do you already have a finished mod that is using this method or are the screens just 'fake shots'?

This is very real. It works fine for me and a couple of people from the german community. We have combined 33 switching mods and raise every day.

I'd really like to have modpacks that are fully configurable, but it looks like so much work (additional to the normal effort of combining mod) that I don't expect more than a handful of people to tackle such a project. For them, however, this guide will be invaluable.

The point is, that it isn't very much work at all. Once you have my code included, it is very simple to make another merged mod switchable.
1) Add the new mod do CIV4ModOptionInfos.xml
2) Add the name of the mod to ModChooserText.xml
3) Add the help text to ModChooserMisc.xml
4) Add the mod to CvEnums.h
5) Add the mod to CyEnumsInterface.cpp

From now on isModOption(...) is valid for the new mod wherever you want. And it doesn't matter if it's a python mod or a SDK mod.

I think this code correctly shows the screen to the host in multiplayer games. What I would like to know is whether the selected options will be correctly passed to all participants of the game (obviously, this is not a problem in Hotseat games). If you have thought of it, how did you manage to do it?

I have no idea, if it works in MP, but I hope so. I have never player Civ4 in MP. But I figured out, that this code is not valid if you play a scenario, because the human player is not always player 0 in scenarios.

By the way:
I have enhanced my code "a bit".
- Every mod has it's own Civilopedia entry and a button.
- I made a new XML node for units and for buildings: <PrereqMod>. You can now switch mods, which come with new units or new buildings (wonders) very easily. The required mod is also shown in the Civilopedia entry for the unit/building right beside the required ressources and has a link to it's own Civilopedia entry. The required mod is also shown in the tooltip for the unit/building.

I will update my tutorial in some days.

Matze
 
MatzeHH said:
I have no idea, if it works in MP, but I hope so. I have never player Civ4 in MP. But I figured out, that this code is not valid if you play a scenario, because the human player is not always player 0 in scenarios.

Matze

It works fine in MP and is already used in the Caesium-Mod.
 
Yea, it does. Gerikes helped me synchronizing the game. See here

Well, as I said, I will update this tutorial soon.

Matze
 
Is it possible to get the rar file back up for download? I tried doing everything you said here and the dll compiles fine but when I try to load warlords it crashes :(

Is there anything else that needs to be done here before warlords will load or should warlords load fine using only what you have in these posts?
 
Well....

I didn't update this for a long time.
In fact, this component is much more advanced now.
It is still used in the Caesium-Mod as chemosh mentioned.
If you want to use it, download the latest version of the Caesium-Mod here:
http://www.civforum.de/showthread.php?t=40241
and search the c++ source code for "// Caesium Mod - Mod Chooser"
You might also take a look at some python files like:
CvModOptionsScreenCallbackInterface.py
CvModOptionsScreen.py

and some xml files:
CIV4ModOptionInfos.xml
CaesiumMod_ChooserCivilopedia.xml
CaesiumMod_ChooserMisc.xml
CaesiumMod_ChooserText.xml

Matze
 
thanks for the reply MatzeHH. I did download the caesium mod and go through that. It took awhile to fix the SDK for compile as it has alot of unresolved symbol errors. I had to remove all calls to winamp mod and had to include FVariableSystem.inl and boost/python from warlords etc.

I am working on a mod which for MP Ladder games that will allow the host to choose the mod to play aswell as record stats etc.
 
thanks for the reply MatzeHH. I did download the caesium mod and go through that. It took awhile to fix the SDK for compile as it has alot of unresolved symbol errors. I had to remove all calls to winamp mod

That's because the project file (CvGameCoreDLL.vcproj) is not included in the source code. Another solution would be to add the 5 winamp related files to the project.

and had to include FVariableSystem.inl and boost/python from warlords etc.

Hmm, I don't know why you had to do those steps.

Well, as long as you got the SDK to compile....

Matze
 
Top Bottom