Error with AI Diplomacy Statements

Putmalk

Deity
Joined
Sep 26, 2010
Messages
2,652
Location
New York
Hey,

I have been trying to solve this problem for about 6 hours now. :( I've created a new trade object "Trade World Map" and have gotten it implemented in the game. The AI correctly values the object, but for some reason, a curious issue has emerged that I have been unable to solve.

I offer Sejong my embassy. When I click "What will you give me for this?" he should offer his other embassy in equal exchange.
DwxhjMQh.jpg


As predicted, he does, but his text is not "What do you think of this deal?", it's "That is not even close to a fair deal."
wZmv6LNh.jpg


As seen below, that clearly is a fair deal.
ADjyadZh.jpg


Again, fair deal that the AI is telling us is not.
fmzmhIAh.jpg


It clearly is correct because the deal is made and we can now see their explored territory.
JShKPeQh.jpg


I now offer a Defensive Pact. I click "What will make this deal work?" Sejong thinks it's a fair deal as is.
iwjsVUbh.jpg


What a jerk! He rejects my offer.
o4pftsxh.jpg


This is crippling to me because my entire diplomacy mod is predicted on professionalism and making sure the human player has good feedback when dealing with the AI.

I think I've narrowed the issue down to TradeLogic.lua:

Code:
----------------------------------------------------------------
----------------------------------------------------------------
function OnEqualizeDeal()
	
	UI.DoEqualizeDealWithHuman();
    
    -- Don't assume we have a message from the AI leader any more
    g_bMessageFromDiploAI = false;
	
end

I want to see what UI.DoEqualizeDealWithHuman() does, but I can't find the object definition for UI anywhere. Where can I find this, and this function? Also knowing where UI.DoWhatDoesAIWant() is defined would be helpful.

Please tell me somebody knows where this is? I'm stumped here! The AI simply should not be doing this, I did not change this diplomacy messages at all!
 
DoEqualizeDealWithHuman has a wrapper in CvDllDealAI.cpp
Code:
bool CvDllDealAI::DoEqualizeDealWithHuman(ICvDeal1* pDeal, PlayerTypes eOtherPlayer, bool bDontChangeMyExistingItems, bool bDontChangeTheirExistingItems, bool& bDealGoodToBeginWith, bool& bCantMatchOffer)
{
	CvDeal* pkDeal = (NULL != pDeal)? static_cast<CvDllDeal*>(pDeal)->GetInstance() : NULL;
	return m_pDealAI->DoEqualizeDealWithHuman(pkDeal, eOtherPlayer, bDontChangeMyExistingItems, bDontChangeTheirExistingItems, bDealGoodToBeginWith, bCantMatchOffer);
}

Code:
bool CvDealAI::DoEqualizeDealWithHuman(CvDeal* pDeal, PlayerTypes eOtherPlayer, bool bDontChangeMyExistingItems, bool bDontChangeTheirExistingItems, bool& bDealGoodToBeginWith, bool& bCantMatchOffer)
{
	bool bMakeOffer;
	PlayerTypes eMyPlayer = GetPlayer()->GetID();
	DEBUG_VARIABLE(eMyPlayer);

	CvAssert(eOtherPlayer >= 0);
	CvAssert(eOtherPlayer < MAX_MAJOR_CIVS);
	CvAssertMsg(eMyPlayer != eOtherPlayer, "DEAL_AI: Trying to equalize human deal, but both players are the same.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.");

	int iDealDuration = GC.getGame().GetDealDuration();
	bCantMatchOffer = false;

	// Is this a peace deal?
	if (pDeal->IsPeaceTreatyTrade(eOtherPlayer))
	{
		pDeal->ClearItems();
		bMakeOffer = IsOfferPeace(eOtherPlayer, pDeal, true /*bEqualizingDeals*/);
	}
	else
	{
		int iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer;
		bMakeOffer = IsDealWithHumanAcceptable(pDeal, GC.getGame().getActivePlayer(), /*Passed by reference*/ iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer, bCantMatchOffer);

		if(bMakeOffer)
		{
			bDealGoodToBeginWith = true;
		}
		else
		{
			bDealGoodToBeginWith = false;
		}

		if(!bMakeOffer)
		{
			/////////////////////////////
			// See if there are items we can add or remove from either side to balance out the deal if it's not already even
			/////////////////////////////

			bool bUseEvenValue = false;

			// Maybe reorder these based on the AI's priorities (e.g. if it really doesn't want to give up Strategic Resources try adding those from us last)

			//DoAddCitiesToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);

			DoAddEmbassyToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, bUseEvenValue);
			DoAddEmbassyToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, bUseEvenValue);

			DoAddResourceToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);
			DoAddResourceToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, iDealDuration, bUseEvenValue);

			DoAddOpenBordersToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);
			DoAddOpenBordersToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, iDealDuration, bUseEvenValue);

			DoAddGoldToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);
			DoAddGoldToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);

			DoAddGPTToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);
			DoAddGPTToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);

			DoRemoveGPTFromThem(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);
			DoRemoveGPTFromUs(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);

			DoRemoveGoldFromUs(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);
			DoRemoveGoldFromThem(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);

			DoAddCitiesToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, bUseEvenValue);

			// Make sure we haven't removed everything from the deal!
			if(pDeal->m_TradedItems.size() > 0)
			{
				bMakeOffer = IsDealWithHumanAcceptable(pDeal, GC.getGame().getActivePlayer(), /*Passed by reference*/ iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer, /*passed by reference*/bCantMatchOffer);
			}
		}
	}

	return bMakeOffer;
}
Looks like pretty readable code even if you are a lua not C++ programmer, but if you have a problem I can try to work out more detail.

