Summary

This MOD COMP allows anyone with a very basic knowledge of MODing Civ to be able to set any unit to receive a promotion (Discussed Below). When a unit does receive a promotion it will display a small bubble with a picture in it, play a predefined sound, and give the unit the promotion it has received. Units can receive promotions from a wide variety of events. These Events range from winning a battle to just moving in your Cultural Borders. These Events can take a wide variety of settings. These settings can range from improvements to promotions.

Section List

Summary
Pointers & Supplies
7 Basic Steps
Notes

Pointers & Supplies [Back to Top]

I have provided the following:
1) A CustomEventManager and the needed CvEventInterface;
2) Documentation;
3) zPromotions;
3) GNU General Public License.txt. Click Here

CAUTION: You should not attempt to edit the Python code unless you know Python basics!!!

5 Basic steps[Back to Top]

To use zPromotions you must do the following to any event manager:
Step 1) [Back to Top]
After the line of code that looks like,
from CvPythonExtensions import * 
put,
from zPromotions import *
so it should look like,
from CvPythonExtensions import *
from zPromotions import *
Step 2) [Back to Top]
At the beginning of the event manager class put:
	class promos(receive):
def __init__(self):
Step 3) [Back to Top]
In the onCombatResult event put the below code:
		if pWinner.canAcquirePromotionAny():
self.promos().doReceive(pWinner, pLoser, None, pLoser.getX(), pLoser.getY(), ["PROMO_COMBAT", "PROMO_COMBAT_PLOT", "PROMO_LOSER"])
CvEventManager.CvEventManager().onCombatResult(argsList) #Call the default function
Step 4) [Back to Top]
In the onUnitMove event put the below code:
		if pUnit.canAcquirePromotionAny():
self.promos().doReceive(pUnit, None, pPlot, pPlot.getX(), pPlot.getY(), ["PROMO_PLOT", "PROMO_CITY_BUILDING", "PROMO_NEUTRAL"])
if pPlot.getOwner() != -1:
self.promos().doReceive(pUnit, None, pPlot, pPlot.getX(), pPlot.getY(), ["PROMO_HOMELAND", "PROMO_BARBARIAN", "PROMO_VASSAL", "PROMO_MASTER", "PROMO_FRIENDLY", "PROMO_ENEMY", "PROMO_HOSTILE"])
if pPlot.isCity():
self.promos().doReceive(pUnit, None, pPlot, pPlot.getX(), pPlot.getY(), ["PROMO_CITY_BUILDING"])
CvEventManager.CvEventManager().onUnitMove(argsList) #Call the default function
Step 5) [Back to Top]
In the onUnitBuilt event put the below code:
		if unit.canAcquirePromotionAny():
self.promos().doReceive(unit, None, unit.plot(), unit.getX(), unit.getY(), ["PROMO_BUILT"])
CvEventManager.CvEventManager().onUnitBuilt(argsList) #Call the default function
Step 6) [Back to Top]
In the onUnitSelected event put the below code:
		if unit.canAcquirePromotionAny():
if unit.specialCargo() == gc.getInfoTypeForString("SPECIALUNIT_FIGHTER"):
self.promos().doReceive(unit, None, None, unit.getX(), unit.getY(), ["PROMO_AIR_CARRIER"])
CvEventManager.CvEventManager().onUnitSelected(argsList) #Call the default function
Step 7) [Back to Top]
Now it is time to create your own promotion event types. The below tells what each entry does:
			self.addPromoType(The Promotion Type,
The Event That Will Give This Promotion (See Below),
The Chance that it will receive a promotion it can be any number between 0 and 100 where 0 does not give a promotion,
The AND data passed to the event,
The OR data passed to the event,
The unit classes that can receive this promotion,
Set to True (True) if you want the unit to receive the promotion and False (False) if you want the unit to lose the promotion,
Set to True (True) if you want the unit to keep the promotion and False (False) if you want the unit to lose the promotion (see below for more info))
A Unique ID (Used because the event manager has a tendency to reload it self and create a ton of the same events, very slow)
And for you more knowledgeable people here is an API style definition:
PROMOTION addPromoType(PromotionType promoType, zEvent eventType, INT promoChance, LIST andData, LIST orData, LIST unitData, BOOL receive, BOOL keep, DICTKEY id)
Here is an example:
			self.addPromoType("PROMOTION_COMBAT5", "PROMO_COMBAT", 10, [], [], [], True, True, "Example1")
