Python Loops and Counter ...

That error means it tried to load CvEventInterface and all files that file loads and in one of them there's an error making it unable to load it. Turn on error logging (if you haven't) and look at what the logs say.


Are iImprovement and iImprovementTurn defined in onBeginGameTurn? I know you give iImprovementTurn a value in onImprovementBuilt but onBeginGameTurn can't find the variable if it was defined inside another function. It needs to either be defined as a global (then you can change the value like you do) or as a variable of the class, in which case you prefix it with self. (including the dot)

As for iImprovement in onBeginGameTurn I assume what you meant is pPlot.getImprovementType().

...and also, there's no such thing as CyPlayer.getPlot or PyPlayer.getPlot. So that's wrong too unless you forgot to paste a large piece of code where you define it or it really is something else than a player, in which case your variable name is confusing. Also you did forget to include some code or pPlayer isn't defined. The game doesn't define it in onBeginPlayerTurn by default so you have to.

And one last thing - that I've noticed atleast - there's no such thing as pPlot.getNumPlots() unless you defined it, in which case you didn't include it. (But there is such a thing as pPlayer.getNumPlots() if pPlayer is a PyHelpers object. Don't know how it works though)
 
Thanks for taking the time out to look at the code and tell me what you found wrong with it, Snarko.

I indented the code resulting in an if statement being true, and brought iImprovementTurn at the top of the file with the global defines, hoping to make it available in all class... I set it to 0 to start off with. There are the two defines I have at the top of the CvCustomeEventManager that I got from Impaler:
Code:
gc = CyGlobalContext()
iImprovementTurn = 0
Did I define it wrong?

Also, when I built the improvement, I did get a new unit. Only thing is that I got a swordsman when I was supposed to get an axeman:
Code:
newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNITCLASS_AXEMAN'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
Don't know why that is ...

Also, afterwards I didn't get a new unit created after 5 turns like I wanted.. this probably has to do with what you've mentioned above, the iImprovementTurn and the other calls I have made which don't seem to exist. In reality, I got them from Kael's Fall from Heaven code, so probably he defined them but I just failed to include them.

The game started without that error I had before, but the error log says this:
Code:
sys.path = ['ASSETS\\PYTHON\\SYSTEM\\email', 'ASSETS\\PYTHON\\SYSTEM\\encodings', 'ASSETS\\PYTHON\\SYSTEM\\wx', 'ASSETS\\PYTHON\\SYSTEM\\wx\\build', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib', 'ASSETS\\PYTHON\\SYSTEM\\wx\\py', 'ASSETS\\PYTHON\\SYSTEM\\wx\\tools', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\colourchooser', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\editor', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\floatcanvas', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\masked', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\mixins', 'ASSETS\\PYTHON\\SYSTEM\\wx\\lib\\ogl', 'ASSETS\\PYTHON\\SYSTEM\\wx\\py\\tests', 'ASSETS\\PYTHON\\SYSTEM\\wx\\tools\\XRCed', 'ASSETS\\PYTHON\\SYSTEM']

sys.modules = {'copy_reg': <module 'copy_reg' from 'C:\Python24\Lib\copy_reg.pyc'>, 'locale': <module 'locale' from 'C:\Python24\Lib\locale.pyc'>, '__main__': <module '__main__' (built-in)>, 'site': <module 'site' from 'C:\Python24\Lib\site.pyc'>, '__builtin__': <module '__builtin__' (built-in)>, 'encodings': <module 'encodings' from 'C:\Python24\Lib\encodings\__init__.pyc'>, 'os.path': <module 'ntpath' from 'C:\Python24\Lib\ntpath.pyc'>, 'encodings.codecs': None, 'ntpath': <module 'ntpath' from 'C:\Python24\Lib\ntpath.pyc'>, 'UserDict': <module 'UserDict' from 'C:\Python24\Lib\UserDict.pyc'>, 'encodings.exceptions': None, 'nt': <module 'nt' (built-in)>, 'stat': <module 'stat' from 'C:\Python24\Lib\stat.pyc'>, 'zipimport': <module 'zipimport' (built-in)>, 'warnings': <module 'warnings' from 'C:\Python24\Lib\warnings.pyc'>, 'encodings.types': None, '_codecs': <module '_codecs' (built-in)>, 'encodings.cp1252': <module 'encodings.cp1252' from 'C:\Python24\Lib\encodings\cp1252.pyc'>, 'sys': <module 'sys' (built-in)>, 'codecs': <module 'codecs' from 'C:\Python24\Lib\codecs.pyc'>, 'types': <module 'types' from 'C:\Python24\Lib\types.pyc'>, '_locale': <module '_locale' (built-in)>, 'signal': <module 'signal' (built-in)>, 'linecache': <module 'linecache' from 'C:\Python24\Lib\linecache.pyc'>, 'encodings.aliases': <module 'encodings.aliases' from 'C:\Python24\Lib\encodings\aliases.pyc'>, 'exceptions': <module 'exceptions' (built-in)>, 'CvPythonExtensions': <module 'CvPythonExtensions' (built-in)>, 'os': <module 'os' from 'C:\Python24\Lib\os.pyc'>}

