Is it possible to disable VP diplomacy trading restrictions? (city trading, one time gold trading vs. GPT)

Saiph300

Chieftain
Joined
Aug 31, 2015
Messages
12
Hi,

I recently started playing VP and one thing that surprised me is how often buttons are "grayed out" when trading stuff with AI. For example, it seems I can't ever buy their cities. The option is often grayed out, it seems I can only buy cities they have conquered from other nations. And the GPT option is also often grayed out, saying something like "Can't trade one-time gold for periodic items".

It's been a very long time since I played vanilla civ 5, but I don't recall there being restrictions like these so my guess is that VP introduced to prevent AI abuse. I totally understand the rationale for GPT vs. gold, not so sure about the cities - regardless I don't plan to exploit the AI and this restriction leads to really frustrating situations like having 998 gold out of 1000 where no one wants to give me the missing 2 gold.

Question:
1. Can someone confirm whether these 2 are Vox Populi change, is that documented somewhere?
2. Can I disable it? (I can change config files etc. or even code if I know where to look at)
 
Hi,

I recently started playing VP and one thing that surprised me is how often buttons are "grayed out" when trading stuff with AI. For example, it seems I can't ever buy their cities. The option is often grayed out, it seems I can only buy cities they have conquered from other nations. And the GPT option is also often grayed out, saying something like "Can't trade one-time gold for periodic items".

It's been a very long time since I played vanilla civ 5, but I don't recall there being restrictions like these so my guess is that VP introduced to prevent AI abuse. I totally understand the rationale for GPT vs. gold, not so sure about the cities - regardless I don't plan to exploit the AI and this restriction leads to really frustrating situations like having 998 gold out of 1000 where no one wants to give me the missing 2 gold.

Question:
1. Can someone confirm whether these 2 are Vox Populi change, is that documented somewhere?
2. Can I disable it? (I can change config files etc. or even code if I know where to look at)
Both are VP changes.

The first change was passed in VP Congress Session #6: (6-60) Can't Sell Cities Founded By Their Current Owner, and is currently hardcoded into the DLL. Someone would have to make an option to disable that. You can open a ticket on our GitHub Issues tracker to request that.

The second change can be turned off. To do so, open MODS\(1) Community Patch\Core Files\Core Changes\DiploAIOptions.sql and change DIPLOAI_TEMPORARY_FOR_PERMANENT_TRADING_SETTING to 2, then save the file.

Side note, in BNW you could not trade one-time Gold without having a Declaration of Friendship, except in peace deals. VP removes this restriction and allows you to trade one-time Gold for other one-time items by default.
 
Both are VP changes.

The first change was passed in VP Congress Session #6: (6-60) Can't Sell Cities Founded By Their Current Owner, and is currently hardcoded into the DLL. Someone would have to make an option to disable that. You can open a ticket on our GitHub Issues tracker to request that.

The second change can be turned off. To do so, open MODS\(1) Community Patch\Core Files\Core Changes\DiploAIOptions.sql and change DIPLOAI_TEMPORARY_FOR_PERMANENT_TRADING_SETTING to 2, then save the file.

Side note, in BNW you could not trade one-time Gold without having a Declaration of Friendship, except in peace deals. VP removes this restriction and allows you to trade one-time Gold for other one-time items by default.
Thanks. After writing this I discovered the voting process, great idea btw. Now I understand why it's called VP :)

I have to admit that I'm very confused by the proposal result. Is city trading really that exploitable? Especially when compared to tech trading or other things present in the game? This is a legit question, not trying to criticize the poll result. I don't remember city trading being particularly exploitable on vanilla civ 5, but probably the multiplayer guys are much more creative than me...

Anyway, I will open an issue for making the cities restriction optional. Of course, I feel like the correct solution would be to improve the AI so that it can properly evaluate cities. It's probably harder, but intuitively, something like: territory size, resources, population, distance from the AI's capital.seems like a good heuristic already... It is probably insanely harder than what I imagine, but if the AI code is available, I might even give it a try...

And thanks for sharing the GPT config option.
 
Thanks. After writing this I discovered the voting process, great idea btw. Now I understand why it's called VP :)

I have to admit that I'm very confused by the proposal result. Is city trading really that exploitable? Especially when compared to tech trading or other things present in the game? This is a legit question, not trying to criticize the poll result. I don't remember city trading being particularly exploitable on vanilla civ 5, but probably the multiplayer guys are much more creative than me...

Anyway, I will open an issue for making the cities restriction optional. Of course, I feel like the correct solution would be to improve the AI so that it can properly evaluate cities. It's probably harder, but intuitively, something like: territory size, resources, population, distance from the AI's capital.seems like a good heuristic already... It is probably insanely harder than what I imagine, but if the AI code is available, I might even give it a try...

