Problem with Spy mission

Monaldinio

Prince
Joined
Jan 6, 2010
Messages
415
Location
Rostock
...with espionage missions you can destroy buildings!

I have a problem that there is a building that creates a bunker unit. When this building is destroyed by a spy mission , the bunker remains, but that should not be.
Is there a way to regulate the problem via Python or must that be made directly in the SDK?

I have no approach for the Python code.:confused:
 
The only way you can check via python is to check the city every turn whether the building exists.
 
The only way you can check via python is to check the city every turn whether the building exists.

I do that, but a quari is missing i think.


Code:
def onCityDoTurn(self, argsList):
		'City Production'
		self.parent.onCityDoTurn(self, argsList)
		pCity = argsList[0]
		iPlayer = argsList[1]

		pPlot = pCity.plot()
		

#####Bunker entsteht beginnt#####	
		
		if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")) > 0:   
			bSetBunker = True  
			pCityPlot = gc.getMap().plot(pCity.getX(), pCity.getY())   
			for iUnit in range (pCityPlot.getNumUnits()):  
				if pCityPlot.getUnit(iUnit).getUnitType() == gc.getInfoTypeForString("UNIT_BUNKER") or pCityPlot.getUnit(iUnit).getUnitType() == gc.getInfoTypeForString("UNIT_BUNKER2") or pCityPlot.getUnit(iUnit).getUnitType() == gc.getInfoTypeForString("UNIT_BUNKER3"):  
  
					bSetBunker = False  
					break  

			if bSetBunker:   
				pPlayer.initUnit(gc.getInfoTypeForString("UNIT_BUNKER"), pCity.getX(), pCity.getY(), UnitAITypes.UNITAI_IMMOBILE, DirectionTypes.DIRECTION_NORTH)
 
Your if statement looks incorrect to me ... though it may work the way it is.

You should check whether each building count is > 0 rather than just the last one so:

PHP:
if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")) > 0:

should be

PHP:
if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")) > 0:
 
1) pPlot is already defined, no need for pCityPlot

2) I see Bunker1, 2 and 3: wouldn't it be simpler to check the UnitClassType instead? Similarly, are these buildings all of the same class?

3) I would first check if the unit is there (if no, no need to check the buildings)

4) I thought you wanted to kill the unit, not to init a new one

5) this is a hit to the performance if you check every city every turn. Probably it's better to check in the dll when a spy destroys a building of that type, then kill bunker too.

Edit: oh yeah, I forgot! As Archid said!
 
1) pPlot is already defined, no need for pCityPlot

2) I see Bunker1, 2 and 3: wouldn't it be simpler to check the UnitClassType instead? Similarly, are these buildings all of the same class?

3) I would first check if the unit is there (if no, no need to check the buildings)

4) I thought you wanted to kill the unit, not to init a new one

5) this is a hit to the performance if you check every city every turn. Probably it's better to check in the dll when a spy destroys a building of that type, then kill bunker too.

Edit: oh yeah, I forgot! As Archid said!

1.) You're right - will delate pCityPlot = gc.getMap().plot(pCity.getX(), pCity.getY()) and will change pCityPlot to pPlot

2.) You're right - I'll change it

3.) Dont know how

4) The Problem is, the Bunker Unit could be destroyed via a battle.
In this case, as long as the Building Perimeter Deffense exists, a new Bunker Unit should be created...thats the main reason why i created this Code.

5) dont know how
 
If you don't want to mess with dll and want to be efficient, use city script data to minimize looping.

1) Set SD of all new cities to 0.
2) Change it to 1 when building built and add the bunker unit.
3) On death of Bunker, set it to 2.
4) onCityDoTurn
if 0, do nothing.

if 1, check whether building still exist.
If building destroyed, set to 0, loop through and kill bunker.

if 2, check whether building still exist.
If active, set to 1, add bunker.
Else, set to 0.
 
If the relation special Building = unit Bunker is an important component of your mod, then the idea of Platyping is interesting.

Otherwise, some people could help you with the dll, if you ask it nicely to Archid for example... :D
 
If you don't want to mess with dll and want to be efficient, use city script data to minimize looping.