This sets it so that all units have a 10% chance of receiving a Combat V promotion from combat.
Now this needs a little clarifying. The first part that reads "PROMOTION_COMBAT5" is the promotion to be given/removed from the winner, it must be inside of quote marks.
"PROMO_COMBAT" tells Civ that the promotion should only be given/removed after a unit wins a battle, it too must be inside quote marks.
The 10 tells Civ that there is only a 10% chance that the winner can be given/lose this promotion, it must not be inside quote marks.
[ ] tells civ that there is no AND data required for this unit to receive a promotion from combat, the [ ] must be present at all times.
[ ] tells civ that there is no OR data required for this unit to receive a promotion from combat, the [ ] must be present at all times.
[ ] tells civ to give this promotion to all unit classes, the [ ] must be present at all times.
The first True tells civ that you want to give the promotion not remove it, this too must not be inside quote marks.
The second True tells civ that you want to let the unit keep the promotion (in this case it could also have been False but that is more advanced and will be discussed below), you must not put this inside quote marks.
And last of all "Example1" is a unique ID, it could just as easily could have been a number outside of the quotes (If it is text it must be inside of quotes) Here are some examples of different IDs:
			self.addPromoType("PROMOTION_COMBAT5", "PROMO_COMBAT", 10, [], [], [], True, True, "1")
			self.addPromoType("PROMOTION_COMBAT5", "PROMO_COMBAT", 10, [], [], [], True, True, 1)
			self.addPromoType("PROMOTION_COMBAT5", "PROMO_COMBAT", 10, [], [], [], True, True, Example1)
The first two are valid ("1", and 1), but the second (Example1) is not because text must be inside of quote marks (there is one an exception, but it is beyond the scope of these instructions). Unseeing code like in the last example would give a message something to the effect of "Example1 Referenced Before Being Bound".

Now let's talk about those square braces. In python they denote a list. They hold a list of items (units, bonuses, cities, etc.), this list can be any size. I have included 3 lists that you can use to tell civ different things about the unit that is supposed to receive the promotion, the techs the units owner should have, and some information about the plot/city that unit is moving to or was built in. Now the first list is called the and prerequisite list, the second is called the or prerequisite list, and the last is the unit class list. In the first list you can put the promotions that the unit must have, the techs that units owner must have, or you could put a mix of both. Then in the second list you can put the bonuses, improvements, features (FEATURE_JUNGLE, FEATURE_FOREST, etc.), or terrain types (TERRAIN_GRASS, TERRAIN_DESERT, etc.) that the plot should have. And the last list holds all of the unit classes (UNITCLASS_TANK, UNITCLASS_MUSKETMAN, etc.) that can receive this promotion. Now here is an example of using the and prerequisite list:
			self.addPromoType("PROMOTION_COVER", "PROMO_COMBAT", 10, ["TECH_ARCHERY", "PROMOTION_COMBATBAT1"], [], [], True, True, "Example2")
