SDK Questions

Voyhkah

Undead
Joined
Apr 25, 2009
Messages
1,444
Location
Earth
This thread can be used for anyone with a SDK question, so that you don't have to keep making new threads, and threads don't go way off topic.

I'll start with my question:

I need a changing variable to influence the happiness of all of a player's cities. The variable is in CvPlayer and is modifiable with the methods getEnviromentMeter, setEnviromentMeter, and changeEnviromentMeter. All of these methods and variables have been properly declared. I looked around and found these two methods, also in CvPlayer:
Code:
CvCity* CvPlayer::firstCity(int *pIterIdx, bool bRev) const
{
	return !bRev ? m_cities.beginIter(pIterIdx) : m_cities.endIter(pIterIdx);
}
Code:
CvCity* CvPlayer::nextCity(int *pIterIdx, bool bRev) const
{
	return !bRev ? m_cities.nextIter(pIterIdx) : m_cities.prevIter(pIterIdx);
}

I do not understand what these methods do.
Can someone help me?
 
But what do the arguments mean? And should I use them?
 
But what do the arguments mean? And should I use them?

pIterIdx is the pointer used to traverse the linked list. Not sure what bRev is about, but you do not need it.
 
A usage example from the existing code:

Code:
CvCity* pLoopCity = NULL;
int iLoop;
for (pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop))
{
 < Your code here >
}

bRev is used for going over the list in a reversed order (end to start) - pass true for this.
 
A usage example from the existing code:

Code:
CvCity* pLoopCity = NULL;
int iLoop;
for (pLoopCity = GET_PLAYER(ePlayer).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(ePlayer).nextCity(&iLoop))
{
 < Your code here >
}

bRev is used for going over the list in a reversed order (end to start) - pass true for this.

This is an entirely technical question, that probably doesn't even matter to the OP, but I'm curious after looking at this.

Why can we pass in a normal integer variable by reference? It would seem to me that we should be passing in an integer pointer. How is the straight up integer, being passed by reference, able to store the pointer value for the linked list?

Also why would they have coded the city list as a doubly linked list? When would you ever want to traverse the list from the tail?
 
Why can we pass in a normal integer variable by reference? It would seem to me that we should be passing in an integer pointer. How is the straight up integer, being passed by reference, able to store the pointer value for the linked list?

firstCity() and nextCity() do take an integer pointer:


Code:
DllExport CvCity* firstCity([B]int *[/B]pIterIdx, bool bRev=false) const;
DllExport CvCity* nextCity([B]int *[/B]pIterIdx, bool bRev=false) const;

I guess you mean the &iLoop.
In this context (when it comes before a variable and not after a type) it's the 'address of' operator, which returns the address of the variable.
In this case, it actually converts a variable of type int to a pointer to that variable (which already holds the address to it).

So when you pass it to the firstCity() function it receives its address and can dereference it (using '*') to access to actual variable.

They could have used a reference, like this:
Code:
DllExport CvCity* firstCity([B]int& [/B]nIterIdx, bool bRev=false) const;

In which case the call would be:
Code:
firstCity(iLoop)

And it would have the same effect, as the variable would have been passed by reference, and therefore can be changed from within the function.

Also why would they have coded the city list as a doubly linked list? When would you ever want to traverse the list from the tail?

Internally they probably only have a doubly-linked list (no reason to maintain one without the back-pointers), so they decided to expose this functionality in the firstCity() and nextCity().

As for what usage would it have - :dunno: - can't think of any right now, but I'm sure more creative minds can think of something.
 
This thread can be used for anyone with a SDK question, so that you don't have to keep making new threads, and threads don't go way off topic.

Last I checked, this entire forum was for python and SDK questions. If you want just one thread for SDK questions (and logically, be extension, one thread for python question) you might as well delete the forum.
 
Last I checked, this entire forum was for python and SDK questions. If you want just one thread for SDK questions (and logically, be extension, one thread for python question) you might as well delete the forum.

I think the intended purpose is similar to the Quick Modding Questions Thread, which was previously being used in the main Creation & Customization forum; basically intended for simple questions that you don't want to start a whole new thread to answer.

And because the thread is here, I may as well use it.

Is there a function which returns the number of players (not the max number of players) in the game? The only things I could find were:

Code:
GC.getInitCore.getNumDefinedPlayers()
GC.getIniInitCore.getNumDefinedPlayers()

but for both of these functions the compiler had an error:

CvInitCore is not defined

I did a search and it appears these functions are never called in the DLL. I can't imagine there isn't a function with such a purpose in the DLL, so if anyone is aware of one, could you let me know?
 
@LyThing94 -
I see 2 issues here:
One - getInitCore is a function call, so it should be:
Code:
GC.getInitCore[B]()[/B].getNumDefinedPlayers()