1) Set SD of all new cities to 0.
2) Change it to 1 when building built and add the bunker unit.
3) On death of Bunker, set it to 2.
4) onCityDoTurn
if 0, do nothing.

if 1, check whether building still exist.
If building destroyed, set to 0, loop through and kill bunker.

if 2, check whether building still exist.
If active, set to 1, add bunker.
Else, set to 0.

:cry::crazyeye::eek:

Thats too much for me. That is beyond my abilities . Programming is hard work for me .
 
The brute force method is what you have been doing.

onCityDoTurn
Check if building active
Yes: loop through to see if there is a bunker. Add new bunker if necessary.
No: loop through and kill any existing bunkers.

This method however, requires looping through all units in all cities regardless of yes or no. It works but is definitely not efficient.

The alternative is to make it espionage immune like Academies or National Wonders and get rid of the problem.
 
espionage immune? Which tag is that?

Monaldinio, if I understand you correctly, the code shown in post #3 with two checks per city turn is already in place. In that case, I would simply use two booleans instead of one.

Check building: yes, bBuilding = True
Check unit: yes, bUnit = True

if bBuilding and not bUnit: init the unit (if Eskimo civilization: init the inuit unit)

elif bUnit and not bBuilding: kill the unit

Both booleans should be declared False before the two checks and the two checks should be on the same level of indentation.
 
Right, it's dll coded.

Spoiler :
Code:
bool CvPlayer::canSpyDestroyBuilding(PlayerTypes eTarget, BuildingTypes eBuilding) const
{
	CvBuildingInfo& kBuilding = GC.getBuildingInfo(eBuilding);
	if (kBuilding.getProductionCost() <= 0)
	{
		return false;
	}
	
	if (::isLimitedWonderClass((BuildingClassTypes)kBuilding.getBuildingClassType()))
	{
		return false;
	}

	return true;
}

Edit: by the way, if you ever want to forbid the destruction of these buildings by spies, this is a good place to code it.
 
espionage immune? Which tag is that?

Monaldinio, if I understand you correctly, the code shown in post #3 with two checks per city turn is already in place.

Thats right, the code is in place. (and works so far, without the Spychanges)


In that case, I would simply use two booleans instead of one.

Check building: yes, bBuilding = True
Check unit: yes, bUnit = True

if bBuilding and not bUnit: init the unit (if Eskimo civilization: init the inuit unit)

elif bUnit and not bBuilding: kill the unit

Both booleans should be declared False before the two checks and the two checks should be on the same level of indentation.


Sorry, but im not so talented as as you guys.
 
OK, I had to rearrange the code anyway. Here it is:
Code:
def onCityDoTurn(self, argsList):
		'City Production'
		self.parent.onCityDoTurn(self, argsList)
		pCity = argsList[0]
		iPlayer = argsList[1]

		pPlot = pCity.plot()

#####Bunker entsteht beginnt#####
		pPlayer = gc.getPlayer(iPlayer)
		bBuilding = False
		bUnit = False
		
		if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")):   
			bBuilding = True  
		
		for iUnit in xrange (pPlot.getNumUnits()):
			pLoopUnit = pPlot.getUnit(iUnit)
			if pLoopUnit.getUnitClassType() == gc.getInfoTypeForString("UNITCLASS_BUNKER"):
				bUnit = True
				if not bBuilding:
					pLoopUnit.kill(False, iPlayer)
					bUnit = False
		
		if bBuilding and not bUnit:
			pPlayer.initUnit(gc.getInfoTypeForString("UNIT_BUNKER"), pCity.getX(), pCity.getY(), UnitAITypes.UNITAI_IMMOBILE, DirectionTypes.DIRECTION_NORTH)

Hope this works for you. :)

Note: turns out that "getNumActiveBuilding()" is a boolean, so that part of your code was working correctly.
 
That's what the API says:
Code:
INT getNumActiveBuilding (BuildingType iIndex)
bool (BuildingID) - is BuildingID active in the city (present & not obsolete)?

Is it wrong? It is confusing.

I did not test it.

Edit: checked in the dll - yes, it's an integer. Of course, usually you only get 1 building of a type in a city. So it's a sort of boolean... :mischief:

Monaldinio, change the line like this then to be sure:
Code:
if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")) > 0:
 
