Python help

ripple01

Emperor
Joined
Mar 7, 2006
Messages
1,254
Location
New York City
I am borrowing some code from tsentom1 that is attached to a wonder that provides a 2% interest on all your gold reserves. I am attempting to attach this to a trait. My python is below, it doesn't throw any errors but it just doesn't seem to work. Any help would be appreciated.

Cheers,
ripple01

Code:
	def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
		iGameTurn, iPlayer = argsList

## Financial Trait Start ##

		pPlayer = gc.getPlayer(iPlayer)

		iTrait = CvUtil.findInfoTypeNum(gc.getTraitInfo,gc.getNumTraitInfos(),'TRAIT_FINANCIAL')

		if (pPlayer.hasTrait(iTrait)):
			iGold = pPlayer.getGold( )
			if pPlayer.getGold( ) >= 2500:
				pPlayer.changeGold( 50 )
			if pPlayer.getGold( ) < 2500:
				if pPlayer.getGold( ) >= 50:
					pPlayer.changeGold( iGold//50 )
				else:
					pPlayer.changeGold( 1 )
					
## Financial Trait End ##
 
What doesn't work? Does it have no effect?

And what's tsentom's code, what's yours?


I would change
PHP:
if (pPlayer.hasTrait(iTrait)):

to

PHP:
if (pPlayer.hasTrait(iTrait)==True):

because i'm not sure, if the first if-clause works without the value and takes true as default-value.
 
That code looks okay. The_J asked the important questions. "Doesn't work" simply "doesn't help". ;) Help us help you.

I would suggest putting in some pyPrint() calls so you can verify that your code is being called. I prefer gc.getInfoTypeForString() to using findInfoTypeNum() since it goes right to the SDK. You might want to check that it finds the trait (!= -1). Also, you don't need to test for < 2500 -- just use an else b/c if it's not >= 2500 it must be < 2500. Other than that, a minor suggestion would be to use iGold throughout your function since you already looked it up.

Code:
	def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
		iGameTurn, iPlayer = argsList