Two - and this is the issue you actually get the 'not defined' error about - the class CvInitCore is only declared in the CvGlobals.h, with the statement:
Code:
class CvCoreInit;

This is called an incomplete declaration, because the compiler can't figure out details such as which methods this class has and what's its size.
To actually call one of its methods, you need a complete declaration which exists in CvInitCore.h.

Add an include statement in the beginning of the file you're adding your code to:
Code:
#include "CvInitCore.h"
 
I'm sidetracking a bit after someone asked me to isolate some code I thought was done (Couldn't test it because my mod is currently missing a lot of vital pieces). As it turns out, and I suspected, not only was the code not done, it wasn't nearly done. I fixed the easy bugs, but one is really driving me nuts. It's in CvGameTextMgr.cpp (or at least I think, but I'll cover that later.

My code creates a new array in the XML called <NoTerrains>, containing a number of tags <= the number of terrains. The tags are called <NoTerrain>. They each contain a terrain (Duh). This is a terrain that the building cannot be built on (Also Duh). I created a new array for CvBuildingInfo: int* NoTerrains . I also created two new methods for this new array, int getNoTerrain(int i) const and int getNoTerrainArray() const .

I wrote the read/write code, so don't worry about that. I also think I have the working bits down, but I haven't gotten to testing that yet. Right now, as I said, I'm stuck on the game text.

I have this code in CvGameTextMgr.cpp:

Code:
void CvGameTextMgr::setBuildingHelp(CvWStringBuffer &szBuffer, BuildingTypes eBuilding, bool bCivilopediaText, bool bStrategyText, bool bTechChooserText, CvCity* pCity)
{
	PROFILE_FUNC();

	CvWString szFirstBuffer;
	CvWString szTempBuffer;
	BuildingTypes eLoopBuilding;
	UnitTypes eGreatPeopleUnit;
	PlayerTypes ePlayer;
	bool bFirst;
	int iProduction;
	int iLast;
	int iI;

	if (NO_BUILDING == eBuilding)
	{
		return;
	}

	CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);


	if (pCity != NULL)
	{
		ePlayer = pCity->getOwnerINLINE();
	}
	else
	{
		ePlayer = GC.getGameINLINE().getActivePlayer();
	}

//Lots of Firaxis Code

//Voyhkah Tweak START********************************************

	for (int i = 0; i < sizeof(kBuilding.getNoTerrainArray()); i++)
	{
		if ((TerrainTypes)kBuilding.getNoTerrain(i) != -1)
		{
			CvTerrainInfo& terrain = GC.getTerrainInfo((TerrainTypes)kBuilding.getNoTerrain(i));
			szBuffer.append(NEWLINE);
		
			szBuffer.append(gDLL->getText("TXT_KEY_DLL_NOTERRAIN", terrain.getDescription()));
		}
	}

//Voyhkah Tweak END**************************

I have this to read the XML:
Code:
//Voyhkah Tweak Start ***********************************
//Message: Sorry to put this somewhere so inconvienient!

	pXML->SetVariableListTagPair(&m_piNoTerrains, "NoTerrains", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos());

//Tweak END******************************

The code compiles, but when I run the game, ALL the buildings say "Cannot be built on Grassland [Grassland was the terrain I tested with, the building was the palace]."
 
First, it seems that you're using an int array to hold the list of terrains in the buildings info. In your case, you should probably use a boolean (bool) array - you only need true/false for each terrain. (See for example the code for TerrainDoubleMoves or FeatureDoubleMoves).

Can you post your relevant code in CvBuildingInfo? Can you also post a sample XML?

Second, your loop in setBuildingHelp is wrong. sizeof() is a compile time operator which determines the size of a given type. Since kBuilding.getNoTerrainArray() returns a pointer, it'll return the size of the pointer in bytes (4 bytes for 32 bit systems).

Since you already know the size of the array, you should use:
Code:
for (int i = 0; i < [B]GC.getNumTerrainInfos()[/B]); i++)

Third, I assume that in the XML you give the value of 1 to the terrains, and the default value in the SetVariableListTagPair() function for an int variable is 0.
That means that the value is always either 0 or 1 - and therefore always different than -1, so the condition is always true. That's why you always see the "cannot be built".

Changing it to boolean will solve this - your condition will change to:
Code:
if ((TerrainTypes)kBuilding.getNoTerrain(i))
 
First, it seems that you're using an int array to hold the list of terrains in the buildings info. In your case, you should probably use a boolean (bool) array - you only need true/false for each terrain. (See for example the code for TerrainDoubleMoves or FeatureDoubleMoves).



A. I do not know where the code for TerrainDoubleMoves is.

B. I was using the ints as TerrainTypes to stand for different terrains.