The other function apparently is in a DLL that hasn't been released yet, and I can't find any lua examples either :(
 
Hambil, many thanks for replying.

I am disappointed that it just wraps into the C++ function (my mod has been exclusively modifying the C++, I hate the LUA so much that it pisses me off to work with). I've traced that function back to front, I can't understand why it's messing with the AI responses.

My last resort, brute force, horrible fix is to just swap the deal acceptance statements...I have no idea why they're wrong in the first place...

The other alternative and the one I will be pursuing first is painstakingly testing out my Version 1 build which should have no problem, then adding code in from Version 2 and seeing if I can catch where the error is there. And that will piss me off.

:(
 
I don't think that's the issue, as the map only updates after the deal is finalized, well after we've clicked to equalize the deal.

Code:
//  --------------------------------------------------------------------------------
// Added by Putmalk
void CvTeam::AcquireMap(TeamTypes eIndex)
{
	// eIndex = seller, GetID() = buyer

	CvAssertMsg(eIndex >= 0, "eIndex is expected to be non-negative (invalid Index)");
	CvAssertMsg(eIndex < MAX_TEAMS, "eIndex is expected to be within maximum bounds (invalid Index)");

	CvMap& kMap = GC.getMap();
	CvPlot* pPlot;
	int iX, iY;
	bool bRevealPlots;

	for(iX = kMap.getGridWidth(); iX--;)
	{
		for(iY = kMap.getGridHeight(); iY--;)
		{
			pPlot = kMap.plotUnchecked(iX, iY);

			if(pPlot == NULL)
			{
				continue;
			}
			if(pPlot->isRevealed( eIndex ))
			{
				pPlot->setRevealed(GetID(), true);
			}
		}
	}

	bRevealPlots = true;

	if(bRevealPlots)
	{
		GC.getMap().updateDeferredFog();
	}

	GC.getMap().verifyUnitValidPlot();

	if((GetID() == GC.getGame().getActiveTeam()) || (eIndex == GC.getGame().getActiveTeam()))
	{
		DLLUI->setDirty(Score_DIRTY_BIT, true);
	}
}

Although I will admit I have no idea what setDirty() does, it was in the Embassy plot reveal code.

Here's my current DoEqualizeDealWithHuman() function. The problem persisted even after commenting out my additions.
Code:
/// Try to even out the value on both sides.  If bFavorMe is true we'll bias things in our favor if necessary
bool CvDealAI::DoEqualizeDealWithHuman(CvDeal* pDeal, PlayerTypes eOtherPlayer, bool bDontChangeMyExistingItems, bool bDontChangeTheirExistingItems, bool& bDealGoodToBeginWith, bool& bCantMatchOffer)
{
	bool bMakeOffer;
	PlayerTypes eMyPlayer = GetPlayer()->GetID();
	DEBUG_VARIABLE(eMyPlayer);

	CvAssert(eOtherPlayer >= 0);
	CvAssert(eOtherPlayer < MAX_MAJOR_CIVS);
	CvAssertMsg(eMyPlayer != eOtherPlayer, "DEAL_AI: Trying to equalize human deal, but both players are the same.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.");

	int iDealDuration = GC.getGame().GetDealDuration();
	bCantMatchOffer = false;

	// Is this a peace deal?
	if (pDeal->IsPeaceTreatyTrade(eOtherPlayer))
	{
		pDeal->ClearItems();
		bMakeOffer = IsOfferPeace(eOtherPlayer, pDeal, true /*bEqualizingDeals*/);
	}
	else
	{
		int iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer;
		bMakeOffer = IsDealWithHumanAcceptable(pDeal, GC.getGame().getActivePlayer(), /*Passed by reference*/ iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer, bCantMatchOffer);

		if(bMakeOffer)
		{
			bDealGoodToBeginWith = true;
		}
		else
		{
			bDealGoodToBeginWith = false;
		}

		if(!bMakeOffer)
		{
			/////////////////////////////
			// See if there are items we can add or remove from either side to balance out the deal if it's not already even
			/////////////////////////////

			bool bUseEvenValue = false;

			// Maybe reorder these based on the AI's priorities (e.g. if it really doesn't want to give up Strategic Resources try adding those from us last)

			//DoAddCitiesToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);

			DoAddEmbassyToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, bUseEvenValue);
			DoAddEmbassyToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, bUseEvenValue);

			DoAddResourceToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);
			DoAddResourceToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, iDealDuration, bUseEvenValue);

			DoAddOpenBordersToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iDealDuration, bUseEvenValue);
			DoAddOpenBordersToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, iDealDuration, bUseEvenValue);

			DoAddGoldToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);
			DoAddGoldToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);

			DoAddGPTToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);
			DoAddGPTToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);

			DoRemoveGPTFromThem(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);
			DoRemoveGPTFromUs(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iDealDuration, bUseEvenValue);

			DoRemoveGoldFromUs(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);
			DoRemoveGoldFromThem(pDeal, eOtherPlayer, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, bUseEvenValue);

			//Added by Putmalk
			DoAddMapsToThem(pDeal, eOtherPlayer, bDontChangeTheirExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, bUseEvenValue);
			DoAddMapsToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, bUseEvenValue);

			DoAddCitiesToUs(pDeal, eOtherPlayer, bDontChangeMyExistingItems, iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountUnderWeWillOffer, bUseEvenValue);

			// Make sure we haven't removed everything from the deal!
			if(pDeal->m_TradedItems.size() > 0)
			{
				bMakeOffer = IsDealWithHumanAcceptable(pDeal, GC.getGame().getActivePlayer(), /*Passed by reference*/ iTotalValueToMe, iValueImOffering, iValueTheyreOffering, iAmountOverWeWillRequest, iAmountUnderWeWillOffer, /*passed by reference*/bCantMatchOffer);
			}
		}
	}

	return bMakeOffer;
}
 
