Adding a tech requirenment for the Mountains: Back to Service Modcomp.

Afforess

The White Wizard
Joined
Jul 31, 2007
Messages
12,239
Location
Austin, Texas
I want to add a tech requirement for the Mountains: Back to Service Modcomp, so that you need a specific tech before you can move onto mountains. I want to add it to the globaldefines.xml, but I am not sure what code I need to add to CvGlobals.cpp (and .h). I didn't see any other examples like a tech requirenment, so I am unsure what I need...
 
Values inside Global[Alt]Defines.xml are all accessed through generic functions:

  • GC.getDefineINT()
  • GC.getDefineFLOAT()
  • GC.getDefineSTRING()
You pass in the XML key for the value you want to acquire.
 
Can you just use the existing TerrainImpassable and TerrainPassableTech groups? This is how galleys are prevented from moving into ocean until astronomy, for example.
 
I decided to go a different route, adding a new boolean value to CvTechInfo, "CanPassPeaks." I Added that with no problems, but I have an issue when I try to reference that function in CvPlot.cpp. Here's the problem code, red is the what I've added:
Code:
bool CvPlot::isImpassable() const
{
/*************************************************************************************************/
/** Afforess	Mountains Start		 08/03/09                                           		 */
/**                                                                                              */
/**                                                                                              */
/*************************************************************************************************/
//	if (isPeak())
//	{
//		return true;
//	}

	if (!GC.getGameINLINE().isOption(GAMEOPTION_MOUNTAINS))
	{
		if (isPeak())
		{
			return true;
		}
	}
	[COLOR="Red"]else if (isPeak())
	{
		if (!GC.getTechInfo().isCanPassPeaks());
		{
			return true;
		}
	}[/COLOR]
	
/*************************************************************************************************/
/** Afforess	Mountains End       END        		                                             */
/*************************************************************************************************/

	if (getTerrainType() == NO_TERRAIN)
	{
		return false;
	}

	return ((getFeatureType() == NO_FEATURE) ? GC.getTerrainInfo(getTerrainType()).isImpassable() : GC.getFeatureInfo(getFeatureType()).isImpassable());
}

When I try to compile that, I get

error C2039: 'isCanPassPeaks' : is not a member of 'std::vector<_Ty>'
with
[
_Ty=CvTechInfo *
]

Can Anyone tell me what I'm doing wrong?
 
You must pass a tech ID to getTechInfo() to lookup. This won't work here because isImpassable() has no way to check which player is calling it. You want peaks to become passable only for players that have researched TECH_MOUNTAINEERING, right? The best you could do here is check if the owner of the plot has researched it, but that would allow invading enemies to pass through peaks if their target had researched the tech.

You have two options that I see:

1. Modify isImpassable() to take a PlayerTypes and check if they've acquired the tech. That function is called in quite a few places (roughly 20-30), some of which have no access to a player (CvMapGenerator). I suppose you would ignore the tech thing for those cases, pass in NO_PLAYER, and return true.

2. Modify CvUnit::canMoveInto() to consider the unit's owner. This is much easier, but I don't know what impact that will have. The Unit AI functions use isImpassable() about 7 times.

Code:
	[B]if (pPlot->isPeak())
	{
		if (!GET_TEAM(getTeam()).isCanPassPeaks())
		{
			return false;
		}
	}
	else[/B] if (pPlot->isImpassable())
	{
		if (!canMoveImpassable())
		{
			return false;
		}
	}

Using the above code, you'd need to add CvTeam::isCanPassPeaks(). This should return a new member variable that gets set in CvTeam::processTech() when the tech is acquired.
 
The best you could do here is check if the owner of the plot has researched it, but that would allow invading enemies to pass through peaks if their target had researched the tech.

That would be an advantage to having that tech. Damn, that means I need to code more AI logic after this too, right?

1. Modify isImpassable() to take a PlayerTypes and check if they've acquired the tech. That function is called in quite a few places (roughly 20-30), some of which have no access to a player (CvMapGenerator). I suppose you would ignore the tech thing for those cases, pass in NO_PLAYER, and return true.

I choose door number 1.

So, the code should look like this?

