How to make a Python mod

So, for example, an industrial era despotic monarchy might have a socialist movement as its main opposition. From time to time - as the opposition gains support among the populace - they would raise demands for political reform.
VERY Tempting. Would be a VERY HUGE project. Alone the design _or_ implementation would be more than I can handle. Even more: To find good play balance means giant work ...

---

To close the bracket: Minimum code example for INPUT just 1 BIT:
Code:
         rebelPopup = CyPopupInfo()
         rebelPopup.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
         rebelPopup.setText("the city of %s is threatened! Rumors of pillage and turmoil ... do you want to invest %s gold in order to support our widely travelled friends?" % (cyCity.getName(), iGold) )
         rebelPopup.setData1(cyCity.getID())
         rebelPopup.setData2(iGold)
         rebelPopup.setOnClickedPythonCallback("BribeRebels")
         rebelPopup.addPythonButton("Yes", "")
         rebelPopup.addPythonButton("No", "")
         rebelPopup.addPopup(eHuman)
and
Code:
def BribeRebels(argsList):
   """Give gold and persuade rebels to move on, away from threatened city"""
   iButtonId = argsList[0]
   iCity = argsList[1]
   iGold = argsList[2]
   if iButtonId == 1:                              # "No"?

---

nomads&rebels moved on, now includes bribing rebels option, moving palace to captured cities option, no rebel generation on already occupied plots ... and some more tweaks I forgot
 

Attachments

Nice! :D
 
After looking through the EventManager, I think that this would spawn a city on 23, 34 at the beginning of the game

Code:
from CvPythonExtensions import *

# constants
gc = CyGlobalContext()
(would I need to set a CyPlayer constannt?)

def onGameStart
eBarbarian = 9
pBarbarian = gc.getPlayer(eBarbarian)
pBarbarian.initCity(23, 34)

edit** I didn't work. Do you see anything wrong?
 
You need more skill, thats all. Read the tutorial and try again.

Basically, you need to make a function call from CvEventManager to the Rebels module. But the function itself needs a block of code, so you need to add a colon and one level of indentation.

Also, enable exceptions and logging (also pop-ups) - your code is generating a lot of errors.
 
Woo hoo! I figured it out! Thanks for helping me with this.

However, how would I make something happen on a certain turn?

would It look anything like this?

Code:
def onBeginGameTurn(self, argsList):
        'Called at the beginning of the end of each turn'
        iGameTurn = argsList[0]
        CvTopCivs.CvTopCivs().turnChecker(iGameTurn)
          ###MyMod
          if iGameTurn == 10:
             CitySpawn.spawnCity()
fyi, MyMod is my mod name, CitySpawn is the file name, and spawnCity(): is the function (?) name.
 
You've got the basic idea, but your indentation is all over the place.

There is a rule for this however: Always add one level of indentation after each colon.

Also: Make sure to only use one tab for each indentation level when modding the preset modules.
 
It should look like this: (I am certain of it :p)

Code:
def onBeginGameTurn(self, argsList):
        'Called at the beginning of the end of each turn'
        iGameTurn = argsList[0]
        CvTopCivs.CvTopCivs().turnChecker(iGameTurn)
        ###MyMod #there is no colon on the previous line therfore it shouldn't be indented past the previous line!
        if iGameTurn == 10:
           CitySpawn.spawnCity()

make sure you are careful with your indentation that caused me a lot of errors when I first started out...

the rules are:

after a colon use the next level of indentation
when using conditionals dont forget that the general format is:

If statement:
next indentation, what will happen if the if fired (if you know what I mean...)
finished If go back to previous indentation
Elif statement:
same as before
for example:
Code:
def eventSenate(): #definition next indentation
    if isHuman("Rome") == True: #conditional next indentation
        if isDate(iSenateYear): #conditional next indentation
            if isPlotOwner(tByzantium, eRome) == False and isPlotCity(tByzantium) == True: #conditional next indentation
                showPopup(senateHeader, senateMessage1) #processes
            else: #the if is finished therefore go back an indentation
                showPopup(senateHeader, senateMessage2)
        elif isDate(iSenateEndYear):
            if isPlotOwner(tByzantium, eRome) == True and isPlotCity(tByzantium) == True:
                giveGold(iSenateGold, eRome)
                showPopup(senateHeader, senateMessage3)
                increaseMissionCounter()
            else:
                showPopup(senateHeader, senateMessage4)

