Attempt to change continents results in CTD

Leoreth

Blue Period
Moderator
Joined
Aug 23, 2009
Messages
37,060
Location
東京藝術大学
Hello everyone, I've run into a Python problem again.

As always, it's about modding RFC - this time I'm trying to dynamically change the continent (or area, to be precise) some tiles belong to. For instance, Mesopotamia and Persia should belong to Europe during the classical era to encourage the Persian AI to interact with Europe. But when I call this function:
Code:
def convertMiddleEast(self):
		if (gc.getMap().plot(76,40).area().getID() == con.iEurope):
			return
		for i in range(72,86+1):
			for j in range(34,46+1):
				pCurrent = gc.getMap().plot(i, j)
                                if ((not pCurrent.isWater()) and pCurrent.area().getID() != con.iEurope):
                                        pCurrent.setArea(con.iEurope)

		for i in range(69,71+1):
			for j in range(40,45+1):
				pCurrent = gc.getMap().plot(i, j)
                                if ((not pCurrent.isWater()) and pCurrent.area().getID() != con.iEurope):
                                        pCurrent.setArea(con.iEurope)

		for i in range(78,86+1):
			for j in range(47,49+1):
				pCurrent = gc.getMap().plot(i, j)
                                if ((not pCurrent.isWater()) and pCurrent.area().getID() != con.iEurope):
                                        pCurrent.setArea(con.iEurope)
The game immediately crashes.

When I comment the setArea method out, there's no problem anymore. What am I doing wrong?

Thanks in advance :)
 
What "setArea"? The setArea method of the CvPlot object is not exposed to Python.

To do this, you would at least have to modify the DLL to make the function available to Python via CyPlot.cpp and CyPlotInterface1.cpp.
 
I just rechecked and found out that Rhye exposed the method to Python:
Code:
//Rhye - start
void CyPlot::setArea(int iNewValue)
{
	if (m_pPlot)
		m_pPlot->setArea(iNewValue);
}
//Rhye - end

He also uses it for the same purpose, for example:
Code:
def processAfrica(self):
                africaID = gc.getMap().plot(65, 10).area().getID()
                if (gc.getMap().plot(56, 29).area().getID() == africaID):
                        return
                for iLoopX in range(48,63+1):
                        for iLoopY in range(22,33+1):
                                pCurrent = gc.getMap().plot(iLoopX, iLoopY)
                                if (not pCurrent.isWater()):
                                        pCurrent.setArea(africaID)                
                print("Sahara now African")
which always worked without problems.
 
It is odd that this simple code would CTD, but I guess you are sure it is a CTD happening because of this code only. What happens if you just execute a direct call at the command prompt like:

CyGlobalContext().getMap().plot(0,0).setArea(0)

If that crashes, are you sure that the existing function processAfrica is really ever executed?

In your example, what is "con"? It isn't defined in the code you show and since you aren't using "self.con" it can't be a class reference either. If it really isn't defined you should get a python exception when entering the function, not a CTD.
 
.con is because of:
Code:
import Consts as con
Thats all.
 
A good question is what is the value of con.iEurope? If it was -1, or some other invalid value, then a CTD is not all that surprising.
 
This is how I get con.iEurope:
Code:
iEurope = gc.getMap().plot(55, 50).getArea()
I'm sure 55, 50 is a tile that belongs to Europe.

And the thing is, I'm quite sure setArea works, because processAfrica definitely gets called without problems.
 
Try inserting debug print messages in the loops to see where the code fails.
 
It's called in the onBeginGameTurn method of the event handler (and then only in the first turn of the game).

Anyway, after some testing I found out that the convertMiddleEast method runs without problem; every tile I want to convert gets converted. The game only crashes the following turn, when the game wants to execute a scripted city founding in the affected area. More specifically:

Code:
def foundCity(self, iCiv, lCity, name, iTurn, iPopulation, iUnitType, iNumUnits):
                if ((iTurn == getTurnForYear(lCity[2]) + lCity[3]) and (lCity[3]<10)): # conversion from years - edead
                        #print self.checkRegion(tUr)
                        bResult, lCity[3] = self.checkRegion(lCity)
			print ("bResult: "+repr(bResult))
                        if (bResult == True):
                                pCiv = gc.getPlayer(iCiv)
				print ("Attempting to found city "+name)
				[COLOR="Red"]# the code gets to this point, then crashes[/COLOR]
                                pCiv.found(lCity[0], lCity[1])
				[COLOR="Red"]# this point is not reached anymore[/COLOR]
				print "City founded"
                                self.getCity((lCity[0], lCity[1])).setName(name, False)
				print "Name set"
                                if (iPopulation != 1):
                                        self.getCity((lCity[0], lCity[1])).setPopulation(iPopulation)
					print "Population set"
                                if (iNumUnits > 0):
                                        self.makeUnit(iUnitType, iCiv, (lCity[0], lCity[1]), iNumUnits, 0)
					print "Units created"
                                return True
                        if (bResult == False) and (lCity[3] == -1):
                                return False

