Detecting buildings in a capital?

Enginseer

Salientia of the Community Patch
Supporter
Joined
Nov 7, 2012
Messages
3,674
Location
Somewhere in California
So, I'm trying to make a mod where if your capital has a certain building due to a World Congress Project's reward, then a certain building will spawn throughout your entire cities. I can copy some of framedarchitecture's LUA on making sure a certain building will spawn throughout every city, but I have a few questions.

Code:
Player:GetCapitalCity();
City:IsCapital();

Do any of these code gets the original capital or wherever the capital is, the player has?

And which code is more reliable?
Code:
Player:CountNumBuildings(buildingID) > 0;
pCity:IsHasBuilding(buildingID);
 
pPlayer:CountNumBuildings() loops all the player's cities and sums pCity:GetNumBuilding()

For a discussion of the various city building counting methods, see this thread
 
Code:
Player:GetCapitalCity();
City:IsCapital();
Do any of these code gets the original capital or wherever the capital is, the player has?
Both these methods work directly on/for a player's current capital.

City:IsOriginalCapital() will tell you whether or not a particular city ever was a player's original capital.

---------------------------------------------------------------------------

Code:
pCapitalCity = pPlayer:GetCapitalCity()
Will give 'pPlayer's current capital city in the "pointer" form so that you can use the variable pCapitalCity within methods such as pCapitalCity:IsOriginalCapital(), which would give you "true/false" as to whether that current capital city was the player's original capital city.

----------------------------------------------------------------------------

Code:
City:IsCapital();
Gives the "true/false" as to whether any city you are currently "examing" via some lua loop, code, or event-hook, is the current capital city for a player.

-----------------------------------------------------------------------------

The following two methods are equivalent for placing the "pointer data" for the player's capital city into variable pCapitalCity:
Code:
local pPlayer = Players[iPlayer]
local pCapitalCity = pPlayer:GetCapitalCity()
vs
Code:
local pPlayer = Players[iPlayer]
local pCapitalCity
for pCity in pPlayer:Cities() do
	if pCity:IsCapital() then
		pCapitalCity = pCity
		break
	end
end
Both methods will give you "nil" for variable pCapitalCity if the player has not founded their capital city yet. So if running, for example, a PlayerDoTurn event, you should probably also include code for pPlayer:IsFoundedFirstCity() which gives "true/false" for whether the player has founded their 1st city yet.

---------------------------------------------------------------------

You can also use the 1st more-direct method of getting the player's capital city and do so even more directly as in:
Code:
local pCapitalCity = Players[iPlayer]:GetCapitalCity()
 
Code:
function OnCityCreation(playerID, x, y)
	local player = Players[playerID]
	local CapitalCity = player:GetCapitalCity()
	local newcity = Map.GetPlot(x, y):GetPlotCity()
	if CapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then
		for city in player:Cities() do
			if city:IsHasBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"]) then
				break;
			else city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 1)
		end
	end
end
GameEvents.PlayerCityFounded.Add( OnCityCreation )
--------------------------------------------------------------------
function OnCityCaptureCompleted(oldOwnerID, bIsCapital, x, y, newOwnerID, pop, bConquest)
	local player = Players[oldOwnerID]
	local CapitalCity = player:GetCapitalCity()
	if not CapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then 
		for city in player:Cities() do
			city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 0)
		end
	end
	local newPlayer = Players[newOwnerID]
    if CapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then 
		for city in newPlayer:Cities() do
			if city:IsHasBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"]) then
				break;
			else city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 1)
			end
		end
	end
end
GameEvents.CityCaptureComplete.Add( OnCityCaptureCompleted );
--------------------------------------------------------------------
function IsDummySellable(playerID, buildingID)
	if (buildingID == GameInfoTypes.["BUILDING_PETRA_DUMMY"]) then return false end
	return true;
end
GameEvents.CityBuildingsIsBuildingSellable.Add(IsDummySellable)

Does this code look right then? I want to make sure that the code I'm using makes sure that as long as I'm getting the civilization's capital, it doesn't have to be the original capital.
 