sys.builtin_module_names = ('CvPythonExtensions', '__builtin__', '__main__', '_bisect', '_codecs', '_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_csv', '_heapq', '_hotshot', '_locale', '_multibytecodec', '_random', '_sre', '_subprocess', '_symtable', '_weakref', '_winreg', 'array', 'audioop', 'binascii', 'cPickle', 'cStringIO', 'cmath', 'collections', 'datetime', 'errno', 'exceptions', 'gc', 'imageop', 'imp', 'itertools', 'marshal', 'math', 'md5', 'mmap', 'msvcrt', 'nt', 'operator', 'parser', 'regex', 'rgbimg', 'sha', 'signal', 'strop', 'struct', 'sys', 'thread', 'time', 'xxsubtype', 'zipimport')
load_module CvEventInterface
load_module CvUtil
load_module traceback
load_module CvCustomEventManager
load_module CvEventManager
load_module CvScreensInterface
load_module CvActionButtonsMainInterface
load_module ScreenInput
load_module CvScreenEnums
load_module time
load_module ActionButtons
load_module CvDomesticAdvisor
load_module PyHelpers
load_module CvTechChooser
load_module CvForeignAdvisor
load_module math
load_module CvMilitaryAdvisor
load_module re
load_module CvFinanceAdvisor
load_module CvReligionScreen
load_module CvCivicsScreen
load_module string
load_module CvVictoryScreen
load_module CvOptionsScreen
load_module CvReplayScreen
load_module CvHallOfFameScreen
load_module CvDanQuayle
load_module CvGameUtils
load_module CvUnVictoryScreen
load_module CvDawnOfMan
load_module CvTechSplashScreen
load_module CvTopCivs
load_module random
load_module CvInfoScreen
load_module CvIntroMovieScreen
load_module CvVictoryMovieScreen
load_module CvWonderMovieScreen
load_module CvEraMovieScreen
load_module CvPediaMain
load_module CvPediaScreen
load_module CvScreen
load_module CvPediaTech
load_module CvPediaUnit
load_module CvPediaBuilding
load_module CvPediaPromotion
load_module CvPediaUnitChart
load_module CvPediaBonus
load_module CvPediaTerrain
load_module CvPediaFeature
load_module CvPediaImprovement
load_module CvPediaCivic
load_module CvPediaCivilization
load_module CvPediaLeader
load_module CvPediaSpecialist
load_module CvPediaHistory
load_module CvPediaProject
load_module CvPediaReligion
load_module CvWorldBuilderScreen
load_module Popup
load_module CvWorldBuilderDiplomacyScreen
load_module CvDebugTools
load_module CvDebugInfoScreen
load_module CvMapGeneratorUtil
load_module CvGFCScreen
load_module CvPopupInterface
load_module CvScreenUtilsInterface
load_module CvScreenUtils
load_module CvWBPopups
load_module CvCameraControls
load_module CvAdvisorUtils
PY:OnInit
load_module CvAppInterface
 
Shqype said:
Also, when I built the improvement, I did get a new unit. Only thing is that I got a swordsman when I was supposed to get an axeman:
Code:
newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNITCLASS_AXEMAN'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
Don't know why that is ...

If you want an axeman you need to use UNIT_AXEMAN, not UNITCLASS_AXEMAN. When you specify UNITCLASS_AXEMAN in initUnit you are using the same value as UNIT_SWORDSMAN. The initUnit method expects a UnitType value not a UnitClassType value.
 
That was simple enough ... I should have known that. Thanks Lopez :)
 
TheLopez said:
If you want an axeman you need to use UNIT_AXEMAN, not UNITCLASS_AXEMAN. When you specify UNITCLASS_AXEMAN in initUnit you are using the same value as UNIT_SWORDSMAN. The initUnit method expects a UnitType value not a UnitClassType value.

