Can anybody tell me how to fix this error?

Caesium

Radiant!
Joined
Jan 14, 2006
Messages
526
I tried to add a menu the Inquisiton Mod, in which you can choose, what religion should be removed from the city.
Can you tell me, where my mistake is?
Here are my files and the error message it puts on the screen:

CvCustomEventManager: (destilled to the main points)
PHP:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005
## 
## This class is passed an argsList from CvAppInterface.onEvent
## The argsList can contain anything from mouse location to key info
## The EVENTLIST that are being notified can be found 

######################################################################################################
# import
######################################################################################################

from CvPythonExtensions import *
import CvUtil
import CvScreensInterface
import CvDebugTools
import CvWBPopups
import PyHelpers
import Popup as PyPopup
import CvCameraControls
import CvTopCivs
import CvEventManager
import sys
import Inquisition

######################################################################################################
# renaming
######################################################################################################
	
gc = CyGlobalContext()
localText = CyTranslator()
PyPlayer = PyHelpers.PyPlayer
PyInfo = PyHelpers.PyInfo

inq = Inquisition.Inquisition()

######################################################################################################
# Custom Eventmanager
######################################################################################################

class CvCustomEventManager(CvEventManager.CvEventManager):
	def __init__(self):
		# initialize base class
		self.parent = CvEventManager.CvEventManager
		self.parent.__init__(self)
		self.Events[7504] = ('', self.__eventInquisitionApply , self.__eventInquisitionBegin)
		self.__LOG_TECH = 0
	
	def onBuildingBuilt(self, argsList):
		self.parent.onBuildingBuilt(self, argsList)
		inq.onBuildingBuilt(argsList)
		
	def __eventInquisitionBegin(self, argsList): #7504
		inq.eventInquisitionBegin(argsList)
		
	def __eventInquisitionApply(self, playerID, userData, popupReturn):
		inq.eventInquisitionApply(playerID, userData, popupReturn)

Inqusitor.py
PHP:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005

from CvPythonExtensions import *
import CvUtil
import Popup as PyPopup
import CvCustomEventManager

# globals
gc = CyGlobalContext()
localText = CyTranslator()

class Inquisition:

	def __init__(self):
		self.list = []

	def onBuildingBuilt(self, argsList):  
		pCity, iBuildingType = argsList      	
		iInquisition = gc.getInfoTypeForString('BUILDING_INQUISITION')
		if iBuildingType == iInquisition:
			self.list = []
			iPlot = pCity.plot()
			iPlayer = iPlot.calculateCulturalOwner()
			StateBelief = gc.getPlayer(pCity.getOwner()).getStateReligion()
			for i in range(gc.getNumReligionInfos()):
				if not StateBelief == i:
					if pCity.isHasReligion(i):
						if not pCity.isHolyCityByType(i):
							self.list.append(i)
			if((len(self.list) > 0) and (gc.getPlayer(iPlayer).isHuman() == 1)):
				CvCustomEventManager.CvCustomEventManager().beginEvent(7504)
			
			elif((len(self.list) > 0) and (gc.getPlayer(iPlayer).isHuman() == 0)):
				iTarget = self.list[CyRandom().get(len(self.list), "Get a random number for religion removal.")]
				pCity.setHasReligion(iTarget, False, True, True)
				for i in range(gc.getNumBuildingInfos()):
					if gc.getBuildingInfo(i).getPrereqReligion() == iTarget:
						pCity.setHasRealBuilding(i, False)
				self.list = []
			pCity.setHasRealBuilding(iInquisition, False)

	def eventInquisitionBegin(self, argsList):
		message='The Inquisition strikes again!'
		popup = PyPopup.PyPopup(7504, EventContextTypes.EVENTCONTEXT_ALL)
		popup.setHeaderString(localText.getText("TXT_KEY_INQUISITION_CHOICE", ()))
	    	popup.createPullDown()
		for i in range(0 , len(self.list)):
		     	popup.addPullDownString(gc.getReligionInfo(self.list[i]).getDescription() , i)
		popup.addButton("OK")
		popup.launch(False, PopupStates.POPUPSTATE_IMMEDIATE)
		CyInterface().addMessage(CyGame().getActivePlayer(),True,25,message,'AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/Actions/Pillage.dds',ColorTypes(7),pCity.getX(),pCity.getY(),True,True)
		return
		
	def eventInquisitionApply(self, argsList, userData, popupReturn):
		pCity, iBuildingType = argsList
		if (popupReturn.getButtonClicked() == 0):
			iTarget = self.list[popupReturn.getSelectedPullDownValue(0)]
			pCity.setHasReligion(iTarget, False, True, True)
			self.list = []
		return