Now this sets it so that any unit that has the Combat I promotion and who's owner has the Archery tech has a 10% chance of receiving a Cover promotion from combat. Now let's take a closer look at this code. As you may remember from before, the first part that reads "PROMOTION_COVER" is the promotion to be given/removed from the unit. Next we have "PROMO_COMBAT", this tells Civ that the promotion should only be given/removed after a unit wins a battle. Then we have the 10, it tells Civ that there is only a 10% chance that the unit can be given this promotion. Now we come to the and prerequisite list, it tells civ that the unit must have the Combat I promotion and that it's owner must have the tech Archery. Then we come to or prerequisite list and it is empty (if a list is defined like this [ ] it is empty). Then we find the last list, the unit class list, and it to is empty. Then we have the first True, this True tells civ that we want to give the promotion (instead of taking it away), next is another True this True doesn't do anything in this example because the PROMO_COMBAT event doesn't use it (we will see how this is used soon). And last of all is the ID, it is set to the string "Example2" (in python a string is anything inside of quote marks).
Now let's say we want to give cover when the unit has Combat 1 and it's owner has the Archery tech, as well as if the plot that unit fought the battle on was either a jungle or forest. This would be accomplished with the below code:
			self.addPromoType("PROMOTION_COVER", "PROMO_COMBAT_PLOT", 10, ["TECH_ARCHERY", "PROMOTION_COMBATBAT1"], ["FEATURE_JUNGLE", "FEATURE_FOREST"], [], True, True, "Example3")
Now let's see if we can create this code on our own. First we copy our previous example, then change the or prerequisite list to hold the 2 features jungle and forest, now it will only accept the feature type (not the feature name), so we go into the Terrain folder inside of our MODs Xml directory and open the CIV4FeatureInfos.xml, then we find the jungle entry and paste it's type inside of the or prerequisite list, while doing this we remember to put the jungles type inside of quote marks. Next we must put a comma after the jungles entry in the or prerequisite list (this tells the civ that we ended the last entry in the list and we are starting a new one) then we continue and use the steps we used to add the jungle to add the forest. Now we have to set the event type to "PROMO_COMBAT_PLOT", this will tell civ that it should check the or prerequisite list before giving the promotion. Last of all we have to set the ID, I have chosen "Example3". Now we have added a new promotion type to the PROMO_COMBAT_PLOT event, so when ever a unit wins a battle that was fought over a plot that had either a jungle or a forest and the unit had Combat 1 and it's owner had Archery, the unit will receive the Cover promotion.
Next we decide that we only what warriors and spearmen (this also includes the Phalanx and Quechua) to receive the Cover promotion from combat (using the above data). This would be accomplished through code like this:
			self.addPromoType("PROMOTION_COVER", "PROMO_COMBAT_PLOT", 10, ["TECH_ARCHERY", "PROMOTION_COMBATBAT1"], ["FEATURE_JUNGLE", "FEATURE_FOREST"], ["UNITCLASS_WARRIOR", "UNITCLASS_SPEARMAN"], True, True, "Example4")
Now to make these changes we must first find the unit class for warriors, to do this we open the Xml directory inside of our MOD then we open the Units folder and open the CIV4UnitClassInfos.xml. We find the warrior's class (UNITCLASS_WARRIOR), now we paste the unit class into the unit class list not forgetting to put inside of quotes as well. Then we place our comma after the warriors class and follow the previous steps to add the spearman's unit class (UNITCLASS_SPEARMAN). Lastly we change the ID to Example4. If we use this code Spearmen and warriors with Combat 1 and who's owner has Archery will have a 10% chance of receiving Cover if they fight a battle over a plot that is either jungle or forest.
Thus far we have only worked with the ID, the and prerequisite list, the or prerequisite list , and the unit class list . Next let's see how the events work and what they do. There are 15 events each doing something at least a little different than all the other events. Here is a list of the events and brief list of what they do:
PROMO_COMBAT: This event evaluates the and prerequisite list as well as the unit class list, but it does not check the or prerequisite list (this is for added speed). This event is called after combat.
PROMO_PLOT: This event checks each list. It checks the or prerequisite list for terrain types, feature types, improvement types, and bonus types when the unit moves to a plot.
PROMO_LOSER_COMBAT: This event checks all but the or prerequisite list. This is a combat event that checks to see if the loser has the promotion that you want to give, if it does it gives the promotion to the winner.
PROMO_COMBAT_PLOT: This event is an exact mix of the PROMO_COMBAT and the PROMO_PLOT.
PROMO_HOMELAND: This event only gives the promotion if you move to a plot that is owned by the unit being moved.
PROMO_NEUTRAL Fixed: This event is the opposite of PROMO_HOMELAND, it gives the promo if the unit leaves it's home land.
Version Info: In v1.0 this event would give the promotion if the player did not own the plot, so it would give the promotion if you moved into someone else's cultural borders. This has been fixed in v1.1.