This actually has to do with the marshalling of enums between C++ and Python. The basic deal is that all of the enums are being translated into integers. So the program makes no distinction between UNIT_SWORDSMAN and UNITCLASS_AXEMAN, because internally, they map to the same number. This is a classic type mismatch problem.

-----

Shqype said:
Right now I am not familiar with dictionaries and need to start some experimentation before I begin with them. But, I will try to keep it simple and try to get the code working with only 1 improvement. This is what I've come up with:

onImprovementBuilt:
Code:
def onImprovementBuilt(self, argsList):
		'Improvement Built'
		iImprovement, iX, iY = argsList
		pPlot = CyMap().plot(iX,iY)
		pPlayer = gc.getPlayer(pPlot.getOwner())
# ...... My additions (Animal Den based on Kael's idea)
	if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_ANIMAL_DEN')):
		newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNITCLASS_BEAR'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
		iImprovementTurn = gc.getGameTurn()

onBeginGameTurn:
Code:
for i in range (pPlot.getNumPlots()):
		pPlot = pPlayer.getPlot(i)

if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_ANIMAL_DEN') and ((iGameTurn - iImprovementTurn) % 5 == 0)):
	newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNITCLASS_BEAR'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)

In theory it should work, I'll test it out later.

That's not going to work, for a number of reasons.

First, CyMap().plot(iX,iY) is not going to get you the plot object you think. You are effectively creating a new map object and getting a (isNone()) CyPlot object from it. You need to call gc.getMap().plot(iX, iY).

Second, as you've already been informed, you need to use the unittype not the unit class. Intuitively, this should make sense to you, as a particular unit class can have multiple unit types (That's how Civ handles UUs--e.g. Swordsman and Praetorians for example).

Third, you get iImprovementTurn but you don't store it anywhere. This is an issue of scope which is a high falutin' CS term for a context (which also have CS connotations). Put another way, think of iImprovementTurn as a nickname for the place in memory where the program has put some information. Code inside the function you defined it in recognizes that nickname, but code outside of it, doesn't. Which is where you get into pathing names like CvEventManager.CvEventManager. Which is sort of like saying "Get me Dave" "Dave who?" "The Dave from Accounting, Dave". Now this is made more complicated, by the idea that iImprovementTurn, as you've defined it, is a local variable. This means it is local to the function, and will be "deallocated" when that function "completes". Which isn't really all that accurate, but this isn't a "how code executes" seminar, so I'll just punt to another analogy. A local variable is like writing the information on a notepad while you are standing in your office lobby and setting it down on the receptionist's desk. While you are standing there, you are pretty confident it won't get lost. Once you leave... iImprovementTurn is the same way. While you are in the function you defined it in, it is there. When you leave, it goes away. Now, you might think, "well, can't I put it in a well known place then?" And the answer is, of course. But, if you have a little bit of knowledge you might think "what I need is a global variable". No. That won't work either. The explanation is more complicated, but suffice to say that the entire universe which your code is executing in gets created and destroyed regularly. There is no where in Python's conception of runtime memory that you can put the value and get access to it reliably on later turns. Put another, consider your entire body of Python code to be a program that the Civ4 engine runs repeatedly, over and over again with different command line options every turn.

That's why I mentioned the setScriptData trick. SetScriptData can effectively represents disk storage in your mind vis a vis your Python code. What you put in there you can access again later and be reasonably certain of its continuity. So iImprovementTurn needs to be stored in the script data of some object that makes sense. Probalby pPlot.

Ok, Number 4. Your sweep of plots in onBeginGameTurn isn't going to work. First off, pPlot isn't defined in onBeginGameTurn (see above). Second off, you can't ask a CyPlot object to "getNumPlots()". It doesn't really make sense to ask that question, either. Remember that pPlot represents (in theory, if it was in fact in scope ;) ) a discrete (specific) plot. There's only one of it. It is an instance of a CyPlot object. I'll avoid expanding on that topic for simplicity's sake and move on. Third, you can't ask a CyPlayer object to "getPlot(index)". The API doesn't allow for it. So, basically, all the pieces of your sweep code will not work.

Instead, try something like this--
Code:
map = gc.getMap()
for iX in range(map.getGridWidth()):
    for iY in range(map.getGridHeight()):
        pPlot = map.plot(iX, iY)

        # Do something

Ok. So now we're sweeping over every plot on the map (it's the only way to do it, as far as I know. No way to get a list of plots specific to a player without first doing this). Now we come to issue number 5. Your generation code. iImprovement is not defined in onBeginGameTurn. pPlayer is undefined from above. iImprovementTurn is undefined. iGameTurn is only defined assuming you started with the base code from the CvEventManager, otherwise you'll have to get it from CyGlobalContext, or from the argslist.

