Converting TsenTom1's mods to BUG format

Joined
Jul 5, 2004
Messages
23,562
Location
Canberra, Australia
Hi, as the title says I am converting some of Tsentom1's python wonders into BUG format. Some I have had no problems with. Others are proving I have gaps in my python knowledge and one is defying reason - so I think it is a dyslexia problem.

Problem 1. to improve the speed of the python I want to keep a flag indicating that the building has been built and who it was being built by. I thought I was doing globals right but evidently not.

Code:
# globals
gi_GreatBath = gc.getInfoTypeForString( 'BUILDING_GREAT_BATH')
gi_RainWaterBasin = gc.getInfoTypeForString( "FEATURE_RAINWATER_BASIN")

gb_GreatBathBuilt = false
gb_GreatBathBuiltBy = None

###################################################
def onBuildingBuilt(argsList):
	'Building Completed'
	pCity, iBuildingType = argsList
	game = gc.getGame()

## Great Bath Start ##

	if iBuildingType == gi_GreatBath:
		BugUtil.debug("Great Bath built (onBuildingBuilt called).")

		gb_GreatBathBuilt = true

		pPlayer = gc.getPlayer(pCity.plot().getOwner())
		gb_GreatBathBuiltBy = pPlayer

The values are still false and None in the other events in the mod when run. I did look at the initialization section in the modders forum but it is blank. :(

Do I need to say global gb_GreatBathBuilt somewhere? In each of the event code functions or at the top?

Problem 2. dyslexia.

As far as I can tell I have done the Trafalgar Square code in the same way I have done all the others. But I get the error saying that the onBuildingBuilt and ocCombatResult functions are not defined. Python and Config XML files attached. I have been loking at it for a couple of days on and off o I need a fresh pair of eyes to look at it.

Thanks.
 
I think there are two key pieces of info you need to understand problem 1:
1) The Python is initialized before the XML.
2) Python statements outside of a class or function definition are run when the file is first loaded.

This means that those two lines of code that start with "gi_" are run before the XML has been loaded so the two gc.getInfoTypeForString() function calls can not return the correct values. The two "gi_" values are therefore presumably both set to -1 since it finds no translations for either of them. When onBuildingBuilt is called it is not passing -1 for the iBuildingType value (it passed the correct value, of course) so the "iBuildingType == gi_GreatBath" is never true.
 
I think there are two key pieces of info you need to understand problem 1:
1) The Python is initialized before the XML.
2) Python statements outside of a class or function definition are run when the file is first loaded.

This means that those two lines of code that start with "gi_" are run before the XML has been loaded so the two gc.getInfoTypeForString() function calls can not return the correct values. The two "gi_" values are therefore presumably both set to -1 since it finds no translations for either of them. When onBuildingBuilt is called it is not passing -1 for the iBuildingType value (it passed the correct value, of course) so the "iBuildingType == gi_GreatBath" is never true.

Ah ha, that explains why the one I did similar to this but as a class works but these don't. Except for the case where I get an error saying that one of the XML values doesn't exist! Wait a minute - why would I be getting a message saying that only the one XML variable that is not in my XML is not defined but not all?

Don't worry - I can do an onGameStart or whatever event to initilaise the global variables.

Problem 1 solved thanks.
 
GE is possibly correct here, but there's more to the story. First, I have found that some global things are available when the Python modules are first loaded, and other things are not. Instead of trying to figure out which are available and when, I added the init feature to BUG. In your configuration XML file add this line:

Code:
<init mod="NameOfAbovePythonModule"/>

Note that you should not include the ".py" extension here. Now change the above module by adding an init() function that BUG will call after it has initialized itself (happens during game load/start after the XMLs have loaded):

Code:
# globals
gi_GreatBath = None
gi_RainWaterBasin = None

gb_GreatBathBuilt = False
gb_GreatBathBuiltBy = None