Code:
bool CvPlot::isImpassable(TeamTypes eTeam) const
{...

....
    else if (isPeak())
    {
        if (!GC.getTechInfo(TeamTypes eTeam).isCanPassPeaks());
        {
            return true;
        }
    }
...


Then, I have to change the header file to have those too, but how do I call the NO_PLAYER? Is there an example somewhere I can see?
 
Before you choose door number 1 understand that you will have to investigate every place (30) that calls this function to determine what to pass in for eTeam. Having said that . . .

To make eTeam use NO_TEAM (for team is the correct parameter, not player as I said originally), you specify a default in the header file.

Code:
bool isImpassable([B]TeamTypes eTeam = NO_TEAM[/B]) const;

and add it without the default in the cpp file

Code:
bool CvPlot::isImpassable([B]TeamTypes eTeam[/B]) const

Note: Since this function is exposed to Python and I don't know how to check in C++ for a None value in Python, you can add a new function to CyPlot called IsImpassableForTeam(TeamTypes). Feel free to implement it or not.​

Now to check if that team knows the correct tech you have two options:

1. Loop over every tech looking for one that a) they have and b) allows peak movement. As this function is called a lot, I highly recommend against this option.

2. Add the function I explained above, CvTeam::isCanPassPeaks(), and use it here.

Code:
    else if (isPeak())
    {
        if (eTeam == NO_TEAM || !GET_TEAM(eTeam).isCanPassPeaks())
        {
            return true;
        }
    }

CvTeam::isCanPassPeaks() returns a new CvTeam variable (bool m_bCanPassPeaks) that you set in CvTeam::processTech(). Use isGoldTrading() as an example; it should work exactly the same.
 
Before you choose door number 1 understand that you will have to investigate every place (30) that calls this function to determine what to pass in for eTeam. Having said that . . .

I'm fine with that.
To make eTeam use NO_TEAM (for team is the correct parameter, not player as I said originally), you specify a default in the header file.

Code:
bool isImpassable([B]TeamTypes eTeam = NO_TEAM[/B]) const;
and add it without the default in the cpp file

Code:
bool CvPlot::isImpassable([B]TeamTypes eTeam[/B]) const

Okay, thanks.
Note: Since this function is exposed to Python and I don't know how to check in C++ for a None value in Python, you can add a new function to CyPlot called IsImpassableForTeam(TeamTypes). Feel free to implement it or not.

I probably won't, but thanks for the heads up
.
Now to check if that team knows the correct tech you have two options:

1. Loop over every tech looking for one that a) they have and b) allows peak movement. As this function is called a lot, I highly recommend against this option.

2. Add the function I explained above, CvTeam::isCanPassPeaks(), and use it here.
Code:
    else if (isPeak())
    {
        if (eTeam == NO_TEAM || !GET_TEAM(eTeam).isCanPassPeaks())
        {
            return true;
        }
    }
CvTeam::isCanPassPeaks() returns a new CvTeam variable (bool m_bCanPassPeaks) that you set in CvTeam::processTech(). Use isGoldTrading() as an example; it should work exactly the same.

Okay. I'll add that function, and get back to you.
 
Wait, I got CvTeam all set up, and now I'm moving on to fixing all the other functions.

Let me just make sure I'm doing this right, all the other functions, like this one should be changed from

Code:
pNewPlot = GC.getMapINLINE().plotSorenINLINE(node->m_iX, node->m_iY);

	if (pNewPlot->isImpassable())
	{
		return FALSE;
	}

to

Code:
pNewPlot = GC.getMapINLINE().plotSorenINLINE(node->m_iX, node->m_iY);

	if (pNewPlot->isImpassable([COLOR="Red"]TeamTypes eTeam[/COLOR]))
	{
		return FALSE;
	}

and the header file change CvPlot.h should tell the function that the default is NO_TEAM?
 
You only specify the type of a parameter when you are declaring/defining the function. When you want to call the function (as above), you only specify the value to pass in. In that code you posted there probably isn't a variable called eTeam to pass in. You need to get a valid TeamTypes value from somewhere.

For the CvUnitAI cases you may want to pass in getTeam() which is a CvUnit function, but you need to examine the code around the call to isImpassable() to determine what you should pass. If you leave all the existing code alone, it will use the default.

This shows the difference between declaring, defining, and calling a function. It also demonstrates how to use a default value for a function.

