New Diplomacy Options

Kailric

Jack of All Trades
Joined
Mar 25, 2008
Messages
3,100
Location
Marooned, Y'isrumgone
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

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&#8217;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&#8217;t seem so static.

Next we need to add the Player&#8217;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 &#8220;isComment&#8221;, in the Python definition&#8221; def determineResponses&#8221;. When our option is found it then checks for what the player&#8217;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 &#8220;def handleUserResponse&#8221;.
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 &#8220;enum DllExport DiploEventTypes&#8221; 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&#8217;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&#8217;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:goodjob:
 
Declaring or stopping war against a Christian civ because its holiness (had bribed the papacy) or its sins (someone paid more)

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.

Easy modders way (I think):
Sending a Noble as a present to a civ (or trading him in a trade screen) in exchange of some truce.

Difficult way (I think):
Having an intricate personal adscription of cities entitled to noblemen of all kinds so that you can tie two noblemen of different civs together and they can access their respective cities and in that way build an alliance between the two civs. This idea needs brushing.
 
K, these sound like good ideas :goodjob: Will these still work for mods where there's not only one main "Pope" civ who applies for almost everyone? Ie if some civs have their own unique parent civ rather than everyone sharing one Pope in common. Or if there are a few Pope-like leaders, who are the Parent of different groups of civs (like Pope parent of Catholic civs, Caliph the parent of Muslim civs, Patriarch parent of Orthodox civs, etc). It would be cool if each Parent can have his own likes and dislikes and decrees etc.
 
K, these sound like good ideas :goodjob: Will these still work for mods where there's not only one main "Pope" civ who applies for almost everyone? Ie if some civs have their own unique parent civ rather than everyone sharing one Pope in common. Or if there are a few Pope-like leaders, who are the Parent of different groups of civs (like Pope parent of Catholic civs, Caliph the parent of Muslim civs, Patriarch parent of Orthodox civs, etc). It would be cool if each Parent can have his own likes and dislikes and decrees etc.

Only if you set up a Civ to be the Religious Center will all Players have the same Civ. And at the moment there is only one Religious Center, but I will make it so you can have more. I need to make so that you can just add a "Parent Civ" to a Civilization as well.
 
All this talk about diplomacy is giving me an idea. In some places we call gDLL->getInterfaceIFace()->addPopup(). The exe will then generate the popup window and it will appear on start turn.

What if we make our own variable containing all the arguments and make a vector of those in CvPlayer. We then add CvPlayer::addPopup() to fill the vector. During the player's turn, if the vector isn't empty, a new button appear. Clicking that button will take something from the vector and call DLL->getInterfaceIFace()->addPopup(). There is then a new answer, which is "postpone answer", which puts the popup arguments back into the vector.

The player can't hit end turn before the vector is empty.

This way we can make a popup window type, which we can answer at any time during the turn instead of at the start of the turn. If you are asked to declare war, you can postpone the answer, look at where possible enemy units are, how many they are and so on and then answer. It would also make sense to be able to check city storage when somebody tries to sell you some yields and so on.

Also it allows a very important improvement. The answers in the popup will be recalculated each time the window pops up. This mean if your only answer is "sorry, I don't have the money", you can postpone the answer, sell something and then the "buy something" answer appears.
 
That's cool, I was having a similar thought on the Holy War, where you could say, "Let me consult my advisors. (Wait a turn)", and then have a turn to check out all the factors. But with your idea we can just have a new generic response added instead. I like it very much!
 
We now have a Delayed Response reply in a bunch of the Diplomatic requests. If the option appears you can select "Let me consult my advisors" and then an "Diplomats Waiting" Icon will appear in the top right top left corner. Select this when you are ready to make your response. Also, you can not go to next turn until all Diplomats are dealt with. "Madness? Let me consult my advisors... This is Sparta!"

We have also separated Open Borders and Trade Relations. Trade Relations does what Open Borders use too, except you are only allowed to move about with none military units. Open Borders will allow you to use any unit. You will need to have Trade Relations before you can have Open Borders.
 