def init():
	global gi_GreatBath, gi_RainWaterBasin
	gi_GreatBath = gc.getInfoTypeForString( 'BUILDING_GREAT_BATH')
	gi_RainWaterBasin = gc.getInfoTypeForString( "FEATURE_RAINWATER_BASIN")

Site note: use False and True with capital first letters for the actual Python constants. Civ4 defines false to be 0 and true to be 1 so that they work okay, but I am always wary of counting on such trickery. ;)

Now for the second problem with your code. Notice the global line in my init() function? It tells Python that those names refer to the global variables defined at the module level. If you don't use that line Python assumes you want those names to be local variables, and they disappear when the function exits. This only applies when writing to global variables; you can read from them without the global keyword, but it doesn't hurt.

You need to add a similar line to your event handler:

Code:
def onBuildingBuilt(argsList):
	'Building Completed'
	pCity, iBuildingType = argsList
	game = gc.getGame()

## Great Bath Start ##

	if iBuildingType == gi_GreatBath:
		BugUtil.debug("Great Bath built (onBuildingBuilt called).")

		[B][COLOR="Red"]global gb_GreatBathBuilt, gb_GreatBathBuiltBy[/COLOR][/B]
		gb_GreatBathBuilt = True

		pPlayer = gc.getPlayer(pCity.plot().getOwner())
		gb_GreatBathBuiltBy = pPlayer

Side note #2: CyCity has getOwner() as well, so you can use "gc.getPlayer(pCity.getOwner())" without first getting the plot the city is on.

Learn Python in 10 Minutes
 
Missed you by 2 minutes. Hopefully you see my post before you add the onGameStart event handler.
 
Missed you by 2 minutes. Hopefully you see my post before you add the onGameStart event handler.

Unfortunately I had just changed the four modules over before seeing your post. Should be easy to change though


Site note: use False and True with capital first letters for the actual Python constants. Civ4 defines false to be 0 and true to be 1 so that they work okay, but I am always wary of counting on such trickery. ;)

I was just keeping it consistent with the original code since I can never remember which such variables have capitals and which don't. Since I am doing other tidying up of the code I may as well convert true to True as well .;)

You need to add a similar line to your event handler:

I don't have an event handler:confused:. I have converted these into modules like in your tutorial - Events

Side note #2: CyCity has getOwner() as well, so you can use "gc.getPlayer(pCity.getOwner())" without first getting the plot the city is on.

Part of the original code - but the plot is needed in another part of the mod so maybe Tsentom1 had a reason for it! I will change it.


Thanks for the link, most of the manuals I have seem to think you are writing python for a once of use not for programing something which will be used often.
 
I don't have an event handler:confused:.

An event handler is merely a function that is called whenever an event is fired. It doesn't have to be defined in an event manager or any other class. The function onBuildingBuilt() is a handler for the buildingBuilt event.

Part of the original code - but the plot is needed in another part of the mod so maybe Tsentom1 had a reason for it! I will change it.

These little tidbits are far from requirements, but slightly more than stylistic differences. Each call to the SDK from Python involves some translation which takes quite a bit more time compared to calling some other Python function. By itself it isn't noticeable, but I point them out so people get in the habit of minimizing the SDK calls--especially the ones that aren't necessary. In this case, it's just one call to plot(). I've seen others use that same code snippet, so I'm pointing it out in case people don't know that CyCity has the same function.

Thanks for the link, most of the manuals I have seem to think you are writing python for a once of use not for programing something which will be used often.

Another great resource I always recommend is How to Think Like a Computer Scientist. which is a free book you can browse or download (or buy from Amazon if you really want). It covers Object Oriented Programming very well using Python as the language.
 
Code:
<init mod="NameOfAbovePythonModule"/>

Still a bit confused.

In my Config\init.xml I have the line

Code:
	<load mod="Great Bath"/>

Which loads/runs my Config\Great Bath.xml which gets the python to do its thing. And contains:-

Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
    Tsentom1's python wonders - The Great Bath of Mohenjo-daro