Traceback (most recent call last):

File "CvEventInterface", line 23, in onEvent

File "CvEventManager", line 164, in handleEvent

File "CvCustomEventManager", line 104, in onBuildingBuilt

File "Inquisition", line 32, in onBuildingBuilt

File "CvEventManager", line 171, in beginEvent

File "CvCustomEventManager", line 263, in __eventInquisitionBegin

File "Inquisition", line 52, in eventInquisitionBegin

NameError: global name 'pCity' is not defined
ERR: Python function onEvent failed, module CvEventInterface
Traceback (most recent call last):

File "CvEventInterface", line 27, in applyEvent

File "CvEventManager", line 184, in applyEvent

File "CvCustomEventManager", line 266, in __eventInquisitionApply

File "Inquisition", line 56, in eventInquisitionApply

TypeError: unpack non-sequence
ERR: Python function applyEvent failed, module CvEventInterface
 
def __eventInquisitionApply(self, playerID, userData, popupReturn):
inq.eventInquisitionApply(playerID, userData, popupReturn)

def eventInquisitionApply(self, argsList, userData, popupReturn):
pCity, iBuildingType = argsList

so ... the first thing you try to do, it appears, is to unpack playerID (as argsList) into pCity and IBuildingType ...

Follow up: a helpful tip when you get an unpack error:
def eventInquisitionApply(self, argsList, userData, popupReturn):
print "eventInquisitionApply argsList", argsList
pCity, iBuildingType = argsList

Then turn on python logging and look in your error logs (or use hapdebugger).
 
surt said:
def __eventInquisitionApply(self, playerID, userData, popupReturn):
inq.eventInquisitionApply(playerID, userData, popupReturn)

def eventInquisitionApply(self, argsList, userData, popupReturn):
pCity, iBuildingType = argsList

so ... the first thing you try to do, it appears, is to unpack playerID (as argsList) into pCity and IBuildingType ...

Follow up: a helpful tip when you get an unpack error:
def eventInquisitionApply(self, argsList, userData, popupReturn):
print "eventInquisitionApply argsList", argsList
pCity, iBuildingType = argsList

Then turn on python logging and look in your error logs (or use hapdebugger).
Could you describe this in more words. I can't understand your explanation.
 
Caesium said:
Could you describe this in more words. I can't understand your explanation.

Sure.

inq.eventInquisitionApply(playerID, userData, popupReturn)

appears to call the eventInquisitionApply method with 3 arguments: the playerid, the userdata, and the popupreturn value.

But the definition of that method appears to expect different arguments:
def eventInquisitionApply(self, argsList, userData, popupReturn):
so you have: the standard self (which you get on every object method in python), argsList, userData, and popupreturn value.

The problem, it seems to me is that you're passing playerid in the slot where you are expecting argsList. I would have expected one of the following, either the call looks like this:

inq.eventInquisitionApply([pCity, iBuildingType], userData, popupReturn)
(note that [pCity, iBuildingType] would pack those two values together into one method argument)

or the method definition could look like this:

def eventInquisitionApply(self, playerId, userData, popupReturn):

In any case, where you are currently getting into trouble is this:

pCity, iBuildingType = argsList

which attempts to take two values 'out' of argsList (by unpacking them, hence your unpack error), which therefore requires that argsList actually contain two values. However, I'm pretty sure that argsList does not contain two things, but instead just contains the playerId which is just one thing.

Edit: added more formatting to clarify the problem point.
One more edit: if you're fairly new to programming, let me know and I can explain the concepts involved here in more detail.
 
surt said:
if you're fairly new to programming, let me know and I can explain the concepts involved here in more detail.
No no, I started with basic, learned pascal, html and some other languages. python is very logic, but I've got problems with my english :D
Belizan said:
pCity is not defined in
def eventInquisitionBegin(self, argsList):
And how do I define it?
 
OK, I tried something completely different, I rewrote the code.
But now another error is coming up, but, in this case, the solution must be very easy, but I can't find it.
PHP:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005

from CvPythonExtensions import *
import CvUtil
import Popup as PyPopup
import CvCustomEventManager

# globals
gc = CyGlobalContext()
localText = CyTranslator()