I would redraft a bit as follows. Plus you had a couple open 'if' or 'for' commands that were missing the closing 'end' commands.
Code:
function OnCityCreation(playerID, x, y)
	local player = Players[playerID]
	local CapitalCity = player:GetCapitalCity()
	local newcity = Map.GetPlot(x, y):GetPlotCity()
	if CapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then
		newcity:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 1)
	end
end
GameEvents.PlayerCityFounded.Add( OnCityCreation )
--------------------------------------------------------------------
function OnCityCaptureCompleted(oldOwnerID, bIsCapital, x, y, newOwnerID, pop, bConquest)
	local pOldPlayer = Players[oldOwnerID]
	local pNewPlayer = Players[newOwnerID]
	local pOldCapitalCity = "NONE"
	if pOldPlayer:IsAlive() then
		pOldCapitalCity = pOldPlayer:GetCapitalCity() -- get the current capital city of the player that lost the city
	end
	local pNewCapitalCity = pNewPlayer:GetCapitalCity() -- get the current capital city of the player that captured the city
	local capturedcity = Map.GetPlot(x, y):GetPlotCity() -- get the city that was captured
	if pOldCapitalCity ~= "NONE" then	--only proceed if we have valid data for pOldCapitalCity
		if not pOldCapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then 
			for city in pOldPlayer:Cities() do
				city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 0)
			end
		end
	end
	if pNewCapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_PETRA"]) then 
		for city in newPlayer:Cities() do
			city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 1)
		end
	else capturedcity:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 0)
	end
end
GameEvents.CityCaptureComplete.Add( OnCityCaptureCompleted );
--------------------------------------------------------------------
function IsDummySellable(playerID, buildingID)
	if (buildingID == GameInfoTypes.["BUILDING_PETRA_DUMMY"]) then return false end
	return true;
end
GameEvents.CityBuildingsIsBuildingSellable.Add(IsDummySellable)
'break' will terminate whatever 'for' or 'while' loop that is executing at that instant, and subsequent items within the loop will not be precessed. So, when wanting the game to run through all the cities a player owns, you should never have 'break' as you have been using it here:
Code:
for city in newPlayer:Cities() do
	if city:IsHasBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"]) then
		break;
	else city:SetNumRealBuilding(GameInfoTypes["BUILDING_PETRA_DUMMY"], 1)
	end
end
because the 1st city encountered that has the BUILDING_PETRA_DUMMY would terminate completely the for city in newPlayer:Cities() do loop, so any city which does not have BUILDING_PETRA_DUMMY will never be given the dummy building if that city came in the list of cities after the 1st city that did have the dummy building.


Spoiler :
And yes I know that efficient coding shouldn't need
Code:
local pOldCapitalCity[color="red"] = "NONE"[/color]
nor
Code:
if pOldCapitalCity[color="red"] ~= "NONE" [/color]then
But that has always given me hives based on inherited habits from previous programming systems.
 
So, I've forgotten to add in the free buildings in every cities after it is built because the LUA above only give the free buildings after a city capture or another settlement of a city. So, using the link that WHoward gave to me in other posts...
Code:
function ConstructionCompleted (ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local pPlayer = Players[ownerId]
	local pCapitalCity = pPlayer:GetCapitalCity()
	if pCapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_GDR_HEADQUARTERS"]) then
		for city in pPlayer:Cities() do
			city:SetNumRealBuilding(GameInfoTypes["BUILDING_GDR_MILITARY_SECRET_DUMMY"], 1)
		end
	end
end
GameEvents.CityConstructed(ConstructionCompleted)
Seems to look correct to me until the error is made:
Code:
attempt to call field 'CityConstructed' (a table value)
 
So, I've forgotten to add in the free buildings in every cities after it is built because the LUA above only give the free buildings after a city capture or another settlement of a city. So, using the link that WHoward gave to me in other posts...
Code:
function ConstructionCompleted (ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local pPlayer = Players[ownerId]
	local pCapitalCity = pPlayer:GetCapitalCity()
	if pCapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_GDR_HEADQUARTERS"]) then
		for city in pPlayer:Cities() do
			city:SetNumRealBuilding(GameInfoTypes["BUILDING_GDR_MILITARY_SECRET_DUMMY"], 1)
		end
	end
end
GameEvents.CityConstructed[COLOR="Blue"].Add[/COLOR](ConstructionCompleted)
Seems to look correct to me until the error is made:
Code:
attempt to call field 'CityConstructed' (a table value)
You forgot the part I added in blue.

However your code is going to fire for every building and wonder built anywhere by anyone any time.

This will make it only execute anything if the building constructed or purchased is the GDR Headquarters:
Code:
function ConstructionCompleted(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	if buildingType == GameInfoTypes["BUILDING_GDR_HEADQUARTERS"] then
		local pPlayer = Players[ownerId]
		local pCapitalCity = pPlayer:GetCapitalCity()
		if pCapitalCity:IsHasBuilding(GameInfoTypes["BUILDING_GDR_HEADQUARTERS"]) then
			for city in pPlayer:Cities() do
				city:SetNumRealBuilding(GameInfoTypes["BUILDING_GDR_MILITARY_SECRET_DUMMY"], 1)
			end
		end
	end
end
GameEvents.CityConstructed.Add(ConstructionCompleted)
 
Maybe I have not read carefully enough, but why not have the XML for the GDR HQ give the free building in every city?

Because the free building in every city is an XML Trait for leaders, not for buildings. Therefore, you would have to use LUA.
 
That's only for a free building in the city. Not a free building throughout all cities.
 
That's only for a free building in the city. Not a free building throughout all cities.
Any building that has <FreeBuilding> gives the specified building in all cities, and continues to do so throughout the remainder of the game so long as (a) the wonder or building with the <FreeBuilding> designated does not go obsolete, (b) the city that constructed the wonder or building with the <FreeBuilding> designated is not captured.

When such a building is captured, the player who conquered the city starts to get the free buildings so long as the wonder or building with the <FreeBuilding> survives the conquest of the city

-------------------------------------------------------------------------------------------------

If contained within the definition of BUILDING_GDR_HEADQUARTERS, <FreeBuilding>BUILDINGCLASS_GDR_MILITARY_SECRET_DUMMY</FreeBuilding> would give the BUILDING_GDR_MILITARY_SECRET_DUMMY in all cities. But the XML-system for buildings would not in any way care whether BUILDING_GDR_HEADQUARTERS was constructed in a player's capital.

To go the entirely-XML route, you should also add this to the xml definition of the BUILDING_GDR_HEADQUARTERS:
Code:
<Building_ClassesNeededInCity>
	<Row>
		<BuildingType>BUILDING_GDR_HEADQUARTERS</BuildingType>
		<BuildingClassType>BUILDINGCLASS_PALACE</BuildingClassType>
	</Row>
</Building_ClassesNeededInCity>
This would only allow BUILDING_GDR_HEADQUARTERS to be built in the capital city.

And then in the code defining BUILDING_GDR_HEADQUARTERS insert the <FreeBuilding> designation as like the following snippet:
Code:
<GameData>
	<Buildings>
		<Row>
			<Type>BUILDING_GDR_HEADQUARTERS</Type>
			.........<snipped lines>......................
			<FreeBuilding>BUILDINGCLASS_GDR_MILITARY_SECRET_DUMMY</FreeBuilding>
Within table <Buildings>, both <FreeBuilding> and <FreeBuildingThisCity> are designated in terms of the Building-Class for the desired free building, not the direct and actual building as <FreeBuilding> is used in table <Traits>.

------------------------------------------------------------------------------------------------

I never actually questioned why you weren't using <FreeBuilding>. It may actually be the simpler solution for you.
 
...why you weren't using <FreeBuilding>. It may actually be the simpler solution for you.

:sad:

But, to go entirely XML route is incorrect as the building is in fact a World Congress reward so it can only be rewarded to a capital.

Irregardless, thanks Craig_Sutter, you managed to crop out 40 lines of LUA code.
 
Back
Top Bottom