1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Vassals

Discussion in 'Community Patch Project' started by Putmalk, Feb 8, 2016.

  1. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Why should a vassal revolting cause other civs to make peace? Seems rather arbitrary in my opinion. The option to make peace should exist, however, because they are now an independent state.

    I would think that if I add any more "tax" rates (for example, a science meter) I would keep them all distinct. Otherwise it's super confusing, and less controllable, for a human player to understand the mechanic (which is really the goal of this update). And a trade route to a vassal makes them like it a lot more (as currently implemented in the dev build). :) Really trying to avoid tying policies, techs, resolutions or other things to the vassal system or diplomacy in general, as it doesn't seem like good behavior.

    I like all of these ideas, except the master being at war (unless they're losing).

    Vassals are not City States. :) The whole point of a vassal agreement is the agreement to protect them rather than annihilate them. Although I kinda like the troops in vassal territory causing a small diplomatic boost. :) Also, as already implemented, demanding from vassals is easier, but not guaranteed.
     
  2. Xaviarlol

    Xaviarlol Warlord

    Joined:
    May 27, 2011
    Messages:
    224
    I like that idea - but I'd make it more like an oppression thing and more like a last resort if the relationship is getting bad and the overlord needs to contain them from revolting. Perhaps if you put military units within 1 tile of their cities it reduces their chance to revolt but lowers relationship and/or increases their military unit production or something like that.
     
  3. Jdoug312

    Jdoug312 Warlord

    Joined:
    Jul 20, 2013
    Messages:
    266
    Thoughts on this Putmalk? After more thought, the second idea may be a bit much, and the first idea would sort of take up that role in a more sane way.
     
  4. Xaviarlol

    Xaviarlol Warlord

    Joined:
    May 27, 2011
    Messages:
    224
    I mean that the Vassal may already be at war with another civ at the time of revolt due to being forced to parttake in overlords wars (am I mistaken in assuming that the vassal always follows the master in to wars?)

    If the vassal revolts against the overlord while it is involved in another war, it will have a very hard time surviving fighting a war on two fronts. If the vassal breaks away from the overlord, I would imagine it would make sense to no longer be involved in the previous wars of the overlord with other civs.
     
  5. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Like the idea of vassals getting more enticed with independence the more other civs successfully declare independence.

    No to breaking off vassalage without explicitly meeting the requirements. Otherwise the mechanic means nothing. :)

    This is an AI issue, not a game mechanic one. Forcing peace is not correct behavior, having the AI want to end an existing war is correct behavior - assuming the other party agrees. :mischief:
     
  6. Xaviarlol

    Xaviarlol Warlord

    Joined:
    May 27, 2011
    Messages:
    224

    Fair enough ;)

    What do you think about the idea of the overlord stationing units near vassal cities to reduce chance of revolt? Formula could be total military strength within 1 or 2 tiles of vassal cities to offset chance of revolt. It could be another more direct way for overlords to maintain grip on vassals, albeit wasteful. It should have negative consequences though - either with the vassal itself or with other civs (ie oppressive behaviour).

    Might be hard to code the AI overlords to do this to their vassals...
     
  7. Gazebo

    Gazebo Lord of the Community Patch Supporter

    Joined:
    Sep 26, 2010
    Messages:
    18,048
    Gender:
    Male
    Location:
    Little Rock
    Putmalk, re diplo code, I've added in the loop you need, and I did the first two of your static integers to give you an idea of what I mean:


    Spoiler :
    Code:
    // Determine how much we are going to tax this player, if we can
    void CvDiplomacyAI::DoDetermineTaxRateForVassalOnePlayer(PlayerTypes ePlayer)
    {
    
    	// Must be able to set taxes for player
    	if(!GET_TEAM(GetPlayer()->getTeam()).CanSetVassalTax(ePlayer))
    		return;
    
    	TeamTypes eMyTeam = GetPlayer()->getTeam();
    	CvTeam& kMyTeam = GET_TEAM(eMyTeam);
    
    	// Do not allow an AI teammate to do this for a human
    	if(!GetPlayer()->isHuman() && kMyTeam.isHuman())
    		return;
    		
    	[B]FStaticVector< int, 128, true, c_eCiv5GameplayDLL > viApproachWeightsPersonality;
    	for(iApproachLoop = 0; iApproachLoop < NUM_MAJOR_CIV_APPROACHES; iApproachLoop++)
    	{
    		viApproachWeightsPersonality.push_back(GetPersonalityMajorCivApproachBias((MajorCivApproachTypes) iApproachLoop));
    	}[/B]
    
    	// Current tax rate
    	int iTaxRate = GET_TEAM(GetPlayer()->getTeam()).GetVassalTax(ePlayer);
    
    	// Make sure we can actually do that...
    	bool bWantToLower = iTaxRate > GC.getVASSALAGE_VASSAL_TAX_PERCENT_MINIMUM();
    	bool bWantToRaise = iTaxRate < GC.getVASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM();
    
    	// Because this function will involve lots of iteration over team members, let's store all alive team members in temporary vectors to improve the speed of this function
    	std::vector<CvPlayerAI*> m_MasterTeam;
    	std::vector<CvPlayerAI*> m_VassalTeam;
    
    	for(int iI=0; iI < MAX_MAJOR_CIVS; iI++)
    	{
    		PlayerTypes eLoopPlayer = (PlayerTypes) iI;
    		if(GET_PLAYER(eLoopPlayer).isAlive())
    		{
    			// Master team
    			if(GET_PLAYER(eLoopPlayer).getTeam() == GetPlayer()->getTeam())
    			{
    				m_MasterTeam.push_back(&GET_PLAYER(eLoopPlayer));
    			}
    			// Vassal team
    			else if(GET_PLAYER(eLoopPlayer).getTeam() == GET_PLAYER(ePlayer).getTeam())
    			{
    				m_VassalTeam.push_back(&GET_PLAYER(eLoopPlayer));
    			}
    		}
    	}
    	
    	MajorCivOpinionTypes eTeamOpinion = MAJOR_CIV_OPINION_NEUTRAL;
    	int iMyCurrentGPT = 0, iMyCurrentGross = 0, iAverageMeanness = 0, iAverageLoyalty = 0, iAverageOpinionScore = 0;
    	for(std::vector<CvPlayerAI*>::iterator it = m_MasterTeam.begin(); it != m_MasterTeam.end(); it++)
    	{
    		iMyCurrentGPT += (*it)->GetTreasury()->CalculateBaseNetGoldTimes100();
    		iMyCurrentGross += (*it)->GetTreasury()->CalculateBaseNetGoldTimes100();
    
    		iAverageMeanness += (*it)->GetDiplomacyAI()->GetMeanness();
    		iAverageLoyalty += (*it)->GetDiplomacyAI()->GetLoyalty();
    		iAverageOpinionScore += (int) (*it)->GetDiplomacyAI()->GetMajorCivOpinion(ePlayer);
    	}
    	iAverageMeanness /= m_MasterTeam.size();
    	iAverageLoyalty /= m_MasterTeam.size();
    	iAverageOpinionScore /= m_MasterTeam.size();
    
    	eTeamOpinion = (MajorCivOpinionTypes) iAverageOpinionScore;
    
    	int iVassalCurrentGPT = 0, iVassalCurrentGross = 0;
    	for(std::vector<CvPlayerAI*>::iterator it = m_VassalTeam.begin(); it != m_VassalTeam.end(); it++)
    	{
    		iVassalCurrentGPT += (*it)->GetTreasury()->CalculateBaseNetGoldTimes100();
    		iVassalCurrentGross += (*it)->GetTreasury()->CalculateGrossGoldTimes100();
    	}
    
    	// Hate him? Don't consider lowering!
    	if(eTeamOpinion == MAJOR_CIV_OPINION_UNFORGIVABLE)
    		bWantToLower = false;
    
    	
    	// Like him? Don't consider raising!
    	if(eTeamOpinion == MAJOR_CIV_OPINION_ALLY)
    		bWantToRaise = false;
    
    	// We have some choice in the direction taxes can go - pick a direction so we can start deciding
    	if(bWantToLower && bWantToRaise)
    	{
    		// We're in dire straights
    		if(iMyCurrentGPT <= 0)
    		{
    			bWantToLower = false;	// don't even consider lowering
    			
    			// Check to see if taxing the vassal the maximum would get us out of dire straights
    			if(bWantToRaise)
    			{
    				// Wouldn't help us out at all
    				if((iVassalCurrentGross * GC.getVASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM() / 100 < 0))
    				{
    					bWantToRaise = false;
    				}
    				// Tax vassal the maximum to get us out of trouble - his feelings be damned
    				else
    				{
    					GET_TEAM(GetPlayer()->getTeam()).DoApplyVassalTax(ePlayer, GC.getVASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM());
    				}
    			}
    		}
    		// Doing fine - have some choice
    		else
    		{	
    
    			int iScoreForLower = 0;
    			int iScoreForRaise = 0;
    
    			// Starting values based on opinion
    			switch(eTeamOpinion)
    			{
    			case MAJOR_CIV_OPINION_ENEMY:
    				iScoreForLower -= [B]viApproachWeightsPersonality[MAJOR_CIV_APPROACH_HOSTILE];
    				iScoreForRaise += viApproachWeightsPersonality[MAJOR_CIV_APPROACH_HOSTILE];
    			case MAJOR_CIV_OPINION_COMPETITOR:
    				iScoreForLower -= viApproachWeightsPersonality[MAJOR_CIV_APPROACH_DECEPTIVE];
    				iScoreForRaise += viApproachWeightsPersonality[MAJOR_CIV_APPROACH_DECEPTIVE];[/B]
    				break;
    			case MAJOR_CIV_OPINION_NEUTRAL:
    				iScoreForLower = 0;
    				iScoreForRaise = 0;
    				break;
    			case MAJOR_CIV_OPINION_FAVORABLE:
    				iScoreForLower = 10;
    				iScoreForRaise = -10;
    				break;
    			case MAJOR_CIV_OPINION_FRIEND:
    				iScoreForLower = 25;
    				iScoreForRaise = -25;
    				break;
    			default:
    				CvAssertMsg(false, "Should not have gotten here.");
    			}
    
    			// Still deciding what to be done?
    			if(bWantToLower && bWantToRaise)
    			{
    				// Is our vassal doing better than us monetarily?
    				if(iVassalCurrentGPT >= iMyCurrentGPT)
    				{
    					iScoreForLower *= 75;
    					iScoreForLower /= 100;
    
    					iScoreForRaise *= 125;
    					iScoreForRaise /= 100;
    				}
    				// He is doing worse than 85% of our GPT
    				else if(iVassalCurrentGPT * 85 <= iMyCurrentGPT * 100)
    				{
    					// Have to like the vassal
    					if(eTeamOpinion > MAJOR_CIV_OPINION_NEUTRAL)
    					{
    						// Determine a percentage to lower
    						int iThreshold = 33;
    
    						// He is doing REALLY bad
    						if(iVassalCurrentGPT * 150 < iMyCurrentGPT * 100)
    							iThreshold = 75;
    
    						int iRand = GC.getGame().getJonRandNum(100, "CvDiplomacyAI: Do we want to give this vassal a nice boost in GPT cus his GPT is less and we like him?");
    						if(iRand < iThreshold)
    						{
    							iScoreForLower *= 150;
    							iScoreForLower /= 100;
    						}
    					}
    				}
    
    				// Raise score for lowering based on loyalty
    				iScoreForLower *= 100 + (iAverageLoyalty - 5) * 10;
    				iScoreForLower /= 100;
    
    				// Raise score for lowering based on meanness
    				iScoreForRaise *= 100 + (iAverageMeanness - 5) * 10;
    				iScoreForRaise /= 100;
    
    				bWantToLower = (iScoreForLower > iScoreForRaise);
    				bWantToRaise = (iScoreForLower < iScoreForRaise);
    			}
    		}
    	}
    
    	CvWeightedVector<int, 20, true> aPossibleValues;	// in case changed, 100 / 5  is a safe bet for number of possible elements
    	
    	// New tax value defaults to current tax rate
    	int iNewTaxValue = iTaxRate;
    
    	// Decided we're going to lower - figure out by how much
    	if(bWantToLower)
    	{
    		int iCurrentIndex = 0;
    
    		// Possible values are determined by increments of 5 starting from below the current tax line
    		for(int i = (iTaxRate - 5); i >= GC.getVASSALAGE_VASSAL_TAX_PERCENT_MINIMUM(); i -= 5)
    		{
    			int iValue = i;
    			int iWeight = (iAverageOpinionScore - 3) * iCurrentIndex + 100;
    			
    			// Determine if we will make at least one GPT profit off of this value, if not, then decentivize
    			if(iVassalCurrentGross * iValue < 10000)
    				iWeight /= 4;
    
    			aPossibleValues.push_back(iValue, iWeight);
    			iCurrentIndex++;
    		}
    
    		RandomNumberDelegate fcn;
    		fcn = MakeDelegate(&GC.getGame(), &CvGame::getJonRandNum);
    		iNewTaxValue = aPossibleValues.ChooseByWeight(&fcn, "Choose the tax value to assign");
    	}
    	// Decided we're going to raise - figure out by how much
    	else
    	{
    		int iCurrentIndex = 0;
    		
    		// Possible values are determined by increments of 5 starting from above the current tax line
    		for(int i = (iTaxRate + 5); i <= GC.getVASSALAGE_VASSAL_TAX_PERCENT_MAXIMUM(); i += 5)
    		{
    			int iValue = i;
    			int iWeight = (3 - iAverageOpinionScore) * iCurrentIndex + 100;
    			
    			// Determine if we will make at least one GPT profit off of this value, if not, then decentivize
    			if(iVassalCurrentGross * iValue < 10000)
    				iWeight /= 4;
    			
    			aPossibleValues.push_back(iValue, iWeight);
    			iCurrentIndex++;
    		}
    
    		RandomNumberDelegate fcn;
    		fcn = MakeDelegate(&GC.getGame(), &CvGame::getJonRandNum);
    		iNewTaxValue = aPossibleValues.ChooseByWeight(&fcn, "Choose the tax value to assign");
    	}
    
    	// Set the tax.
    	kMyTeam.DoApplyVassalTax(ePlayer, iNewTaxValue);
    }
    


    The idea here is that a civ's personality-based integer (somewhere between 1 and 10, but sometimes higher than that) is used to 'define' their behavior towards a vassal based on opinion. Should make them a little less predictable, but in an easily-controlled way.

    G
     
  8. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    I think too complicated. :)
     
  9. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Thanks for that, maybe I'll add it in during beta testing.

    Should be feature complete soon. =) I think my last step is the master overview in the vassal overview, and then a metric ton of bugtesting and UAT, from the community :). I am targeting feature-complete by sunday.
     
  10. Crimson13

    Crimson13 Prince

    Joined:
    Jul 11, 2012
    Messages:
    373
    So should I not start a game 'til then? :p

    I am looking forward to the new things that you've put up though, it really appears to add more depth to vassalage.
     
  11. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Haha, maybe not. :crazyeye:

    I'm trying to make this update save game compatible. That is an issue I'll need to work with G to get it integrated correctly. Feature complete is untested and probably won't be deployed to CP and CBP til a bit after that.

    Go ahead and keep playing, and when I'm ready to release it to the Beta, everyone will know. :)
     
  12. Gazebo

    Gazebo Lord of the Community Patch Supporter

    Joined:
    Sep 26, 2010
    Messages:
    18,048
    Gender:
    Male
    Location:
    Little Rock
    I wouldn't worry about save game compatibility. Most users will start new with the mod anyways.

    G
     
  13. CharacterZero

    CharacterZero Chieftain

    Joined:
    Feb 25, 2016
    Messages:
    13
    How is the trade value for capitulation calculated? I'm fighting a civ with a small city that's surrounded by enemies. It's literally him against the world and his max trade value is around 2800. I briefly saw the actual trade value for capitulation and it was about 20 million.

    I find trade values to work weird in general. I was fighting a civ who had two cities left and his trade value was such that capitulation was an option. After letting an allied city-state take one of his cities his trade value went down to 400 (capitulation impossible) when he was in a seemingly worse predicament.

    Basically whether it's the capitulation value or max trade values I have found forcing capitulation too hard as it is.
     
  14. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    20 million? Sounds like (2^32 / 2 - 1) / 100 or INT_MAX / 100 (was it 21,474,836?). Why is that value being displayed? :crazyeye: That's basically the "not gonna happen" value.

    As for what's causing it? Could be numerous things - things I'd like to check out and see if they can't be fixed! Thats TODO for Beta but I haven't worked on it cus I'm still developing features! But if it hit INT MAX, you can forget about the AI doing it.

    Edit:needed to fix errors
     
  15. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Doing some housecleaning right now, want to check out my logic for checking if vassalage is acceptable (voluntarily and capitulation) and touching up on the UI so things are clearer for players.
     
  16. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    Figured I'd post now so I can get some feedback.

    Here are the new methods for determining whether vassalage is acceptable. Could contain bugs:

    Code:
    ///	Do we want to capitulate to ePlayer? (bWar: is this a wartime assessment?)
    bool CvDiplomacyAI::IsVassalageAcceptable(PlayerTypes ePlayer, bool bWar)
    {
    	CvAssertMsg(ePlayer >= 0, "DIPLOMACY_AI: Invalid Player Index.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.");
    	CvAssertMsg(ePlayer < MAX_MAJOR_CIVS, "DIPLOMACY_AI: Invalid Player Index.  Please send Jon this with your last 5 autosaves and what changelist # you're playing.");
    
    	// We can't become ePlayer's vassal
    	if(!GET_TEAM(GET_PLAYER(ePlayer).getTeam()).canBecomeVassal(GetPlayer()->getTeam()))
    		return false;
    
    	// Human teams can capitulate, but the AI can't do it for him and he must accept on the trade screen
    	if(GET_TEAM(GetPlayer()->getTeam()).isHuman() || GetPlayer()->IsAITeammateOfHuman())
    		return false;
    
    	// Vassalage never acceptable if I have vassals already
    	if(GET_TEAM(GetPlayer()->getTeam()).GetNumVassals() > 0)
    		return false;
    
    	// Split this function into two evaluations, capitulation (war) and voluntary (voluntary)
    	if(bWar)
    	{
    		return IsCapitulationAcceptable(ePlayer);
    	}
    	else
    	{
    		return IsVoluntaryVassalageAcceptable(ePlayer);
    	}
    }
    
    // Do we want to capitulate due to war to ePlayer?
    bool CvDiplomacyAI::IsCapitulationAcceptable(PlayerTypes ePlayer)
    {
    	TeamTypes eOurTeam = GetPlayer()->getTeam();
    	CvTeam& kOurTeam = GET_TEAM(eOurTeam);
    
    	TeamTypes eTheirTeam = GET_PLAYER(ePlayer).getTeam();
    	CvTeam& kTheirTeam = GET_TEAM(eTheirTeam);
    
    	if(!kOurTeam.isAtWar(eTheirTeam))
    		return false;
    
    	// How's the war going?
    	WarStateTypes eWarState = GetWarState(ePlayer);
    	if(eWarState >= WAR_STATE_STALEMATE ||
    		eWarState == NO_WAR_STATE_TYPE)
    		return false;
    	
    	int iWantVassalageScore = 0;
    	if(eWarState == WAR_STATE_NEARLY_DEFEATED)
    		iWantVassalageScore = 100;
    	else
    		iWantVassalageScore = 70;
    
    	// Mod based on distance
    	switch(GetPlayer()->GetProximityToPlayer(ePlayer))
    	{
    		case PLAYER_PROXIMITY_DISTANT:
    			iWantVassalageScore *= 33;
    			iWantVassalageScore /= 100;
    			break;
    		case PLAYER_PROXIMITY_FAR:
    			iWantVassalageScore *= 66;
    			iWantVassalageScore /= 100;
    			break;
    		case PLAYER_PROXIMITY_CLOSE:
    			iWantVassalageScore *= 100;
    			iWantVassalageScore /= 100;
    			break;
    		case PLAYER_PROXIMITY_NEIGHBORS:
    			iWantVassalageScore *= 120;
    			iWantVassalageScore /= 100;
    			break;
    	}
    
    	// Factor in this guy's military strength
    	switch(GetPlayerMilitaryStrengthComparedToUs(ePlayer))
    	{
    		case STRENGTH_IMMENSE:
    			iWantVassalageScore *= 200;
    			iWantVassalageScore /= 100;
    			break;
    		case STRENGTH_POWERFUL:
    			iWantVassalageScore *= 150;
    			iWantVassalageScore /= 100;
    			break;
    		case STRENGTH_STRONG:
    			iWantVassalageScore *= 125;
    			iWantVassalageScore /= 100;
    			break;
    		default:
    			return false;
    			break;
    	}
    
    	// We're going for conquest...
    	if(IsGoingForWorldConquest())
    	{
    		iWantVassalageScore *= 50;
    		iWantVassalageScore /= 100;
    	}
    
    	// Lost our capital?
    	if(GetPlayer()->IsHasLostCapital())
    	{
    		iWantVassalageScore *= 150;
    		iWantVassalageScore /= 100;
    
    		// to this player?
    		CvPlot* pOriginalCapitalPlot = GC.getMap().plot(GET_PLAYER(ePlayer).->GetOriginalCapitalX(), GET_PLAYER(ePlayer).GetOriginalCapitalY());
    		if(pOriginalCapitalPlot != NULL)
    		{
    			if(pOriginalCapitalPlot->isCity())
    			{
    				if(GET_PLAYER(pOriginalCapitalPlot->getOwner()).getTeam() == GET_PLAYER(ePlayer).getTeam())
    				{
    					iWantVassalageScore *= 150;
    					iWantVassalageScore /= 100;
    				}
    			}
    		}
    	}
    
    	// They have more civs than us!
    	if(kOurTeam.getAliveCount() < kTheirTeam.getAliveCount())
    	{
    		iWantVassalageScore *= 200;
    		iWantVassalageScore /= 100;
    	}
    	else if(kOurTeam.getAliveCount() > kTheirTeam.getAliveCount())
    	{
    		iWantVassalageScore *= 50;
    		iWantVassalageScore /= 100;
    	}
    
    	int iCulturalDominanceOverUs = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(m_pPlayer->GetID()) - GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer);
    
    	iWantVassalageScore *= (100 + 10 * iCulturalDominanceOverUs);
    	iWantVassalageScore /= 100;
    
    	int iOurTechs = kOurTeam.GetTeamTechs()->GetNumTechsKnown();
    	int iTheirTechs = kTheirTeam.GetTeamTechs()->GetNumTechsKnown();
    	int iTechPercent = 0;
    	if(iTheirTechs == 0)
    		iTechPercent = INT_MAX;
    	else
    		iTechPercent = iOurTechs * 100 / iTheirTechs;
    
    	if(iTechPercent < 50) {
    		iWantVassalageScore *= 250;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 75) {
    		iWantVassalageScore *= 150;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 85) {
    		iWantVassalageScore *= 120;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 95) {
    		iWantVassalageScore *= 105;
    		iWantVassalageScore /= 100;
    	}
    	else {
    		iWantVassalageScore *= 100;
    		iWantVassalageScore /= 100;
    	}
    
    
    	int iThreshold = /*100*/ GC.getVASSALAGE_CAPITULATE_BASE_THRESHOLD();
    	return (iWantVassalageScore > iThreshold);
    }
    
    // Do we want to voluntarily become ePlayer's vassal?
    bool CvDiplomacyAI::IsVoluntaryVassalageAcceptable(PlayerTypes ePlayer)
    {
    	TeamTypes eOurTeam = GetPlayer()->getTeam();
    	CvTeam& kOurTeam = GET_TEAM(eOurTeam);
    
    	TeamTypes eTheirTeam = GET_PLAYER(ePlayer).getTeam();
    	CvTeam& kTheirTeam = GET_TEAM(eTheirTeam);
    
    	// No cities
    	if(kTheirTeam.getNumCities() == 0)
    		return false;
    
    	// We have more members - no
    	if(kOurTeam.getAliveCount() > kTheirTeam.getAliveCount())
    		return false;
    
    	std::vector<PlayerTypes> aOurTeam;
    	std::vector<PlayerTypes> aTheirTeam;
    	for(int iI = 0; iI < MAX_MAJOR_CIVS; iI++)
    	{
    		PlayerTypes eLoopPlayer = (PlayerTypes) iI;
    		if(GET_PLAYER(eLoopPlayer).isAlive())
    		{
    			if(GET_PLAYER(eLoopPlayer).getTeam() == eOurTeam)
    				aOurTeam.push_back(eLoopPlayer);
    			if(GET_PLAYER(eLoopPlayer).getTeam() == eTheirTeam)
    				aTheirTeam.push_back(eLoopPlayer);
    		}
    	}
    
    	int iAverageOpinionScore = 0;
    	int iOurCapitals = 0;
    	int iTheirCapitals = 0;
    	for(std::vector<PlayerTypes>::iterator it = aOurTeam.begin(); it != aOurTeam.end(); it++)
    	{
    		iOurCapitals += GET_PLAYER(*it).GetNumCapitalCities();
    		iAverageOpinionScore += (int) GET_PLAYER(*it).GetDiplomacyAI()->GetMajorCivOpinion(ePlayer);
    	}
    	for(std::vector<PlayerTypes>::iterator it = aTheirTeam.begin(); it != aTheirTeam.end(); it++)
    	{
    		iTheirCapitals += GET_PLAYER(*it).GetNumCapitalCities();
    	}
    	iAverageOpinionScore /= aOurTeam.size();
    	MajorCivOpinionTypes eOpinion = (MajorCivOpinionTypes) iAverageOpinionScore;
    
    	if(eOpinion <= MAJOR_CIV_OPINION_COMPETITOR)
    		return false;
    
    	// We possess more capitals than them
    	if(iOurCapitals > iTheirCapitals)
    		return false;
    
    	// We're going for world conquest?
    	if(IsGoingForWorldConquest())
    	{
    		if(GetPlayerMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_STRONG)
    			return false;
    	}
    
    	MajorCivApproachTypes eTrueApproach = GetMajorCivApproach(ePlayer, /*bHideTrueFeelings*/ false);
    	
    	// if we're planning on war or hostile with this player, don't want to be his voluntary vassal
    	if(eTrueApproach == MAJOR_CIV_APPROACH_WAR ||
    		eTrueApproach == MAJOR_CIV_APPROACH_HOSTILE)
    		return false;
    
    	//Denouncement in either direction?
    	if(IsDenouncedPlayer(ePlayer) || GET_PLAYER(ePlayer).GetDiplomacyAI()->IsDenouncedPlayer(GetPlayer()->GetID()))
    			return false;
    
    	// Don't accept vassalage from players too far away
    	if(GetPlayer()->GetProximityToPlayer(ePlayer) < PLAYER_PROXIMITY_CLOSE)
    		return false;
    
    	// Player is not strong
    	if(GetPlayerMilitaryStrengthComparedToUs(ePlayer) <= STRENGTH_AVERAGE)
    		return false;
    
    	// Are we dominating them in some way?
    	InfluenceLevelTypes eInfluence = GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer);
    	if(eInfluence >= INFLUENCE_LEVEL_INFLUENTIAL)
    		return false;
    
    	// No voluntary capitulation if there's less than 50% of the players ever alive
    	if(GC.getGame().countMajorCivsAlive() < (GC.getGame().countMajorCivsEverAlive() / 2 ))
    		return false;
    
    	// Has this player a military promise against us?
    	if(IsPlayerBrokenMilitaryPromise(ePlayer))
    		return false;
    
    	// Do we think this player is a warmonger?
    	if(GetWarmongerThreat(ePlayer) >= THREAT_SEVERE)
    		return false;
    
    	// Differing ideologies?
    	if(GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree() != NO_POLICY_BRANCH_TYPE &&
    		GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree() != NO_POLICY_BRANCH_TYPE &&
    		GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree() != GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree())
    		return false;
    
    	// If we got down here, then vassalage is possible - let's evaluate
    	int iWantVassalageScore = 0;
    	
    	int iPercentPopulation = kOurTeam.getTotalPopulation() * 100 / kTheirTeam.getTotalPopulation();
    	if(iPercentPopulation >= 100)
    		return false;
    	
    	if(iPercentPopulation < 50)
    		iWantVassalageScore += 75;
    	else if(iPercentPopulation < 75)
    		iWantVassalageScore += 40;
    	else if(iPercentPopulation < 85)
    		iWantVassalageScore += 25;
    	else
    		iWantVassalageScore += 10;
    
    	int iOurTechs = kOurTeam.GetTeamTechs()->GetNumTechsKnown();
    	int iTheirTechs = kTheirTeam.GetTeamTechs()->GetNumTechsKnown();
    	int iTechPercent = 0;
    	if(iTheirTechs == 0)
    		iTechPercent = INT_MAX;
    	else
    		iTechPercent = iOurTechs * 100 / iTheirTechs;
    
    	// We have a lot more techs than them!
    	if(iTechPercent > 130)
    		return false;
    	else if(iTechPercent < 50) {
    		iWantVassalageScore *= 250;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 75) {
    		iWantVassalageScore *= 150;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 85) {
    		iWantVassalageScore *= 120;
    		iWantVassalageScore /= 100;
    	}
    	else if(iTechPercent < 95) {
    		iWantVassalageScore *= 105;
    		iWantVassalageScore /= 100;
    	}
    	else {
    		iWantVassalageScore *= 100;
    		iWantVassalageScore /= 100;
    	}
    
    	// Mod based on opinion
    	switch(eOpinion)
    	{
    		case MAJOR_CIV_OPINION_NEUTRAL:
    			iWantVassalageScore *= 100;
    			iWantVassalageScore /= 100;
    			break;
    		case MAJOR_CIV_OPINION_FAVORABLE:
    			iWantVassalageScore *= 110;
    			iWantVassalageScore /= 100;
    			break;
    		case MAJOR_CIV_OPINION_FRIEND:
    			iWantVassalageScore *= 120;
    			iWantVassalageScore /= 100;
    			break;
    		case MAJOR_CIV_OPINION_ALLY:
    			iWantVassalageScore *= 125;
    			iWantVassalageScore /= 100;
    			break;
    	}
    
    	int iCulturalDominanceOverUs = GET_PLAYER(ePlayer).GetCulture()->GetInfluenceLevel(m_pPlayer->GetID()) - GetPlayer()->GetCulture()->GetInfluenceLevel(ePlayer);
    
    	iWantVassalageScore *= (100 + 10 * iCulturalDominanceOverUs);
    	iWantVassalageScore /= 100;
    
    	// Factor in this guy's military strength
    	switch(GetPlayerMilitaryStrengthComparedToUs(ePlayer))
    	{
    		case STRENGTH_IMMENSE:
    			iWantVassalageScore *= 200;
    			iWantVassalageScore /= 100;
    			break;
    		case STRENGTH_POWERFUL:
    			iWantVassalageScore *= 150;
    			iWantVassalageScore /= 100;
    			break;
    		case STRENGTH_STRONG:
    			iWantVassalageScore *= 125;
    			iWantVassalageScore /= 100;
    			break;
    		default:
    			return false;
    	}
    
    	switch(GetPlayer()->GetProximityToPlayer(ePlayer))
    	{
    		case PLAYER_PROXIMITY_NEIGHBORS:
    			iWantVassalageScore *= 133;
    			iWantVassalageScore /= 100;
    			break;
    		case PLAYER_PROXIMITY_CLOSE:
    			iWantVassalageScore *= 110;
    			iWantVassalageScore /= 100;
    			break;
    		default:
    			return false;
    	}
    
    	// Reduce score based on at war count
    	iWantVassalageScore -= 15 * kTheirTeam.getAtWarCount(true);
    
    	int iThreshold = /*100*/ GC.getVASSALAGE_CAPITULATE_BASE_THRESHOLD();
    
    	return (iWantVassalageScore > iThreshold);
    }
    
     
  17. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    I am not sure if the mod is feature complete, but I am currently doing a stable check to see if everything is not gonna break, then I want to work on pushing it to a Beta build for others to play, and then I'll add some other stuff (like help screens, civilopedia, etc.)
     
  18. Tarzan737

    Tarzan737 Warlord

    Joined:
    Dec 18, 2015
    Messages:
    212

    Awesome!
     
  19. Putmalk

    Putmalk Deity

    Joined:
    Sep 26, 2010
    Messages:
    2,623
    Location:
    New York
    I think I'm nearing the end.

    Here is a compilation of the current patch notes I have

    Spoiler :

    General
    • Masters now receive a +33% tourism bonus against their vassals
    • Vassals can no longer be considered for Choose Host or World Leader &#8211; but they still become host of the World Congress upon founding it.
    • Death of a Vassal's unit inside his territory now counts for failed protect score
    • Death of a Vassal's unit inside enemy territory no longer counts for failed protect score
    • Pillaged vassal territory now applies a small failed protect score
    • Notifications related to Vassalage have received an upgrade to be more descriptive.
    • Upon capitulation (forced vassalage as part of a peace treaty), the new vassal will reset negative diplomatic modifiers similar to civ resurrection. Voluntary vassals are not affected by this.
    • You can declare war on your vassals, but other teams cannot declare war on your vassals. This provides two options of breaking vassalage: declaration of war, and peaceful liberation (see Vassal Liberation section).
    • Extended range of failed protect score near vassal cities to 3 hexes away.
    • Added a new Vassalage Overview &#8211; see the Vassalage Overview section
    • Technology Trading has been moved to Scientific Theory

    AI
    • AI Vassals can denounce their masters &#8211; the weight is determined based on how well you've treated your vassal.
    • AI Masters will never denounce their vassals
    • Retooled AI evaluation on the following:
      • Wants to capitulate
      • Wants to become your voluntary vassal
      • Wants to end vassalage with master
      • Wants to accept human's bid to end vassalage
    • Now evaluate a few flavors (expansion, offense, military training defense, culture, wonder) when determining whether a vassal wants to become a voluntary vassal or not.
      • For example: Ramesses is 22, Enrico is 10, Shaka is -34, Attila is -14
      • Note to modders: if you delete those flavors, then the flavor mod difference for that flavor is 0
    • Common sense evaluation: If an AI is going for diplomatic victory, and the United Nations has started, then they will never accept voluntary vassalage and will attempt to leave vassalage immediately.

    New Feature: Vassal Overview
    • In an effect to provide a unified and easy to manage screen for all things related to Vassalage, I have created the Vassal Overview
    • If you have vassals, you can select each one individually to receive more details about them
    • The Vassal Overview provides a concise description of the type of vassalage, how long the vassalage has lasted (and the turn it started) as well as helpful tooltips to provide more meaning to things that can be unclear to the player.
    • On the left side of the Vassal Details box, you can see basic stats (similar to the top panel for your own civ) for your vassal. You can also see what tech they are researching, and how long
    • On the right side of the Vassal Details box, you can set your vassal taxes and view details on how they view your vassalage and why they feel that way. You can also liberate your vassal (when applicable, see Vassal Liberation section)

    New Feature: Vassal Taxes
    • You can set vassal taxes on intervals of 5% (from 0%-25%).
    • Vassal taxes are applied per vassal, but distributed evenly amongst teammates.
    • Vassal taxes are calculated at the beginning of the vassal's turn and are a percentage of their gross (positive gold) income.
    • The heavier you tax a vassal, the more they will hate you.
    • AI teammates will never set taxes for human players.
    • AI opponents will set taxes based on a number of factors, including how much they like you, and how both of your financial situations are.

    New Feature: Vassal Liberation
    • Masters can now liberate their vassals after a certain period of turns (50 turns on Standard).
    • Liberation of vassals acts as a peaceful removal of vassalage, and grants a huge positive modifier for liberating a vassal
    • You may liberate vassals when eligible through the new Vassalage Overview
    • You may request independence from masters through the new Vassalage Overview
    • AI masters will liberate you if they like you, and they do not deem you a threat to them
    • This is not the same as resurrecting a player, so you will not receive a reset in negative diplomatic modifiers.

    Bugfixes
    • Fixed a bug where voluntary vassalage was not being correctly set
    • Fixed a bug where you could call the LUA function DoBecomeVassal() with invalid civs (for example, could set a civ to be a vassal of itself)
    • Reversed the function for canBecomeVassal() to be actually sane. This mostly affects modders:
      • Team1.canBecomeVassal(Team2) now checks if Team1 can become Team2's vassal, was Team2 can become Team1's vassal
    • Added new function CanMakeVassal() which is simply the reverse of CanBecomeVassal()
    • Fixed issue where DealAI did not send offers to revoke human vassals

    UI
    • Complete pass on tooltips on the Diplomacy Trade screen related to "Vassal State", "Capitulation", and "Liberation" in an effort to provide more clarity to the player

     
  20. Wario Mario

    Wario Mario Warlord

    Joined:
    Jul 18, 2006
    Messages:
    277
    Location:
    Wario Ware INC
    Putmalk is awesome :cool:
    I can't wait to see all these vassal ideas in action!
     

Share This Page