Now, assuming you fix your initUnit call, you are going to have to get the other pieces yourself and test accordingly. Some things to keep in mind. First, onBeginGameTurn is called *once* per turn at the start of the turn cycle. Not once per player. That is onBeginPlayerTurn. Second, you will need to get iImprovementTurn back from whereever you have stored it. You will potentially want to compare pPlot.getOwner() to something that lets you know they have the right to use Dens. (Remember that if cultural borders shift, the owner of the Den can change). If Dens are not capturable, you can store the owner's id at the same time you store the turn. This makes things particularly easy since then you only have to to test that id to the current owner, and one sweep is sufficient.

If your Den's are capturable, then you need to come up with a different test for "is allowed to use a Den". This could be a tech (you can disable techs by Civ to restrict access), or it could be the ability to build a certain kind of unit (your UU for instance). Or you could record who can and can not use Dens by hand at the beginning of the game and store it on the script data of each player, for instance. You could even test against hard coded civilization ids. It's really up to you.

Those are the only problems I see with the code apriori. When testing this stuff out it is good to remember that you can to a certain extent mod and go. Civ 4 will reload your Python scripts everytime you alt-tab in and out. (It tells you as much "Python modules reloaded..."). If you have setup logging in your .ini file, you can use the logs, but I don't care for them (although you don't have a choice for some things, like map generation scripts). Personally, I tend to use the in-game event log. Using code something like this...

Code:
            #iX = pUnit.getX()
            #iY = pUnit.getY()
            #message = "Your %s has been granted a promotion(%d-%d)!" %(pUnit.getName(), iXP, iXP2)
            #CyInterface().addMessage(pUnit.getOwner(),True,len(message),message,'AS2D_BOMBARDED',1,'Art/Interface/Buttons/Units/Fighter.dds',ColorTypes(7),iX,iY,True,True)

Obviously, you'd want to uncomment it, and change the message variable to whatever you want to "inspect", and make sure you have valid X,Ys. You could set them to 0 even, but I usually set them to some coordinate meaningful to what I'm testing (in your case, probably the Den you were testing on that pass).
 
