Establishing Connections

Tholish

Emperor
Joined
Jul 5, 2002
Messages
1,344
Location
Japan
I'm trying to make a building work like airports did in Civ 3, where any city with an airport is connected to any other city with an airport. Phungus420 was trying to help me and I wound up with this, which I put in CvEventManager. On loading my mod, I got a series of exceptions ending in "Can't assign to a function call" or something like that, and the modified CvEventManager failing to load.

def onBuildingBuilt(self, argsList):
'Building Completed'
pCity, iBuildingType = argsList
game = gc.getGame()
#Phungus's Snippet
pCapitol=pPlayer.getCapitalCity()
iAirport=gc.getTypeForString("BUILDING_SPACEPORT")
if(pCity.getNumRealBuilding(iAirport)>0):
pCity.isConnectedTo(pCapitol)=true
#End Phungus's Snippet

isConnectedTo is a function in CvCity and getCapitalCity is a real function with bad spelling in CvPlayer. However creating the variable pCapitol and setting it equal to the value returned for the capitol city doesn't allow isConnected to to be set equal to it. I guess that's because isConnectedTo is a function that gets that value whenever any function needs it, and doesn't retain it, so you can't just go in from outside and set it, an object oriented type thing, but if not this way then how?
 
Just of note the original thread is here:
http://forums.civfanatics.com/showthread.php?t=315905

In the PythonAPI I found this call as well:
BOOL isConnectedToCapital (PlayerType ePlayer)
But I wasn't sure how to use it, the PlayerType ePlayer thing confuses me. So I went with the BOOL isConnectedTo (CyCity pCity) call and, using the created pCapitol variable above with the getCapitalCity () call.
 
On loading my mod, I got a series of exceptions ending in "Can't assign to a function call" or something like that, and the modified CvEventManager failing to load.

This is exactly the error.

pCity.isConnectedTo(pCapitol)=true

The isConnectedTo() function checks if one city is connected to another city. It returns True if it is and False otherwise. You cannot make a connection using this function. Functions return results, and you cannot alter the result the function should return by assigning to the function. You need to change the function's code or the data it uses to calculate its result.

Imagine a function isBrother() that tells you if one person is the brother of another. If I ask you that question about my friend Bill, you'll answer, "No." I can't just say, "Yes," back to you and have you magically become brothers. I could tell you that two guys are considered brothers if they have friends in common. Now you would answer, "Yes," to the question isBrother() because I changed the function itself.

BTW, doesn't the normal Airport already connect the city to the capital?

Side Note: A capital is the city that is the center of government for a region (state, country, etc). A capitol is a building that houses said government.

The capitol building is located in Sacramento, the capital of California.​
 
Ah, so it is spelled right.

But yes, Airports do not connect cities at all. I attach three screenshots from an unmodified game of Civ4 BtS 3.17, future era Advanced start.
The main screen view shows two cities, one connected to Pigs and one to Aluminum. The City Screen shots show that both cities have airports and the bonus they are connected to but not the bonus the other city is connected to.

Somewhere along the line this capability has been removed.

So, I have to find something else that makes connections and copy what it does.

I found this in CvPlot

bool CvPlot::isTradeNetworkConnected(const CvPlot* pPlot, TeamTypes eTeam) const
{
FAssertMsg(eTeam != NO_TEAM, "eTeam is not assigned a valid value");
...
if (isCity())
{
if (pPlot->isNetworkTerrain(eTeam))
{
return true;
}
}

If I understood all the details involved here, I could maybe write another conditional statement in here and that would really do it. I'd have to look for some function that checks for some value that I can make an XML tag for.
 

Attachments

  • twocities.jpg
    twocities.jpg
    211.7 KB · Views: 73
  • Airport&Aluminum.jpg
    Airport&Aluminum.jpg
    165 KB · Views: 82
  • Airport&Pork.jpg
    Airport&Pork.jpg
    168.6 KB · Views: 91
It helps to use code tags so we can read what you paste more easily :)

[code]Place Code here[/code]


If you plan to write up a new XML tag, I would do something like an integer value of what range the airport will connect you to other cities at. So then you could create a building of "Airport" which connects you to cities within 10 tiles, and another building of "Continental Airport" which can connect you to cities within 40 tiles.


Can't remember if the guy figured it out, but someone worked on this before. Could be useful information already gathered.
 
That got me to a thread where this was worked on, though I believe never perfected. It is being done by customizing the dll, not by just python. I sent a PM to salaminizer and asked why the result was never posted as a modcomp. Salaminizer said it was integrated into other stuff and that at some future time might be posted here. I suppose based on material there, I could do it myself, with a little effort, once I get my VC2005 properly set up for the sdk dll. Think I left debug on the first time and decided to work on other aspects of my mod first for a little while. Thanks for the help xienwolf, emperorfool, and phungus420.
 
