[python] causing diplomacy action?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
In my mod I have a religious victory. Playtester feedback is that this victory is too easy to achieve, because the AI players don't defend against it. All you have to do is get open borders and then spam missionaries.

I have a routine which detects when any player gets above 50% religious influence, so I can give a popup to warn the human player. I would like this routine to also trigger all the AI's to cancel open borders with the player who got the 50%. I think this will be a big help.

I can see a function diploEvent in the python interface, but it isn't clear how to use it, and none of the Firaxis mods call it. There is a callback in CvGameUtils.py, AI_doDiplo, but again there are no examples I can find where it does anything.

How can I use python to cancel open borders between A and B?
 
This works but it's kinda buggy. For example when I tested it, I (as player 0) had open borders with player 6. Calling closeOpenBorders(0,6) not only cancelled the deal, but the other player got offended and gave me the "Maybe in a few years" redline on re-opening them. However, calling closeOpenBorders(6,0) did nothing at all. I tried to write it so the order doesn't matter, but I was operating pretty much in the dark and obviously didn't succeed.

Anyhow, it's a starting point for you.
Code:
def closeOpenBorders (iPlayerA, iPlayerB):
	""" Cancels any deals involving Open Borders between the two players.

	Returns True if a deal was cancelled.
	Returns False if no applicable deal was found.
	"""
	for i in range(gc.getGame().getIndexAfterLastDeal()):
		deal = gc.getGame().getDeal(i)
		if ((deal.getFirstPlayer() == iPlayerA and deal.getSecondPlayer() == iPlayerB) or (deal.getSecondPlayer() == iPlayerB and deal.getFirstPlayer() == iPlayerA)):
			for j in range(deal.getLengthFirstTrades()):
				tradeData = deal.getFirstTrade(j)
				if tradeData.ItemType == TradeableItems.TRADE_OPEN_BORDERS:
					deal.kill()
					return True
	return False

I basically just looked at how the Foreign Advisor accessed the list of current trades and then just started trying stuff to see what would happen. :p For further reference, look at the CyDeal class and TradeableItems data types in the API and maybe poke around CvDeal.cpp in the SDK.
 
Wow, that's great. I have never looked at the deal objects. I will try it out. BTW, I see why 0,6 worked and 6,0 didn't, despite your double-ended test. Reread it carefully: you wrote if (1==A and 2==B) or (2==B and 1==A).
 
Perhaps then skip the dual test and only go one way. 0,6 will have 0 cancel the deal and 6,0 will have 6 cancel the deal. I assume that each pairwise deal like this is in the list twice, otherwise how can you make a specific side kill the deal?

To be clearer, you want the AIs who are afraid of losing to cancel the deal so the player with the high religion spread doesn't pay the diplo penalties.

As for game mechanics, maybe you should make the closing of borders dependent on the AI's attitude toward the player. AI's that are Friendly will typically vote for a diplomatic victory, so maybe they should keep open borders in the face of a religious victory.
 
Right, if they are friendly or if they are vassals, they will not cancel open borders. Those details I can handle, it was the mechanics of canceling that I could not work out.
 
Right, if they are friendly or if they are vassals, they will not cancel open borders. Those details I can handle, it was the mechanics of canceling that I could not work out.

Looks like I spoke too soon. I can use CyTeam.isVassal to find if the player about to cancel is a vassal. But, grr ... I must be overlooking the method to tell what is the attitude of one player/team to another.

There is nothing like getAttitude I can find; how can I tell if player1 is friendly to player2?

I have coded my function using deal.kill and it is working. But, it only shows up as a transient message. If an AI player actually cancels a deal with a human player, doesn't the LH window pop up with some text? Is there any way to get that effect?

I am not positive that this popup will be desirable; if the player has OB with a number of AI's and they all show up to cancel, that is a bunch of popups. Still, I would like to experiment with this behavior since transient messages can easily be overlooked by the player.

EDIT: oh, and another question. deal.kill() doesn't take any arguments, so I can't tell the engine whether player1 canceled the deal or player2 canceled the deal. It seems that the player who didn't cancel, should be mad at the player who did cancel. I can't reproduce this in my test game so far; I cancel, and the other player doesn't like me any less. So I guess it is two more questions: does it ever make a difference which player cancels, and if so, then how can I specify which player canceled the deal?

EDIT2: based on my interpretation of the deal object, if A and B make open borders, there is only one deal. Consider a more complex deal where A trades deer and furs to B, who gives oil in return. This is apparently stored as one deal, where A is FirstPlayer and B is SecondPlayer. Then iterating over deal.FirstTrades will return deer and furs, iterating over deal.SecondTrades will return oil. The code written by Dresden is correct, *as long as* there is never a deal where OB is traded for something else. Otherwise we would have to check both FirstTrades and SecondTrades to see if an item is OB.
 
For more fun and excitement, I have been reminded that as a human player, I cannot cancel a deal for ten turns after I have made it. For example, suggest open borders with somebody, then two turns later try to cancel it. The game prevents you, until 10 turns have passed.