class Inquisition:

	def __init__(self):
		self.list = []

	def onBuildingBuilt(self, argsList):  
		pCity, iBuildingType = argsList      	
		iInquisition = gc.getInfoTypeForString('BUILDING_INQUISITION')
		if iBuildingType == iInquisition:
			self.list = []
			iPlot = pCity.plot()
			iPlayer = iPlot.calculateCulturalOwner()
			StateBelief = gc.getPlayer(pCity.getOwner()).getStateReligion()
			for i in range(gc.getNumReligionInfos()):
				if not StateBelief == i:
					if pCity.isHasReligion(i):
						if not pCity.isHolyCityByType(i):
							self.list.append(i)
			if((len(self.list) > 0) and (gc.getPlayer(iPlayer).isHuman() == 1)):
				message='The Inquisition strikes again!'
				popup = PyPopup.PyPopup()
				popup.setHeaderString(localText.getText("TXT_KEY_INQUISITION_CHOICE", ()))
				popup.createPullDown()
				for i in range(0 , len(self.list)):
				     	popup.addPullDownString(gc.getReligionInfo(self.list[i]).getDescription() , i)
				popup.addButton("OK")
				popup.launch(False, PopupStates.POPUPSTATE_IMMEDIATE)
				CyInterface().addMessage(CyGame().getActivePlayer(),True,25,message,'AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/Actions/Pillage.dds',ColorTypes(7),pCity.getX(),pCity.getY(),True,True)
				if (popup.getButtonClicked() == 0):
					iTarget = self.list[popup.getSelectedPullDownValue(0)]
					pCity.setHasReligion(iTarget, False, True, True)
					self.list = []

			
			elif((len(self.list) > 0) and (gc.getPlayer(iPlayer).isHuman() == 0)):
				iTarget = self.list[CyRandom().get(len(self.list), "Get a random number for religion removal.")]
				pCity.setHasReligion(iTarget, False, True, True)
				for i in range(gc.getNumBuildingInfos()):
					if gc.getBuildingInfo(i).getPrereqReligion() == iTarget:
						pCity.setHasRealBuilding(i, False)
				self.list = []
			pCity.setHasRealBuilding(iInquisition, False)
Traceback (most recent call last):

File "CvEventInterface", line 23, in onEvent

File "CvEventManager", line 164, in handleEvent

File "CvCustomEventManager", line 104, in onBuildingBuilt

File "Inquisition", line 41, in onBuildingBuilt

AttributeError: PyPopup instance has no attribute 'getButtonClicked'
ERR: Python function onEvent failed, module CvEventInterface
 
Again, you might want to take Surt's advise. getButtonClicked() is defined on popupreturn, which you only get via a callback when you define your own callback event for a popup. Popup itself does not define getButtonClicked(), so popup.getButtonClicked() doesn't make any sense. To my knowledge there is no analog of a modal dialog in the Python layer. Popupstate_Immediates should be used for notifications only (messages to the player for which no response is required) and even then only if you are certain the player will get to respond before you have brought up another notification.
 
OK, I understand, but, how can I describe it, could you give me more than a hint, for example a corrected code?
 
given what you want to do, I'm not clear that it is possible. The problem you're having is that in your new code example:

if (popup.getButtonClicked() == 0):

isn't valid, as there is no getButtonClicked method on cypopup. I don't think they made very good provision for the creation of new interactive dialogs during gameplay. Can you think of a way to make your mod work without the interactive dialog ... or have you seen a mod that does have a similar interactive dialog? (asking because most of us, not having written up such dialogs, won't be able to come up with a work around unless someone has succeeded before).
 
surt said:
given what you want to do, I'm not clear that it is possible. The problem you're having is that in your new code example:

if (popup.getButtonClicked() == 0):

isn't valid, as there is no getButtonClicked method on cypopup. I don't think they made very good provision for the creation of new interactive dialogs during gameplay. Can you think of a way to make your mod work without the interactive dialog ... or have you seen a mod that does have a similar interactive dialog? (asking because most of us, not having written up such dialogs, won't be able to come up with a work around unless someone has succeeded before).
I reduced my idea back to a random choice.
I copied the code form the FreeTech mod, available at the apolython forum.
 
It looks like their beginEvent is responsible for creating a PyPopup which is not the same as a CyPopup (its actually a container for a CyPopup), and that the applyEvent takes a popupreturn value as one of the arguments, and that's what you can read the result of the dialog from.
 
Caesium said:
What does that mean?

Their code looks like:

def eventFreeTechApply(self, playerID, userData, popupReturn):
if (popupReturn.getButtonClicked() == 0):


your code has:
if (popup.getButtonClicked() == 0):

the problem appears to be that you're calling getButtonClicked() on a popup (CyPopup), instead of a popupReturn (CyPopupReturn). The problem, of course, is how do you actually get a popup return (other than maybe through the event process as freetech seems to be doing), and that I don't know, since the python api documentation doesn't indicate any function that takes or returns a CyPopupReturn.

http://civilization4.net/files/modding/PythonAPI_v160/
 
Back
Top Bottom