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 onBegin
PlayerTurn. 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).