And thanks for sharing the GPT config option.
axatin and ilteroi both had trouble getting the AI to value city buying and selling properly, especially because there's a lot of situations where it's not smart for the AI to trade that a human could distinguish immediately but the AI would need complex logic to figure out (e.g., can they send troops to reinforce a newly-acquired border city, or can the other player trade them a city for Gold and then declare war to recover the city).

If you feel like giving it a shot yourself, feel free to make a proposal in Session #9 - it may be a while until that happens, though, considering how much work will be required to implement the results of Session #8.

There's two relevant functions, CvDealClasses::IsPossibleToTradeItem() and CvDealAI::GetCityValueForDeal(). I included the relevant parts of the first function, followed by the entirety of the second function. The programming language is C++.
Code:
/// Is it actually possible for a player to offer up this trade item?
/// The Data parameters can be -1, which means we don't care about whatever data is stored there (e.g. -1 for Gold means "can we trade ANY amount of Gold?")
bool CvDeal::IsPossibleToTradeItem(PlayerTypes ePlayer, PlayerTypes eToPlayer, TradeableItems eItem, int iData1, int iData2, int iData3, bool bFlag1, bool bFinalizing)
{
    // Can't trade something to nobody, yourself, City-States, or Barbarians
    if (ePlayer <= NO_PLAYER || eToPlayer <= NO_PLAYER || ePlayer == eToPlayer || ePlayer >= MAX_MAJOR_CIVS || eToPlayer >= MAX_MAJOR_CIVS)
        return false;

    // Can't trade anything if you're dead
    if (!GET_PLAYER(ePlayer).isAlive() || !GET_PLAYER(eToPlayer).isAlive())
        return false;

    CvPlayer* pFromPlayer = &GET_PLAYER(ePlayer);
    CvPlayer* pToPlayer = &GET_PLAYER(eToPlayer);
    TeamTypes eFromTeam = pFromPlayer->getTeam();
    TeamTypes eToTeam = pToPlayer->getTeam();
    CvTeam* pFromTeam = &GET_TEAM(eFromTeam);
    CvTeam* pToTeam = &GET_TEAM(eToTeam);

    bool bPeaceDeal = pFromTeam->isAtWar(eToTeam);
    bool bHumanToHuman = pFromPlayer->isHuman() && pToPlayer->isHuman();
    bool bSameTeam = eFromTeam == eToTeam;
    bool bOneSided = this->GetSurrenderingPlayer() != NO_PLAYER || this->GetDemandingPlayer() != NO_PLAYER || this->GetRequestingPlayer() != NO_PLAYER;

    // Can't trade anything if embargoed by the World Congress (except for peace deals)
    if (MOD_BALANCE_VP && !bPeaceDeal && eItem != TRADE_ITEM_DECLARATION_OF_FRIENDSHIP)
    {
        CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague();
        if (pLeague && pLeague->IsTradeEmbargoed(ePlayer, eToPlayer))
        {
            return false;
        }
    }

    if (!bHumanToHuman)
    {
        // Can't trade anything (except peace itself) in a peace deal if you're not the one surrendering
        if (bPeaceDeal)
        {
            if (eItem != TRADE_ITEM_PEACE_TREATY && eItem != TRADE_ITEM_THIRD_PARTY_PEACE)
            {
                if (bOneSided && this->GetSurrenderingPlayer() != ePlayer)
                    return false;
                else if (!bOneSided && !pFromPlayer->isHuman())
                    return false;
            }
        }
        // AI won't add anything to its side for a demand
        else if (bOneSided && this->GetDemandingPlayer() == ePlayer)
        {
            return false;
        }
        // AI won't add anything to its side for a request
        else if (bOneSided && this->GetRequestingPlayer() == ePlayer)
        {
            return false;
        }
    }

    // AI will refuse to trade temporary items for permanent items.
    if (BlockTemporaryForPermanentTrade(eItem, ePlayer, eToPlayer))
        return false;

    ////////////////////////////////////////////////////
    //////// INDIVIDUAL TRADE ITEMS ////////////////////
    ////////////////////////////////////////////////////

    switch (eItem)
    {
    case TRADE_ITEM_CITIES:
        {
            // Some game options restrict all city trades
            if (GC.getGame().IsAllCityTradingDisabled())
                return false;
            else if (GC.getGame().IsAICityTradingDisabled() && !bHumanToHuman)
                return false;
            else if (GC.getGame().IsAICityTradingHumanOnly() && !pFromPlayer->isHuman() && !pToPlayer->isHuman())
                return false;

            // Can't trade a city to a human in an OCC game
            if (GC.getGame().isOption(GAMEOPTION_ONE_CITY_CHALLENGE) && pToPlayer->isHuman())
                return false;

            // Buyer needs an embassy in peacetime (except for deals between teammates)
            if (!bPeaceDeal && !bSameTeam && !pToTeam->HasEmbassyAtTeam(eFromTeam))
                return false;

            if (iData1 != -1)
            {
                // Make sure the city actually exists
                CvPlot* pPlot = GC.getMap().plot(iData1, iData2);
                CvCity* pCity = pPlot ? pPlot->getPlotCity() : NULL;
                if (pCity == NULL)
                    return false;

                // Can't trade your capital
                if (pCity->isCapital())
                    return false;

                // Can't trade someone else's city
                if (pCity->getOwner() != ePlayer)
                    return false;

                // Can't trade a city if sapped, blockaded, or took damage last turn (except in a peace deal)
                // Also can't trade a city that the seller originally founded (except between humans, teammates or in a peace deal)
                if (!bPeaceDeal)
                {
                    if (pCity->GetSappedTurns() > 0)
                        return false;

                    if (pCity->getDamageTakenLastTurn() > 0)
                        return false;

                    if (!bHumanToHuman && !bSameTeam && pCity->getOriginalOwner() == ePlayer)
                        return false;

                    if (pCity->GetCityCitizens()->AnyPlotBlockaded())
                        return false;
                }
            }

            if (!bFinalizing)
            {
                // Can't already have this city in the deal
                if (iData1 != -1 && IsCityTrade(ePlayer, iData1, iData2))
                    return false;

                // If trading with AI, can't trade more than one city per player at a time
                if (!bHumanToHuman && ContainsItemType(TRADE_ITEM_CITIES, ePlayer))
                    return false;
            }
            break;
        }
    }

    return true;
Code:
/// How much is a City worth - that is: how much would the buyer pay?
int CvDealAI::GetCityValueForDeal(CvCity* pCity, PlayerTypes eAssumedOwner)
{
    //can't trade capitals ever
    if (!pCity || pCity->isCapital())
        return INT_MAX;

    //note that it can also happen that a player pretends to buy a city they already own, just to see the appropriate price
    CvPlayer& assumedOwner = GET_PLAYER(eAssumedOwner);
    bool bPeaceTreatyTrade = assumedOwner.IsAtWarWith(pCity->getOwner());

    // Don't buy any cities that aren't ours if we're unhappy
    if (!bPeaceTreatyTrade && pCity->getOwner() != eAssumedOwner && pCity->getOriginalOwner() != eAssumedOwner && GET_PLAYER(eAssumedOwner).IsEmpireUnhappy())
        return INT_MAX;

    //if we already own it and trade voluntarily ...
    if (!bPeaceTreatyTrade && pCity->getOwner() == eAssumedOwner)
    {
        //traded this city before? Don't give it away again.
        if (pCity->IsTraded(eAssumedOwner))
            return INT_MAX;

        //don't sell if less than 5 cities!
        if (assumedOwner.getNumCities() < 5)
            return INT_MAX;

        //do not trade special cities
        if (pCity->IsOriginalCapital() || pCity->GetCityReligions()->IsHolyCityAnyReligion())
            return INT_MAX;
    }

    //initial value
    int iItemValue = 20000;

    //economic value is important
    int iEconomicValue = pCity->getEconomicValue(eAssumedOwner);
    int iEconomicValuePerPop = (iEconomicValue / (max(1, pCity->getPopulation())));
    iItemValue += (max(1,iEconomicValue-1000)/3); //tricky to define the correct factor

    //prevent cheesy exploit: founding cities just to sell them
    if (!bPeaceTreatyTrade && (GC.getGame().getGameTurn() - pCity->getGameTurnFounded()) < (42 + GC.getGame().randRangeExclusive(0, 5, CvSeeder(iEconomicValue))))
        return INT_MAX;

    //If not as good as any of our cities, we don't want it.
    int iBetterThanCount = 0;
    int iCityLoop = 0;
    for(CvCity* pLoopCity = assumedOwner.firstCity(&iCityLoop); pLoopCity != NULL; pLoopCity = assumedOwner.nextCity(&iCityLoop))
    {
        int iScore = (pLoopCity->getEconomicValue(eAssumedOwner) / (max(1, pLoopCity->getPopulation())));
        if (iEconomicValuePerPop > iScore)
            iBetterThanCount++;
    }
    //better than half of the buyer's cities?
    if (iBetterThanCount > assumedOwner.getNumCities() / 2)
        iItemValue *= 2;

    //first some amount for the territory (outside of the first ring)
    int iNewInternalBorderCount = 0;
    int iOldInternalBorderCount = 0;
    int iCityTiles = 0;
    for(int iI = RING1_PLOTS; iI < RING5_PLOTS; iI++)
    {
        CvPlot* pLoopPlot = pCity->GetCityCitizens()->GetCityPlotFromIndex(iI);
        if (!pLoopPlot)
            continue;

        //if it belongs to the city
        if (pCity->GetID() == pLoopPlot->getOwningCityID())
            iCityTiles++;
        else if (pLoopPlot->getOwner() == eAssumedOwner)
            //belongs to another one of the buyer's cities
            iNewInternalBorderCount++;
        else if (pLoopPlot->getOwner() == pCity->getOwner())
            //belongs to another one of the current owner's cities
            iOldInternalBorderCount++;
    }

    //this is how much ANY plot is worth to the buyer right now
    int goldPerPlot = assumedOwner.GetBuyPlotCost();
    iItemValue += goldPerPlot * 50 * iCityTiles;

    //important. it's valuable to have as much internal border as possible
    iItemValue += goldPerPlot * 80 * iNewInternalBorderCount;

    //unhappy cities are worth less
    iItemValue -= pCity->getUnhappyCitizenCount() * goldPerPlot * 30;

    // premium if buyer founded it
    if (pCity->getOriginalOwner() == eAssumedOwner)
    {
        iItemValue *= 120;
        iItemValue /= 100;
    }

    // premium if buyer currently owns it
    if ( pCity->getOwner() == eAssumedOwner )
    {
        iItemValue *= 120;
        iItemValue /= 100;
    }
    else if (iNewInternalBorderCount == 0 && iOldInternalBorderCount == 0)
    {
        //don't buy a city which doesn't overlap with existing cities, unless it's isolated from the current owner as well
        return INT_MAX;
    }

    if (pCity->IsPuppet())
    {
        iItemValue *= 70;
        iItemValue /= 100;
    }

    //don't want the trouble
    if (pCity->IsResistance())
    {
        iItemValue *= (100-10*min(5,pCity->GetResistanceTurns()));
        iItemValue /= 100;
    }

    //obviously the owner doesn't really want it
    if (pCity->IsRazing())
    {
        iItemValue *= 50;
        iItemValue /= 100;
    }

    // don't want to lose wonders
    int iWonders = pCity->getNumWorldWonders();
    if (pCity->getOwner() == eAssumedOwner)
        iWonders += pCity->getNumNationalWonders();

    if (iWonders > 0)
    {
        iItemValue *= (100 + iWonders*11);
        iItemValue /= 100;
    }

    // add some friction
    if (pCity->getOwner() != eAssumedOwner)
    {
        //don't sell to warmongers
        switch (GET_PLAYER(pCity->getOwner()).GetDiplomacyAI()->GetWarmongerThreat(eAssumedOwner))
        {
        case THREAT_NONE:
        case THREAT_MINOR:
            break; // No change in value.
        case THREAT_MAJOR:
            iItemValue *= 2;
            break;
        case THREAT_SEVERE:
        case THREAT_CRITICAL:
            if (bPeaceTreatyTrade)
                iItemValue *= 3;
            else
                return INT_MAX;
            break;
        }

        // reduce city value if it's about to be captured by the assumed owner
        if (bPeaceTreatyTrade && pCity->plot()->GetNumEnemyUnitsAdjacent(pCity->getTeam(), NO_DOMAIN, NULL, false, GET_PLAYER(eAssumedOwner).getTeam(),true) >= 2)
        {
            int iHPPercent = 100 * (pCity->GetMaxHitPoints() - pCity->getDamage()) / pCity->GetMaxHitPoints();
            iItemValue *= max(10, min(2*iHPPercent, 100));
            iItemValue /= 100;
        }

        if (!bPeaceTreatyTrade && !assumedOwner.isHuman())
        {
            // don't buy a city we're trying to liberate (exploitable)
            // this is not an ideal solution - ideally AI would check whether the city is at risk of getting recaptured if liberated ... but will do for now
            if (assumedOwner.GetDiplomacyAI()->IsTryingToLiberate(pCity))
                return INT_MAX;
        }
    }

    //OutputDebugString(CvString::format("City value for %s from %s to %s is %d\n", pCity->getName().c_str(), sellingPlayer.getName(), buyingPlayer.getName(), iItemValue).c_str());
    return max(iItemValue, pCity->getPopulation()*729);
}
 
Back
Top Bottom