in the end I still don't know if it can be done in Python, but it's working with the SDK, and connects every city with the building. that is, no range checking or anything like that.

I'll include in the description the changes to the XML too, but it will surely become trivial if you ever need to to again.

I have added one XML tag to the buildings file:
Code:
<bCityConnected>0</bCityConnected>
You may add this to the building you want to use as connection. You can rename it too to avoid confusion in the code, but everything I'll paste here uses CityConnected.

You also need to add this to the Civ4BuildingSchema file.
Add this
Code:
<ElementType name="bCityConnected" content="textOnly" dt:type="boolean"/>
where the others ElementType are.
Then under
Code:
<ElementType name="BuildingInfo" content="eltOnly">
include:
Code:
<element type="bCityConnected" minOccurs="0"/>

This settles for the XML. Now assuming everything's OK with your SDK setup.
We'll start by reading the new tag from the XML, in CvInfos.cpp
Look for bool CvBuildingInfo::read(CvXMLLoadUtility* pXML), then add:
Code:
pXML->GetChildXmlValByName(&m_bCityConnected, "bCityConnected");
to this function.

Then look for void CvBuildingInfo::read(FDataStreamBase* stream), there are several stream->Read(...).
Include
Code:
stream->Read(&m_bCityConnected);
somewhere. I have added it after stream->Read(&m_bAllowNukes).
Do the same in CvBuildingInfo::write(...):
Code:
stream->Write(m_bCityConnected);
Again, there's a write for allownukes. You may add after that line.

Now look for bool CvBuildingInfo::isAllowsNukes() const. After this one, include:
Code:
bool CvBuildingInfo::isCityConnected() const
{
	return m_bCityConnected;
}

Look for CvBuildingInfo::CvBuildingInfo() :.
Again go to the line with allowNukes, and add the line below so it becomes like this:
Code:
m_bAllowsNukes(false),
m_bCityConnected(false),	//our new line
m_piPrereqAndTechs(NULL),

Now open CvInfos.h
Look for class CvBuildingInfo : public CvHotkeyInfo
There are several DLLExport functions. Add
Code:
DllExport bool isCityConnected() const;
after DllExport bool isAllowsNukes() const;.
(I'm not sure if DllExport is needed but AFAIK it's OK to include it)
And add:
Code:
bool m_bCityConnected;
after bool m_bAllowNukes.

--

Now we will continue with CvCity.h
Just before the private: declarations (that is, under public declarations), I have added:
Code:
void setCityConnected(bool bCityConnected);
	bool isCityConnected() const;
Then in the end of the private declarations:
Code:
bool m_bCityConnected;
We have added two methods: one to check if the building is able to connect cities and the other to set the m_bCityConnected variable that we have declared as private (that is, can only be changed through the use of setCityConnected method).

Alright, now we need to implement the functions in CvCity.cpp, in the end of the file:
Code:
void CvCity::setCityConnected(bool bCityConnected)
{
	m_bCityConnected = bCityConnected;
}

bool CvCity::isCityConnected() const
{
	return m_bCityConnected;
}
As I said, we set the variable in the first function and return it in the second function.

In this same file, in the first function CvCity::CvCity(), add in the end of the function:
Code:
m_bCityConnected = NULL;
Now in void CvCity::init, add in the end:
Code:
setCityConnected(false);
Now in void CvCity::reset, again in the end of the function:
Code:
m_bCityConnected = false;

Now in void CvCity::processBuilding,
After
Spoiler :
Code:
if (iChange > 0)
		{
			CorporationTypes eCorporation = (CorporationTypes)GC.getBuildingInfo(eBuilding).getFoundsCorporation();
			if (NO_CORPORATION != eCorporation && !GC.getGameINLINE().isCorporationFounded(eCorporation))
			{
				setHeadquarters(eCorporation);
			}
		}

Add:
Spoiler :
Code:
if (GC.getBuildingInfo(eBuilding).isCityConnected())
		{
			setCityConnected(true);
			GC.getGameINLINE().updatePlotGroups();
			updateTradeRoutes();
		}