Belizan said:
(it's the only way to do it, as far as I know. No way to get a list of plots specific to a player without first doing this)

PyPlayer in PyHelpers have both getPlotList and getPlotIDList. Can use that or "steal" the code. (Yes I realise you're still checking the entire map. But if it's going to be moved to onBeginPlayer turn it should make things slightly easier.)
 
Thank you Belizan for that long, informative post. It taught me things I didn't know, like how storing variables in a function don't get saved for next turn and they start from scratch again! This is something very important and I thank you for informing me.

Now, I understand most of what you said, but the scriptdata is still troubling me a little bit. Do you think you could put a generic example in psuedocode so I could get a better understanding? In making iImprovementTurn stored as script data for the object pPlot, would I have to create a dictionary? And is this the only way to create such a global variable?
 
snarko said:
PyPlayer in PyHelpers have both getPlotList and getPlotIDList. Can use that or "steal" the code. (Yes I realise you're still checking the entire map. But if it's going to be moved to onBeginPlayer turn it should make things slightly easier.)

Yeah, but, as you point out, it just sweeps the map anyway. Given what he's trying to do, he can probably do it all in one sweep anyway, at which point, he's better off knowing how the underlying API is manipulated to achieve the results he desires.
 
Shqype said:
Thank you Belizan for that long, informative post. It taught me things I didn't know, like how storing variables in a function don't get saved for next turn and they start from scratch again! This is something very important and I thank you for informing me.

Now, I understand most of what you said, but the scriptdata is still troubling me a little bit. Do you think you could put a generic example in psuedocode so I could get a better understanding? In making iImprovementTurn stored as script data for the object pPlot, would I have to create a dictionary? And is this the only way to create such a global variable?

Fortunately for you, my brother got a phonecall right as we were going out to lunch. Let's see...

So, Get/SetScriptData() is a facility provided in the API for the Civ4 world's equivalent of persistant storage (and I resisted using the term stable storage here ;) ). Whatever you put into the script data of an object, will be retrievable from that object anytime later when you grab that object again. Through saves and loads, etc.

The issue with the dictionary part is that you only get *one* script data field on each object. So, let's say, for instance, with your den, you want to store two pieces of information, the Den's creation turn, and the Den's original owner. But you only have one script data field. So what do you do? You can only store on value in the script data.

One solution is the dictionary I outlined originally. The logic goes like this.

I want to store two things, but script data only take one value.
I can put the two things I want to store into a Python collection (like a dictionary), but script data will not store a complex (what .NET might call a ref) object, it only stores simple objects, discrete numbers, etc. (This is simplification, but.. moving on).
So I take the dictionary object I have and a I serialize it into a flat value using a Python library called Pickle.
I take my serialized (numerified) dictionary and store it in the script data of the object.

Now, anytime I get that object I can get the script data, use pickle to convert it back into a dictionary, and then access the dictionary to retrieve my two values--the owner and the creation turn.

Remember, that the data stored in the object is whatever the values were of the dictionary when you serialized it and stored it. If you later go and change the values of the dictionary, the stored version will not be affected (unless you subsequently serialize it and set the script data with it).

Conversely, I *believe* you could also just set the script data to a tuple with both your values. But I haven't tested it, and one day you may want to store three values, or four.. or heaven forbid, an arbitrary number.

To make all this easier to use, we create our own "plotDictionary" api. We create functions which let us "getPlotDictionary" and "setPlotDictionary". You might even consider making some functions called "setImprovementCreatedTurn" and "setImprovementOriginalOwner". In each of these functions you capture the basic functionality of your solution.

Code:
def setPlotDictionary(pPlot, pDict):
    iSerialized = cPickle.dumps(pDict)
    pPlot.setScriptData(iSerialized)

And so on it goes. This make more sense now?
 
I thought that the Plot Dictionary would be able to store the values of the turn the improvement was created and the original owner... I understood that the dictionary is capable of storing multiple variables (like those 2 mentioned above) and thus would not need to have them as 2 separate functions.

Now, can these functions be defined with a function, such as onImprovementBuilt? Or must they be defined separately, and then be called from the function where they are needed?
 
Shqype said:
I thought that the Plot Dictionary would be able to store the values of the turn the improvement was created and the original owner... I understood that the dictionary is capable of storing multiple variables (like those 2 mentioned above) and thus would not need to have them as 2 separate functions.

Now, can these functions be defined with a function, such as onImprovementBuilt? Or must they be defined separately, and then be called from the function where they are needed?

The functions are only short cuts for you. In the code, the two look almost identical..

Code:
def getCTurn(pPlot):
    pD = getPlotDictionary(pPlot)
    return pD["creationTurn"]

def getOOwner(pPlot):
    pD = getPlotDictionary(pPlot)
    return pD["originalOwner"]

Just a short cut encapsulation of the operation you are performing--getting the creation turn or original owner. Totally not necessary to make as separate functions.
 
Thanks for your explanations Belizan. I had trouble understanding the script data, but using theLopez's Military Bases mod as a reference (which makes use of the SD-Toolkit), I've been able to pickle and serialize the variables that I need to be stored, with his help.

Code:
	def onImprovementBuilt(self, argsList):
		'Improvement Built'
		self.parent.onImprovementBuilt(self, argsList);
                iImprovement, iX, iY = argsList
                pPlot = gc.getMap().plot(iX, iY)
                pPlayer = gc.getPlayer(pPlot.getOwner())
                if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_ANIMAL_DEN')):
                    newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AXEMAN'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)
                    improvementTurnBuilt = sdSetVal("Gjergj Kastrioti", pPlot, "GameTurnImprovementBuilt", gc.getGameTurn())

Code:
	def onBeginPlayerTurn(self, argsList):
		'Called at the beginning of a players turn'
		self.parent.onBeginPlayerTurn(self, argsList);
		iGameTurn = gc.getGameTurn()
                improvementTurnBuilt = sdGetVal("Gjergj Kastrioti", pPlot, "GameTurnImprovementBuilt")
                        
		map = gc.getMap()
                for iX in range(map.getGridWidth()):
                    for iY in range(map.getGridHeight()):
                        pPlot = map.plot(iX, iY)
                            
                        if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_ANIMAL_DEN')):
                            # CyInterface().addImmediateMessage("Got Animal Den!")
                            if ((iGameTurn - improvementTurnBuilt) % 5 == 0):
                                # CyInterface().addImmediateMessage("Mod condition met...")
                                newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AXEMAN'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)

Code:
	def setupScript(self, pPlot):
		scriptDict = {  "GameTurnImprovementBuilt" : 0,}
		sdEntityInit("Gjergj Kastrioti", pPlot, scriptDict)

Now, obviously I am still having some problems. On creation of the improvement, the unit is generated, but the sweep to create another unit every 5 turns still doesn't work. I believe this is because of what you said before, in onBeginPlayerTurn it doesn't know what "iImprovement" is, because it was not defined. For onImprovementBuilt, it is entered as an argument of the argsList ... perhaps it should be an argument for onBeginPlayerTurn as well? Also, I realize that I did not specify which arguments to set for that function, so that is also causing my desired results not to appear.

If you have suggestions, critiques, or other things to teach me, because I still have alot to learn, by all means, post :)
 
