Dom Pedro's Mod Helper for Python Modders

Dom Pedro II

Modder For Life
Joined
Apr 3, 2002
Messages
6,811
Location
Exit 16, New Jersey
Dom Pedro's Mod Helper
or How I learned to stop worrying and love Python

Anybody who has talked to me for any length of time about modding knows I have hated python for a long time. While I still don't like its form, I've come to realize that python has a number of important advantages over the SDK. I've started working on scenarios that require small, unique tweaks that don't really justify tinkering with C++, but I've still found python to either be too cumbersome or not powerful enough to do everything I want. This mod comp attempts to strengthen python by using the SDK and XML to work more effectively with it. I designed it for my own needs, but I figured I would share it with the world.

Enclosed in the file and attached separately below is a quick and dirty tutorial for how to use this mod comp.

I am currently accepting suggestions for other things they would like to see python able to do, so let me know.


WHAT IT DOES:
This mod component does not change gameplay. It is specifically to make the lives of modders who can use python easier, and give them greater power over the code. Hopefully, this simplified system will also encourage people to go a step further in their modding beyond XML and try their hand at python scripting.

This code takes the system used for calling python methods for Events and expands it to other areas of the game. Modders can attach new python methods to units, promotions, specialists, buildings, civics, civilizations, leaders, religions and more in the XML that will be read from python. This allows players to create small, self-contained python methods that are executed at the appropriate time without complicated checks in python.

With this mod component, you will be able to have specialists give the same benefits as buildings, allow Wonders all the effects of Civics, and virtually any other possible combination. You can have buildings that automatically spawn units in regular intervals, and much much more. Many of the SDK functions that were previously not exposed to python have now been exposed so modders can use the new system to its full potential.

EDIT: I also forgot to mention, I threw in some code to the TechInfos that lets you give announcements to all players who have met the first discoverer of a tech. This has no gameplay consequences, I just liked the idea of having messages appearing like "A Sumerian inventor has become the first person to build a heavier-than-air aircraft!" or something like that.

Changes in v 0.3:

Added code to easily script new Action Buttons based in part on talchas's Action Buttons 2.0

Added CIV4ActionButtonInfos.xml

Updated the Tutorial

Changes in v 0.2:

PythonAIWeights:

Added <PythonAIWeight> tag to UnitInfos, PromotionInfos, BuildingInfos, CivicInfos, ReligionInfos, SpecialistInfos.

Added <iAIWeight> to PromotionInfos, ReligionInfos, and SpecialistInfos

Added new tags to CIV4EventTriggerInfos.xml:

<TriggerImages> - Allows for random, era-specific images be displayed with the event popup
<TriggerSounds> - Allows for random, era-specific sounds to play with the event popup

<PrereqOrEvents> - Prerequisite OR Events allows either/or event prerequisites

<PythonProbability> - Allows modders to have the probability of
<PythonCityTriggerValue> - Gives modders control over what criteria makes a city better or worse for a trigger... allows more fine tuning beyond the existing PythonCanDoCity tag
<PythonPickBuilding> - Select a building based on any criteria rather than letting the computer randomly pick one.
<PythonPickCivic> - Select a civic
<PythonPickBonus> - Select a bonus based on any critiera
<PythonPickReligion> - Select a religion based on any criteria
<PythonPickCorporation> - Select a corporation based on any criteria
<PythonPickCulture> - Select a particular culture (i.e. Player) in a particular city
<PythonPickOtherPlayer> - Select other player based on any criteria rather than being randomly selected by the computer
<PythonPickTech> - Select a tech
<PythonText> - Can create display text dynamically.

Added new tags to CIV4EventInfos.xml

<PythonText> - Dynamic display text for the event popup buttons
<PythonGold> - Dynamically set gold cost/reward
<PythonBestTech> - Choose a tech to be awarded
<PythonBestCivic> - Choose a civic to switch to
 

Attachments

  • Mod Helper Tutorial.pdf
    485.2 KB · Views: 366
Holy smokes! This looks great! I have about a thousand lines of python into Dune Wars and about a thousand for Fury Road. This would make a lot of things simpler.

There are a bunch of things which are possible to do by modifying CvGameUtils.py, such as AI_unitUpdate, AI_chooseProduction, etc. These would all be more convenient if they were controlled from xml tags as well, to allow mod merging without untangling a chain of if statements inside these functions.