And the last file, CvPlot.cpp:
go to void CvPlot::updatePlotGroup
and in the end of this function add the following piece of code:
Spoiler :
Code:
if (isCity())
		if (getPlotCity()->isCityConnected())
    {
        TeamTypes team = GET_PLAYER(ePlayer).getTeam();
        CvCity* pLoopCity;
        int iI;
        int iLoop;
        for (iI = 0; iI < MAX_PLAYERS; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
                {
                    pAdjacentPlotGroup = pLoopCity->plot()->getPlotGroup(ePlayer);
                    if ((pAdjacentPlotGroup != NULL) && (pAdjacentPlotGroup != pPlotGroup))
                    {
						if ((pLoopCity->isCityConnected()) &&  (pLoopCity->isVisible(team, false)))
                        {
                            if (pPlotGroup == NULL)
                            {
                                pAdjacentPlotGroup->addPlot(this);
                                pPlotGroup = pAdjacentPlotGroup;
                                FAssertMsg(getPlotGroup(ePlayer) == pPlotGroup, "ePlayer's plot group is expected to equal pPlotGroup");
                            }
                            else
                            {
                                FAssertMsg(getPlotGroup(ePlayer) == pPlotGroup, "ePlayer's plot group is expected to equal pPlotGroup");
                                GC.getMapINLINE().combinePlotGroups(ePlayer, pPlotGroup, pAdjacentPlotGroup);
                                pPlotGroup = getPlotGroup(ePlayer);
                                FAssertMsg(pPlotGroup != NULL, "PlotGroup is not assigned a valid value");
                            }
                        }
                    }
                }
            }
        }
		if (pPlotGroup == NULL)
		{
			GET_PLAYER(ePlayer).initPlotGroup(this);
		}
    }

now go to bool CvPlot::isTradeNetwork(TeamTypes eTeam)
and add
Code:
if (isCity(true,eTeam))
	if (getPlotCity()->isCityConnected())
    {
        return true;
    }

now go to bool CvPlot::isTradeNetworkConnected
and add
Code:
if (isCity(true,eTeam))
		if (getPlotCity()->isCityConnected())
		{
	        return true;
	    }
	if (pPlot->isCity(true,eTeam))
		if (pPlot->getPlotCity()->isCityConnected())
		{
			return true;
		}



That should be enough. As I said, I picked up the code from other changes, so there's a little chance that there might be something missing here. And I hope you understand everything :lol: if there's something confusing please tell me, and do the same if it doesn't work :p
 
So you did it without connecting it to airlift.

In my CvCity.h its "Protected", rather than "Private."

Unforunately I still haven't got the SDK working, though I did all the above. (attachment removed).

Thank, you very much Salaminizer.
 
What error are you getting when you try to compile?
 
I gave up on 2005 and decided to try code blocks.
I had a block that prevented a good compile. Once I cut it out all was OK. I took a look.

I had put a block in the wrong place in CvPlot

void CvPlot::updatePlotGroupBonus instead of
void CvPlot::updatePlotGroup

After moving it the block still came out as wrong.
 

Attachments

  • cbscreen.jpg
    cbscreen.jpg
    263.2 KB · Views: 64
CvPlot.cpp(1076) : error C2065: 'ePlayer' : undeclared identifier