pPlot.getImprovementType() <-- returns the plots improvement. iImprovement in onImprovementBuilt does just that, returns the plots improvement.

You can't add arguments for the predefined functions. Adding iImprovement wouldn't make sense anyway, since a players turn doesn't have a plot.
 
In onBeginPlayerTurn I redefined it as this:
Code:
iImprovement = pPlot.getImprovementType()

That should take care of the problem of iImprovement not being defined in onBeginPlayerTurn...
 
Belizan said:
First, CyMap().plot(iX,iY) is not going to get you the plot object you think. You are effectively creating a new map object and getting a (isNone()) CyPlot object from it. You need to call gc.getMap().plot(iX, iY).

Actually, CyMap().plot(iX,iY) should work ... it does in map scripts. I believe CyMap() is a singleton.

Anyone from firaxis care to confirm/deny/explain?
 
Both worked for the code I had in place (in generating a unit on improvement built). The only remaining problem is getting a new unit on that improvement every x amount of turns.
 
iGameTurn is only defined assuming you started with the base code from the CvEventManager, otherwise you'll have to get it from CyGlobalContext, or from the argslist.
I got an attribute error:
'CyGlobalContext' object has not attribute 'getGameTurn'
So, this doesn't work:
Code:
iGameTurn = gc.getGameTurn()
 
Try CyGame().getGameTurn()

In general CyGlobalContext are for game independent things while CyGame are for things related to the current game.
 
Shqype said:
I got an attribute error:

So, this doesn't work:
Code:
iGameTurn = gc.getGameTurn()

iGameTurn = gc.getGame().getGameTurn()

You should check your calls against the Python API.

http://civilization4.net/files/modding/PythonAPI/

As for CyMap() (or CyGame() for that matter) being a Singleton, I've never bothered to check :/.

self.parent.onImprovementBuilt(self, argsList);

No semi-colon.

pPlayer = gc.getPlayer(pPlot.getOwner())

pPlot.getOwner() may return -1 (no owner)

improvementTurnBuilt = sdSetVal("Gjergj Kastrioti", pPlot, "GameTurnImprovementBuilt", gc.getGameTurn())

...

iGameTurn = gc.getGameTurn()
improvementTurnBuilt = sdGetVal("Gjergj Kastrioti", pPlot, "GameTurnImprovementBuilt")

The aforementioned gc.getGameTurn() problem. I am not familiar with whatever library you are using, so it is hard to say if there are other issues with this pair of calls.

All of these things you should be able to test in smaller more discrete code samples to build up your knowledge base of Python and the Civ4 Python API. Use the messaging system I showed you earlier and just try testing each feature step by step in OnEndPlayerTurn(), alt-tabbing out to change what you are testing or alter it to get it to work. You aught to be able to solve all of this by now.

if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_ANIMAL_DEN')):

iImprovement is out of scope. pPlot.getImprovementType().

newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_AXEMAN'), pPlot.getX(), pPlot.getY(), UnitAITypes.NO_UNITAI)

pPlayer is out of scope. gc.getPlayer(pPlot.getOwner()), check for isNone(). I dont' recall offhand if getPlayer() will complain about -1 or give you a pPlayer that isNone(), to be safe, I'd do it in two steps (or test it).

Algorithmicly you have some issues--
You are adding a unit to all Dens, regardless over own, on every player's turn. (You don't check if the owner is allowed to use it, and you check all Dens at the start of each player's turn. e.g. All dens on Player 0's Turn1, then all Dens again on Player 1's Turn1, etc.)

scriptDict = { "GameTurnImprovementBuilt" : 0,}
sdEntityInit("Gjergj Kastrioti", pPlot, scriptDict)

Again, I'm not familiar with the library, so I can't comment on your usage. Also not the way I define dictionaries, but I believe it's valid.
 
I'll need time to fix, experiment, and test. Thanks for your lengthy post. I'll try to make the most of it.
 
Back
Top Bottom