[SDK] Connecting cities through buildings

salaminizer

Colorado Internacional
Joined
Aug 12, 2006
Messages
221
Location
Porto Alegre, Brasil
Hi,

First of all, I don't know if it's possible to do what I want with Python, so I went directly to the SDK.
I want to have cities connected either by jump lanes (that I want to get rid of LATER) or by a special building.

I've changed CvCity.cpp:

Code:
bool CvCity::isConnectedTo(CvCity* pCity) const
{
	//return plot()->isConnectedTo(pCity);
	return plot()->isConnectedTo(pCity) || (getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER")) == pCity->getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER")));
}

I've added the buildings to two systems using the World Builder, and one of the system has a resource, which doesn't appear in the other system screen, and that led me to think that my "solution" didn't work.

So I want to know if it could be done with Python (I don't to have it hardcoded at all) and how would I do that, and if not, how would I do that as well :p

Apparently there's an old mod that would do that but from what I've seen it's abandoned (More Trade Routes) and in the end didn't feature this

Thanks

UPDATE:

I've changed CvCity::updateTradeRoutes() and added the code above to the function. Now both cities have Trade Routes to the other, however, the resource is still not shared. even if I find it and update here, knowing if it's possible to do in Python will be helpful.
 
I think you instead want to be working with the function CvPlot::isTradeNetworkConnected. Seems that this one works more with resource access.


Instead of hardcoding the SDK, it may be better to create a new field in the XML for the BuildingInfos which allows a building to connect tradenetworks. Then you set it up so that if that building is constructed it marks the city as "BuildingNetworked" and you'll have your function in isTradeNetworkConnected check for both cities being BuildingNetworked.
 
Go with what xienwolf suggested (genericizing and the function to use), but here's a problem with your code:

Code:
getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER")) 
== 
pCity->getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER"))

This checks that both cities have the same number of that building type. I think you want to make sure they both have at least one:

Code:
getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER")) >= 1
&&
pCity->getNumRealBuilding((BuildingTypes)GC.getInfoTypeForString("BUILDING_TRANSPORTER")) >= 1
 
ok, thanks for the suggestions. now I'm almost there, but it seems that what I've tried with isTradeNetworkConnected is not working at all.

I've added:
Code:
if (isCity(true,eTeam))
	{
		if (pPlot->isCity(true,eTeam))
		{
			if ((pPlot->getPlotCity()->isCityConnected()) && (getPlotCity()->isCityConnected()))
			{
				return true;
			}
		}
	}

I thought that at least the cities would gain trade routes or anything like that, but as I said, nothing happens. I have modified everything else and it compiled correctly and the game loads well too (added the tag, modified CvInfos, modified CvCity, etc)

by the way, I will keep the jump lanes to connect resources, so what I want is cities sharing resources when both have the building.
 
Looking at the code again it seems that this section of the code is intended to link a plot to a city, but never links a city to a city. So it isn't too likely that it would work here quite as desired.


I looked over a lot more areas, and from what I can gather it seems you might be seeking to have the cities join the same PlotGroup (thus have to deal with CvPlotGroup). All of the sections I can find where numBonuses for a City are changed deal with the PlotGroup Bonus count pretty much in the same manner.

Thus far I haven't looked to see precisely HOW you join a plotgroup (or probably more accurately, merge plot groups together).
 
I found out something in the More Trade Route and it has the part about airports connecting cities.

Spoiler :

Code:
// CA edit -- added connect all cities with airports
    if (hasAirlift())
    {
        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->getMaxAirlift()) && (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);
		}
    }
// CA edit end