Code:
[B]// header file function declaration[/B]
bool isFive(int x = 5)


[B]// source file function definition[/B]
bool isFive(int x)
{
    return x == 5;
}


[B]// source file function call[/B]
int y = 2;
bool yIsFive = isFive(y);  // <-- will return false because y doesn't equal 5

bool fiveIsFive = isFive(5);  // <-- will return true because 5 equals 5

bool defaultIsFive = isFive();  // <-- will return true because the default value is 5
 
You only specify the type of a parameter when you are declaring/defining the function. When you want to call the function (as above), you only specify the value to pass in. In that code you posted there probably isn't a variable called eTeam to pass in. You need to get a valid TeamTypes value from somewhere.

I think I understand.
For the CvUnitAI cases you may want to pass in getTeam() which is a CvUnit function, but you need to examine the code around the call to isImpassable() to determine what you should pass. If you leave all the existing code alone, it will use the default.

What's wrong with letting it use the default?
This shows the difference between declaring, defining, and calling a function. It also demonstrates how to use a default value for a function.

Code:
[B]// header file function declaration[/B]
bool isFive(int x = 5)


[B]// source file function definition[/B]
bool isFive(int x)
{
    return x == 5;
}


[B]// source file function call[/B]
int y = 2;
bool yIsFive = isFive(y);  // <-- will return false because y doesn't equal 5

bool fiveIsFive = isFive(5);  // <-- will return true because 5 equals 5

bool defaultIsFive = isFive();  // <-- will return true because the default value is 5

So declaration is where, in that case, the integer "x", was equal to the value "5".
Definition is where the function should end up being equal to five.

I already understood the concept of boolean pretty well, because of my XML work, but I'm only into my fourth week of Programming Class, so I don't know much...yet.
 
Using the default NO_TEAM may be fine. An example where it won't be fine is in CvUnit::canMoveInto(). If you don't pass in the unit's team, your tech will have no effect, and peaks will remain impassable for everyone.

Function Declaration: This tells the compiler what the function's signature looks like which includes its name, parameter types with any default values, and return type.

Function Definition: This tells the compiler how the function does its job. It must match the declaration, but it omits all default values and the parameter names don't have to match those in the declaration, but it's good practice that they do.

Function Call: This tells the compiler why the function . . . :lol: no it doesn't. It merely makes a call to the function specifying the values to use for the parameters. Each function call can specify a different set of parameter values; this is what makes functions reusable in different parts of the code.

If a parameter has a default value, it will be passed to the function if a particular call doesn't specify a value for that parameter. If a parameter has a default, all parameters after it must have a default. If you omit a parameter in a call, you must omit all parameters after it.

These latter two rules come about because parameters are strictly positional. The names you give the parameters in the declaration are ignored, and the names in the definition only create local variables to hold the values pass by the caller. Any variable names you use when calling the function pertain only to the code where the function is called. There is no correlation between function call variable names and function declaration/definition parameter names.

Code:
[B]// foo.c[/B]
bool isFive(int x) { ... }

...

[B]// bar.c[/B]
int x = ...
if (isFive(x)) { ... }

The two "x" variables here are completely unrelated. When isFive() is called, whatever value the x in foo.c has is passed into isFive() and gets stored in a new local variable called x (coincidentally) in the isFive() function. You should read up on variable scoping rules in C/C++ as their very important to understanding code.

A good rule of thumb is that most of the variables you see in functions do not exist outside those functions. This is called "local" scope as they are local to the function.
 
Using the default NO_TEAM may be fine. An example where it won't be fine is in CvUnit::canMoveInto(). If you don't pass in the unit's team, your tech will have no effect, and peaks will remain impassable for everyone.

I'll make sure I'm aware of that.
Function Declaration: This tells the compiler what the function's signature looks like which includes its name, parameter types with any default values, and return type.

Function Definition: This tells the compiler how the function does its job. It must match the declaration, but it omits all default values and the parameter names don't have to match those in the declaration, but it's good practice that they do.

Function Call: This tells the compiler why the function . . . :lol: no it doesn't. It merely makes a call to the function specifying the values to use for the parameters. Each function call can specify a different set of parameter values; this is what makes functions reusable in different parts of the code.