-->
<mod id="Great_Bath" module="Great_Bath">
  	<event type="buildingBuilt" function="onBuildingBuilt"/>
   	<event type="cityBuilt" function="onCityBuilt"/>
   	<event type="cityAcquired" function="onCityAcquired"/>
   	<event type="cityLost" function="onCityLost"/>
</mod>

I am assuming that I put the <init mod...> in the init.xml before the <load mod...> i.e.
Code:
	<init mod="Great_Bath"/>
	<load mod="Great Bath"/>
and that the <init mod...> refers to the python file while the <load mod...> refers to an XML file.
 
An event handler is merely a function that is called whenever an event is fired. It doesn't have to be defined in an event manager or any other class. The function onBuildingBuilt() is a handler for the buildingBuilt event.
Ehich is exactly what I call them when talking, but when writing in these forums I feel that it is confusing to do so since there is a class called EventHandler or something like it ;).


These little tidbits are far from requirements, but slightly more than stylistic differences. Each call to the SDK from Python involves some translation which takes quite a bit more time compared to calling some other Python function. By itself it isn't noticeable, but I point them out so people get in the habit of minimizing the SDK calls--especially the ones that aren't necessary. In this case, it's just one call to plot(). I've seen others use that same code snippet, so I'm pointing it out in case people don't know that CyCity has the same function.

Most of my changes have been about improving the speed by reducing the SDK calls and such like. That is why I have globals which I set up once rather than calling the SDK on every building built or combat result event. So I do appreciate your hints.

Another great resource I always recommend is How to Think Like a Computer Scientist. which is a free book you can browse or download (or buy from Amazon if you really want). It covers Object Oriented Programming very well using Python as the language.

My main problem is 40 years of programming in many different computer languages. I have even programmed one computer using switches to set the registers :crazyeye:.

:mischief:Ah, the good old days.:mischief:
 
Not quite with the XML; it's even easier. The <init> goes in "Great Bath.xml" since it relates to the Great Bath module. Since you reference the Python module inside the <mod> element, the <init> can be really short:

Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
    Tsentom1's python wonders - The Great Bath of Mohenjo-daro
-->
<mod id="Great_Bath" module="Great_Bath">
  	<event type="buildingBuilt" function="onBuildingBuilt"/>
   	<event type="cityBuilt" function="onCityBuilt"/>
   	<event type="cityAcquired" function="onCityAcquired"/>
   	<event type="cityLost" function="onCityLost"/>

   	[B][COLOR="Red"]<init/>[/COLOR][/B]
</mod>

You omit the module="Great Bath" because you put it into <mod>, and you omit the function="init" as long as you name the function init().

I feel that it is confusing to do so since there is a class called EventHandler or something like it ;).

That class is called EventManager because it manages the events and their handlers--everything having to do with events, actually. A better name for that class would be EventDispatcher because it receives events and dispatches them to the appropriate handlers.

Event handler is a perfect name for functions like onBuildingBuilt because it handles the situation that causes the event to be fired.

SDK: Yo! Isabelle just built a building.
BugEventManager: Okay, I'll dispatch it to someone who cares.
onBuildingBuilt(): Give it to me. I can handle it!​

Most of my changes have been about improving the speed by reducing the SDK calls and such like. That is why I have globals which I set up once rather than calling the SDK on every building built or combat result event. So I do appreciate your hints.

Not only that, but I feel it makes the code easier to read. That gets more into personal taste, though. :)

My main problem is 40 years of programming in many different computer languages.

Wow, and I thought I was old at this a long time! :p I suspect the main impediment isn't so much the various languages but more that the majority of those were likely procedural and more straight-forward. Nowadays the language is only 0.01% of the learning curve. Add on top of that the hundreds of libraries and functions you need to learn, and each language does it differently. It adds up!

But it sure is fun! :D
 
Top Bottom