Also, there are some entry/exit routines that would be helpful. I was trying to do something when entering *or leaving* a civic. It looks like your PythonCallback is called upon entering the civic; could you define one for leaving a civic? Also something like canDoCivic would be helpful.

I can probably go through my existing code and come up with a dozen more suggestions!

How does this code interact with BUG? That is, merging your sdk source code changes with BUG seems like it would be a big undertaking; do you plan to premerge with some of the common modcomps like BUG?
 
There are a bunch of things which are possible to do by modifying CvGameUtils.py, such as AI_unitUpdate, AI_chooseProduction, etc. These would all be more convenient if they were controlled from xml tags as well, to allow mod merging without untangling a chain of if statements inside these functions.

Indeed, that's one of the reasons why I did this. I personally don't like coding in python, so anything that makes it less complicated is a plus in my book :) How would you suggest AI_unitUpdate and AI_chooseProduction being controlled from the XML though?

Also, there are some entry/exit routines that would be helpful. I was trying to do something when entering *or leaving* a civic. It looks like your PythonCallback is called upon entering the civic; could you define one for leaving a civic? Also something like canDoCivic would be helpful.

The callback method for a particular civic is called both when it's being activated or deactivated (along with specialists, religions, promotions, etc.) One of the arguments passed to the python method is the integer "iChange", which is either a 1 or a -1 depending on whether you're switching to or switching from a particular civic. When you create a new python method, you're going to want to use this to determine what exactly is supposed to happen. Actually, if you're doing something simple like giving a happiness bonus, it's very simple. Just do something like:

player.changeExtraHappiness((iChange * iCivicHappiness))

That way when you activate the civic, it will add (1 * iCivicHappiness) to extra happiness, and when you deactive it, it will add (-1 * iCivicHappiness) which should subtract the exact same amount of happiness. That's how it's done in the SDK when processing civics, buildings, resources, etc.

Also, isn't canDoCivic already a function? Is there a reason why there should be civic-specific canDo methods defined in the XML?

I can probably go through my existing code and come up with a dozen more suggestions!

Fire away. Worst I can say is no ;)

How does this code interact with BUG? That is, merging your sdk source code changes with BUG seems like it would be a big undertaking; do you plan to premerge with some of the common modcomps like BUG?

I haven't tried to merge this with any other modcomps, but I think it should be fairly easy to do since it doesn't really add any new content. That's left to the modders.
 
You could attempt to control some AI decisions with tags along the lines of <AIPythonWeight> which will call out to python and check for any weight values early in the decision process, allowing you to prevent further processing with a return value. The main decision would be to have a return value of an array for all weight values being considered, or allow the check to process on each unit/building/project/whatever being checked. Could lead to considerable slowdown the second way, but be confusing for new modders the first way.

UnitUpdate type of intercept can just be a simple <PythonPerTurn> function in which there is a check for isHuman() in the python which then leads to a series of orders being given for AI units that override normal control.
 
Looks great! (although I think no amount of love would make me not hate python)

I agree personally... but actually, that's what makes this cool. You'll have to do a lot less work with python to get the same effects. As a result, I'm hoping that it will get people who aren't good with python to give it a shot to make their mods better.

Any ideas of the effect of merging this with WoC (Lite) modular XML loading?

Super awesomeness I would imagine... This mod comp is very useful, but it's not particularly complicated or revolutionary. The Events have used this technique from the beginning, so I doubt that there would be much of a conflict with most existing modcomps. I even created a separate python file just for the new methods so people wouldn't have to merge multiple python files.

You could attempt to control some AI decisions with tags along the lines of <AIPythonWeight> which will call out to python and check for any weight values early in the decision process, allowing you to prevent further processing with a return value. The main decision would be to have a return value of an array for all weight values being considered, or allow the check to process on each unit/building/project/whatever being checked. Could lead to considerable slowdown the second way, but be confusing for new modders the first way.

That's a very good idea. I had been trying to think of ways to have the AI use this as well as give people more control over the AI.

UnitUpdate type of intercept can just be a simple <PythonPerTurn> function in which there is a check for isHuman() in the python which then leads to a series of orders being given for AI units that override normal control.

Well, most of the info types have a <PythonDoTurn> tag if that's what you're saying. This would allow people to have particular buildings, civics, etc. perform per turn functions like spawning units and such. Of course, as the tutorial shows, if you add conditions to that method, you can make it so that they will perform the actions every X number of turns rather than every turn.
 
