Kailric
Jack of All Trades
I am wanting to write a tutorial on how to add new Diplomacy Options, but I would like to add a new Diplomacy option in the process. I keep forgetting the steps myself so I want to get them wrote down. The question is what option should I do? Here is a list of some of the things I have in mind, please post your own...
Fix up Ransom Knight Code
Leaders will appear and offer to Random back your Knights defeated in battle
Poaching and Hunting Rights
You can give or take away Hunting Rights on your land. This I will save until I visit the whole Hunting mechanic and do it all at once.
Trade Commodity Deals
Such as a certain amount of Goods each turn is delivered to a City.
Pope Decrees (I take these ideas from the different Councils the Papacy held)
Banned Civics
This is already in the develop branch
Banned Techs
Such as Crossbow. I can easily mod the Banned Civics code to included this
Declare a City Holy
You can no longer attack that City and it is declared holy. Either by a Martyr, a Relic, Shrine, etc.
Declare Holy War
The Pope declares a Crusade against one of his rivals
Truce of God
Ends all Wars for a certain amount of turns
Robbers of pilgrims and of merchants shall be excommunicated
This will be a good one for when we add the Bandit profession
There are plenty more Councils to draw inspiration from for Papal Decrees
Giving away your Sons and Daughters to marriage in order to form alliances was a big deal back then. I wonder if we could make this fit into M:C some how.
Current Diplomacy Options
Trade Techs
Make Research Pacts
Help each other on a Research Project
Trade Relations
This is required before someone will Open Their Borders. Merchants and Traders are the only ones allowed to cross borders
Open Borders
Any unit my cross borders
Contact Info
As in Civ games you can trade Contacts.
Hunting Rights
Hunters are allowed on each others land. Still need to add Diplomacy Options to ban Native Civs from hunting on your land.
Adding new Diplomacy Options and Events Tutorial
In this Tutorial we will give a brief example on how to add new Diplomacy Options and Events. To do this you will need to be able to work with XML, Python, and C++ in the DLL.
First off we need to decide what we want our new Diplomacy Option to do. For this tutorial we will be adding a new Crusade Diplomacy Option for the Pope. The goal is for the Pope to check to see if there are any Civs that he is Annoyed with and then check to see if there are any Players he is Pleased with. If he finds both he may ask the one to Crusade against the other, while staying out of the fight himself.
First we can visit CIV4DiplomacyInfos.xml to create our new option:
A DiplomacyInfo is setup with responses. Think of it as a conversation. For the Initial Response the Pope will state his request. You can have Civs set up to give Unique responses by creating another Response, but for this exercise we just need the Pope’s response.
First we need to define a Type. The Type will need to be Unique and not used by any other Diplomacy Option. Here we have used AI_DIPLOCOMMENT_ACCEPT_HOLY_WAR.
So, we have added the Pope to the list of Leaders. The Code will check here for the Leader in question. If you do not add a Leader then all Leaders will use this Response. So, technically since the Pope is the only Leader that will use this DiplomacyInfo we do not have to add a Leader here.
Moving on, the DiplomacyText is the text that will be displayed in the game. It is the message the Pope is speaking to the player. You can use multiple text here. For example we can add a AI_DIPLO_JOIN_HOLY_WAR_2, which has a slightly different message. This can add a bit of randomness to the conversations so they don’t seem so static.
Next we need to add the Player’s Options:
Here we have simply added the Type and what the DiplomacyText will be. For this initial tutorial this is all we will need.
For the next step we will be using Python. I use the Free Komodo python editor.
Open up CvDiplomacy.py in your chosen editor and find a suitable spot to add our code. I chose to put the initial code just below the Kiss Pinky code.
First we allow the code to find our new Option in “isComment”, in the Python definition” def determineResponses”. When our option is found it then checks for what the player’s choices will be. Here we have added the Accept Holy War option plus the Generic NO_JOIN_WAR option.
With this much codded the Diplomacy Popup can appear in the game with the Pope asking the Player if he wants to go on a Crusade. The Player will have the two options to choose from. Now we must add code for what happens when the Player makes a choice. This is handled in the Python definition “def handleUserResponse”.
We have added our code just above the generic No Join War comment. Here is where we set it up so that Python speaks to the DLL and makes the magic happen. This is handled in the DiploEventTypes code, which we will describe in detail in a bit. First to finish our Python code, we have chosen to allow the conversation to continue by adding the AI_DIPLOCOMMENT_THANKS comment, which is generic code that allows you to return to a general state of Diplomacy Options. However, If we want the Conversation to end after the Player chooses to accept we should replace this code with diploScreen.closeScreen().
As you can see you can actually keep the conversation going by adding new AI_DIPLOCOMMENT types. Also, here is where you change the Emotions the AI player displays in the game by using the def performHeadAction. By using the examples given you can cause the in Game model of the AI player to show a Friendly attitude, Furious attitude, etc. However, the LeaderHead model in question has to have the animations for this to truly take effect.
Now, to get this new option to work we must setup the DLL. This is the C++ work. First we need to edit CvEnums.h to add our new DiploEventTypes, which does not break saved games by the way. Add the DIPLOEVENT_ACCEPT_HOLY_WAR to the end of the function “enum DllExport DiploEventTypes” using the existing code for example.
You will also need to add this enum to the CyEnumsInterface.cpp in order for Python to know what this new enum is. Add .value("DIPLOEVENT_ACCEPT_HOLY_WAR", DIPLOEVENT_ACCEPT_HOLY_WAR) to the python::enum_<DiploEventTypes>("DiploEventTypes") values.
Now that the DLL and Python know what our new enum is we move on to the brains of the operation.
First we open CvPlayerAI.cpp and here we need to added a new function. I created the new function bool CvPlayerAI::AI_doDiploAskJoinHolyWar(PlayerTypes ePlayer) which I will not go into the details on adding functions here as this is covered in other tutorials on the web plus it helps to have a general knowledge of C++ to do this part. However, I will explain where in the code this new function needs to go.
Our new function, AI_doDiploAskJoinHolyWar will allow the Pope to search for a target player to ask others to Crusade against. This new function is added to void CvPlayerAI::AI_doDiplo(). This function is called every turn and cycles through all the Diplomacy Options and attempts to find one that has all the right elements in place in order to happen.
I add the below code just before the AI checks if it wants to AI_doDiploAskJoinWar
, which is it asks someone if they want to join a war. In the below code it basically checks if the Player has been contacted yet, if not it checks to see if there are any Holy Wars to negotiate and if so it sets the Human player as having been contacted. This prevents the AI from having multiple requests per turn on human players.
That sets up the initial popup, but now we need to add what happens when the Player makes his choice. This is handled in CvPlayer.cpp’s void CvPlayer::handleDiploEvent
Go there now and we add the below code:
Here, we set the Crusading Player at war with target Player. There are a couple of other things here that are important. We change the MEMORY_ACCEPTED_JOIN_WAR memory enum by 1. This allows the Pope to have a positive modifier to this event. Memory types allow the AI to remember what has or hasn’t happened to them in relation to other players. This effects their attitudes towards that player. For this event we simply reused the generic Join War memory but perhaps we should create a new memory type specific to this event. That however is outside this tutorial.
The other thing that happens is another memory type is increased, that being MEMORY_HIRED_WAR_ALLY. This memory type tells the AI that it has recently hired an Ally which is used in other parts of the code.
Though I did not display it, there is one other type of enum that is used besides Memory types and that is the Contact types. Each Leader has Contract type attributes that determine how often and when the AI will contact another player for all the different Diplomatic events. For this example I reused the CONTACT_JOIN_WAR timer in the AI_doDiploAskJoinHolyWar function. When the AI contacts the player we do:
This sets it up so that this AI Player will not Contact this Player on this issue for a set amount of more turns.
Each Diplomatic Event can have its own MemoryTypes and ContactTypes that can greatly increase realistic AI behavior. Note; adding either of these two will break saved games.
That's it for now. I must head on out as the Pope just asked me to Crusade against some ruffians. Happy Coding
Fix up Ransom Knight Code
Leaders will appear and offer to Random back your Knights defeated in battle
Poaching and Hunting Rights
You can give or take away Hunting Rights on your land. This I will save until I visit the whole Hunting mechanic and do it all at once.
Trade Commodity Deals
Such as a certain amount of Goods each turn is delivered to a City.
Pope Decrees (I take these ideas from the different Councils the Papacy held)
Banned Civics
This is already in the develop branch
Banned Techs
Such as Crossbow. I can easily mod the Banned Civics code to included this
Declare a City Holy
You can no longer attack that City and it is declared holy. Either by a Martyr, a Relic, Shrine, etc.
Declare Holy War
The Pope declares a Crusade against one of his rivals
Truce of God
Ends all Wars for a certain amount of turns
Robbers of pilgrims and of merchants shall be excommunicated
This will be a good one for when we add the Bandit profession
There are plenty more Councils to draw inspiration from for Papal Decrees
Giving away your Sons and Daughters to marriage in order to form alliances was a big deal back then. I wonder if we could make this fit into M:C some how.
Current Diplomacy Options
Trade Techs
Make Research Pacts
Help each other on a Research Project
Trade Relations
This is required before someone will Open Their Borders. Merchants and Traders are the only ones allowed to cross borders
Open Borders
Any unit my cross borders
Contact Info
As in Civ games you can trade Contacts.
Hunting Rights
Hunters are allowed on each others land. Still need to add Diplomacy Options to ban Native Civs from hunting on your land.
Adding new Diplomacy Options and Events Tutorial
Spoiler :
In this Tutorial we will give a brief example on how to add new Diplomacy Options and Events. To do this you will need to be able to work with XML, Python, and C++ in the DLL.
First off we need to decide what we want our new Diplomacy Option to do. For this tutorial we will be adding a new Crusade Diplomacy Option for the Pope. The goal is for the Pope to check to see if there are any Civs that he is Annoyed with and then check to see if there are any Players he is Pleased with. If he finds both he may ask the one to Crusade against the other, while staying out of the fight himself.
First we can visit CIV4DiplomacyInfos.xml to create our new option:
Code:
<DiplomacyInfo>
<Type>AI_DIPLOCOMMENT_ACCEPT_HOLY_WAR</Type>
<Responses>
<Response>
<Civilizations/>
<Leaders>
<Leader>
<LeaderType>LEADER_THE_ANGLO_POPE</LeaderType>
<bLeaderType>1</bLeaderType>
</Leader>
</Leaders>
<Attitudes/>
<DiplomacyPowers/>
<DiplomacyText>
<Text>AI_DIPLO_JOIN_HOLY_WAR_1</Text>
</DiplomacyText>
</Response>
</Responses>
</DiplomacyInfo>
A DiplomacyInfo is setup with responses. Think of it as a conversation. For the Initial Response the Pope will state his request. You can have Civs set up to give Unique responses by creating another Response, but for this exercise we just need the Pope’s response.
First we need to define a Type. The Type will need to be Unique and not used by any other Diplomacy Option. Here we have used AI_DIPLOCOMMENT_ACCEPT_HOLY_WAR.
So, we have added the Pope to the list of Leaders. The Code will check here for the Leader in question. If you do not add a Leader then all Leaders will use this Response. So, technically since the Pope is the only Leader that will use this DiplomacyInfo we do not have to add a Leader here.
Moving on, the DiplomacyText is the text that will be displayed in the game. It is the message the Pope is speaking to the player. You can use multiple text here. For example we can add a AI_DIPLO_JOIN_HOLY_WAR_2, which has a slightly different message. This can add a bit of randomness to the conversations so they don’t seem so static.
Next we need to add the Player’s Options:
Code:
<DiplomacyInfo>
<Type>USER_DIPLOCOMMENT_ACCEPT_HOLY_WAR</Type>
<Responses>
<Response>
<Civilizations/>
<Leaders/>
<Attitudes/>
<DiplomacyPowers/>
<DiplomacyText>
<Text>AI_DIPLO_ACCEPT_HOLY_WAR_1</Text>
</DiplomacyText>
</Response>
</Responses>
</DiplomacyInfo>
Here we have simply added the Type and what the DiplomacyText will be. For this initial tutorial this is all we will need.
For the next step we will be using Python. I use the Free Komodo python editor.
Open up CvDiplomacy.py in your chosen editor and find a suitable spot to add our code. I chose to put the initial code just below the Kiss Pinky code.
Code:
elif (self.isComment(eComment, "AI_DIPLOCOMMENT_KISS_PINKY")):
#iDiplomacyType = 1
# We can accept their demands
self.addUserComment("USER_DIPLOCOMMENT_KISS_PINKY", -1, -1)
# Or reject them...
player = gc.getPlayer(gc.getGame().getActivePlayer())
eYield = player.getHighestTradedYield()
iCityId = player.getHighestStoredYieldCityId(eYield)
city = player.getCity(iCityId)
if city.isNone():
szCityName = u""
else:
szCityName = city.getNameKey()
self.addUserComment("USER_DIPLOCOMMENT_TAX_PARTY", eYield, -1, szCityName, gc.getYieldInfo(eYield).getTextKey())
#TKs Med
elif (self.isComment(eComment, "AI_DIPLOCOMMENT_ACCEPT_HOLY_WAR")):
# We can accept their demands
self.addUserComment("USER_DIPLOCOMMENT_ACCEPT_HOLY_WAR", -1, -1)
# Or reject them...
self.addUserComment("USER_DIPLOCOMMENT_NO_JOIN_WAR", -1, -1)
First we allow the code to find our new Option in “isComment”, in the Python definition” def determineResponses”. When our option is found it then checks for what the player’s choices will be. Here we have added the Accept Holy War option plus the Generic NO_JOIN_WAR option.
With this much codded the Diplomacy Popup can appear in the game with the Pope asking the Player if he wants to go on a Crusade. The Player will have the two options to choose from. Now we must add code for what happens when the Player makes a choice. This is handled in the Python definition “def handleUserResponse”.
Code:
#TK if we accept the holy war
elif (self.isComment(eComment, "USER_DIPLOCOMMENT_ACCEPT_HOLY_WAR")):
diploScreen.diploEvent(DiploEventTypes.DIPLOEVENT_ACCEPT_HOLY_WAR, diploScreen.getData(), -1)
self.setAIComment(self.getCommentID("AI_DIPLOCOMMENT_THANKS"))
# If we refuse to join their war
elif (self.isComment(eComment, "USER_DIPLOCOMMENT_NO_JOIN_WAR")):
diploScreen.diploEvent(DiploEventTypes.DIPLOEVENT_NO_JOIN_WAR, -1, -1)
self.setAIComment(self.getCommentID("AI_DIPLOCOMMENT_JOIN_DENIED"))
We have added our code just above the generic No Join War comment. Here is where we set it up so that Python speaks to the DLL and makes the magic happen. This is handled in the DiploEventTypes code, which we will describe in detail in a bit. First to finish our Python code, we have chosen to allow the conversation to continue by adding the AI_DIPLOCOMMENT_THANKS comment, which is generic code that allows you to return to a general state of Diplomacy Options. However, If we want the Conversation to end after the Player chooses to accept we should replace this code with diploScreen.closeScreen().
As you can see you can actually keep the conversation going by adding new AI_DIPLOCOMMENT types. Also, here is where you change the Emotions the AI player displays in the game by using the def performHeadAction. By using the examples given you can cause the in Game model of the AI player to show a Friendly attitude, Furious attitude, etc. However, the LeaderHead model in question has to have the animations for this to truly take effect.
Now, to get this new option to work we must setup the DLL. This is the C++ work. First we need to edit CvEnums.h to add our new DiploEventTypes, which does not break saved games by the way. Add the DIPLOEVENT_ACCEPT_HOLY_WAR to the end of the function “enum DllExport DiploEventTypes” using the existing code for example.
You will also need to add this enum to the CyEnumsInterface.cpp in order for Python to know what this new enum is. Add .value("DIPLOEVENT_ACCEPT_HOLY_WAR", DIPLOEVENT_ACCEPT_HOLY_WAR) to the python::enum_<DiploEventTypes>("DiploEventTypes") values.
Now that the DLL and Python know what our new enum is we move on to the brains of the operation.
First we open CvPlayerAI.cpp and here we need to added a new function. I created the new function bool CvPlayerAI::AI_doDiploAskJoinHolyWar(PlayerTypes ePlayer) which I will not go into the details on adding functions here as this is covered in other tutorials on the web plus it helps to have a general knowledge of C++ to do this part. However, I will explain where in the code this new function needs to go.
Our new function, AI_doDiploAskJoinHolyWar will allow the Pope to search for a target player to ask others to Crusade against. This new function is added to void CvPlayerAI::AI_doDiplo(). This function is called every turn and cycles through all the Diplomacy Options and attempts to find one that has all the right elements in place in order to happen.
I add the below code just before the AI checks if it wants to AI_doDiploAskJoinWar
, which is it asks someone if they want to join a war. In the below code it basically checks if the Player has been contacted yet, if not it checks to see if there are any Holy Wars to negotiate and if so it sets the Human player as having been contacted. This prevents the AI from having multiple requests per turn on human players.
Code:
if (!kPlayer.isHuman() || !abContacted[kPlayer.getTeam()])
{
if (AI_doDiploAskJoinHolyWar(ePlayer))
{
if (kPlayer.isHuman())
{
abContacted[kPlayer.getTeam()] = true;
}
}
}
That sets up the initial popup, but now we need to add what happens when the Player makes his choice. This is handled in CvPlayer.cpp’s void CvPlayer::handleDiploEvent
Go there now and we add the below code:
Code:
case DIPLOEVENT_ACCEPT_HOLY_WAR:
AI_changeMemoryCount(ePlayer, MEMORY_ACCEPTED_JOIN_WAR, 1);
GET_TEAM(GET_PLAYER(ePlayer).getTeam()).declareWar(((TeamTypes)iData1), false, WARPLAN_DOGPILE);
for (iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == ((TeamTypes)iData1))
{
GET_PLAYER((PlayerTypes)iI).AI_changeMemoryCount(getID(), MEMORY_HIRED_WAR_ALLY, 1);
}
}
}
break;
Here, we set the Crusading Player at war with target Player. There are a couple of other things here that are important. We change the MEMORY_ACCEPTED_JOIN_WAR memory enum by 1. This allows the Pope to have a positive modifier to this event. Memory types allow the AI to remember what has or hasn’t happened to them in relation to other players. This effects their attitudes towards that player. For this event we simply reused the generic Join War memory but perhaps we should create a new memory type specific to this event. That however is outside this tutorial.
The other thing that happens is another memory type is increased, that being MEMORY_HIRED_WAR_ALLY. This memory type tells the AI that it has recently hired an Ally which is used in other parts of the code.
Though I did not display it, there is one other type of enum that is used besides Memory types and that is the Contact types. Each Leader has Contract type attributes that determine how often and when the AI will contact another player for all the different Diplomatic events. For this example I reused the CONTACT_JOIN_WAR timer in the AI_doDiploAskJoinHolyWar function. When the AI contacts the player we do:
Code:
AI_changeContactTimer(ePlayer, CONTACT_JOIN_WAR, GC.getLeaderHeadInfo(getPersonalityType()).getContactDelay(CONTACT_JOIN_WAR));
This sets it up so that this AI Player will not Contact this Player on this issue for a set amount of more turns.
Each Diplomatic Event can have its own MemoryTypes and ContactTypes that can greatly increase realistic AI behavior. Note; adding either of these two will break saved games.
That's it for now. I must head on out as the Pope just asked me to Crusade against some ruffians. Happy Coding