and it is in CvPlot::updatePlotGroup (as xienwolf mentioned, there's something about the PlotGroups :p)

however, the isAirlift is binded to the Plot. I have changed the condition to "if (isCity()) { if (getPlotCity()->isCityConnected()) etc" and also changed the getMaxAirlift() to isCityConnected() too. but it didn't work...

Before editing I said I wasn't sure if the city was CityConnected, but I have checked with Python and when I add the building via the WorldBuilder the city becomes CityConnected, so that's definetely correct.
 
that section of code is just one facet of making airlift count as part of the trade network.

1. make sure the plot groups count as connected. you got that in last post.

2. make sure the cities are tradeable, or force it. in CvPlot.cpp, look for this
Code:
bool CvPlot::isTradeNetwork(TeamTypes eTeam) const
and this
Code:
bool CvPlot::isTradeNetworkConnected(const CvPlot* pPlot, TeamTypes eTeam) const

3. i stacked this ability on top of airlift, but you could use another property, provided you add that in the schema file.
 
There's still something wrong...
I've added the group portion to the updatePlotGroup function
I've added:

Code:
if (isCity(true,eTeam))
	if (getPlotCity()->isCityConnected())
    {
        return true;
    }
to isTradeNetwork

and

Code:
if (isCity(true,eTeam))
		if (getPlotCity()->isCityConnected())
		{
	        return true;
	    }
	if (pPlot->isCity(true,eTeam))
		if (pPlot->getPlotCity()->isCityConnected())
		{
			return true;
		}

to isTradeNetworkConnected. in fact, in this last function, I tried with only the isCity condition (ie, without the second if) and also:

Code:
if (isCity(true,eTeam))
	{
		if (pPlot->isCity(true,eTeam))
		{
			CvCity* pCity = pPlot->getPlotCity();
			if ((pCity->isCityConnected()) && (getPlotCity()->isCityConnected()))
			{
				return true;
			}
		}
	}
 
Use UI via python to debug that values returned from isConnected, isTradeNetwork, etc. actually return the values you want.
You might also want to force in SDK to execute updatePlotGroup at the beginning of each player's turn.

...in CvPlot::updatePlotGroup...the isAirlift is binded to the Plot. I have changed the condition to "if (isCity()) { if (getPlotCity()->isCityConnected()) etc" and also changed the getMaxAirlift() to isCityConnected() too. but it didn't work...

The whole point of updatePlotGroup is to combine the plot groups. Checking isCityConnected during the update is not going to do anything. You need another criteria to see if the cities are connected. I recommend using an XML property for the building like I did with airlift.
 
Use UI via python to debug that values returned from isConnected, isTradeNetwork, etc. actually return the values you want.
You might also want to force in SDK to execute updatePlotGroup at the beginning of each player's turn.



The whole point of updatePlotGroup is to combine the plot groups. Checking isCityConnected during the update is not going to do anything. You need another criteria to see if the cities are connected. I recommend using an XML property for the building like I did with airlift.

hmm sorry but I don't get it... I thought I did what you just said.
as xienwolf suggested, I have added an attribute bCityConnect to the buildings, which sets the city to isCityConnected. I thought that portion of updatePlotGroup loops through the cities and when the CITY has an airlift building, it adds the city to the group, so changing getMaxAirlift() to isCityConnected() would do the trick, because that should be my criteria (and I tested isCityConnected() with python, it's working). :confused:

as I said, you have an isAirlift function "tied" to the Plot, so what I did was: test if the plot is a city and if the plot city isCityConnected, then loop through the cities to check the isCityConnected() of every city.

edit: ok, isCityConnected, isTradeNetwork, isTradeNetworkConnected all working. only updatePlotGroup remains...
 
Sorry, I mistook isCityConnected for isConnectedTo. Also, you use isCity() and getPlotCity() in various places. I don't know what their differences are, so that might be an issue.

If all else fails, try updatePlotGroup without checking for isCityConnected. Basically this should connect ALL cities regardless of buildings. And see if that works.
 
ok, neither isCityConnected() (after isCity()) nor pLoopCity->isCityConnected() apparently works.
what you said worked fine, both cities were connected. for some reason isCityConnected() is returning false in updatePlotGroup. it's weird because with Python the return value is correct... :confused:
 
I've been trying to follow along in case I can help out with the C++ as I haven't poked about much in the SDK itself (mostly to replicate code in Python when a function isn't exposed to it). At this point it would really help to see an updated view of your code so people don't have to fully understand each change along the way to see where you are now.
 
right and thanks again to everyone helping out :p

CvPlot.cpp

CvPlot::isTradeNetwork (added code)
Code:
if (isCity(true,eTeam))
	if (getPlotCity()->isCityConnected())
    {
        return true;
    }

CvPlot::isTradeNetworkConnected (added code)
Code:
if (isCity(true,eTeam))
		if (getPlotCity()->isCityConnected())
		{
	        return true;
	    }
	if (pPlot->isCity(true,eTeam))
		if (pPlot->getPlotCity()->isCityConnected())
		{
			return true;
		}

CvPlot::updatePlotGroup
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);
		}
    }