-Could you show what you have in line 1076 in CvPlott.cpp (also it wouldn't hurt to include a couple lines above and below it, to give context to the code)? That's where you need to start (and often fixing the initial error will take care of the others that follow).
 
Code:
	if (isCity())
		if (getPlotCity()->isCityConnected())
    {
        TeamTypes team = GET_PLAYER(ePlayer).getTeam();
        CvCity* pLoopCity;
        int iI;

Its at 6460 now.

ePlayer is used a LOT. It shouldn't be undeclared. But the function uses () for parameters so I changed it to (PlayerTypes ePlayer) and will now test. Ta da....

Which was a resounding failure because I don't know what I'm doing, screenshot attached, overloaded function. Maybe I shouldn't change the definitiion, but I am anyway. There, its in CvPlot.h. Lets see...messed up all kinds of sfuff that uses it "No Overloaded function, takes zero arguments." So if I change the definition I have to change everything that uses it. Maybe that "const" needs to be there...

||=== CvGameCoreDLL, Final Release Win32 ===|
CvPlot.cpp|6445|error C2511: 'void CvPlot::updatePlotGroup(PlayerTypes) const' : overloaded member function not found in 'CvPlot'|
||=== Build finished: 1 errors, 0 warnings ===|
...nope.
On second thought, putting the block in UpdatePlotGroup itself, which is a multipurpose function, might be wrong. Since I am dealing with trying to get Bonuses to work, it may be that updating the other one is better and just needs to be properly overloaded, so I'll try that...that didn't work either. So that's the situation. Sigh.
 

Attachments

  • anothercbshot.jpg
    anothercbshot.jpg
    37.5 KB · Views: 76
Its a function that existed, "updatePlotGroup" and I changed it by putting in the code posted by Salaminizer. Since it doesn't work because of "ePlayer" being an undeclared identifier, even though it is used by many other functions because they recieve it in the parameter list, I'm thinking the modified updatePlotGroup needs "ePlayer" in the parameters. When I tried putting it there, in a variety of ways that caused a different problem since it overloads. So I changed the function prototype in the header and that caused another problem since updatePlotGroup is called frequently by other functions and changing the prototype for it really messes things up.

I was thinking the problem may be that I misunderstood where Salaminizer wanted the block, so I tried it in two different places. The only thing left to do is go with the changed prototype and try to change the parameter list wherever the function is called, but I fear that is definitely a bad idea, jumping into the qucksand. I know functions can be overloaded, can have different parameter lists under the right circumstances, but really any attempt at this point would be flailing. This next step is huge: I would have to actually understand what is going on.
 
You could try this simple change. Here I assume that ePlayer should be the owner of the city on the current plot. Otherwise, I don't know which player it should be.

Code:
if (isCity())
    if (getPlotCity()->isCityConnected())
    {
        [B]PlayerTypes ePlayer = getOwner();[/B]
        TeamTypes team = GET_PLAYER(ePlayer).getTeam();
        ...
 
Tried that. Got rid of THAT undeclared identifier problem, but all the others were still there, nearly every line in that block. Phungus's maxim failed in this case, the first error didn't cause the others. There's a parameter missing. Maybe I should PM salaminizer again or reexamine the old thread. Tommorrow.
 
Here's my stab at a couple more errors. Can you post a new screenshot of the errors as the ePlayer and getTeam errors should be fixed now.

Code:
if (isCity())
    if (getPlotCity()->isCityConnected())
    {
        TeamTypes team = GET_PLAYER(ePlayer).getTeam();
        [B][COLOR="SandyBrown"]CvPlotGroup* pPlotGroup = NULL;[/COLOR][/B]
        CvCity* pLoopCity;
        int iI;
        int iLoop;
        for (iI = 0; iI < MAX_PLAYERS; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
                {
                    [B][COLOR="SandyBrown"]CvPlotGroup*[/COLOR][/B] pAdjacentPlotGroup = pLoopCity->plot()->getPlotGroup(ePlayer);
                    ...

I get the feeling that pPlotGroup should hold an existing plot group coming into this code, but perhaps not. pAdjacentPlotGroup was just missing a declaration. I suspect there are a few lines above the first line here that are missing from the post.
 
Code:
void CvPlot::updatePlotGroup()
{
	PROFILE_FUNC();

	int iI;

	for (iI = 0; iI < MAX_PLAYERS; ++iI)
	{
		if (GET_PLAYER((PlayerTypes)iI).isAlive())
		{
			updatePlotGroup((PlayerTypes)iI);
		}
	}
		if (isCity())
		if (getPlotCity()->isCityConnected())
    {
        PlayerTypes ePlayer = getOwner();
        TeamTypes team = GET_PLAYER(ePlayer).getTeam();
        CvCity* pLoopCity;
        int iI;
        int iLoop;
        for (iI = 0; iI < MAX_PLAYERS; iI++)
        {
            if (GET_PLAYER((PlayerTypes)iI).isAlive())
            {
                for (pLoopCity = GET_PLAYER((PlayerTypes)iI).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iI).nextCity(&iLoop))
                {
                    pAdjacentPlotGroup = pLoopCity->plot()->getPlotGroup(ePlayer);
                    if ((pAdjacentPlotGroup != NULL) && (pAdjacentPlotGroup != pPlotGroup))
                    {
						if ((pLoopCity->isCityConnected()) &&  (pLoopCity->isVisible(team, false)))
                        {
                            if (pPlotGroup == NULL)
                            {
                                pAdjacentPlotGroup->addPlot(this);
                                pPlotGroup = pAdjacentPlotGroup;
                                FAssertMsg(getPlotGroup(ePlayer) == pPlotGroup, "ePlayer's plot group is expected to equal pPlotGroup");
                            }
                            else
                            {
                                FAssertMsg(getPlotGroup(ePlayer) == pPlotGroup, "ePlayer's plot group is expected to equal pPlotGroup");
                                GC.getMapINLINE().combinePlotGroups(ePlayer, pPlotGroup, pAdjacentPlotGroup);
                                pPlotGroup = getPlotGroup(ePlayer);
                                FAssertMsg(pPlotGroup != NULL, "PlotGroup is not assigned a valid value");
                            }
                        }
                    }
                }
            }
        }
		if (pPlotGroup == NULL)
		{
			GET_PLAYER(ePlayer).initPlotGroup(this);
		}
    }
}
 

Attachments

  • MoreMessages.jpg
    MoreMessages.jpg
    101.5 KB · Views: 93
I think you put the code in the wrong function. There must also be an existing updatePlotGroup(PlayerTypes ePlayer) function given this line in the original code:

Code:
updatePlotGroup((PlayerTypes)iI);
 
Ah, there is. It was the very next function.

void CvPlot::updatePlotGroup(PlayerTypes ePlayer, bool bRecalculate).

Build process completed for everything, now there's a new problem probably to do with my installation and or use of CodeBlocks. Debug information module size exceeded.
 
Back
Top Bottom