when defining functions and classes (classes are the technical stuff) make sure to have a colon at the end of definition for example:

def giveGold(iGold):

Don't mix spaces and tabs!

make sure to close your ()[]{} or the code will go on to the next line and cause errors!

you have 1 else, many elifs and 1 if before ANY elifs so:
if
elif
elif
(...)
elif
else

and finally have fun (and you will, I very much look forward to when I get to do the python for my mods :D)

hope that wasn't too confusing :D
 
Alright. As of late I've had a lot of trouble with python because I haven't worked with it in awhile.

Here's an example of something I made today:
Code:
from CvPythonExtensions import *
from PyHelpers import *

# constants
gc = CyGlobalContext()

iEgypt = gc.getPlayer(0)
iUnit = 9

def spawnUnit():
	iEgypt.initUnit(iUnit, 69, 36)

Why doesn't this work (I've been going into the in-game console and hitting "import Unitspawn", I've yet to make a call back)???
 
Importing a function makes it callable - it doesn't equal a actual function call.

Furthermore, the CyPlayer.initUnit() method takes 5 parameters (arguments for methods) while you're only supplying 3. It won't work.
 
Ohhh, I thought if you didn't put anything, it would be default.
But even when I changed it to iEgypt.initUnit(iUnit, 69, 36, 4, 0) it still didn't do anything...
 
as baldyr said... you are simply importing the module and making the function callable but not actually calling it

after importing the module you can then enter into the command line:
spawnUnit() and that will spawn the units
 
I made a call, but still nothing.

Code:
def onBeginGameTurn(self, argsList):
        'Called at the beginning of the end of each turn'
        iGameTurn = argsList[0]
        CvTopCivs.CvTopCivs().turnChecker(iGameTurn)
        ###MyMod 
        if iGameTurn == 2:
           Unitspawn.spawnUnit()
and yes, I have "import Unitspawn at the top
 
:lol: I think I see what is wrong

You defined the constant iEgypt in Unitspawn.py and did this:
import Unitspawn

now am I correct in thinking that this itself will not import the constant (well it will but extra procedures will be required) but the function only?

try:

from Unitspawn import *

then call spawnUnit() without the "Unitspawn." and that should work..

otherwise try custom python imports (talk to baldyr about that...)
 
:( I dont know then... try making a call in the console...:
from Unitspawn import *

spawnUnits()


have co-ordinates been set?
 
Ohhh, I thought if you didn't put anything, it would be default.
But even when I changed it to iEgypt.initUnit(iUnit, 69, 36, 4, 0) it still didn't do anything...
You need a UnitAIType and a DirectionType - not merely integer values.

The fact that you're not getting any exceptions (?) is testament to the fact that you're not calling the function.

At this point, you might wanna backtrack what you've learned about Python scripting and make sure you haven't missed something vital along the way. Because it shouldn't be this hard.
 
Somebody please correct me if I'm wrong, but it seems like every subdirectory under <ModName>/Assets/Python will be searched for modules. So for example, if I have

<ModName>/Assets/Python/Foo/FooEvents.py
<ModName>/Assets/Python/EntryPoints/CvEventInterface.py


Then in my CvEventInterface.py I can have a line like

Code:
from FooEvents import *

and it will load it.

If that's not the case, then where do I indicate that the Foo subdirectory needs to be in the search path. I'm only asking because I've looked at several mods and I can't find anywhere that somebody's found it necessary to modify sys.path or anything like that.

Can someone please confirm?
 
Back
Top Bottom