CvCity.cpp
CvCity::processBuilding (added code)
Code:
if (GC.getBuildingInfo(eBuilding).isCityConnected())
		{
			setCityConnected(true);
		}

Two new functions:
Code:
void CvCity::setCityConnected(bool bCityConnected)
{
	m_bCityConnected = bCityConnected;
}

bool CvCity::isCityConnected() const
{
	return m_bCityConnected;
}

IMO this is the important stuff. the rest is python exposure and header declarations.
 
And how does it fail? Do you simply see no change in the game's behavior? Do you get a failed assert message? Have you compiled with asserts on to test this? Can you add debug output in the SDK so you can see that the code paths you expect are being followed?

The worst part about the SDK is that there are no comments. What the heck is the difference between

isTradeNetwork()​

and

isTradeNetworkConnected()​

? I can look it up later and will probably figure it out based on the parameters each takes.

Edit: It's what I thought, kinda. Second involves two plots, whereas the first just checks a single plot to see if it's part of your trade network.

And what about updatePlotGroup()? When is this function called (in response to what action)? If it's not called after a building is built, then its connectedness would never change, right?
 
it DIDN'T work before. I've managed to debug it and it's kinda working. it seems that updatePlotGroup is called whenever a new city is built or the terrain is altered (roads, etc) - I don't know where the "turn start" code is located so I haven't forced it yet.

I've set a breakpoint to pLoopCity->isCityConnected() (to explain you it's easier)
what I did (I start with a founded city): add a new city. nothing happens (the breakpoint isn't reached - alright). back to the WB, I add a building to any of the cities and nothing happens (if I set another breakpoint before, I can see the properties of the city in question, and they have bCityConnected = true). I add a third city, and now the first two cities get connected to each other, with a trade route between them and resources shared. whenever I add a new city, updatePlotGroup is called.

AFAIK ProcessBuilding is called even if I add a building using the WorldBuilder, because cities get bCityConnected = true after I leave the WB. What I don't know is if updatePlotGroup is called after a city finishes its production. if not, it seems I'll have to force it.

I don't know if the WB really affects anything, I'd bet it does but apparently that's not a problem.

I'll also check the normal DLL again because it MUST be working, my tests were probably wrong. and thanks for the tip about debugging EmperorFool, I should have considered it before.

there's also another thing that I'm curious about: one city has a resource with the required improvement and when it shares with other cities there's a (N) in the resource icon in the city screen (N is the number of cities). the bonus is added correctly in every city - I'm just wondering if that (N) is ok.

I'll keep updating here.

UPDATE: BAH, I'm really sorry about these last posts. it was obvious that the normal DLL would work, but I had to see it myself :p there's still the updatePlotGroup matter, I'll probably call it in the end of processBuilding, but any of you have suggestions I'd like to know.

pending tasks (so you know what I'm doing :p): find out if the trade routes/res sharing behaviour is correct.
 
UPDATE: BAH, I'm really sorry about these last posts. it was obvious that the normal DLL would work, but I had to see it myself :p there's still the updatePlotGroup matter, I'll probably call it in the end of processBuilding, but any of you have suggestions I'd like to know.

that could be it. I didn't bother with updatePlotGroup after a building is completed. That's why I did that at the beginning of each player's turn. Doing this right after a new building should yield immediate results.
 
Yes, I think you'll have to call updatePlotGroup() yourself. Since no buildings affect plot groups, processBuilding() doesn't call it in the original DLL. This seems like the best place to put it so that the connection happens immediately.

AFAIK, all the WB tools affect the map/cities/players exactly as game events do, i.e. adding a building calls processBuilding() from setNumRealBuildings() or whatever it's called.
 
ya it works if you call it inside processBuilding(). I now call it right after
Code:
changeMaxAirlift(GC.getBuildingInfo(eBuilding).getAirlift() * iChange);
which is part of the original code
so it looks like this:
Code:
		changeMaxAirlift(GC.getBuildingInfo(eBuilding).getAirlift() * iChange);
// CA edit -- added update plot groups after airport
		if (getMaxAirlift()) {
			GC.getGameINLINE().updatePlotGroups();
			updateTradeRoutes();
		}
// CA edit end

actually i might move it to the top since new trade routes would affect other benefits the building might give.
 
Top Bottom