PROMO_CITY_BUILDING New: This event checks each list. It checks the or prerequisite list for building types only. This allows you to give a promotion to a unit when it moves into a city with at least one of the set buildings.
PROMO_BUILT New: This event checks each list. It checks the or prerequisite list for terrain types, feature types, improvement types, building types, and bonus types. This is called when a unit is built.
PROMO_BARBARIAN New: This event is the opposite of PROMO_HOMELAND, it gives the promo if the unit leaves it's home land. Contrary to what it's name implies it doesn't only give the promotion if you move into a plot that is un owned, instead it gives the promotion if the unit moves to a plot that it's owner doesn't own.
PROMO_VASSAL New: This event is the similar to the PROMO_HOMELAND, it gives the promo if the unit enters the cultural borders of a vassal.
PROMO_MASTER New: This event is the similar to the PROMO_HOMELAND, it gives the promo if the unit enters the cultural borders of the master.
PROMO_FRIENDLY New: This event is the similar to the PROMO_HOMELAND, it gives the promo if the unit enters the cultural borders of a player who has open borders with the unit's owner.
PROMO_ENEMY
New: This event is the similar to the PROMO_HOMELAND, it gives the promo if the unit enters the cultural borders of a player who is at war with the units owner.
PROMO_HOSTILE New: This event is the similar to the PROMO_HOMELAND, it gives the promo if the unit enters the cultural borders of a player who is not at war with, have open borders with, is not the vassal of, and is not the master of the units owner. Designed for units that ignore open borders agreements.
PROMO_AIR_CARRIER New: This event gives a promotion to an Aircraft Carrier if it has a SPECIALUNIT_FIGHTER (Fighter or Jet Fighter) on board.
QuircksWill only give the promotion to the Carrier if the carrier is selected, and will only remove the promotion when the carrier is selcted.

Now let's take a more in depth look at these events.

PROMO_COMBAT:
Uses: It uses the and prerequisite list and the unit class
list.
Called: After Combat
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_COMBAT", 10, [], [], [], True, True, "PROMO_COMBAT")

This code gives all units a 10% chance of receiving Combat 1 from combat.

PROMO_PLOT:
Uses:
It uses the lists. It checks the or prerequisite list for terrain types, feature types, improvement types, and bonus types. Called: When a unit moves
Example:

			self.addPromoType("PROMOTION_COMBAT1", "PROMO_PLOT", 10, [], [], [], True, False, "PROMO_PLOT")
This code gives all units a 10% chance of receiving Combat 1 when a unit moves. When the unit moves off of the plot it loses the promotion.
Better Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_PLOT", 10, [], [], ["TERRAIN_DESERT", "FEATURE_JUNGLE"], True, True, "PROMO_PLOT")

Gives all units a 10% chance of receiving Combat 1 when a unit moves onto a plot that is desert or has a jungle on it. When the unit moves off of the plot it loses the promotion.


PROMO_LOSER_COMBAT:
Uses:
It uses the and prerequisite list and the unit class list.
Called: After Combat
Example:

			self.addPromoType("PROMOTION_COMBAT1", "PROMO_LOSER_COMBAT", 10, [], [], [], True, True, "PROMO_LOSER_COMBAT")