The API is written by humans, so it is not suprising to have mistakes. In fact, there is a list of mistakes in the stickied thread.

In python, anything can be used in a boolean comparison, so there is nothing wrong in writing
Code:
if 12345:
for instance. Hence, the previous code will work.
 
Good to know, thank you! :)

By the way, I checked the stickied thread before writing. Perhaps you could add this one. It's just a precision as it's only the second line that is wrong (I think that we can change the number of building instance per city in the GlobalDefines, I suppose that's what CITY_MAX_NUM_BUILDINGS stands for - this would technically not make it a boolean any more!).
 
Oh thank you guys for your help!

But after i destroyed the buidling Perimeter Defense via spy mission...the Bunker Unit (wich was created by finisching the perimter Defense) still exists.
Thats the code:


Code:
pPlayer = gc.getPlayer(iPlayer) 
bBuilding = False
		bUnit = False
		
		if pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PERIMETER_DEFENSE")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_PIRATES_SEAWALL")) > 0 or pCity.getNumActiveBuilding(gc.getInfoTypeForString("BUILDING_WELLEN_SEAWALL")) > 0:   
			bBuilding = True  
		
		for iUnit in xrange (pPlot.getNumUnits()):
			pLoopUnit = pPlot.getUnit(iUnit)
			if pLoopUnit.getUnitClassType() == gc.getInfoTypeForString("UNITCLASS_BUNKER"):
				bUnit = True
				if not bBuilding:
					pLoopUnit.kill(False, iPlayer)
					bUnit = False
		
		if bBuilding and not bUnit:
			pPlayer.initUnit(gc.getInfoTypeForString("UNIT_BUNKER"), pCity.getX(), pCity.getY(), UnitAITypes.UNITAI_IMMOBILE, DirectionTypes.DIRECTION_NORTH)





Just one last question

Code:
def onBuildingBuilt(self, argsList):
		'Building Completed'
		self.parent.onBuildingBuilt(self, argsList)
		pCity, iBuildingType = argsList
		game = gc.getGame()
		
		player = pCity.getOwner()
		pPlayer = gc.getPlayer(player)
		pPlot = pCity.plot()

	


#####Abfrage ob nach fertigstellung des Perimeters schon Bunker vorhanden, wenn vorhanden, keinen weiteren Bunker setzen#####			
			
[COLOR="Red"]		if iBuildingType == gc.getInfoTypeForString('BUILDING_PERIMETER_DEFENSE') or iBuildingType == gc.getInfoTypeForString('BUILDING_PIRATES_SEAWALL') or iBuildingType == gc.getInfoTypeForString('BUILDING_WELLEN_SEAWALL'):[/COLOR]
			iBunker = gc.getInfoTypeForString('UNIT_BUNKER')
			bBunker = false
			for iUnit in range (pPlot.getNumUnits()):    #####Achte hier daruaf, dass pCityPlot korrekt definiert wird!!!#####
				iUnitType = pPlot.getUnit(iUnit).getUnitType()
				if (iUnitType == iBunker):
					bBunker = true
					break
			if (bBunker == false):
				pUnit = pPlayer.initUnit( iBunker , pCity.getX(), pCity.getY(), UnitAITypes.UNITAI_IMMOBILE, DirectionTypes.DIRECTION_NORTH)
#####Abfrage ENDE#####

Is it better to change the red line into...

Code:
def onBuildingBuilt(self, argsList):
		'Building Completed'
		self.parent.onBuildingBuilt(self, argsList)
		pCity, iBuildingType = argsList
		game = gc.getGame()
		
		player = pCity.getOwner()
		pPlayer = gc.getPlayer(player)
		pPlot = pCity.plot()


#####Abfrage ob nach Fertigstellung des Perimeters schon Bunker vorhanden, wenn vorhanden, keinen weiteren Bunker setzen#####			
			
		[COLOR="Red"]if iBuildingType == gc.getInfoTypeForString('BUILDING_PERIMETER_DEFENSE') > 0 or iBuildingType == gc.getInfoTypeForString('BUILDING_PIRATES_SEAWALL') > 0 or iBuildingType == gc.getInfoTypeForString('BUILDING_WELLEN_SEAWALL') > 0:
[/COLOR]

Can i do so or is it none sense?
 
Top Bottom