Okay. Thanks for the uh...definitions.
If a parameter has a default value, it will be passed to the function if a particular call doesn't specify a value for that parameter. If a parameter has a default, all parameters after it must have a default. If you omit a parameter in a call, you must omit all parameters after it.

These latter two rules come about because parameters are strictly positional. The names you give the parameters in the declaration are ignored, and the names in the definition only create local variables to hold the values pass by the caller. Any variable names you use when calling the function pertain only to the code where the function is called. There is no correlation between function call variable names and function declaration/definition parameter names.

Code:
[B]// foo.c[/B]
bool isFive(int x) { ... }

...

[B]// bar.c[/B]
int x = ...
if (isFive(x)) { ... }
The two "x" variables here are completely unrelated. When isFive() is called, whatever value the x in foo.c has is passed into isFive() and gets stored in a new local variable called x (coincidentally) in the isFive() function. You should read up on variable scoping rules in C/C++ as their very important to understanding code.

A good rule of thumb is that most of the variables you see in functions do not exist outside those functions. This is called "local" scope as they are local to the function.

Okay, now I understand a bit more. So, variables declared in the header are global variables, usable in anywhere in that C++ source file, or in any other file that imports that file. Variables declared inside of an indivual function are for that function only. I never learned this before, because Java, which is similar to C++, and coincidentally, what language I'm learning, doesn't allow global variables. Interesting...
 
C++ and Java are nearly identical in this regard except that C++ can have global variables as you've mentioned. However, the SDK code uses only a handful of global variables.

Most of the variables you see declared in the header files are class member variables. Like the functions defined in the same place, they are tied to a class (CvUnit, CvPlayer, CvPlot, etc). The trick is that the class itself only declares/defines the variables; they don't exist until you instantiate (make one of) a class.

As an example, there is a single CvMap object instance for the game. If it were a 10 x 10 super tiny map, there would be 100 CvPlot objects. Each CvPlot object has its own m_iX and m_iY member variables. A CvPlot member function can reference those variables directly since there is an implicit reference to the CvPlot object on which the function was called.

That probably threw you, but maybe you've covered Object Oriented Programming already and understand the difference between objects and their classes. A class defines the behavior while an object is a single instance of that class and holds the data (member variable values) specific to it.

Real world example: A Car is a class that has a make, year, and license plate, each strings for simplicity. The Car class itself doesn't store any data. Instead, "my car" is an object of type Car with make "Dodge Viper", year "1974", and license plate "Q123XYZ".
 
If a parameter has a default value, it will be passed to the function if a particular call doesn't specify a value for that parameter. If a parameter has a default, all parameters after it must have a default. If you omit a parameter in a call, you must omit all parameters after it.

I'm not quite sure I understand this. If the function doesn't call the particular variable, lime TeamTypes eTeam, would it just ignore the irrelevant data, or give an error because of extra data?
 
Here's an example that adds zero to three integers and returns their sum:

Code:
int add(int x = 0, int y = 0, int z = 0);

int add(int x, int y, int z)
{
    return x + y + z;
}

int sum = add(2, 6, 3);

How could you omit just the 2nd parameter when calling it?

Code:
int sum = add(2, 3);

How is this any different from omitting the 3rd parameter? This is why when you omit a parameter, you must omit all parameters after it. So the above is treated as if you omitted z. For this reason, once you specify a default value for a parameter, you must specify defaults for all parameters after it. This is illegal:

Code:
int add(int x = 0, int y, int z);

It's really not that big of a deal. You'll get a compiler error if you do this that should be fairly clear.
 
Thanks, EmperorFool, that makes much more sense now.

The_J,

I just learned about overloading on Friday, so it's pretty new to me. + and - are pretty obvious examples of this, correct?

Now, back to my real goal here, adding tech requirement. I'm going to go through to code, and will report back upon success/failure.
 
BtW, in Python you can write a function, which can take different numbers of variables, so what EF wrote doesn't count for every language ;).

I just learned about overloading on Friday, so it's pretty new to me. + and - are pretty obvious examples of this, correct?

You mean, +/- with integer and float? Yes, that's a good example.

And you're learning programming atm? -> Modding this Dll is probably the best practice you can have.
 
Back
Top Bottom