TC01
Deity
Table of Contents:
- Introduction
- Simple Button
- Advanced Button: Plot Picking
- AI Usage: Python Scripting
- Graphical Effects
Introduction
What is a python action button? It's simple- a button you can press on the unit's control panel (the buttons like move, sleep, skip turn, kill, etc) that does something in-game. Specific examples of python action buttons are the Afterworld abilities that each unit in Afterworld can get, and the Gods of Old religious missions available to Great Prophets.
This is something that you might not need to do and might prefer to handle by adding a command or mission to the SDK. But maybe you would rather make a python action button. Why? Well, there are a few reasons, some of which are listed below:
1. You've never edited the DLL before and are bad at C++, or are having trouble compiling a DLL.
3. You don't want to include a new DLL with your mod just for one button.
So, let's assume one of those conditions before has applied and you are going to make a python action button (hence why you are reading this tutorial).
To do this, you will need to edit at least two files:
-CvMainInterface.py
-CvGameUtils.py
Depending on how complicated this gets, you will need other files:
-CvEventManager.py
-PushButtonUtils.py (A custom python file we will create in this tutorial)
This tutorial, for now, will cover four topics:
1. A very simple button.
2. A button that picks a plot.
3. How the AI can be scripted to "use" buttons.
4. Graphical effects and sound when you press a button.
Okay, let's say we want to make a simple button, that when pressed, has a random chance of finding a resource on the tile you are standing on. We'll call this the Prospect ability.
First, you need to open up CvMainInterface.py (Assets\Python\Screens) in your editing program. I highly recommend Notepad++, whatever you like works (except for Notepad, which is a bad program). You need to search for/scroll down to the function "def updateSelectionButtons", which starts out looking like this:
Code:
# Will hide and show the selection buttons and their associated buttons
def updateSelectionButtons( self ):
global SELECTION_BUTTON_COLUMNS
global MAX_SELECTION_BUTTONS
global g_pSelectedUnit
screen = CyGInterfaceScreen( "MainInterface", CvScreenEnums.MAIN_INTERFACE )
pHeadSelectedCity = CyInterface().getHeadSelectedCity()
pHeadSelectedUnit = CyInterface().getHeadSelectedUnit()
global g_NumEmphasizeInfos
global g_NumCityTabTypes
global g_NumHurryInfos
global g_NumUnitClassInfos
global g_NumBuildingClassInfos
global g_NumProjectInfos
global g_NumProcessInfos
global g_NumActionInfos
We don't want to mess with any of this. So scroll down to the end of the function (it might take some time). Assuming this is normal BTS, you should see something like this:
Code:
elif (not CyEngine().isGlobeviewUp() and pHeadSelectedUnit and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY):
self.setMinimapButtonVisibility(True)
if (CyInterface().getInterfaceMode() == InterfaceModeTypes.INTERFACEMODE_SELECTION):
if ( pHeadSelectedUnit.getOwner() == gc.getGame().getActivePlayer() and g_pSelectedUnit != pHeadSelectedUnit ):
g_pSelectedUnit = pHeadSelectedUnit
iCount = 0
actions = CyInterface().getActionsToShow()
for i in actions:
screen.appendMultiListButton( "BottomButtonContainer", gc.getActionInfo(i).getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )
screen.show( "BottomButtonContainer" )
if ( not CyInterface().canHandleAction(i, False) ):
screen.disableMultiListButton( "BottomButtonContainer", 0, iCount, gc.getActionInfo(i).getButton() )
if ( pHeadSelectedUnit.isActionRecommended(i) ):#or gc.getActionInfo(i).getCommandType() == CommandTypes.COMMAND_PROMOTION ):
screen.enableMultiListPulse( "BottomButtonContainer", True, 0, iCount )
else:
screen.enableMultiListPulse( "BottomButtonContainer", False, 0, iCount )
iCount = iCount + 1
if (CyInterface().canCreateGroup()):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_CREATEGROUP").getPath(), 0, WidgetTypes.WIDGET_CREATE_GROUP, -1, -1, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
if (CyInterface().canDeleteGroup()):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_SPLITGROUP").getPath(), 0, WidgetTypes.WIDGET_DELETE_GROUP, -1, -1, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
elif (CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY):
self.setMinimapButtonVisibility(True)
return 0
Our code is going to go between the last iCount = iCount + 1 and the elif statement below that.
Here, we can control what units the action button appears to. You could set it to only appear to units in a city with a specific building, for instance. Or a unit with a certain promotion or combination of promotions, or a unit with specific script data. Or virtually anything. For our prospect button, we'll make it only appear to Workers.
Code:
if (CyInterface().canDeleteGroup()):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_SPLITGROUP").getPath(), 0, WidgetTypes.WIDGET_DELETE_GROUP, -1, -1, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
[COLOR="Red"] pUnit = g_pSelectedUnit
iUnitType = pUnit.getUnitType()
pUnitOwner = gc.getPlayer( pUnit.getOwner( ))
if pUnitOwner.isTurnActive( ):
if iUnitType == gc.getInfoTypeForString('UNIT_WORKER'):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_PROSPECT").getPath(), 0, WidgetTypes.WIDGET_GENERAL, 300, 300, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
[/COLOR]
elif (CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY):
self.setMinimapButtonVisibility(True)
return 0
The new code is in red. What does all of it mean? Well, pUnit is the unit that you have selected. iUnitType is defined as it's unitType, and pUnitOwner is the player who owns the unit. ArtFileMgr.getInterfaceArtInfo("INTERFACE_PROSPECT").getPath() is a button that exists only in the art files (CIV4ArtDefines_Interface.xml). We have to create this later or get an error that will say "None has no attribute .getPath()".
The two widget values (here I've defined them as 300, 300, are arbitrary numbers. That is, they aren't entirely arbritrary. You will use them elsewhere in this file and in CvGameUtils.py (to control what happens when you press the button and what text you see when you mouseover the button). But as long as the game doesn't already use these numbers for something else, you can use anything.
Finally, the rest of the code simply appends the button to the other buttons in game. It will be added to the end of the buttons.
Next, you will need to go all the way down the page to def handleInput, which should read:
Code:
def handleInput (self, inputClass):
return 0
Basically, as of now, it does nothing.
We want to add code that will cause something to happen when we click the button. Remember, it needs to correspond with the two widget values we defined above.
Code:
def handleInput (self, inputClass):
[COLOR="Red"] if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 300 and inputClass.getData2() == 300):
self.pPushedButtonUnit = g_pSelectedUnit
iX = self.pPushedButtonUnit.getX()
iY = self.pPushedButtonUnit.getY()
pProspect = CyMap().plot(iX, iY)
if pProspect.getBonusType(-1) == -1:
iRnd = CyGame().getSorenRandNum(100, "Prospect")
if iRnd >= 80:
if pProspect.isHills():
pProspect.setBonusType(gc.getInfoTypeForString("BONUS_IRON"))
g_pSelectedUnit.changeMoves(60)[/COLOR]
return 0
This is pretty simple, I could write a more complicated function. But basically what it does is checks what the numbers we defined above are (widgets/inputClass.getData()/whatever you want to call them). If they are 300, 300 (which is what we said they were above), it will trigger this code. This code gets the plot that the unit you pushed the button for was standing on and rolls a random number between 0 and 100. If the number is 80 or above, and if the plot is a hill, and if there's no resource on the hill already, then the plot will get an iron resource.
Also note that I added in a flat movement cost to using the ability. You can make this value whatever you want, but you should probably make it something. FFH prevents you from using any ability more then once a turn (no matter what). But it's easier to make it simply cost one movement point and thus prevent you from prospecting until you get a resource on the specific tile.
I could have, as I said, added in multiple checks, like randomly assign a minable resource to a hill, or randomly assign resources to specific terrains, but I decided not to make it so complicated for this tutorial.
Now, to the next file: CvGameUtils.py:
We want to scroll to the function "def getWidgetHelp" in this file. Here you can define what text will appear when you mouseover something. The function currently looks like this:
Code:
def getWidgetHelp(self, argsList):
eWidgetType, iData1, iData2, bOption = argsList
return u""
We need to add code that checks what the value (300, 300) is and then adds text, like this:
Code:
def getWidgetHelp(self, argsList):
eWidgetType, iData1, iData2, bOption = argsList
[COLOR="Red"] iType = WidgetTypes.WIDGET_GENERAL
if (eWidgetType == iType):
if (iData1 == 300):
return CyTranslator().getText("TXT_KEY_BUTTON_PROSPECT",())[/COLOR]
return u""
And you're done. You should test the game to make sure this works as intended. (It might not if I made a mistake in the tutorial).
So now we want to make a button that when pressed, causes a specific plot to be picked. This example came up recently in a thread in the SDK and Python forum, and it's actually the reason why I learned how to do this. After I figured out how to make a button work to answer the question, I started using them in my mods. This technically isn't part of making a python push button, but I decided to include it since it's an example of advanced button making and also is something used extensively by Firaxis. Both Afterworld and Gods of Old make use of python buttons and plot picking.
Anyway, we want to make a teleporter. This will only be visible if a unit has the "Teleport" promotion (I'm not making the promotion for you here, sorry), and will allow your unit to move to any plot on the map that it can see. This is because there are no real ways to pick a plot you cannot see.
We're going to start out with basically the same process- I'm going to keep the code I added in the above tutorial so you can compare, in fact. Anyway, open CvMainInterface.py and find the end of the updateSelectionButtons() function.
As I said, our code should make it so that the button only appears when the unit has the promotion Teleport. This means that instead of needing to create a new ArtInterface in the XML, we can simply use the button for the promotion. Thus, the code will be slightly different.
Code:
if (CyInterface().canDeleteGroup()):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_SPLITGROUP").getPath(), 0, WidgetTypes.WIDGET_DELETE_GROUP, -1, -1, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
pUnit = g_pSelectedUnit
iUnitType = pUnit.getUnitType()
pUnitOwner = gc.getPlayer( pUnit.getOwner( ))
if pUnitOwner.isTurnActive( ):
if iUnitType == gc.getInfoTypeForString('UNIT_WORKER'):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_PROSPECT").getPath(), 0, WidgetTypes.WIDGET_GENERAL, 300, 300, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
[COLOR="Red"] if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_TELEPORT')):
screen.appendMultiListButton( "BottomButtonContainer", gc.getPromotionInfo(gc.getInfoTypeForString('PROMOTION_TELEPORT')).getButton(), 0, WidgetTypes.WIDGET_GENERAL, 301, 301, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1[/COLOR]
elif (CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY):
self.setMinimapButtonVisibility(True)
return 0
The red code is the new stuff. As I mentioned, instead of using the Art file manager we're simply getting the promotion info and getting it's button for PROMOTION_TELEPORT. (if this promotion is not present, you will get an error "None type has no attribute getButton()"). Also, the numbers are 301, 301 instead of 300, 300. I'm allowing for the fact that I already used 300, 300 above in the first part of the tutorial (and kept that code there for this part).
Now, we want to add to the handleInput() function again. This time, though, since we are picking a plot, we don't want to actually do anything much here. We need to store some data and execute the plot picking command.
Code:
def handleInput (self, inputClass):
if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 300 and inputClass.getData2() == 300):
self.pPushedButtonUnit = g_pSelectedUnit
iX = self.pPushedButtonUnit.getX()
iY = self.pPushedButtonUnit.getY()
pProspect = CyMap().plot(iX, iY)
if pProspect.getBonusType(-1) == -1:
iRnd = CyGame().getSorenRandNum(100, "Prospect")
if iRnd >= 80:
if pProspect.isHills():
pProspect.setBonusType(gc.getInfoTypeForString("BONUS_IRON"))
g_pSelectedUnit.changeMoves(60)
[COLOR="Red"] if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 301 and inputClass.getData2() == 301):
PushButtonUtils.iPushButton = 1
PushButtonUtils.pPushedButtonUnit = g_pSelectedUnit
CyInterface().setInterfaceMode(InterfaceModeTypes.INTERFACEMODE_PYTHON_PICK_PLOT)[/COLOR]
return 0
What is "PushButtonUtils.iPushButton"? Well, those two lines are storing two pieces of data in a seperate file, PushButtonUtils, that I mentioned we'd have to create above. You can store any data you want here, as long as you create a define for it in Push Button Utils. And that name is arbitrary, for instance Gods of Old's file is called GodsOfOld.py and Afterworld's is Afterworld.py. But we'll cover creating that file next.
Finally, the last line of the added code simply sets the Python Pick Plot interface mode. You get the plot-sized cursor that, when it highlights an eligible plot, turns green, and when it is on a non-eligible plot, it turns gray.
But before we do anything with picking a plot, we need to create and import our new file, PushButtonUtils.py.
This may sound ominous, creating a new file, but really it's not that hard. All you have to do is create a new file (you can save a python file in Notepad++, for instance) in the Assets\Python directory. In this tutorial, we'll call it PushButtonUtils.py. There are two ways to make one, the easy way and the hard way. The easy way is good for only storing data, but you may want your file to do other stuff, and then it is better to do it the hard way. And it's not too much harder.
This is the easy way. Note this is the Gods of Old file from the Gods of Old mod, you can make the comments be whatever you want or have the variables listed here whatever you want:
Code:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2007
iPushButton = 0
pPushedButtonUnit = 0
That's it. iPushButton and pPushedButtonUnit are both 0 because they are integers. (Well, pPushedButtonUnit is really a CvUnit object, but it is being treated as an integer in this case). Booleans and strings would have to be defined as true or "".
The hard way is to actually import other files and create a class, like this:
Code:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2007
from CvPythonExtensions import *
from PyHelpers import PyPlayer
gc = CyGlobalContext()
class PushButton:
def __init__(self):
iPushButton = 0
pPushedButtonUnit = 0
For this, you don't actually need the gc = CyGlobalContext(), but this is best if you start adding more stuff to this file, forget that you didn't define gc as CyGlobalContext(), and get errors when trying to use gc. instead of CyGlobalContext().
Now, you need to import your file from the MainInterface file (and other files, but we'll do that when we get there).
If you did it the easy way, well, it's easy. You just add one line to the top of CvMainInterface.py, and other files, where other import statements are listed:
Code:
import PushButtonUtils
If you did it the hard way, you need to not only do this but also add the line below to where globals are defined, usually directly after the imports. These globals are variables that can be called from the entire file, like gc for instance (instead of writing out CyGlobalContext() each time you wanted to get something from the XML).
Code:
PushButtonUtils = PushButton()
Then you can write PushButtonUtils.[something] and you know that [something] is being called from class PushButton in the PushButtonUtils.py file. I believe you don't actually need this line and could just write PushButton().[something], but it's easier this way in the tutorial because I can use the same variable, PushButtonUtils, for both methods.
Next, we want to control what happens when we pick a plot. To do this we have to open CvEventManager.py. Before we go to the plot picking function, you must add the import statements here as well, to the top of the file, so the event manager can read data stored in PushButtonUtils.py. After doing that, search for the function def onPlotPicked(self, argsList).It should look like this (in normal BTS):
Code:
def onPlotPicked(self, argsList):
'Plot Picked'
pPlot = argsList[0]
CvUtil.pyPrint('Plot was picked at %d, %d'
%(pPlot.getX(), pPlot.getY()))
We want to add code that checks which button was pressed, and if it was, move the unit who pressed the button from it's current plot to the new plot, AND implement a flat movement cost. This isn't that hard.
I mention "check which button was pressed", because while we only have one pick plot button, you will need a way to tell which button was pressed in this function, and the best way to do that is to save a variable in the PushButtonUtils file as "iPushButton" and then check what that is in the Event Manager.
We need to add this code:
Code:
def onPlotPicked(self, argsList):
'Plot Picked'
pPlot = argsList[0]
CvUtil.pyPrint('Plot was picked at %d, %d'
%(pPlot.getX(), pPlot.getY()))
[COLOR="Red"] pCaster = PushButtonUtils.pPushedButtonUnit
pCaster.changeMoves(60)
iX = pPlot.getX()
iY = pPlot.getY()
if PushButtonUtils.iPushButton == 1:
pCaster.setXY(iX, iY, false, true, true)[/COLOR]
This is pretty simple. I can change the caster's (FFH terminology) movement points as soon as the function is called because this only occurs after you actually pick a plot. I then check if the button is 1 (which is what I set iPushButton to in the handleInput() function after you pressed the Teleport button), and if it is, I set the caster's x, y coordinates to iX, iY (the coordinates of the plot).
Now, we need to add the mouseover text in GameUtils.py again. Here, the process is exactly the same as before.
Code:
def getWidgetHelp(self, argsList):
eWidgetType, iData1, iData2, bOption = argsList
iType = WidgetTypes.WIDGET_GENERAL
if (eWidgetType == iType):
if (iData1 == 300):
return CyTranslator().getText("TXT_KEY_BUTTON_PROSPECT",())
[COLOR="Red"] if (iData1 == 301):
return CyTranslator().getText("TXT_KEY_BUTTON_TELEPORT",())[/COLOR]
return u""
Finally, there's one last thing we can do, and probably should. Before picking a plot, the canPickPlot function is called in CvGameUtils.py to check whether you can- it controls the green or gray cursor when you mouseover a plot in plot-picking mode. Here's the function currently:
Code:
def canPickPlot(self, argsList):
pPlot = argsList[0]
return true
We probably want to make it so that you can't teleport onto a tile with enemy units or cities on them. (It would be awesome if we made it so that you would attack them, but unfortunately I don't know if that's doable so I'm not going to try. You're welcome to.) To do this, you're going to have to import PushButtonUtils.py in the CvGameUtils.py file using the same method you used in the Event Manager and in the Main Interface file.
Code:
def canPickPlot(self, argsList):
pPlot = argsList[0]
[COLOR="Red"] pCaster = PushButtonUtils.pPushedButtonUnit
pCasterTeam = gc.getTeam(pCaster.getOwner())
if PushButtonUtils.iPushButton == 1:
for i in range(pPlot.getNumUnits()):
pUnit = pPlot.getUnit(i)
if gc.getTeam(pUnit.getOwner()).isAtWar(pCasterTeam):
return false
if pPlot.isCity():
pCity = gc.getPlotCity()
if gc.getTeam(pCity.getOwner()).isAtWar(pCasterTeam):
return false
if not gc.getTeam(pPlot.getOwner()).isOpenBorders(pCasterTeam):
return false[/COLOR]
return true
So this is the code you should add to the canPickPlot function. It will check if the owners of the units on the plot are at war with the owner of the caster (the unit who pressed the button.) Then, it will check if the plot is a city, and if it is, if the city's owner is at war with the caster's owner. And finally, it will prevent you from teleporting into a plot whose owner does not have an open borders agreement with you. Again, though, note that the code is only triggered if the push button was "1", the value we assigned to the teleport button.
So we're done with the plot picking button. Again, if you're using this, you should probably test it in case I made a mistake. If I did, please mention it here and I'll fix it.
Now, we're going to create a third button that the AI will use. This sounds impossible, but it's not.
What we can do is, when certain conditions are met, "fake" the triggering of the button. For instance, for the Prospect button we created, scripting in onUnitMove could be set to trigger a function (that would do the prospect ability) if the unit that moved was a worker, on a tile with no resources, and had a movement point to spare. We can also do more advanced stuff to allow the game to "remember" to trigger the ability on the next turn if it does not have a movement point to spare- if it moved onto a tile where it could cast the ability using it's last movement point.
For now, the button we're making is a "Quell City Disorder" ability. It will allow any military unit to try and restore order in a rioting city. We can make it so that it also damages the unit (they take casualties trying to stop the rioters). But we're also going to make the AI use it.
First, let's open up CvMainInterface.py and go to the end of def updateSelectionButtons where our other buttons are. Here, we'll add the first bit of code.
Code:
if (CyInterface().canDeleteGroup()):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_BUTTONS_SPLITGROUP").getPath(), 0, WidgetTypes.WIDGET_DELETE_GROUP, -1, -1, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
pUnit = g_pSelectedUnit
iUnitType = pUnit.getUnitType()
pUnitOwner = gc.getPlayer( pUnit.getOwner( ))
[COLOR="red"] pUnitPlot = CyMap().plot(pUnit.getX(), pUnit.getY())[/COLOR]
if pUnitOwner.isTurnActive( ):
if iUnitType == gc.getInfoTypeForString('UNIT_WORKER'):
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_PROSPECT").getPath(), 0, WidgetTypes.WIDGET_GENERAL, 300, 300, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_TELEPORT')):
screen.appendMultiListButton( "BottomButtonContainer", gc.getPromotionInfo(gc.getInfoTypeForString('PROMOTION_TELEPORT')).getButton(), 0, WidgetTypes.WIDGET_GENERAL, 301, 301, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1
[COLOR="Red"] if gc.getUnitInfo(pUnit).isMilitaryHappiness():
if pUnitPlot.isCity():
if (pPlot.getPlotCity()).isOccupation():
screen.appendMultiListButton( "BottomButtonContainer", ArtFileMgr.getInterfaceArtInfo("INTERFACE_QUELL_REVOLT").getPath(), 0, WidgetTypes.WIDGET_GENERAL, 302, 302, False )
screen.show( "BottomButtonContainer" )
iCount = iCount + 1[/COLOR]
elif (CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_HIDE_ALL and CyInterface().getShowInterface() != InterfaceVisibility.INTERFACE_MINIMAP_ONLY):
self.setMinimapButtonVisibility(True)
return 0
Instead of checking for a specific promotion, I chose to get the unit info for the unit and then see if the <bMilitaryHappiness> XML tag was set to 1. This is a quick way of checking if the unit would already provide "happiness", or as the Modiki says, "Counts as a Military Unit in the eyes of the rabble." That seems to be what we would want here. I also checked if the plot had a city (I added a new line of code defining pUnitPlot as the unit's plot), and if that city was in revolt. These are rather specific conditions- you will only see the button if you meet them.
Also note that I use the ArtFile Manager again and define a new button, INTERFACE_QUELL_REVOLT (that you will have to make yourself) and that the numbers are 302, 302, since I am already using 300 and 301 for the previous two buttons.
Now, we go to def handleInput and add our code there.
Code:
def handleInput (self, inputClass):
if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 300 and inputClass.getData2() == 300):
self.pPushedButtonUnit = g_pSelectedUnit
iX = self.pPushedButtonUnit.getX()
iY = self.pPushedButtonUnit.getY()
pProspect = CyMap().plot(iX, iY)
if pProspect.getBonusType(-1) == -1:
iRnd = CyGame().getSorenRandNum(100, "Prospect")
if iRnd >= 80:
if pProspect.isHills():
pProspect.setBonusType(gc.getInfoTypeForString("BONUS_IRON"))
g_pSelectedUnit.changeMoves(60)
if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 301 and inputClass.getData2() == 301):
PushButtonUtils.iPushButton = 1
PushButtonUtils.pPushedButtonUnit = g_pSelectedUnit
CyInterface().setInterfaceMode(InterfaceModeTypes.INTERFACEMODE_PYTHON_PICK_PLOT)
[COLOR="Red"] if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 302 and inputClass.getData2() == 302):
PushButtonUtils.doQuellRevolt(g_pSelectedUnit)[/COLOR]
return 0
Note that instead of doing anything here, I'm calling a function in PushButtonUtils, doQuellRevolt. The advantage of doing that in this case is that then, after I define the conditions I want for the AI to use the "button", I can call the same function if those conditions are met. This saves you from having to write the same code twice. However, doing it this way requires you to have used the "Advanced" mode of construction PushButtonUtils.py. It needs to have been set up like this:
Code:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2007
from CvPythonExtensions import *
from PyHelpers import PyPlayer
gc = CyGlobalContext()
class PushButton:
def __init__(self):
iPushButton = 0
pPushedButtonUnit = 0
We want to add a function to class PushButton that, when called, does the effects of the Quell Revolt button. This will continue in the next post below: