Question about 1 city civs (like Venice)

Craig_Sutter

Deity
Joined
Aug 13, 2002
Messages
2,773
Location
Calgary, Canada
So I have created a couple of one city civilizations. They have similar traits to Venice, but no "Merchant of Venice" or clones thereof.

One such trait:

Code:
<Traits>
		<Row>
			<Type>TRAIT_CITY_OF_LIGHT</Type>
			<Description>TXT_KEY_TRAIT_CITY_OF_LIGHT</Description>
			<ShortDescription>TXT_KEY_TRAIT_CITY_OF_LIGHT_SHORT</ShortDescription>
			<NoAnnexing>true</NoAnnexing>
		</Row>
	</Traits>

	<Trait_NoTrain>
		<Row>
			<TraitType>TRAIT_CITY_OF_LIGHT</TraitType>
			<UnitClassType>UNITCLASS_SETTLER</UnitClassType>
		</Row>
	</Trait_NoTrain>

	<Language_en_US>

		<Row Tag="TXT_KEY_TRAIT_CITY_OF_LIGHT">
			<Text>Only one city allowed.  No annexing.  All cities transferred to Franks.  Gain Science and Gold from buildings built by Franks.  Construction of iron and horse supported units spawns units in Frank cities with improved iron and horse resources. </Text>
		</Row>
		<Row Tag="TXT_KEY_TRAIT_CITY_OF_LIGHT_SHORT">
			<Text>The City of Light</Text>
		</Row>

	</Language_en_US>

What will happen when I take policies that would give settlers or otherwise acquire settlers... with I get "Merchants of Venice" (bad), or Settlers (not good, but not so bad), or "Merchants" (fine), or nothing (not so good)?

Do I have to change anything in Policies or another place?

Thanks.
 
For policies at least, it's hard coded to go looking for the Merchant of Venice (or another unit that can buy city states):

Code:
// slewis
// for venice
if (pUnitEntry->IsFound() && GetPlayerTraits()->IsNoAnnexing())
{
	// drop a merchant of venice instead
	// find the eUnit replacement that's the merchant of venice
	for(int iVeniceSearch = 0; iVeniceSearch < GC.getNumUnitClassInfos(); iVeniceSearch++)
	{
		const UnitClassTypes eVeniceUnitClass = static_cast<UnitClassTypes>(iVeniceSearch);
		CvUnitClassInfo* pkVeniceUnitClassInfo = GC.getUnitClassInfo(eVeniceUnitClass);
		if(pkVeniceUnitClassInfo)
		{
			const UnitTypes eMerchantOfVeniceUnit = (UnitTypes) getCivilizationInfo().getCivilizationUnits(eVeniceUnitClass);
			if (eMerchantOfVeniceUnit != NO_UNIT)
			{
				CvUnitEntry* pVeniceUnitEntry = GC.getUnitInfo(eMerchantOfVeniceUnit);
				if (pVeniceUnitEntry->IsCanBuyCityState())
				{
					pNewUnit = initUnit(eMerchantOfVeniceUnit, iX, iY);				
					break;
				}
			}
		}
	}
}
else
{
	pNewUnit = initUnit(eUnit, iX, iY);
}

However, judging from the above, if you give your civ a UU which overrides the merchant of venice and *doesn't* have the ability to purchase city states, it may just do nothing. (Or it may crash, depending on how well Firaxis' code is guarded.) I would imagine the implementation is similar elsewhere that civs receive settlers for non-standard reasons.
 
All of the "Venice Logic" is based off the trait attribute NoAnnexing and not the Trait_NoTrain entries.

If a civ with the NoAnnexing trait acquires a settler they will get a unit with the CanBuyCityState attribute instead.

Be aware there is a bug in the game core that means that if a civ with the NoAnnexing trait loses its capital, when it retakes it, it cannot liberate its old capital but must annex it.
 
For policies at least, it's hard coded to go looking for the Merchant of Venice (or another unit that can buy city states):

The variables in that code are very badly named, as it's actually looking for a civ specific unit with the CanBuyCityStates attribute, which for Venice happens to be the MoV. If the civ doesn't have a specific unit with that attribute (or there isn't a generic one), they will get nothing.
 
So if I do nothing, to be clear, then these one city civs (lacking a Merchant of Venice or clone thereof) will simply get nothing.

If that's the case, I think I'll do lua to grant a generic Merchant unit when the appropriate liberty policy is chosen.

Are there any unforeseen circumstances which would otherwise generate a Merchant of Venice for these one city civilizations? I would like to compensate them with a generic Merchant in all such circumstances rather than have them just getting nothing.

I think you may have meant that the one city civ with no-annex must puppet its capital... I hope simply "pCity:SetPuppet (false);" for their capital can fix that...

Thanks for the assistance to both of you.
 
So if I do nothing, to be clear, then these one city civs (lacking a Merchant of Venice or clone thereof) will simply get nothing.
Correct

If that's the case, I think I'll do lua to grant a generic Merchant unit when the appropriate liberty policy is chosen.
Only really works if there are no other mods in use that change policies ;)

Are there any unforeseen circumstances which would otherwise generate a Merchant of Venice for these one city civilizations? I would like to compensate them with a generic Merchant in all such circumstances rather than have them just getting nothing.
The code is the same in all places, anywhere Venice (or NoAnnex civs) could get a settler, they will get a "CanBuyCityState" unit instead. You could probably create a unit with that attribute, detect the creation of it and replace it with a merchant.

I think you may have meant that the one city civ with no-annex must puppet its capital...
Oops :blush:

I hope simply "pCity:SetPuppet (false);" for their capital can fix that...
While that will un-puppet it, I'm not sure that it would re-instate it as the capital.
 
This might work... I'm going to do some testing to see what happens...I also might have to account for liberating a city.

Code:
-- when a city is captured, and that city is the former capital of the HRE it is unpuppeted

function UnPuppetHRE (oldPlayerID, bCapital, iX, iY, newPlayerID, bConquest)

local newPlayer = Players[newPlayerID];
local oldPlayer = Players[oldPlayerID];
local plot = Map.GetPlot(iX, iY);
local pCity = plot:GetPlotCity();
local OriginalOwner = Players[pCity:GetOriginalOwner()]

--Set up HRE

	for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do

	local pHRE = Players[iPlayer]

   		if (GameInfo.Civilizations.CIVILIZATION_FRANCE.ID == pHRE:GetCivilizationType()) then
	
		HRE = pHRE
		iHRE = iPlayer

		end
	end

-- City exists?

	if pCity ~= nil and pCity:IsOriginalCapital() and newPlayer == OriginalOwner and newPlayer == HRE and pCity:IsPuppet () then
									
				pCity:SetPuppet (false);
				pCity:SetNumRealBuilding(GameInfoTypes["BUILDING_PALACE"], 1);
				print(pCity:GetName(), "...is unpuppeting and creating palace" );
	
	end

end

GameEvents.CityCaptureComplete.Add(UnPuppetHRE)

This won't unpuppet the capital if it is not the original capital, but I'm going to see if I need to install a capital after the original capital is captured... will there be a new capital? In which case, I'll also have to unpuppet that one... or if no capital is created in another city, install a capital via player:findcapital (think that is the function), unpuppet it, then install a palace if that isn't done via the function.

Off to experiment...
 
These functions seem to work in relation to unpuppeting the capital and repuppeting non-capitals... may be better events to tie into, and the city capture function seems uneeded in light of the 2nd function. Should also work in the case of liberation (the second function again).

I tested it using IGE. Conquered the capital... it moved to a new city. The city subsequently unpuppeted. Then allowed the recapture of the original capital... the palace moved there on its own. The ex-capital repuppeted and its palace disappeared. The new (original) capital reacquired its palace and was unpuppeted.

Test appear to show everything is working.

There may be a better event hook... I do not know. And as I said, the city conquest function is likely not even needed.

Code:
-- when a city is captured, and that city is the former capital of the HRE it is unpuppeted

function UnPuppetHRE (oldPlayerID, bCapital, iX, iY, newPlayerID, bConquest)

local newPlayer = Players[newPlayerID];
local oldPlayer = Players[oldPlayerID];
local plot = Map.GetPlot(iX, iY);
local pCity = plot:GetPlotCity();
local OriginalOwner = Players[pCity:GetOriginalOwner()]
local HRE
local iHRE

--Set up HRE

	for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do

	local pHRE = Players[iPlayer]

   		if (GameInfo.Civilizations.CIVILIZATION_PERSIA.ID == pHRE:GetCivilizationType()) then
	
		HRE = pHRE
		iHRE = iPlayer

		end
	end

-- City exists?

	if pCity ~= nil and pCity:IsOriginalCapital() and newPlayer == OriginalOwner and newPlayer == HRE and pCity:IsPuppet () then
									
				pCity:SetPuppet (false);
				print(pCity:GetName(), "...is unpuppeting and creating palace" );
	
	end

end

GameEvents.CityCaptureComplete.Add(UnPuppetHRE)


function RePuppet ()

	if Game.GetGameTurn() >= 0 then

		for iPlayer=0, GameDefines.MAX_MAJOR_CIVS-1 do

		local pPlayer = Players[iPlayer];

			if pPlayer:IsEverAlive() then 

				if (GameInfo.Civilizations.CIVILIZATION_PERSIA.ID == pPlayer:GetCivilizationType()) or
				(GameInfo.Civilizations.CIVILIZATION_FRANCE.ID == pPlayer:GetCivilizationType()) then
			
				-- Enumerate cities
			
					for pCity in pPlayer:Cities() do
							
					-- City exists and and is not the capital and is not puppeted?  Then puppet.
						
						if pCity ~= nil and not pCity:IsCapital() and not pCity:IsPuppet () then 
					
						pCity:SetPuppet (true);
						print (pCity:GetName() ,"has been puppeted");

					-- City exists and and is the capital and is puppeted?  Then unpuppet.
						
						elseif pCity ~= nil and pCity:IsCapital() and pCity:IsPuppet () then
									
						pCity:SetPuppet (false);
						print(pCity:GetName(), "...is unpuppeting and creating palace" );	
											
    					end
					end
				end
			end
		end
	end
end


--to set trait changes

local g_lastTurn = -1

local function OnPlayerDoTurn(iPlayer)

	local gameTurn = Game.GetGameTurn()

	if g_lastTurn < gameTurn then
		g_lastTurn = gameTurn

		--per game turn function here

		RePuppet ()
		
	end
end

GameEvents.PlayerDoTurn.Add(OnPlayerDoTurn)

Will work on the Merchant of Venice stuff now.
 
Rather than making a faux Merchant of Venice, I've decided to install Merchants in the capitals of one city civs that can't create a settler.

The following code adds a merchant at Collective Rule policy and Optics technology.

I don't know if I missed any other instances that might generate a Merchant of Venice (if it were available to a civ). The civilizations ought to generate generic Merchants from great person points. I've not experimented with that but I think it is most likely.

Code:
--this is to add merchants at adoption of a certain policy

function OnPolicyAdopted(playerID, policyID)

	if policyID == (GameInfo.Policies["POLICY_COLLECTIVE_RULE"].ID) then
	
	local pPlayer = Players[playerID];
			
		if pPlayer:IsEverAlive() then 

			if GameInfo.Civilizations.CIVILIZATION_PERSIA.ID == pPlayer:GetCivilizationType() or 
			GameInfo.Civilizations.CIVILIZATION_FRANCE.ID == pPlayer:GetCivilizationType() then
						
			local pCity = pPlayer:GetCapitalCity()
			local pPlot = pCity:GetCityIndexPlot();
			local Spawnunit;
			local iSpawnX = pPlot:GetX();
			local iSpawnY = pPlot:GetY();

			Spawnunit = pPlayer:InitUnit(GameInfoTypes["UNIT_MERCHANT"], iSpawnX, iSpawnY, UNITAI_MERCHANT, DIRECTION_NORTHWEST );
			print (pCity:GetName() ,"...is spawning a Merchant from policy... ");
				
			end
		end
	end
end

GameEvents.PlayerAdoptPolicy.Add(OnPolicyAdopted);


--this is to add merchants at acquisition of a certain technology

function OnTechResearched(team, tech, change)

local pPlayer
local pPlayerTeamID

--local pPlayerTeam

if	(tech == (GameInfoTypes["TECH_OPTICS"])) then

	for playerID=0, GameDefines.MAX_MAJOR_CIVS-1 do

	local pPlayer = Players[playerID];

		if pPlayer:IsEverAlive() then 

			if (GameInfo.Civilizations.CIVILIZATION_PERSIA.ID == pPlayer:GetCivilizationType()) or 
			(GameInfo.Civilizations.CIVILIZATION_FRANCE.ID == pPlayer:GetCivilizationType()) then
	
			local pPlayerTeamID = pPlayer:GetTeam()
			
				if	pPlayerTeamID == team then
						
				local pCity = pPlayer:GetCapitalCity()
				local pPlot = pCity:GetCityIndexPlot();
				local Spawnunit;
				local iSpawnX = pPlot:GetX();
				local iSpawnY = pPlot:GetY();

				Spawnunit = pPlayer:InitUnit(GameInfoTypes["UNIT_MERCHANT"], iSpawnX, iSpawnY, UNITAI_MERCHANT, DIRECTION_NORTHWEST );
				print (pCity:GetName() ,"...is spawning a Merchant from technology... ");
				end
			end
		end
	end
end
end

GameEvents.TeamTechResearched.Add(OnTechResearched);

Hope this is helpful to someone who creates a one-city civ in future.

PS I've modded France and Persia to be one-city civs, just in case you are wondering.
 
Note that if a NoAnnexing Civ is played in a later-era start which would provide the player with extra Settlers, they will receive a CanBuyCityState unit in its place. Make sure to include code to handle this at game initialization.
 
Back
Top Bottom