This code gives all units a 10% chance of receiving Combat 1 from the loser of combat if the loser already has combat 1.

PROMO_COMBAT_PLOT:
Uses: It uses the lists. It checks the or prerequisite list for terrain types, feature types, improvement types, and bonus types.
Called: After Combat
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_COMBAT_PLOT", 10, [], [], [], True, True, "PROMO_COMBAT_PLOT")
This code gives all units a 10% chance of receiving Combat 1 from combat.
Better Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_COMBAT_PLOT", 10, [], [], ["TERRAIN_DESERT", "FEATURE_JUNGLE"], True, True, "PROMO_COMBAT_PLOT")
Gives all units a 10% chance of receiving Combat 1 when a unit wins a battle on a plot that is desert or has a jungle on it.

PROMO_HOMELAND:
Uses: It uses the and prerequisite list and the unit class
list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_HOMELAND", 10, [], [], [], True, False, "PROMO_HOMELAND")
This code gives all units a 10% chance of receiving Combat 1 when they move into their cultural borders. When the unit moves off of the plot it loses the promotion.

PROMO_NUETRAL Fixed:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_NUETRAL", 10, [], [], [], True, False, "PROMO_NUETRAL")
This code gives all units a 10% chance of receiving Combat 1 when they move out of their cultural borders. When the unit moves off of the plot it loses the promotion.