My idea for the diplomacy

It might or might not fit to the Medieval: Conquest, but you should check it out. I really feel that diplomacy could be lot more interesting than it is now ( in all Civ games).

While I like the idea of spending points to do things, I'll have to think it through more.


On another note, I have been trading Techs with Genseric in my current game and it has me thinking. While Genseric loves my techs and is willing to pay a decent price, there should be a draw back to giving away all your techs to Minor Civs. Perhaps, if a Minor Civs tech level is the Same or Greater than yours there is a Chance they will advance to Major Civ Status and become more Competition, how about them apples?

Currently, Minor Civs will offer you Techs that you do not have, so Tech trading is advancing along pretty well.

Edit: Hmm, I wonder if you added a boolean for bIsNative. Currently when a Player is checked for being a Native it all goes back to the Civilization Info. I wonder what would happen if we added a boolean check, and if a Minor Civ becomes a Major Civ the boolean would be false. I wonder what that would do? :D
 
Another Diplomacy option idea:

In my current game I am "friends" with the Turks but their Cultural Borders are advancing over mine. My capital city has lost one of its tiles because of this, so I am thinking that we can have a new Agreement that allows you to keep what borders you currently have and also return any lost tiles that are adjacent to your cities. Basically once your borders meet, no Civ's territory will advance over the other.

Would this be a good idea? What could the agreement be called?
 
Sovereignty Agreement/Accord/Concession?

I think it would need to be locked to the highest relations only, as 'cultural warfare' is one of those peaceful methods of conquest, that have you have to try and engage in like everything else.

Right, only if you are on very good terms. Perhaps "Sovereignty of Borders Agreement".
 
It could be interesting if there was Pope elections in the game. Getting your candidate in the Papal office would give you some control over the Pope. For example declaring the Crusade. And it would be one prerequirement for some sort of "Emperor' victory type. Maybe cross production would have some effect for how many votes you have.

I know that electing the Pope might not fit well with current game mechanics, but I just think it's interesting idea.
 
All this talk about diplomacy is giving me an idea. In some places we call gDLL->getInterfaceIFace()->addPopup(). The exe will then generate the popup window and it will appear on start turn.

What if we make our own variable containing all the arguments and make a vector of those in CvPlayer. We then add CvPlayer::addPopup() to fill the vector. During the player's turn, if the vector isn't empty, a new button appear. Clicking that button will take something from the vector and call DLL->getInterfaceIFace()->addPopup(). There is then a new answer, which is "postpone answer", which puts the popup arguments back into the vector.

The player can't hit end turn before the vector is empty.

This way we can make a popup window type, which we can answer at any time during the turn instead of at the start of the turn. If you are asked to declare war, you can postpone the answer, look at where possible enemy units are, how many they are and so on and then answer. It would also make sense to be able to check city storage when somebody tries to sell you some yields and so on.

Also it allows a very important improvement. The answers in the popup will be recalculated each time the window pops up. This mean if your only answer is "sorry, I don't have the money", you can postpone the answer, sell something and then the "buy something" answer appears.

Could we go as far as implementing our own Diplomacy Screen? I can imagine that it would remove lot of possible limitations of current diplomacy interface. At least I believe that if we use some imagination the diplomacy options could be much more interesting. Maybe some group diplomacy could occur or some really sophisticated trade deals or something.
 
Could we go as far as implementing our own Diplomacy Screen? I can imagine that it would remove lot of possible limitations of current diplomacy interface. At least I believe that if we use some imagination the diplomacy options could be much more interesting. Maybe some group diplomacy could occur or some really sophisticated trade deals or something.

Since we have complete control over when a Diplomacy Screen should appear (in the DLL) we there for have full control over every aspect of that Diplomacy Screen. If you wanted to work up some grand details on a proposed change and post in a new thread about it then by all means go for it. :goodjob: My hands are full at the moment with other aspects.
 
Back
Top Bottom