So, in order to play fair, I cannot allow the AI to make an open borders agreement, then two turns later cancel it because I went over the danger threshold. That means I need to know on what turn a deal was created. There is no information in the trade data objects about that.

Can I get a notification of when a deal is made between any two players, AI or human, so that I can store it my own self?
 
Oh, somehow I missed CyPlayer.AI_getAttitude. But I am still looking for info about having the right player cancel the deal, popping the LH to announce the cancellation, and postponing a cancellation if the deal was recently agreed.
 
We have a 20 question-per-post limit here at CFC, but I'll let ya slide this time. ;)

There is nothing like getAttitude I can find; how can I tell if player1 is friendly to player2?

You'll love this one: it's called AI_getAttitude(), and it is on CyPlayer and takes a player ID.

Code:
# From BUG's AttitudeUtils module
def getAttitudeCategory (nPlayer, nTarget):
	"""Returns the attitude level nPlayer has toward nTarget [0,4]."""
	if hasAttitude(nPlayer, nTarget):
		return [B]gc.getPlayer(nPlayer).AI_getAttitude(nTarget)[/B]
	return None

I have coded my function using deal.kill and it is working. But, it only shows up as a transient message. If an AI player actually cancels a deal with a human player, doesn't the LH window pop up with some text? Is there any way to get that effect?

You are correct about a window popping up when an AI cancels a deal. I'm not sure if you can make this happen. I know you can check if an AI will accept a deal from Python, but I haven't experimented with this. I would take a look at CyDiplomacy and CvDiplomacy. Maybe you can initiate the diplomacy window.

I am not positive that this popup will be desirable; if the player has OB with a number of AI's and they all show up to cancel, that is a bunch of popups.

In this case I as a player would prefer a simple popup telling me that everyone is closing their borders and why rather than each AI do it separately.

However, I was going to suggest that instead of every AI closing at the same time, have a percentage chance (based on difficulty maybe?) that any given AI will close its borders once you get over the threshold, or make it depend on the percentage of your religion's spread. It could depend on other factors like attitude, aggressiveness, total value of all trade routes they have with you, etc. Maybe you check each time you spread your religion into a city of theirs rather than every turn.

My point is that I prefer a more fluid rule with some randomness built in rather than a hard 60% and everyone closes their borders all on the same turn.

It seems that the player who didn't cancel, should be mad at the player who did cancel.

Actually, I think you only get the diplomatic penalty if you agree to another AI's trade embargo. I just tested in my current game, and canceling every deal against an AI causes no penalty. I am positive that joining a trade embargo earns you the penalty.

Now, this game mechanic sounds very much like a trade embargo of a different sort -- a religion embargo. :) So maybe you want a diplomatic penalty. I suppose you could create one of your own, though I suspect that humans will be pursuing this victory more often than AIs.

The code written by Dresden is correct, *as long as* there is never a deal where OB is traded for something else. Otherwise we would have to check both FirstTrades and SecondTrades to see if an item is OB.

I'm writing code right now for BUG that intercepts the diplomacy window actions, including trades, gifts, and demands, and I've found that both sides trade OB to each other in a single deal. Therefore, Dresden's code of searching only one side of the deal should work.

You are correct, though, that you'll be canceling other trades in these cases. Tough petunias. This is precisely why I always trade OB in a separate trade, especially since all AIs value it the same (yours vs. theirs), unlike maps and bonuses.

That means I need to know on what turn a deal was created. There is no information in the trade data objects about that.

CyDeal.getInitialGameTurn() doesn't work for this? If not, I'll have my DiplomacyEvents code done soon and you're free to use it. You'll get a new event for each trade.
 
We have a 20 question-per-post limit here at CFC, but I'll let ya slide this time. ;)

Fortunately there is no 20-answer limit, thanks for your help. I am now using AI_getAttitude and the whole function is doing enough for a first playtest.

You are correct about a window popping up when an AI cancels a deal. I'm not sure if you can make this happen. I know you can check if an AI will accept a deal from Python, but I haven't experimented with this. I would take a look at CyDiplomacy and CvDiplomacy. Maybe you can initiate the diplomacy window.

I could certainly use some pointers on that. In CyDiplomacy I see diploEvent, which looks promising, but DiploEventType doesn't appear to have a cancel choice. Since there could be multiple deals, the event would also have to take an argument listing the deal number. I can't find any examples at cfc, or in any of the Firaxis mods, or in the random assortment of mods I have downloaded.

However, I was going to suggest that instead of every AI closing at the same time, have a percentage chance (based on difficulty maybe?) that any given AI will close its borders once you get over the threshold, or make it depend on the percentage of your religion's spread. It could depend on other factors like attitude, aggressiveness, total value of all trade routes they have with you, etc. Maybe you check each time you spread your religion into a city of theirs rather than every turn.

Yes, that would be better. I am trying a simple solution first. It may be that canceling open borders is either "too fatal" to this victory or "not fatal enough", so I need to experiment with the simple solution first. Thanks for pointing out getInitialGameTurn, which I had also missed. But I will postpone using this due to the added complexity of delaying the cancellation, until after I playtest to see if the basic mechanic works.
 
Top Bottom