## Financial Trait Start ##

		[B]def alert(msg):
			CyInterface().addImmediateMessage(msg, "")[/B]

		pPlayer = gc.getPlayer(iPlayer)

		iTrait = [B]gc.getInfoTypeForString('TRAIT_FINANCIAL')[/B]
		[B]if iTrade == -1:
			alert("no financial trait!")
			return[/B]

		if (pPlayer.hasTrait(iTrait)):
			iGold = pPlayer.getGold( )
			if [B]iGold[/B] >= 2500:
				[B]alert("spend some of that cash bro!")[/B]
				pPlayer.changeGold( 50 )
			elif [B]iGold[/B] >= 50:
				[B]alert("pass Go and collect %d!" % (iGold // 50))[/B]
				pPlayer.changeGold( iGold // 50 )
			else:
				[B]alert("go buy a taco!")[/B]
				pPlayer.changeGold( 1 )
					
			[B]alert("gold before: %d, after: %d" % (iGold, pPlayer.getGold()))[/B]
		
## Financial Trait End ##

if (pPlayer.hasTrait(iTrait)==True):

This isn't necessary in general. Here you are forcing the expression on the left to be True instead of 1 or a non-empty string. Given that hasTrait() returns a boolean value (True or False), it has no effect and just makes it harder to read IMHO.
 
Thanks for the responses.

What I meant by not working is that the effect that should be happening (interest being added to the player's gold amount every turn) is not happening. I am seeing no error messages and nothing in the Python logs. It just doesn't fire.

I have another working Python event for this trait (Financial) that gives the civ a cache of gold at the start of the game. So I'm playing Financial and I start the game with 250 gold. I should be collecting interest of 5 gold right from my first turn. My gold counter does not increase and I'm not receiving any of the log messages I took from EF's post above. It's really odd that it is not firing at all.

Cheers,
ripple01
 
How are you adding these events to the game? Are you using CvCustomEventManager or something else? If yes, did you add your event handling function to it for the correct event?
 
You CvEventManager.py works for me :dunno:.
 

Attachments

  • ripple-financial.JPG
    ripple-financial.JPG
    122.1 KB · Views: 69
Very strange, I tried it again and I get nothing. The first event fires as expected and grants the bonus gold to financial civs but the second one doesn't. That is very strange that it would work on your end and not mine.

Edit: OK, I did some testing. I was using this in RevolutionsDCM 2.0 pure with only this one file and for some reason the event does not fire. If I take the CvEventManager file and set up a mod with that file being the only thing in it it works fine. There must be something in RevolutionsDCM 2.0 that is causing this not to fire. I'll report it to glider1...

Thanks all for your help.

Cheers,
ripple01
 
Look in EntryPoints/CvEventsInterface.py to see which event manager RevDCM is using. I bet that it's using CvCustomEventManager which extends CvEventManager. However, perhaps it is overriding the event you're trying to use. Instead, you should create your own event manager with only your events and add it to CvCustomEventManager.
 
OK, I really appreciate all the help. EF, I'm reading the docs to add events to BUG (as RevDCM uses BUG) and I'm running into one more small problem. I created a config XML (CivFusion.xml) that reads as follows:

Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
	CivFusion
	by ripple01
	Copyright (c) 2008 The BUG Mod.
-->
<mod id="CivFusion" 
	 name="CivFusion" 
	 author="ripple01" 
	 version="0.75" 
	 date="5/25/2009"
	 url="http://forums.civfanatics.com/showthread.php?t=287856">
	 
	<event type="GameStart" module="CivFusion" function="onGameStart"/>
	<event type="BeginTurn" module="CivFusion" function="onBeginTurn"/>
</mod>

I then created a CivFusion.py file and put it in the Python subfolder. The CivFusion.py includes the following:

Code:
####################################
### CivFusion by ripple01 ###
####################################


class CivFusion:

	def onGameStart(self, argsList):
		'Called at the start of the game'

## Financial Trait Start ##

		for iPlayerLoop in range(gc.getMAX_CIV_PLAYERS()):

			pPlayer = gc.getPlayer(iPlayerLoop)

			if (pPlayer.isAlive()):

				iTrait = CvUtil.findInfoTypeNum(gc.getTraitInfo,gc.getNumTraitInfos(),'TRAIT_FINANCIAL')
			
				if (pPlayer.hasTrait(iTrait)==True):  

					estiEnd = CyGame().getEstimateEndTurn()
					if ( estiEnd >= 1000 ):
						pPlayer.setGold(300)
					elif ( estiEnd >= 700 ):
						pPlayer.setGold(250)
					elif ( estiEnd >= 500 ):
						pPlayer.setGold(200)
					elif ( estiEnd >= 300 ):
						pPlayer.setGold(150)

## Financial Trait End ##


	def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
		iGameTurn, iPlayer = argsList


## Financial Trait Start ##

		def alert(msg):
			CyInterface().addImmediateMessage(msg, "")

		pPlayer = gc.getPlayer(iPlayer)

		iTrait = gc.getInfoTypeForString('TRAIT_FINANCIAL')
		if iTrait == -1:
			alert("no financial trait!")
			return

		if (pPlayer.hasTrait(iTrait)):
			iGold = pPlayer.getGold( )
			if iGold >= 2500:
				alert("spend some of that cash bro!")
				pPlayer.changeGold( 50 )
			elif iGold >= 50:
				alert("pass Go and collect %d!" % (iGold // 50))
				pPlayer.changeGold( iGold // 50 )
			else:
				alert("go buy a taco!")
				pPlayer.changeGold( 1 )
					
			alert("gold before: %d, after: %d" % (iGold, pPlayer.getGold()))
		
## Financial Trait End ##

Now I get an error in my Python logs that reads: ConfigError: Module 'CivFusion' must define function or class 'onGameStart'. Does "Module" mean my mod's Python file where all this stuff is defined? I think I am close to having this right, just missing one small piece somewhere. Or maybe I am way off target...

Cheers,
ripple01
 
I should make this more clear in the docs. If you use the <event> elements in the XML, they refer to a module-level function. You should be able to remove the "class CivFusion:" line and outdent the functions below it and be good to go.

Code:
####################################
### CivFusion by ripple01 ###
####################################

[s]class CivFusion:[/s]

[B]# remove one indentation level of all following lines...[/B]

def onGameStart(self, argsList):
	'Called at the start of the game'

## Financial Trait Start ##

	for iPlayerLoop in range(gc.getMAX_CIV_PLAYERS()):
		...

If you want to stick with the functions being in a class (for example you have an old module designed to work with CvCustomEventManager), you use the <events> element to point to the class. Then the __init__ function of that class must call addEventHandler(...) for each event.
 
OK, getting closer, the CivFusion.py is parsing correctly, but something is throwing up a Python error in another file.

Traceback (most recent call last):

File "CvEventInterface", line 30, in onEvent

File "BugEventManager", line 318, in handleEvent

File "BugEventManager", line 323, in _dispatchEvent

File "BugEventManager", line 335, in _handleDefaultEvent

File "BugUtil", line 479, in __call__

File "BugUtil", line 476, in call

TypeError: onGameStart() takes exactly 2 arguments (1 given)
ERR: Python function onEvent failed, module CvEventInterface

Any suggestions? EF, should I take this to the BUG Forum?

Cheers,
ripple01
 
My bad, you also need to remove the first "self" parameter from all those event-handling functions because they are no longer part of a class:

Code:
def onGameStart([s][B]self,[/B][/s] argsList):
 
Thanks, EF, that was the cause of the issue. Now I get no python errors and the event seems to be properly hooked into the BUG Event Manager.

The first part of my event for Financial civs (adding a cache of gold to start the game with) is now working exactly as intended. But the second part (add interest to the treasury each turn) is still refusing to work and I'm pulling my hair out trying to figure out why. I'm not receiving any error messages or anything in the logs, it just doesn't fire. I appreciate your help and I'm sorry this has seemed to flare into a bigger issue than I thought it was at first.

Here's the revised text of my CivFusion.py file:

Code:
####################################
### CivFusion by ripple01 ###
####################################

from CvPythonExtensions import *
import CvUtil
gc = CyGlobalContext()

def onGameStart(argsList):
	'Called at the start of the game'

## Financial Trait Start ##

	for iPlayerLoop in range(gc.getMAX_CIV_PLAYERS()):

			pPlayer = gc.getPlayer(iPlayerLoop)

			if (pPlayer.isAlive()):

				iTrait = CvUtil.findInfoTypeNum(gc.getTraitInfo,gc.getNumTraitInfos(),'TRAIT_FINANCIAL')
			
				if (pPlayer.hasTrait(iTrait)==True):  

					estiEnd = CyGame().getEstimateEndTurn()
					if ( estiEnd >= 1000 ):
						pPlayer.setGold(300)
					elif ( estiEnd >= 700 ):
						pPlayer.setGold(250)
					elif ( estiEnd >= 500 ):
						pPlayer.setGold(200)
					elif ( estiEnd >= 300 ):
						pPlayer.setGold(150)

## Financial Trait End ##

def onBeginPlayerTurn(argsList):
	'Called at the beginning of a players turn'
	iGameTurn, iPlayer = argsList

## Financial Trait Start ##

	def alert(msg):
		CyInterface().addImmediateMessage(msg, "")

		pPlayer = gc.getPlayer(iPlayer)

		iTrait = gc.getInfoTypeForString('TRAIT_FINANCIAL')
		if iTrait == -1:
			alert("no financial trait!")
			return

		if (pPlayer.hasTrait(iTrait)):
			iGold = pPlayer.getGold( )
			if iGold >= 2500:
				alert("spend some of that cash bro!")
				pPlayer.changeGold( 50 )
			elif iGold >= 50:
				alert("pass Go and collect %d!" % (iGold // 50))
				pPlayer.changeGold( iGold // 50 )
			else:
				alert("go buy a taco!")
				pPlayer.changeGold( 1 )
					
			alert("gold before: %d, after: %d" % (iGold, pPlayer.getGold()))
		
## Financial Trait End ##

Cheers,
ripple01
 
You need to insert "Player" into the event type and handler in your XML:

Code:
<event type="Begin[B]Player[/B]Turn" module="CivFusion" function="onBegin[B]Player[/B]Turn"/>

The type must match exactly what's in CvEventManager, including case, and the function must do the same to the function in CvFusion.py.
 
I've done this, and it is still not firing. I tested it under a clean BUG 3.6 install to ensure that it wasn't something in RevDCM that was causing it to not work. I've posted my CivFusion.py and CivFusion.xml files. Could someone test them to see if you can reproduce? Again, the code works in vanilla (in CvEventManager.py) and I'm 99.9% I have everything set up correctly to fire the events from BUG.

Cheers,
ripple01
 

Attachments

You have embedded all of the onBeginPlayerTurn code under the alert() function. You can pull alert() outside of the function if that makes it easier to understand for you:

Code:
def alert(msg):
	CyInterface().addImmediateMessage(msg, "")

def onBeginPlayerTurn(argsList):
	'Called at the beginning of a players turn'
	iGameTurn, iPlayer = argsList

## Financial Trait Start ##

	pPlayer = gc.getPlayer(iPlayer)
	iTrait = gc.getInfoTypeForString('TRAIT_FINANCIAL')
	if iTrait == -1:
		alert("no financial trait!")
		return

	if (pPlayer.hasTrait(iTrait)):
		iGold = pPlayer.getGold( )
		if iGold >= 2500:
			alert("spend some of that cash bro!")
			pPlayer.changeGold( 50 )
		elif iGold >= 50:
			alert("pass Go and collect %d!" % (iGold // 50))
			pPlayer.changeGold( iGold // 50 )
		else:
			alert("go buy a taco!")
			pPlayer.changeGold( 1 )
				
		alert("gold before: %d, after: %d" % (iGold, pPlayer.getGold()))
		
## Financial Trait End ##

So the event was firing, onBeginPlayerTurn() was being called, but the only code in the actual function that did anything was the first line. Everything below alert() was part of the alert() embedded function, so it was never called.
 
Back
Top Bottom