I'm also thinking about some more stuff for the UnitInfos that can be called when initiating combat. In particular, I'm think about combat modifier, first strikes, and withdrawal scripts.


Ok, I'm going to add the AI code xienwolf suggested right now.

I also have code that I worked on for another project that involves modifying Events that I'm going to add into this because it's relevant to this and extremely powerful. This new code will hit event modding like an atomic bomb.
 
I've done lots of XML before, but I'm extremely inexperienced in python, so this question I'm pretty sure is kind of stupid, but I noticed that in this "mod" there are some XML files. Are this required for this thing to work, as in do I have to add in the new tags to every single XML file in an old mod? Or can I just use my old XML files with the python and sdk here?
 
I've done lots of XML before, but I'm extremely inexperienced in python, so this question I'm pretty sure is kind of stupid, but I noticed that in this "mod" there are some XML files. Are this required for this thing to work, as in do I have to add in the new tags to every single XML file in an old mod? Or can I just use my old XML files with the python and sdk here?

Please, ask all the questions you want. I need to know where people are getting stuck so I can include it in a more comprehensive tutorial.

Alright, let's suppose for a moment, that you wanted to create a Wonder called Leonardo's Workshop that automatically upgraded your units ala Civ2.

You would create a python method that cycles through all of your units every turn and then upgrades any units that have valid upgrade paths. Let's suppose you give it the rather inelegant name "doLeonardoWorkshopDoTurn". But how do you get the game to recognize this python method? That's where the XML comes in...

In the CIV4BuildingInfos.xml, you put in the <PythonDoTurn> tag "doLeonardoWorkshopDoTurn". What you're essentially doing by adding that tag is telling the program, "Look for the doLeonardoWorkshopDoTurn method." When a player builds the Leonardo's Workshop, it will be processed in the SDK. If you leave this tag blank, the game will simply assume that you don't want this Wonder to do anything special in python and will move onto the next bit of code.

But you only need to add these tags to the things that you actually want to use python. In other words, if you just want to make one Wonder that uses a python method, you don't have to copy all of the files from my modcomp into your mod. You just need to copy and paste my Buildings schema file into your Buildings folder, and you'll need to add the XML tag in the proper place for the Wonder you want. If your mod is using another modified DLL, you'll have to merge the two first, which gets a bit trickier.
 
Looks like the file size shrunk too.

Yes, I forgot that I had been using the Debug DLL (which is quite a bit bigger than the normal DLL), and I'd zipped that along with the v0.1. v0.2 has the correct "Final Release" DLL.
 
But you only need to add these tags to the things that you actually want to use python. In other words, if you just want to make one Wonder that uses a python method, you don't have to copy all of the files from my modcomp into your mod. You just need to copy and paste my Buildings schema file into your Buildings folder, and you'll need to add the XML tag in the proper place for the Wonder you want.

Alright, that's all I needed to know. So, just to make sure I understand, basically what you're saying is that this tag is optional.

If so, then that sounds great. Thanks a lot!
 
Alright, that's all I needed to know. So, just to make sure I understand, basically what you're saying is that this tag is optional.

If so, then that sounds great. Thanks a lot!

Yes, the XML tags are only necessary for the items that you want to make use of python code.
 
Having an efficient, easy to use way to add action buttons would be great. There are several existing modcomps which enable action buttons in pure python, but some of them add complexity rather than subtracting it. What is the design that you would propose for this?

When I add action buttons in python, I have to make a bunch of changes in CvMainInterface and it is a little messy. I would like to have one callback to determine if the button should appear at all, another one to determine if the button should be greyed out *with a help message*, and then there is the linking you have to do through ModNetMessage to be called when the button is clicked.
 
Having an efficient, easy to use way to add action buttons would be great. There are several existing modcomps which enable action buttons in pure python, but some of them add complexity rather than subtracting it. What is the design that you would propose for this?

When I add action buttons in python, I have to make a bunch of changes in CvMainInterface and it is a little messy. I would like to have one callback to determine if the button should appear at all, another one to determine if the button should be greyed out *with a help message*, and then there is the linking you have to do through ModNetMessage to be called when the button is clicked.

Well, I was just thinking of merging talchas's Action Buttons 2.0, which is done mostly in the SDK I believe.
 
Top Bottom