PROMO_CITY_BUILDING:
Uses: It uses the lists. It checks the or prerequisite list for building types.
Called: When a unit moves into a city.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_CITY_BUILDING", 10, [], [], [], True, False, "PROMO_CITY_BUILDING")
This code gives all units a 10% chance of receiving Combat 1 when they move into a city (owned by it's owner). When the unit moves out of the city it loses the promotion.
Better Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_CITY_BUILDING", 10, [], [], ["BUILDING_BARRACKS"], True, False, "PROMO_CITY_BUILDING")
This code gives all units a 10% chance of receiving Combat 1 when they move into a city (owned by their owner) that has a barracks. When the unit moves out of the city it loses the promotion.

PROMO_BUILT:
Uses: It uses the lists.
It checks the or prerequisite list for terrain types, feature types, improvement types, building types, and bonus types.
Called: When a unit is built.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_BUILT", 10, [], [], [], True, False, "PROMO_BUILT")
This code gives all units a 10% chance of receiving Combat 1 when they built. When the unit moves out of the city it loses the promotion.
Better Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_BUILT", 10, [], [], ["BUILDING_BARRACKS"], True, False, "PROMO_BUILT")
This code gives all units a 10% chance of receiving Combat 1 when they built in a city that has a barracks. When the unit moves out of the city it loses the promotion.

PROMO_BARBARIAN New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_BARBARIAN", 10, [], [], [], True, False, "PROMO_BARABRIAN")
This code gives all units a 10% chance of receiving Combat 1 when they move into the cultural borders of a vassal civ. When the unit moves out of the vassal's culture it loses the promotion.

PROMO_MASTER
New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_MASTER", 10, [], [], [], True, False, "PROMO_MASTER")
This code gives all units a 10% chance of receiving Combat 1 when they move into the cultural borders of a master civ. When the unit moves out of the master's culture it loses the promotion.

PROMO_FRIENDLY New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_FRIENDLY", 10, [], [], [], True, False, "PROMO_FRIENDLY")
This code gives all units a 10% chance of receiving Combat 1 when they move into the cultural borders of a civ that has open borders. When the unit moves out of the civ's culture it loses the promotion.

PROMO_ENEMY New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_ENEMY", 10, [], [], [], True, False, "PROMO_ENEMY")
This code gives all units a 10% chance of receiving Combat 1 when they move into the cultural borders of a civ that is at war. When the unit moves out of the civ's culture it loses the promotion.

PROMO_HOSTILE New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_HOSTILE", 10, [], [], [], True, False, "PROMO_HOSTILE")
This code gives all units a 10% chance of receiving Combat 1 when they move into the cultural borders of a civ that doesn't have open borders, is not at war, is not the vassal, and is not the master of the unit's owner. When the unit moves out of the civ's culture it loses the promotion.

PROMO_AIR_CARRIER New:
Uses: It uses the and prerequisite list and the unit class list.
Called: When a unit moves.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_AIR_CARRIER", 10, [], [], [], True, False, "PROMO_AIR_CARRIER")
This code gives all units a 10% chance of receiving Combat 1 when they are selected and are carrying a SPECIALUNIT_FIGHTER (Fighter, Jet Fighter).


Next let's talk about the 2 Boolean values (A boolean value is one that can only be True or False). The first one when set to True tells civ to give the promotion to the unit, this is the only value we have used thus far. If you set it to False it will remove the promotion.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_COMBAT", 10, [], [], [], False, True, "BOOL1")
This will give every unit a 10% chance of losing Combat 1 after combat.
Some events don't allow you to keep the promotion you receive so I created the second boolean value. If it is set to True the unit will get to keep the promotion.
Example:
			self.addPromoType("PROMOTION_COMBAT1", "PROMO_PLOT", 10, [], [], [], True, True, "BOOL2")
This will give all units a 10% chance of receiving Combat 1 when they move onto a plot, and allow them to keep the promotion when it leaves the plot.
If you set it to False it uses the default value.

There are some things that you should know about the PROMO_HOMELAND and PROMO_NUETRAL events. For Zebra 9's Additions I wanted to receive a promotion Homeland 1 and if you already had Homeland 1 I wanted you to receive Homeland 2 at a 10% chance, my code looked like this:
									 self.addPromoType("PROMOTION_HOMELAND2", "PROMO_HOMELAND", 10, [], [], [], True, False, 1)
self.addPromoType("PROMOTION_HOMELAND1", "PROMO_HOMELAND", 100, [], [], [], True, False, 2)
Well after much work I finally got the homeland 2 promotion, but then I moved my unit into my city and I lost homeland 2, it was then that I realized that it would reevaluate a unit when it moved if it decided it should not have the promotion it would remove it (it is supposed to do this). So to fix the problem I changed the code so that you had a 10% chance of receiving and keeping Homeland 2, but I also added PROMO_NUETRAL so that when I left my cultural borders I would have 100% chance of losing homeland 2. My final result code looks like this:
									 self.addPromoType("PROMOTION_HOMELAND2", "PROMO_HOMELAND", 10, [], [], [], True, True, 1)
self.addPromoType("PROMOTION_HOMELAND1", "PROMO_HOMELAND", 100, [], [], [], True, False, 2)
self.addPromoType("PROMOTION_HOMELAND2", "PROMO_NUETRAL", 100, [], [], [], False, False, 2)
This works in the way that I want, and hopefully helps you from having a crazy day of trying to figure out what is happening. Now remember PROMO_NUETRAL, PROMO_BARBARIAN, PROMO_VASSAL, PROMO_MASTER, PROMO_FRIENDLY, PROMO_ENEMY, and PROMO_HOSTILE all have the same quirck, so try to use code like above.

Just incase you didn't know you only need to have 1 of the ORs that you passed to be True to get the promotion, but you need all of the ANDs that you passed to be True to get the promotion. Now if you have a mix of ANDs and ORs you have to have at least 1 OR come out True and all of the ANDs have to come out True or you wont get a promotion. Remember that if you misspell something it will give an error, while you are testing or playing, telling you what it could not find.
Now I would like to suggest that you do not do any operations that would remove a default game promotion or a promotion that you added that is intended to be used like a normal promotion without careful consideration. The reason being that once removed you have to earn another promotion before you can get it again. So I would suggest that you only remove promotions that you have given to a unit using this code.
Notes [Back to Top]:
No Notes