I've seen this before (from a user standpoint). It was when using the CivUp version of Whoward's Trade Opportunities. The UI would say the AI was willing to make a deal, but when I tried they refused. So, it's not just you if that helps. In fact, I've seen the exact (Looks good to me/then refuse the deal) behavior in CivUp.
 
Did whoward ever specify a way to fix this issue?

I tested brute-force switching the replies. It didn't do anything. That "Equalize deal" button is doing something wonky. I'll try and trace it and diagnose it again, but who knows. This is really weird stuff.
 
I only remember him vaguely saying something to the effect that adding in all those popups and activating all those buttons (his version doesn't do allow you to click on a resource and open the trade window, for example) was the kind of thing that caused stuff to break which is why he didn't do it.

That is the best of my memory, but he will be along soon I am sure to straighten all this out.
 
Did whoward ever specify a way to fix this issue?

No, as the error/problem was introduced by Thal when he incorporated and then rewrote Trade Ops into CivUP/GEM.

The "map issue" is one of adding resources/improvements to the map after a tile has been "claimed" by a city - so won't be a factor here.

We do not have the code for the UI object as it is part of the UI DLL (ContextPtr, Controls, UI, etc) and we don't have that

A number of UI methods appear to do simple "marshal and call" on core DLL methods (ie the ones we do have) but a number do additional logic as well - especially if they involve panning the map. So what is going on in that UI method you can only speculate over (not much help I'm afraid)

Have you somehow pushed the enums out, so the message number being sent as the AI reply is not matching the txt_keys?

EDIT: DEAL_RESPONSE_UNACCEPTABLE is only set in one place in CvDealAI.cpp, so silly question time, have you logged iDealValueToMe, iValueImOffering and iValueTheyreOffering at that point to check the numbers make sense?
 
Have you somehow pushed the enums out, so the message number being sent as the AI reply is not matching the txt_keys?

EDIT: DEAL_RESPONSE_UNACCEPTABLE is only set in one place in CvDealAI.cpp, so silly question time, have you logged iDealValueToMe, iValueImOffering and iValueTheyreOffering at that point to check the numbers make sense?

I have not logged these values, how would you suggest I go about doing this? EDIT: Nvm, I'll figure out to do it myself and I'll post with the values.

Also, I did not change the enums:
Code:
enum DealOfferResponseTypes
{
    NO_DEAL_RESPONSE_TYPE = -1,

    // WARNING: the order of these values is very important, do not change unless you know what you're doing!
    DEAL_RESPONSE_GENEROUS,
    DEAL_RESPONSE_ACCEPTABLE,
    DEAL_RESPONSE_UNACCEPTABLE,
    DEAL_RESPONSE_INSULTING,
    // WARNING: the order of these values is very important, do not change unless you know what you're doing!

    NUM_DEAL_RESPONSES,
};
 
So, cool. My error is "fixed". I put that in quotations because I have absolutely no idea what went wrong.

I reverted back to Version 1. I went super slow when implementing my functions, first just editing the .h files, then testing, then adding the functions in (without calling them in the file), then testing, and then finally calling the functions. Everything works now, I only have two more functions to add, but the behavior isn't occurring anymore.

My guess? I must have messed with an enum somewhere. In this version, I put all my enums at the end of the list, so they didn't change a value somewhere by accident. Either way, the fruits of my labor:

2myH0nzl.jpg
 
Isn't there an issue with the AI rejecting deals it itself suggests regardless of mods?

That was not really the issue here. This was a texts problem, or more specifically, a DoEqualizeDealWithHuman() problem.
 
I just wondered if the issue might have been an indirect one, as a result of a general problem :)

I'm not sure. But it was consistently broken, so I figured it was my error. I guess I did something wrong, just can't pin it on anything.
 
I put all my enums at the end of the list,

Almost certainly what solved the problem. A lot of the standard enums are expected to match to the ID's assigned in the database from the core XML files.

Another thing you don't want to do is change the order of parameters in method signatures (ie always add your own parameters at the end) for any of the objects exposed to Lua - otherwise you can break the Lua interface / CTD when BasicLuaMethod calls the changed method with the parameters in the wrong order
 
Isn't there an issue with the AI rejecting deals it itself suggests regardless of mods?
What I experience sometimes is that if you have made a deal for 30 turns, and the deal comes to an end, the AI will approach you and say "we should renew this deal" and then when you hit accept, it tells you the terms are not acceptable. So clearly it should have approached you with a message instead saying that the old terms were no longer acceptble. Whether there are other cases than this, I don't know, but this one I have had happen to me, albeit not too often.
 
Almost certainly what solved the problem. A lot of the standard enums are expected to match to the ID's assigned in the database from the core XML files.

Another thing you don't want to do is change the order of parameters in method signatures (ie always add your own parameters at the end) for any of the objects exposed to Lua - otherwise you can break the Lua interface / CTD when BasicLuaMethod calls the changed method with the parameters in the wrong order

Duly noted.

Thank you all for your help! If you're interested in playing the mod, check my signature out. More features out soon!
 
Back
Top Bottom