Thanks for your help so far!
 
Weird, indeed. All I can think of is that the lCity list contains invalid values in first and second place. Try adding a:
Code:
print("lCity: ", lCity)
Chances are that there is a None/-1 value in there somewhere.

If everything else fails, try the alternate CyPlayer.initCity() method.
Spoiler :
CyCity initCity (INT x, INT y)
initCity( plotX, plotY ) - spawns a city at x,y
 
...so...if you postpone the area exchange, then it will not crash?
In the beginning, I called my method later and also got a CTD some turns later (scripted cities get founded all through the course of the mod, at least through the period where my changed continents would matter). I don't know if the city founding is the only malfunctioning method though.

Weird, indeed. All I can think of is that the lCity list contains invalid values in first and second place. Try adding a:
Code:
print("lCity: ", lCity)
Chances are that there is a None/-1 value in there somewhere.

If everything else fails, try the alternate CyPlayer.initCity() method.
Spoiler :
CyCity initCity (INT x, INT y)
initCity( plotX, plotY ) - spawns a city at x,y
Thanks, I tried initCity but got the same problem (not surprising because PyPlayer.found() actually calls PyPlayer.initCity()).

The values in lCity must be right as well, because the error occurs right when the first city is to be founded (and that's Yerushalayim, which works without problems without my previous continent change). The log confirms this:
Python debug log said:
Attempting to found city Yerushalayim with [73, 38, -3000, 0]

If nobody has an idea on this question, can somebody tell me how I can set the "area" of specific tiles prior to launching the game? That is, in the scenario files? Rhye must have found some way to do this, because Europe, Asia and Africa are one continuous landmass there and despite Civ's usual rules still count as different continents. Yet I can't find any mentioning of that in say, the mod's worldbuilder files.
 
Double, no triple, check the city coordinates again. It might not be a valid tile to begin with (like Peak/Water). Something illegal like that could cause a crash.
 
Probably he has set the areas in onGameStart.
Thanks, but I just found his continent definition in CvMap::calculateAreas() :)

Will try to work from there now, maybe doing what I want with the SDK somehow solves the problem.

Double, no triple, check the city coordinates again. It might not be a valid tile to begin with (like Peak/Water). Something illegal like that could cause a crash.
Only for you I even placed a marker on the plains tile where Jerusalem is supposed to be on the map and it had the coordinates 73, 38. :D

By the way, we rarely see you in the RFC subfora these days. Don't you modmod it anymore?
 
By the way, we rarely see you in the RFC subfora these days. Don't you modmod it anymore?
Eh, check my title. :D It doesn't say "mod-modder" anymore, does it? :lol:

The truth be told, I was doing the PyScenario thing for a long time and volunteered to write some Python for someone in another context (other than RFC, that is). It quickly evolved into several projects, some of which are still on-going.

I'll return to RFC mod-modding and this sub-forum soon enough. Right now I'm just having too much fun with doing these custom projects on the main modding forum. And I'm learning a lot that I probably wouldn't have otherwise.

The Lena avatar is the same, however. :p
 
Double, no triple, check the city coordinates again. It might not be a valid tile to begin with (like Peak/Water). Something illegal like that could cause a crash.

That's not illegal enough, that works ;).


If the city does not get immediatly destroyed in the same players round in which it is founded, i haven't experienced any crashes with cities, can't really see what causes this here :/.
 
I currently suspect one arcane side effect of the deeper effects of setArea(). The internally called processArea() method is quite large and sets a lot of lists and variables, many of which I think also influence AI behaviour ... maybe my actions changed something there to something completely unexpected.

However, I've currently simply disabled all of Rhye's artificial continent barriers, which serves my current purposes well enough ;)

Thanks again, maybe I return with my questions once I start with something more sophisticated again :)
 
Top Bottom