No, I haven't developed a split personality disorder and don't think I'm Jon . Apolyton's Modiki has been offline forever now, and it's pretty hard to find a copy of this document. It's a guide to Python in Civ4 by Jon Shafer (aka Trip), written back in 2004, but still very much relevant and highly useful to those who want to begin using Python in Civ4.
Everything below and in the next post is written by Jon. I am merely reposting.
------------------------------------------------------------------------
Well, I took some time off from working on my own mod projects to work on this, hopefully stirring up a bit more interest among other people. Modding in Python is really not difficult, but getting over the first hump is intimidating - at least it was for me. Now that I've learned it, I know some of the stumbling blocks I hit when trying to learn Python and particularly how it was implimented in CIV. Hopefully this resource will assist other people. If no one uses it and makes mods I'm going to give you all such a pinch. :P
I'm not really going to go over some of the basic things in programming, since there are better sources out on the net and in the real world than me for that sort of thing. I'm also not going to go over a lot of the stuff in Python itself for the same reason. For basics on both programming and Python itself, check out Gingerbread Man's thread over at CFC, as he's done a fine job in organizing things there.
What I will cover is a bit of OOP (Object Oriented Programming), as it's not something covered in GBM's lessons too extensively (he has a lesson on classes, but pretty much everything in CIV Python stuff relies on OOP), but I will also go over a few more basic things just to set things up the way I want, as well as for review.
Basic Python
If you have some prior programming experience or have gone through GBM's lessons you know that the primary structure of programs involves functions and classes. Classes contain functions and variables and are used to organize ones that are related into 'objects' that can be accessed by calling the class and the variable/function together. I will give examples of this later, but for now it's simply important to know the structure of things.
One thing that's unique about Python when compared to most languages is that variables do not actually store data, but reference 'objects' (different concept from what we've already covered). In most other languages data is stored in memory and given a type (integer, string, etc.) and label (the name of the variable). In Python data is stored in memory as an object and the type is stored there, while variables simply REFER, or point to the location of that data in memory (known as pointers in other languages). What this means is that when you instantiate, or create a variable, you don't need to set a type, because the type is stored with the object itself and not the variable. There are also other things that differ from other languages when working with pointers instead of data containers, but those already experienced with programming will already know, and those who aren't won't need to know. To keep things fairly simple (for me, mostly) I will simply refer to variables with normal variable terminology rather than treating them specially like the pointers they are.
Something important to remember in Python is that there is no use of brackets to denote encapsulation - everything is done through indentation. It's important to keep an eye on your indentation and make sure nothing is stray in your code. I like to delete all of the white space at the end of what I'm working with just to make sure nothing is screwed up.
Classes in Python
CIV relies heavily on the use of classes, so knowing how to use, manipulate and create them is important. As explained elsewhere, a class is simply created by using the "class Mod:" command, where the name of the class being created is "Mod". Functions are denoted with the "def ExFunction:" command, with "ExFunction" being its name. A function present in most classes is "__init__". This is a special function that is run the first time a class is called by CIV and never again (until the next time the program/mod is run). This is a good place to set up things that you want available later on without having to continually set them up.
Functions inside classes must always have at least one argument: "self". This refers to the class and allows you to use other parts of the class in that function. However, when CALLING a function you can ignore the argument. For example, a variable named "tempVar" would be created in a class' __init__ function through the command "self.tempVar = x" with "x" being the data you want to store in it. The variable would be referenced in the class's functions in the same way: self.tempVar. The more you use classes (either already created or of your own design) to mod the more you will run across these types of references. To call other functions inside a class it works the same way. A function created through "def testFunction(self):" would be called from another function with the command "self.testFunction()." This is obviously not the way to call functions from other files or classes, but I will get to that shortly.
Functions always return something, that is, calling them gives you a value. For example, the calling of "Math().getPi()" may return 3.14. In other languages you can denote a function that you don't want to return something (i.e. you just want it to do something) by giving it the type Void, but that's not necessary in Python because as with variables, function types are stored internally. Inside of a function you can use a "return x" to exit the function and replace the function call with the value of "x", or simply say "return 0" to exit - though a return statement is not necessary if you don't want to use one.
Here's a short example showing the usage of the things I've already presented:
This does a few basic things: it creates a class named "TestClass" and an "instance" variable as it's called (meaning a variable belonging to a class) named "TestVariable" and sets it to the value of "Test String". When the "TestClass" is first called __init__ is ran, which changes TestVariable to the value "String Test". At this point, the class is inactive, waiting for an outside call of one of its other functions. If the function "TestFunction" is called from outside, it sets the instance variable TestVariable equal to the value returned by the instance function FunctionTest, which is the string "Wheeeeeee". That's basically all there is to it.
Now then, calling functions and references variables within a class is fairly easy, but doesn't hold much power. Calling functions in other files and classes is usually much more important, as that is how programmers will have to access CIV data - you may be able to write a great class of your own, but without info from CIV it's not going to do much!
To access information and functions in another file, that file must first be imported. This is a simple task, completed through the use of the "import" command. If you have a file named PyTestMod, then to import it and its contents (classes and functions) you simply use the command "import PyTestMod". This allows you to call things within that file through "PyTestMod.blah", with "blah" being a function, class or variable in that file. However, if you plan on using the contents of this file extensively, you can take a shortcut and remove this step by saying "from PyTestMod import bleh". What this does is that it makes it so that you can do a straight call to whatever "bleh" is, be it a class, function or variable, without having to include the "PyTestMod." with it - which saves time and code. The drawback is that if you import a lot in this way it can be hard to track down where you're actually accessing things from, because there's no "PyTestMod." label in front of the references. To import everything in a file you use an asterisk: "from PyTestMod import *".
Let's return to the previous code example to illustrate how this works.
As you can see here, both classes function in the exact same way. However, the syntax is different, with the second class having less to type, which is advantageous. As warned above though, it's not easy to tell what file TestClass() comes from. Well, maybe it's not hard to track in this example, but when you get a lot more code flying around it can be.
I will now speak briefly about encapsulation, which fits in well with the current topic. Encapsulation means that certain parts (usually an import or a variable) of a file or class or function are only accessible 'locally', meaning within the context of the file/class/function you're working with. In the above example PyTestMod is imported twice - once in each class. This is necessary because each import call is "encapsulated" within each class - one call doesn't carry over. To make a single call carry over to both files you would use the import command above both classes within the "scope" (as it's called) of the entire file. Likewise, if you had used the import command inside one of the classes __init__ functions, the import would only be valid within that single function, and if there were any other functions in that class you would need to import in those as well. Note though that an import or variable creation does not carry to HIGHER levels (you can't use an import in a function throughout the entire class), but it does carry over to LOWER levels - if you have an import in a class then all of the functions in that class "see" the contents of the import as well.
There is a way to cheat and make a variable or object usable throughout the entire class even if it's created within a function (and thus, normally out of the scope of the whole class). The command is "global" - "global MyGlobalVariable" for example, and is used occasionally in CIV.
Python in CIV
There are other ways to simplify the calling of certain classes and functions. One way is through the assignment of simpler names (there's a better term for this, I know it, but I can't think of it :P) to certain complex calls. In CIV you will often have object/class calls that go 4 or 5 or more objects deep, and typing "CyGlobalContext().getPlayer(1).getUnit(0).getDamag e()" over and over again gets tedius - fast. But there is a solution. You can substitute part of this string with a simpler call if you so choose. You can make it as simple as you want. For example, you can assign the previous call to a single term - "Damage". To do this you simply say "Damage = CyGlobalContext().getPlayer(1).getUnit(0).getDamag e()". Every time you say "Damage" thereafter it will make that entire long reference call. Note what I said about object references a while back. Python doesn't actually assign the value of that term to "Damage", but it tells the program that variable "Damage" to POINT to the value of that long term. Again, this may not be significant for those who aren't familiar with reference calls and OOP, but for those who are, the differences between this and normal assignment is pretty obvious.
While the value of that long term is equal to an integer value (the damage of that particuar) unit, you can assign simpler names to object and class calls as well. For example, you can assign the unit object (CyGlobalContext().getPlayer(1).getUnit(0)) to "Unit" through "Unit = CyGlobalContext().getPlayer(1).getUnit(0)". You can then do things like "Unit.getDamage()" or "Unit.getName()" or other functions that the unit class/object possesses. You can also use a combination of these terms to cover a wider variety of things.
For example, this is something I like to do a lot in the Python console in games just to make things easier to work with:
This does the exact thing as above, only it also allows me to use GC, Player and Unit independently of the single "Damage" I had above. With the above code I could now do something simple like "Unit.getName()" and it will give me the name of that unit - rather than having to to use CyGlobalContext().getPlayer(1).getUnit(0).getName( ). Quite a bit of the CIV code does things along these lines, and if you take a look you'll see there's usually a gc (CyGlobalContext) in most every file. After getting some experience using them, it's quite useful in cutting down on all the stuff you have to read and type. However, it can be kind of tricky to keep track of all that's being done, and a lot may look alien at first.
Here's an example of a wrapper class (Player.py in the py folder) that Jesse wrote. As he and Gramps explained elsewhere the wrapper grabs the big long code and puts it into smaller and more manageable sections.
Here you can see "gc" takes the place of "CyGlobalContext()" and Jesse creates the PyPlayer class. The class inputs a single argument, the Player's ID. On the initialization of the class, the player's ID is stored in an instance variable ID (accessed in the class through "self.ID") and the actual player OBJECT is stored in self.player by using a GlobalContext function that grabs a player based upon what ID you feed it.
The isNone() function then takes the self.player object and returns whether or not the player is valid (exists) through Player.isNone(), a function in the Player object.
To call this class you would first import the Player file, then call the class and its functions. To call the isNone() function above you would use the command "Player.PyPlayer(0).isNone()" - where Player is the imported file, PyPlayer(0) is the wrapper class from above and its single input argument, and isNone() is the function that is being requested. It could be done the same way by the command: "CyGlobalContext().getPlayer(0).isNone()", it's just that the first is shorter, which is why Jesse wrote the wrapper classes to begin with.
This concludes the section dedicated to OOP. Next I will present other topics, such as iteration and other useful info. Finally, I will create a detailed guide on the classes and functions available for a modder's machinations.
This next segment is somewhat related to the previous one, but I wanted to break it up because it was getting too long.
Iteration (Looping)
The next section I will cover is iteration, as it is different from other languages and rather important in most CIV modding.
Iteration is basically the process of going through every element in a related set. This is usually done if you want to find a particular object that matches a certain criterion, to set a particular attribute for every object in a set, or other related things. One thing I've used it for is in my Rebellion mod to cycle through every player in the game (and in turn cycling through every city each player has ) in order to find what player to spawn "rebels" for.
An example of iteration through a player's list of cities follows:
The first lines set up my object reference shortcuts (Player and CyPlayer are the same, but reference the Player object in different ways - the first through Jesse's Player wrapper, and the other without it), and the final line is the iteration.
"CityList" is a List (Python object type) that contains City object instances (actual cities) like an array. You could pick out individual cities through their index: CityList[0] grabs the player's first city, while CityList with no index simply refers to the whole structure. "City" is what you're using to store the City object instance that is currently being looked at. The whole line loops through the player's city list, making "City" the current object. Within the for loop structure (after the # blah) you can then do all sorts of things: for example, City.getName() would grab the current city's name. City.hasReligion(0) returns whether or not this city has religion '0' (1 means it does, 0 means it doesn't).
Sometimes you might just want to loop through the INDEX of how many Cities there are - that is, you would want City to be an integer rather than a City object. To do that you modify the line slightly:
"range" allows us to use the length of the structure inside the parenthesis - in this case it gets the length of the player's city list. What this code snippet does is loops through every city index - so "print(CityIndex)" in this case would print out an integer (0 through the length of the list). Note that in the previous example trying "print(City)" will result in an error because while you can print out an integer, you cannot print out a complex City object!
The ability to iterate through objects and lists in this way gives great power in checking things and changing things with many of the things in CIV. Other things you can loop through is the player list, a player's unit list, religions list and more.
CIV Function Assignment
Merely checking values is only part of modding - programmers are also able to change certain values in CIV (though not as many as I'd like ).
There are a great many functions in a variety of classes that allow you to change things about the game, but I will only cover a few here. Later I will make a massive catalogue of the most useful classes and functions as a reference, so specifics now are less important than the general structure of things.
One of the more tricky assignments that I had to hunt down was the ability to change the religions present in a city. Using Python you can add or subtract any religion in a city, as well as set each religion's influence in said city.
The function in the City class to set a city's religion info is ".setHasReligion(int Religion ID, int Religion Influence, bool Announce)"
This means that the setHasReligion() function accepts 3 arguments, two integers and a boolean. Religion ID 0 is Judaism, so any time you're dealing with a TYPE of religion of "0" that means you're working with Judaism. I will use it for my following example. A religion's influence is how strong a religion is in this city - an influence of 0 means it's not present. The third argument determines whether or not the game announces to the player that the city has converted to this religion (if you go from 0 influence to any other positive integer), and it used in the normal game to announce when a religion spreads to a new city. Setting it to 1 or 0 makes no difference when removing a religion from a city.
This following example can be easily done in the CIV Python console. To enter it, press Ctrl-Shift-C. To make this work, you need to found your first city:
Entering this in line-by-line will make your first/capital city Jewish with an influence of 10, and additionally a message will pop up on the screen.
This next part will remove Judaism from your capital, by making its influence in this city 0:
That concludes this section. I'll take things a bit farther in the next section.
Creating a Unit in CIV
Changing whether or not there's a religion in a city is one thing, but creating a brand new unit out of thin air is quite another. Thankfully, it's not that difficult if you know the right function. A similar function exists for cities, but unfortunately not for players.
The function to create a new unit belongs to the Player class, and has the structure as follows: ".initUnit(int UnitType, int XLoc, int YLoc, obj UnitAI)". UnitType is an integer from 0 to something (the last unit); XLoc is the x-value where the unit appears, YLoc is the y-value where the unit appears, and UnitAI is an object which contains stuff related to what AI the unit uses. Right now you should only use the string "UnitAITypes.NO_UNITAI" in this location - using something else later may be possible, but for now that's all that should be used.
Now then, to create a unit things are fairly straightforward. We will create a Warrior, which has an ID of 9. The next two arguments are a bit trickier. Where on the map do you want the Warrior to appear? Well, just picking numbers out of thin air is possible, but it might land the unit on top of an enemy city, on a peak or even in the water - which probably wouldn't end well. So we need to be careful in where we put it - how about in the capital city?
First build your first city then try the following in the CIV console:
The first 3 lines I've already covered.
The next two involve a new class/object: plot. Plots are the different tiles on the map. Plot (0,0) is in the far upper left-hand corner (I think, either that or the lower left-hand) and the Plot object can be accessed through the "plot()" function in the CyCity class (accessed from CyPlayer using the getCity(x) function... and so on). After we find the capital city's plot, we then find its X and Y values and assign them to a new pair of variables. The final line is the actual function which creates the unit. As outlined above, the first argument is the Warrior's ID, the second the unit's X, the third the unit's Y and the final the Unit's AI. When called as above, a new Warrior will appear in the capital! Voila.
As I said, something similar can be done to create cities. Under the player class is the initCity() function, which takes two parameters that we've seen before, the city's location: the first being X, and the second Y. It shouldn't be that hard to figure out by now, but if anyone tries and needs some help, just go ahead and post.
I've covered the basics now. I can't think of anything else to add to this section, but if I do I will post it later. The final section I have planned is a nice organized list of useful classes and functions for easy reference.
Everything below and in the next post is written by Jon. I am merely reposting.
------------------------------------------------------------------------
Well, I took some time off from working on my own mod projects to work on this, hopefully stirring up a bit more interest among other people. Modding in Python is really not difficult, but getting over the first hump is intimidating - at least it was for me. Now that I've learned it, I know some of the stumbling blocks I hit when trying to learn Python and particularly how it was implimented in CIV. Hopefully this resource will assist other people. If no one uses it and makes mods I'm going to give you all such a pinch. :P
I'm not really going to go over some of the basic things in programming, since there are better sources out on the net and in the real world than me for that sort of thing. I'm also not going to go over a lot of the stuff in Python itself for the same reason. For basics on both programming and Python itself, check out Gingerbread Man's thread over at CFC, as he's done a fine job in organizing things there.
What I will cover is a bit of OOP (Object Oriented Programming), as it's not something covered in GBM's lessons too extensively (he has a lesson on classes, but pretty much everything in CIV Python stuff relies on OOP), but I will also go over a few more basic things just to set things up the way I want, as well as for review.
Basic Python
If you have some prior programming experience or have gone through GBM's lessons you know that the primary structure of programs involves functions and classes. Classes contain functions and variables and are used to organize ones that are related into 'objects' that can be accessed by calling the class and the variable/function together. I will give examples of this later, but for now it's simply important to know the structure of things.
One thing that's unique about Python when compared to most languages is that variables do not actually store data, but reference 'objects' (different concept from what we've already covered). In most other languages data is stored in memory and given a type (integer, string, etc.) and label (the name of the variable). In Python data is stored in memory as an object and the type is stored there, while variables simply REFER, or point to the location of that data in memory (known as pointers in other languages). What this means is that when you instantiate, or create a variable, you don't need to set a type, because the type is stored with the object itself and not the variable. There are also other things that differ from other languages when working with pointers instead of data containers, but those already experienced with programming will already know, and those who aren't won't need to know. To keep things fairly simple (for me, mostly) I will simply refer to variables with normal variable terminology rather than treating them specially like the pointers they are.
Something important to remember in Python is that there is no use of brackets to denote encapsulation - everything is done through indentation. It's important to keep an eye on your indentation and make sure nothing is stray in your code. I like to delete all of the white space at the end of what I'm working with just to make sure nothing is screwed up.
Classes in Python
CIV relies heavily on the use of classes, so knowing how to use, manipulate and create them is important. As explained elsewhere, a class is simply created by using the "class Mod:" command, where the name of the class being created is "Mod". Functions are denoted with the "def ExFunction:" command, with "ExFunction" being its name. A function present in most classes is "__init__". This is a special function that is run the first time a class is called by CIV and never again (until the next time the program/mod is run). This is a good place to set up things that you want available later on without having to continually set them up.
Functions inside classes must always have at least one argument: "self". This refers to the class and allows you to use other parts of the class in that function. However, when CALLING a function you can ignore the argument. For example, a variable named "tempVar" would be created in a class' __init__ function through the command "self.tempVar = x" with "x" being the data you want to store in it. The variable would be referenced in the class's functions in the same way: self.tempVar. The more you use classes (either already created or of your own design) to mod the more you will run across these types of references. To call other functions inside a class it works the same way. A function created through "def testFunction(self):" would be called from another function with the command "self.testFunction()." This is obviously not the way to call functions from other files or classes, but I will get to that shortly.
Functions always return something, that is, calling them gives you a value. For example, the calling of "Math().getPi()" may return 3.14. In other languages you can denote a function that you don't want to return something (i.e. you just want it to do something) by giving it the type Void, but that's not necessary in Python because as with variables, function types are stored internally. Inside of a function you can use a "return x" to exit the function and replace the function call with the value of "x", or simply say "return 0" to exit - though a return statement is not necessary if you don't want to use one.
Here's a short example showing the usage of the things I've already presented:
Code:
# File PyTestMod # First Class class TestClass: # Create First Variable TestVariable = "Test String" def __init__(self): # Change the variable self.TestVariable = "String Test" def TestFunction(self): self.TestVariable = self.FunctionTest() def FunctionTest(self): return "Wheeeeeee"
Now then, calling functions and references variables within a class is fairly easy, but doesn't hold much power. Calling functions in other files and classes is usually much more important, as that is how programmers will have to access CIV data - you may be able to write a great class of your own, but without info from CIV it's not going to do much!
To access information and functions in another file, that file must first be imported. This is a simple task, completed through the use of the "import" command. If you have a file named PyTestMod, then to import it and its contents (classes and functions) you simply use the command "import PyTestMod". This allows you to call things within that file through "PyTestMod.blah", with "blah" being a function, class or variable in that file. However, if you plan on using the contents of this file extensively, you can take a shortcut and remove this step by saying "from PyTestMod import bleh". What this does is that it makes it so that you can do a straight call to whatever "bleh" is, be it a class, function or variable, without having to include the "PyTestMod." with it - which saves time and code. The drawback is that if you import a lot in this way it can be hard to track down where you're actually accessing things from, because there's no "PyTestMod." label in front of the references. To import everything in a file you use an asterisk: "from PyTestMod import *".
Let's return to the previous code example to illustrate how this works.
Code:
# Another File class ClassX: import PyTestMod def __init__(self): PyTestMod.TestClass().TestFunction() class ClassY: from PyTestMod import * def __init__(self): TestClass().TestFunction()
I will now speak briefly about encapsulation, which fits in well with the current topic. Encapsulation means that certain parts (usually an import or a variable) of a file or class or function are only accessible 'locally', meaning within the context of the file/class/function you're working with. In the above example PyTestMod is imported twice - once in each class. This is necessary because each import call is "encapsulated" within each class - one call doesn't carry over. To make a single call carry over to both files you would use the import command above both classes within the "scope" (as it's called) of the entire file. Likewise, if you had used the import command inside one of the classes __init__ functions, the import would only be valid within that single function, and if there were any other functions in that class you would need to import in those as well. Note though that an import or variable creation does not carry to HIGHER levels (you can't use an import in a function throughout the entire class), but it does carry over to LOWER levels - if you have an import in a class then all of the functions in that class "see" the contents of the import as well.
There is a way to cheat and make a variable or object usable throughout the entire class even if it's created within a function (and thus, normally out of the scope of the whole class). The command is "global" - "global MyGlobalVariable" for example, and is used occasionally in CIV.
Python in CIV
There are other ways to simplify the calling of certain classes and functions. One way is through the assignment of simpler names (there's a better term for this, I know it, but I can't think of it :P) to certain complex calls. In CIV you will often have object/class calls that go 4 or 5 or more objects deep, and typing "CyGlobalContext().getPlayer(1).getUnit(0).getDamag e()" over and over again gets tedius - fast. But there is a solution. You can substitute part of this string with a simpler call if you so choose. You can make it as simple as you want. For example, you can assign the previous call to a single term - "Damage". To do this you simply say "Damage = CyGlobalContext().getPlayer(1).getUnit(0).getDamag e()". Every time you say "Damage" thereafter it will make that entire long reference call. Note what I said about object references a while back. Python doesn't actually assign the value of that term to "Damage", but it tells the program that variable "Damage" to POINT to the value of that long term. Again, this may not be significant for those who aren't familiar with reference calls and OOP, but for those who are, the differences between this and normal assignment is pretty obvious.
While the value of that long term is equal to an integer value (the damage of that particuar) unit, you can assign simpler names to object and class calls as well. For example, you can assign the unit object (CyGlobalContext().getPlayer(1).getUnit(0)) to "Unit" through "Unit = CyGlobalContext().getPlayer(1).getUnit(0)". You can then do things like "Unit.getDamage()" or "Unit.getName()" or other functions that the unit class/object possesses. You can also use a combination of these terms to cover a wider variety of things.
For example, this is something I like to do a lot in the Python console in games just to make things easier to work with:
Code:
GC = CyGlobalContext() Player = GC.getPlayer(1) Unit = Player.getUnit(0) Damage = Unit.getDamage()
Here's an example of a wrapper class (Player.py in the py folder) that Jesse wrote. As he and Gramps explained elsewhere the wrapper grabs the big long code and puts it into smaller and more manageable sections.
Code:
gc = CyGlobalContext() class PyPlayer: "CyPlayer Helper Functions - Requires Player ID to initialize instance" def __init__(self, playerID): " Called whenever a new PyPlayer instance is created " self.ID = playerID self.player = gc.getPlayer(self.ID) def isNone(self): "boolean - Is the CyPlayer Instance Valid?" return self.player.isNone()
The isNone() function then takes the self.player object and returns whether or not the player is valid (exists) through Player.isNone(), a function in the Player object.
To call this class you would first import the Player file, then call the class and its functions. To call the isNone() function above you would use the command "Player.PyPlayer(0).isNone()" - where Player is the imported file, PyPlayer(0) is the wrapper class from above and its single input argument, and isNone() is the function that is being requested. It could be done the same way by the command: "CyGlobalContext().getPlayer(0).isNone()", it's just that the first is shorter, which is why Jesse wrote the wrapper classes to begin with.
This concludes the section dedicated to OOP. Next I will present other topics, such as iteration and other useful info. Finally, I will create a detailed guide on the classes and functions available for a modder's machinations.
This next segment is somewhat related to the previous one, but I wanted to break it up because it was getting too long.
Iteration (Looping)
The next section I will cover is iteration, as it is different from other languages and rather important in most CIV modding.
Iteration is basically the process of going through every element in a related set. This is usually done if you want to find a particular object that matches a certain criterion, to set a particular attribute for every object in a set, or other related things. One thing I've used it for is in my Rebellion mod to cycle through every player in the game (and in turn cycling through every city each player has ) in order to find what player to spawn "rebels" for.
An example of iteration through a player's list of cities follows:
Code:
# NewObj: Active player Player = Player.PyPlayer(GC.getActivePlayer().getID()) CyPlayer = CyGlobalContext().getPlayer(GC.getActivePlayer().getID()) # NewObj: Determine player's list of cities CityList = Player.getCityList() # NewObj: Loop through each of the active player's cities for City in CityList: # Blah...
"CityList" is a List (Python object type) that contains City object instances (actual cities) like an array. You could pick out individual cities through their index: CityList[0] grabs the player's first city, while CityList with no index simply refers to the whole structure. "City" is what you're using to store the City object instance that is currently being looked at. The whole line loops through the player's city list, making "City" the current object. Within the for loop structure (after the # blah) you can then do all sorts of things: for example, City.getName() would grab the current city's name. City.hasReligion(0) returns whether or not this city has religion '0' (1 means it does, 0 means it doesn't).
Sometimes you might just want to loop through the INDEX of how many Cities there are - that is, you would want City to be an integer rather than a City object. To do that you modify the line slightly:
Code:
for CityIndex in range(CityList): # Blah...
The ability to iterate through objects and lists in this way gives great power in checking things and changing things with many of the things in CIV. Other things you can loop through is the player list, a player's unit list, religions list and more.
CIV Function Assignment
Merely checking values is only part of modding - programmers are also able to change certain values in CIV (though not as many as I'd like ).
There are a great many functions in a variety of classes that allow you to change things about the game, but I will only cover a few here. Later I will make a massive catalogue of the most useful classes and functions as a reference, so specifics now are less important than the general structure of things.
One of the more tricky assignments that I had to hunt down was the ability to change the religions present in a city. Using Python you can add or subtract any religion in a city, as well as set each religion's influence in said city.
The function in the City class to set a city's religion info is ".setHasReligion(int Religion ID, int Religion Influence, bool Announce)"
This means that the setHasReligion() function accepts 3 arguments, two integers and a boolean. Religion ID 0 is Judaism, so any time you're dealing with a TYPE of religion of "0" that means you're working with Judaism. I will use it for my following example. A religion's influence is how strong a religion is in this city - an influence of 0 means it's not present. The third argument determines whether or not the game announces to the player that the city has converted to this religion (if you go from 0 influence to any other positive integer), and it used in the normal game to announce when a religion spreads to a new city. Setting it to 1 or 0 makes no difference when removing a religion from a city.
This following example can be easily done in the CIV Python console. To enter it, press Ctrl-Shift-C. To make this work, you need to found your first city:
Code:
GC = CyGlobalContext() Player = GC.getPlayer(0) City = Player.getCity(0) City.setHasReligion(0,10,1)
This next part will remove Judaism from your capital, by making its influence in this city 0:
Code:
City.setHasReligion(0,0,1)
Creating a Unit in CIV
Changing whether or not there's a religion in a city is one thing, but creating a brand new unit out of thin air is quite another. Thankfully, it's not that difficult if you know the right function. A similar function exists for cities, but unfortunately not for players.
The function to create a new unit belongs to the Player class, and has the structure as follows: ".initUnit(int UnitType, int XLoc, int YLoc, obj UnitAI)". UnitType is an integer from 0 to something (the last unit); XLoc is the x-value where the unit appears, YLoc is the y-value where the unit appears, and UnitAI is an object which contains stuff related to what AI the unit uses. Right now you should only use the string "UnitAITypes.NO_UNITAI" in this location - using something else later may be possible, but for now that's all that should be used.
Now then, to create a unit things are fairly straightforward. We will create a Warrior, which has an ID of 9. The next two arguments are a bit trickier. Where on the map do you want the Warrior to appear? Well, just picking numbers out of thin air is possible, but it might land the unit on top of an enemy city, on a peak or even in the water - which probably wouldn't end well. So we need to be careful in where we put it - how about in the capital city?
First build your first city then try the following in the CIV console:
Code:
GC = CyGlobalContext() Player = GC.getPlayer(0) City = Player.getCity(0) CityX = City.plot().getX() CityY = City.plot().getY() Player.initUnit(9, CityX, CityY + 1, UnitAITypes.NO_UNITAI)
The next two involve a new class/object: plot. Plots are the different tiles on the map. Plot (0,0) is in the far upper left-hand corner (I think, either that or the lower left-hand) and the Plot object can be accessed through the "plot()" function in the CyCity class (accessed from CyPlayer using the getCity(x) function... and so on). After we find the capital city's plot, we then find its X and Y values and assign them to a new pair of variables. The final line is the actual function which creates the unit. As outlined above, the first argument is the Warrior's ID, the second the unit's X, the third the unit's Y and the final the Unit's AI. When called as above, a new Warrior will appear in the capital! Voila.
As I said, something similar can be done to create cities. Under the player class is the initCity() function, which takes two parameters that we've seen before, the city's location: the first being X, and the second Y. It shouldn't be that hard to figure out by now, but if anyone tries and needs some help, just go ahead and post.
I've covered the basics now. I can't think of anything else to add to this section, but if I do I will post it later. The final section I have planned is a nice organized list of useful classes and functions for easy reference.
Comment