C. How would you do an array of booleans? With two dimensions? And how would youy deal with the differences of order in the array? I mean in XML parsing, that is.
 
A. You should really use the 'find' option in your editor. If you use Visual Studio, try 'Find in Files' (Ctrl-Shift-F), and select 'Entire Project' or 'Entire Solution'. It will really make life easier.

B + C.
The way that SetVariableListTagPair works is that it allocates an array the size of possible slots: in this case - an array the size of Num Terrain Infos. This array can be of type boolean. You identify which terrain is referenced according to the index, so when you call:
Code:
(TerrainTypes)kBuilding.getNoTerrain(i)
when i is the TerrainType, you get the value associated with this terrain type.
The value itself does not tell you which terrain it is.

SetVariableListTagPair already handles the order issue - it finds the value associated with TERRAIN_XXX and places the matching value in the correct place in the array.

So no need for 2 dimensions, only bool*.
 
I do not understand. How does it settle the order issue?

Do I make my XML look like:

<NoTerrains>
<NoTerrain>
<NoTerrainType>TERRAIN_GRASSLAND</NoTerrainType>
<bNo>1</bNo>
</NoTerrain>
</NoTerrains>

?
 
I do not understand. How does it settle the order issue?

Do I make my XML look like:

<NoTerrains>
<NoTerrain>
<NoTerrainType>TERRAIN_GRASSLAND</NoTerrainType>
<bNo>1</bNo>
</NoTerrain>
</NoTerrains>

?

Yes, that should do.
And then the XML code which reads it:
Code:
pXML->SetVariableListTagPair(&m_pbNoTerrains, "NoTerrains", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos());

Where m_pbNoTerrains is defined as:
Code:
bool* m_pbNoTerrains;

Do a search for m_pbTerrainNative in CvUnitInfo (CvInfos.cpp & CvInfos.h) to see all the things you need to do with this variable.
 
So I have this in XML:

Code:
<NoTerrains>
				<NoTerrain>
					<NoTerrainType>TERRAIN_GRASSLAND</NoTerrainType>
					<bNo>1</bNo>
				</NoTerrain>
			</NoTerrains>
This in Schema:
Code:
<ElementType name="NoTerrainType" content="textOnly"/>
	<ElementType name="bNo" content="textOnly" dt:type="bool"/>
	<ElementType name="NoTerrain" content="eltOnly">	
		<element type="NoTerrainType" />
		<element type="bNo"/>
	</ElementType>
	<ElementType name="NoTerrains" content="eltOnly">
		<element type="NoTerrain" minOccurs="0" maxOccurs="*" />
	</ElementType>

This in XML Loading:

Code:
pXML->SetVariableListTagPair(&m_pbNoTerrains, "NoTerrains", sizeof(GC.getTerrainInfo((TerrainTypes)0)), GC.getNumTerrainInfos());

This in CvCity.cpp:
Code:
	CvBuildingInfo& building = GC.getBuildingInfo(eBuilding);
	CvPlot* thisplot = plot();
	TerrainTypes terrain = thisplot->getTerrainType();
	for (int i = 0; i < GC.getNumTerrainInfos(); i++)
	{
		TerrainTypes thisTerrain = (TerrainTypes)i;
		if (building.getNoTerrain(i) == true && thisTerrain == terrain)
		{
			return false;
		}
	}

And this in CvGameTextMgr.cpp

Code:
	for (int i = 0; i < GC.getNumTerrainInfos(); i++)
	{
		if (kBuilding.getNoTerrain(i) == true)
		{
			CvTerrainInfo& terrain = GC.getTerrainInfo((TerrainTypes)i);
			szBuffer.append(NEWLINE);
		
			szBuffer.append(gDLL->getText("TXT_KEY_DLL_NOTERRAIN", terrain.getDescription()));
		}
	}

Is that it?
 
I think you're missing the:
Code:
<element type="NoTerrains" minOccurs="0">
definition inside BuildingInfo element (but you might have it in your scheme), and the rest seems fine, but it's hard to tell like this. You'll have to run it and see if it works.

Just one thing: There's no need to add the '== true' since a bool variable already evaluates to true or false.
So:
Code:
if (kBuilding.getNoTerrain(i))

is enough.
 
I think you're missing the:
Code:

<element type="NoTerrains" minOccurs="0">

It's there, I just forgot to show it.

I fixed a few Schema problems (Example: dt:type="bool" insted of dt:type="boolean"), and it fully loaded, but when I went to the Civilopedia and clicked the Wonders tab, I got this error message:
Code:
Assert Failed

File:  CvGlobals.cpp
Line:  1097
Expression:  eTerrainNum < GC.getNumTerrainInfos()
Message:  

----------------------------------------------------------
 
Anyone going to help!
 
Back
Top Bottom