Building To Improvement Linkages

Update. I'm working through the code and play-testing to see what the debugging messages tell me about what is happening with the adjusted code. It will probably take until the coming weekend before I feel confident in posting an updated code-file. Between work and whatnot I only get a couple of hours a day to really work on modding code and test thereof.
 
OK, so I've identified the root bug in the code and it is a rather glaring and giant one in terms of "logic" (or rather lack thereof) so I will need to completely re-write the code over the weekend. I know what I need to do, however. So it should not be undoable to fix the code over the coming weekend or at least sometime by around Xymas.
 
Glad to hear you've been able to identify the issue! I appreciate all your work on this, don't worry if it takes a bit!
 
Current code which appears so far as I can tell to be working properly
Spoiler :

Code:
-- CityBuildingsToNearbyImprovementsHandler
-- Author: LeeS
-- DateCreated: 4/22/2016 3:49:21 PM
--------------------------------------------------------------
local tRealBuildingData = {}	--don't change this line
--------------------------------------------------------------
--[[

USER CONFIGURABLE VARIABLES

	Make changes to these variables as desired and needed



]]--
--------------------------------------------------------------

local bRequiresSpecificCiv = false
local iSpecificRequiredCiv = GameInfoTypes["CIVILIZATION_AMERICA"]
local bDoNotApplyToMinorCivs = true




----------------------------------------------------------------
--enter data into table tRealBuildingData here

--each subtable with table 'Counters' for a 'Real' building MUST have a designation of 'DummyBuilding=XXXXX'
----------------------------------------------------------------


tRealBuildingData[GameInfoTypes.BUILDING_GRANARY] = { Counters={
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_WHEAT_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_FARM, ResourceType=GameInfoTypes.RESOURCE_WHEAT, LimitPerCity=4, PlotsPerEffect=1},
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_CAMP, ResourceType=GameInfoTypes.RESOURCE_DEER, MustBeWorked=false, LimitPerCity=2, PlotsPerEffect=1},
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_FARM, MustBeWorked=false, DoNotCountResourceTiles=true, LimitPerCity=2, PlotsPerEffect=1}
} }
tRealBuildingData[GameInfoTypes.BUILDING_WORKSHOP] = {ApplyToAllInClass=true, Counters={ {DummyBuilding=GameInfoTypes["BUILDING_WORKSHOP_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_LUMBERMILL, LimitPerCity=-1 },
{DummyBuilding="LUMBERMILL_TOTAL", ImprovementType=GameInfoTypes.IMPROVEMENT_LUMBERMILL, MustBeOwned=false, MustBeWorked=false, LimitPerCity=-1 },
{DummyBuilding="LUMBERMILL_OWNED", ImprovementType=GameInfoTypes.IMPROVEMENT_LUMBERMILL, MustBeOwned=true, MustBeWorked=false, LimitPerCity=-1 }}}
tRealBuildingData[GameInfoTypes.BUILDING_WORKSHOP].SpecialHandling = (
	function(BuildingID, tDummyQuantitiesTable, pCity, iPlayer)
		local pPlayer = Players[iPlayer]
		if not pPlayer:IsHuman() then return end
		local iDummyBuilding = GameInfoTypes["BUILDING_WORKSHOP_DUMMY"]
		local iOwnedLumbermills = tDummyQuantitiesTable["LUMBERMILL_OWNED"]
		local iTotalLumbermills = tDummyQuantitiesTable["LUMBERMILL_TOTAL"]
		local iNumberDummyToAdd = tDummyQuantitiesTable[iDummyBuilding]
		local sDummyBuildingName = Locale.ConvertTextKey(GameInfo.Buildings[iDummyBuilding].Description)
		print("The total number of Lumbermills within the working range of " .. pCity:GetName() .. " is " .. iTotalLumbermills)
		print("The total number of Lumbermills within the working range of " .. pCity:GetName() .. " that are owned by player " .. pPlayer:GetName() .. " is " .. iOwnedLumbermills)
		print("The total number of Dummy Buildings " .. sDummyBuildingName .. " that should be added if " .. pCity:GetName() .. " has a Workshop-Class Building is " .. iNumberDummyToAdd)
		if pCity:IsHasBuilding(BuildingID) then
			pCity:SetNumRealBuilding(iDummyBuilding, iNumberDummyToAdd)
		else pCity:SetNumRealBuilding(iDummyBuilding, 0)
		end
	end
	)












------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
--Debug Printing -- You can safely change bDebugprint to boolean true
------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

local bDebugprint = false
function DebugToLog(sMessage)
	if (bDebugprint == true) then
		print(sMessage)
	end
end

------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
--Don't make changes in the following code
------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

local bDataIsIrreconcilable = false
local tCityImprovementEffectUnlockerBuildings = {}
local tCityLocalImprovements = { }


---------------------------------------------------------------------------------------------------------------------------
--Is Building Within Class Correct for Player
---------------------------------------------------------------------------------------------------------------------------
function IsBuildingCorrectForPlayer(iPlayer, iBuilding)
	local pPlayer = Players[iPlayer]
	local sCivilizationType = GameInfo.Civilizations[pPlayer:GetCivilizationType()].Type
	local sBuildingName = GameInfo.Buildings[iBuilding].Type
	local sBuildingClass = GameInfo.Buildings[iBuilding].BuildingClass
	local sDefaultBuilding
	for row in GameInfo.BuildingClasses("Type='" .. sBuildingClass .. "'") do
		sDefaultBuilding = row.DefaultBuilding
	end
	for row in GameInfo.Civilization_BuildingClassOverrides("CivilizationType='" .. sCivilizationType .. "'") do
		if sBuildingClass == row.BuildingClassType then
			return (sBuildingName == row.BuildingType)
		end
	end
	return (sBuildingName == sDefaultBuilding)
end

---------------------------------------------------------------------------------------------------------------------------
--function IrreconcilableData handles the busy-work of print error data to the lua log and making an error notification
---------------------------------------------------------------------------------------------------------------------------
function IrreconcilableData(sOriginator, sMessage)
	local sLongMessage = sOriginator .. ": Bad Data Was Encountered"
	if sMessage then
		sLongMessage = sOriginator .. ": " .. sMessage
	end
	local sShortMessage = "[COLOR_NEGATIVE_TEXT]Invalid Data within table tRealBuildingData[ENDCOLOR]"
	Players[0]:AddNotification(NotificationTypes["NOTIFICATION_GENERIC"], sShortMessage .. "[NEWLINE][NEWLINE]" .. sLongMessage, sShortMessage)
	print(sLongMessage)
end


--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


function CityHasPrimaryBuildings(pCity)
	for PrimaryBuildingID,Datatable in pairs(tCityImprovementEffectUnlockerBuildings) do
		if pCity:IsHasBuilding(PrimaryBuildingID) then
			return true
		end		
	end
	return false
end


--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


function CountCityImprovementsAndImplementEffectsForCity(pCity, iPlayer, tTableAlreadyCountedPlots)
	local sCityName = pCity:GetName()
	if (CityHasPrimaryBuildings(pCity) == false) then
		DebugToLog("The City of " .. sCityName .. " has NO buildings we are interested in for Building-Improvement Linkages: nothing to do for this city")
		return tTableAlreadyCountedPlots
	end
	local pPlayer = Players[iPlayer]
	local tNumberCityLocalImprovements = {}
	for ImprovementID,PrimaryBuildingDataTable in pairs(tCityLocalImprovements) do
		if not tNumberCityLocalImprovements[ImprovementID] then
			tNumberCityLocalImprovements[ImprovementID] = {}
		end
		for PrimaryBuildingID,ResourceDataTable in pairs(PrimaryBuildingDataTable) do
			if not tNumberCityLocalImprovements[ImprovementID][PrimaryBuildingID] then
				tNumberCityLocalImprovements[ImprovementID][PrimaryBuildingID] = {}
			end
			for ResourceID,DummyBuildingTable in pairs(ResourceDataTable) do
				if not tNumberCityLocalImprovements[ImprovementID][PrimaryBuildingID][ResourceID] then
					tNumberCityLocalImprovements[ImprovementID][PrimaryBuildingID][ResourceID] = {}
				end
				for DummyBuilding,SettingsTable in pairs(DummyBuildingTable) do
					tNumberCityLocalImprovements[ImprovementID][PrimaryBuildingID][ResourceID][DummyBuilding] = 0
				end
			end
		end
	end
	-- scanning through the city plots
	DebugToLog("The City of " .. sCityName .. " has buildings we are interested in for Building-Improvement Linkages")
	local iNumPlots = pCity:GetNumCityPlots()
	for i = 0, iNumPlots - 1 do
		local pPlot = pCity:GetCityIndexPlot(i)
		if pPlot ~= nil then
			local iPlotX, iPlotY = pPlot:GetX(), pPlot:GetY()
			local iPlotID = pPlot:GetPlotIndex()
			local iPlotOwner = pPlot:GetOwner() 
			local iImprovementType = pPlot:GetImprovementType()
			local iResourceType = pPlot:GetResourceType()
			local bPlotIsPillaged = pPlot:IsImprovementPillaged()
			local bPlotIsBeingWorked = pPlot:IsBeingWorked()
			local bCityIsWorkingPlot = (pPlot:GetWorkingCity() == pCity)
			local bPlotOwnerIsCurrentPlayer = (iPlotOwner == iPlayer)
				--================================================================================================================
				-- this bit here is a bit brutal but I'm a bit rusty on the more-direct lua methods to achieve this for Civ5
				--================================================================================================================
				--================================================================================================================
				-- this tells us whether or not the player has revealed the resource this plot contains
				--================================================================================================================
				local sResourceTypeTech = "NONE"
				local iResourceTypeTech = "NONE"
				local tResourceTable = GameInfo.Resources[iResourceType]
				if (tResourceTable ~= nil) then
					if (tResourceTable.TechReveal ~= nil) and (tResourceTable.TechReveal ~= "NULL") and (tResourceTable.TechReveal ~= "null") and (tResourceTable.TechReveal ~= -1) then
						sResourceTypeTech = tResourceTable.TechReveal
						local tTechTable = GameInfo.Technologies[sResourceTypeTech]
						if (tTechTable ~= nil) then
							iResourceTypeTech = tTechTable.ID
						end
					end
				end
				local bPlayerHasResourceRevealTech = true
				if (iResourceTypeTech ~= "NONE") then
					bPlayerHasResourceRevealTech = Teams[pPlayer:GetTeam()]:GetTeamTechs():HasTech(iResourceTypeTech)
				end
				--================================================================================================================
			if tCityLocalImprovements[iImprovementType] then
				local sImprovementName = Locale.ConvertTextKey(GameInfo.Improvements[iImprovementType].Description)
				DebugToLog("A Valid Improvement for the Handler has been found! The Improvement is a " .. sImprovementName .. " : the plot grid location is X" .. iPlotX .. ",Y" .. iPlotY)
				for PrimaryBuildingID,ResourceTable in pairs(tCityLocalImprovements[iImprovementType]) do
					if pCity:IsHasBuilding(PrimaryBuildingID) then
						local sBuildingName = Locale.ConvertTextKey(GameInfo.Buildings[PrimaryBuildingID].Description)
						DebugToLog("The city has building " .. sBuildingName)
						if tCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType] and (bPlayerHasResourceRevealTech == true) then
							DebugToLog("The plot has a resource on it from the list for the Improvement/Building combo")
							for DummyBuilding,SettingsData in pairs(tCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType]) do
								local bAddToCount = true
								if SettingsData.MustBeOwned then
									DebugToLog("The plot with the " .. sImprovementName .. " must be owned to be counted")
									bAddToCount = bPlotOwnerIsCurrentPlayer
									DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
								end
								if bAddToCount then
									if (bPlayerHasResourceRevealTech == false) then
										DebugToLog("The Resource on the plot has not yet been revealed so cannot be counted")
										bAddToCount = false
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if SettingsData.CannotBePillaged then
										DebugToLog("The plot with the " .. sImprovementName .. " cannot be pillaged to be counted")
										bAddToCount = not bPlotIsPillaged
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if SettingsData.DoNotCountResourceTiles and (iResourceType ~= -1) then
										DebugToLog("The plot with the " .. sImprovementName .. " cannot have a Resource to be counted")
										bAddToCount = false
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if bPlotIsBeingWorked then
										DebugToLog("The plot with the " .. sImprovementName .. " is being worked")
										bAddToCount = bCityIsWorkingPlot
										DebugToLog("The plot with the " .. sImprovementName .. " is being worked is by the correct city is " .. tostring(bCityIsWorkingPlot))
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									else
										DebugToLog("The plot with the " .. sImprovementName .. " is not being worked")
										if SettingsData.MustBeWorked then
											DebugToLog("The plot with the " .. sImprovementName .. " must be worked to be counted")
											DebugToLog("The plot with the " .. sImprovementName .. " is NOT being worked so cannot be counted")
											bAddToCount = false
											DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
										else
											if (SettingsData.CountSameTileOnlyOnce == true) then
												DebugToLog("The plot with the " .. sImprovementName .. " can only be counted once")
												if tTableAlreadyCountedPlots[DummyBuilding][iPlotID] then
													DebugToLog("The plot with the " .. sImprovementName .. " has already been counted for the current dummy building")
													bAddToCount = false
													DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
												end
												if (bAddToCount == true) then
													if not tTableAlreadyCountedPlots[DummyBuilding] then
														tTableAlreadyCountedPlots[DummyBuilding] = {[iPlotID]="counted"}
													else
														tTableAlreadyCountedPlots[DummyBuilding][iPlotID]="counted"
													end
												end
											end
										end
									end
								end
								if bAddToCount then
									DebugToLog("The plot with the " .. sImprovementName .. " should be counted")
									tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding] = tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding] + 1
									DebugToLog("Before adjustment for LimitPerCity the " .. sImprovementName .. " total plots count for " .. sBuildingName .. " is " .. tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding])
									if (SettingsData.LimitPerCity > 0) then
										if tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding] > SettingsData.LimitPerCity then
											tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding] = SettingsData.LimitPerCity
										end
									end
									DebugToLog("After adjustment for LimitPerCity the " .. sImprovementName .. " total plots count for " .. sBuildingName .. " is " .. tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding])
								end
							end
						else
							if (iResourceType ~= -1) then
								DebugToLog("The plot had a resource that was not listed as desired or the resource has not yet been revealed to the player")
							else
								DebugToLog("The plot had NO resource but the code is currently only looking for plots that have resources")
							end
						end
							--==================================================================================================================================================================================================
							--what to do when the ResourceType on the plot does not match to any resource listed in the sub-table "tCityLocalImprovements[iImprovementType][PrimaryBuildingID]{[iResourceType1], [iResourceType2]}"
							--no ResourceType specified is now treated as "iResourceType == -1000" in the table(s) for "tCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType]"
							--so no ResourceType specified should not be confusable in the code with "pPlot:GetResourceType() == -1" when the plot has no resource
							--the code should always get here when no ResourceType was specified in the original "Counters" table.
							--no longer really true-->: the code should also always get here when the plot's ResourceType was not in the list of required resources specified in the original "Counters" table.
							--==================================================================================================================================================================================================
						if tCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000] then
							--still not sure that this part of the logic is really correct but it sure seems reasonable
						--if tCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000] or (not tCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType]) then
							DebugToLog("The plot was seen as not having a resource or as not having a resource from the required list")
							for DummyBuilding,SettingsData in pairs(tCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000]) do
								local bAddToCount = true
								--if (bPlayerHasResourceRevealTech == false) and tCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType] then
								--	DebugToLog("The plot has a resource that is in the list of resources needed but the resource is not revealed to the player")
								--	bAddToCount = false
								--	DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
								--end
								if bAddToCount then
									if SettingsData.MustBeOwned then
										DebugToLog("The plot with the " .. sImprovementName .. " must be owned to be counted")
										bAddToCount = bPlotOwnerIsCurrentPlayer
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if SettingsData.CannotBePillaged then
										DebugToLog("The plot with the " .. sImprovementName .. " cannot be pillaged to be counted")
										bAddToCount = not bPlotIsPillaged
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if SettingsData.DoNotCountResourceTiles and (iResourceType ~= -1) then
										DebugToLog("The plot with the " .. sImprovementName .. " cannot have a Resource to be counted")
										bAddToCount = false
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									end
								end
								if bAddToCount then
									if bPlotIsBeingWorked then
										DebugToLog("The plot with the " .. sImprovementName .. " is being worked: it is being worked by the correct city is " .. tostring(bCityIsWorkingPlot))
										bAddToCount = bCityIsWorkingPlot
										DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
									else
										DebugToLog("The plot with the " .. sImprovementName .. " is not being worked")
										if SettingsData.MustBeWorked then
											DebugToLog("The plot with the " .. sImprovementName .. " must be worked to be counted")
											DebugToLog("The plot with the " .. sImprovementName .. " is NOT being worked so cannot be counted")
											bAddToCount = false
											DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
										else
											if (SettingsData.CountSameTileOnlyOnce == true) then
												DebugToLog("The plot with the " .. sImprovementName .. " can only be counted once")
												if tTableAlreadyCountedPlots[DummyBuilding][iPlotID] then
													DebugToLog("The plot with the " .. sImprovementName .. " has already been counted for the current dummy building")
													bAddToCount = false
													DebugToLog("The plot can be counted is evaluated as " .. tostring(bAddToCount))
												end
												if (bAddToCount == true) then
													if not tTableAlreadyCountedPlots[DummyBuilding] then
														tTableAlreadyCountedPlots[DummyBuilding] = {[iPlotID]="counted"}
													else
														tTableAlreadyCountedPlots[DummyBuilding][iPlotID]="counted"
													end
												end
											end
										end
									end
								end
								if bAddToCount then
									DebugToLog("The plot with the " .. sImprovementName .. " should be counted")
									tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding] = tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding] + 1
									DebugToLog("Before adjustment for LimitPerCity the " .. sImprovementName .. " total plots count for " .. sBuildingName .. " is " .. tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding])
									if (SettingsData.LimitPerCity > 0) then
										if tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding] > SettingsData.LimitPerCity then
											tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding] = SettingsData.LimitPerCity
										end
									end
									DebugToLog("After adjustment for LimitPerCity the " .. sImprovementName .. " total plots count for " .. sBuildingName .. " is " .. tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][-1000][DummyBuilding])
								end
							end
						end
					end
				end
			end
		end
	end
	for PrimaryBuildingID,BuildingDataTable in pairs(tCityImprovementEffectUnlockerBuildings) do
		local sBuildingIDName = Locale.ConvertTextKey(GameInfo.Buildings[PrimaryBuildingID].Description)
		if IsBuildingCorrectForPlayer(iPlayer, PrimaryBuildingID) then
			DebugToLog("Processing Dummy Buildings to be added for Primary Building " .. sBuildingIDName .. " for the city of " .. sCityName)
			local tDummyQuantitiesToSet = {}
			for Item,CounterData in pairs(BuildingDataTable.Counters) do
				local DummyBuilding = (CounterData.DummyBuilding or -1)
				local PlotsPerEffect = (CounterData.PlotsPerEffect or 1)
				local iImprovementType = CounterData.ImprovementType
				local iResourceType = (CounterData.ResourceType or -1000)
				if not tDummyQuantitiesToSet[DummyBuilding] then
					tDummyQuantitiesToSet[DummyBuilding] = 0
				end
				if PlotsPerEffect ~= 1 then
					tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding] = math.floor(tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding]/PlotsPerEffect)
				end
				tDummyQuantitiesToSet[DummyBuilding] = tDummyQuantitiesToSet[DummyBuilding] + tNumberCityLocalImprovements[iImprovementType][PrimaryBuildingID][iResourceType][DummyBuilding]
			end
			----------------------------------------------------------------------------------------------------------------------------------------------------------------
			--the following is for Debug purposes only
			if (bDebugprint == true) then
				for DummyBuildingNeeded,QuantityToSet in pairs(tDummyQuantitiesToSet) do
					if type(DummyBuildingNeeded) == "number" then
						print(sCityName .. " ..... " .. Locale.ConvertTextKey(GameInfo.Buildings[DummyBuildingNeeded].Description) .. " has a quantity to set value of " .. QuantityToSet)
					elseif type(DummyBuildingNeeded) == "string" then
						print(sCityName .. " ..... " .. DummyBuildingNeeded .. " has a quantity to set value of " .. QuantityToSet)
					else print(sCityName .. " ..... Invalid Data for DummyBuildingNeeded in table tDummyQuantitiesToSet:" .. tostring(DummyBuildingNeeded))
					end
				end
			end
			----------------------------------------------------------------------------------------------------------------------------------------------------------------
			if BuildingDataTable.SpecialHandling == "NONE" then
				for DummyBuildingNeeded,QuantityToSet in pairs(tDummyQuantitiesToSet) do
					if pCity:IsHasBuilding(PrimaryBuildingID) then
						pCity:SetNumRealBuilding(DummyBuildingNeeded, QuantityToSet)
					else
						pCity:SetNumRealBuilding(DummyBuildingNeeded, 0)
					end
				end
			else	--this is where we do special handling
				BuildingDataTable.SpecialHandling(PrimaryBuildingID, tDummyQuantitiesToSet, pCity, iPlayer)
			end
		else
			DebugToLog("No processing of Dummy Buildings the city of " .. sCityName .. " for Primary Building " .. sBuildingIDName .. " is needed because Building " .. sBuildingIDName .. " is not correct for player # " .. iPlayer)
			DebugToLog("Player # " .. iPlayer .. " has a unique version of Building " .. sBuildingIDName .. ", or Building " .. sBuildingIDName .. " is itself a unique replacement, or else the Database table BuildingClasses has been malformed by a mod for that Building-Class")
		end
	end
	return tTableAlreadyCountedPlots
end







--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

--XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX



for BuildingID,DataTable in pairs(tRealBuildingData) do
	local bApplyToAllInClass = (DataTable.ApplyToAllInClass or false)
	local BuildingSpecialHandling = (DataTable.SpecialHandling or "NONE")
	----------------------------------------------------------------------------------------------------------------------
	--error checking on data entered by user into table tRealBuildingData
	--looking only for errors that are going to cause spectacular failure of the code
	----------------------------------------------------------------------------------------------------------------------
	for Item,CounterData in pairs(DataTable.Counters) do
		local sRealBuildingName = Locale.ConvertTextKey(GameInfo.Buildings[BuildingID].Description)
		if not CounterData.DummyBuilding then
			IrreconcilableData("Game Loading", "Dummy Building needed is missing for a Counter for 'Real' Building " .. sRealBuildingName .. "! A valid GameInfoTypes reference or a Unique Text-String must be supplied")
			bDataIsIrreconcilable = true
		end
		local sDummyName = CounterData.DummyBuilding
		local sDummyNameDataType = type(sDummyName)
		local sDummyBuildingType = "NONE"
		if sDummyNameDataType == "number" then			
			for row in GameInfo.Buildings("ID='" .. CounterData.DummyBuilding .. "'") do
				sDummyBuildingType = row.Type
			end
			if sDummyBuildingType == "NONE" then
				IrreconcilableData("Game Loading", "For Counter with DummyBuilding as " .. sDummyName .. " within the data for " .. sRealBuildingName .. "! The DummyBuilding has been designated as an integer value (ie, as if it were an ID # for a valid Building) but there is no matching Building ID # within table <Buildings>")
				bDataIsIrreconcilable = true
			else
				sDummyName = Locale.ConvertTextKey(GameInfo.Buildings[CounterData.DummyBuilding].Description)
			end
		elseif sDummyNameDataType == "string" then
			if ((BuildingSpecialHandling == nil) or (BuildingSpecialHandling == "NONE")) then
				IrreconcilableData("Game Loading", "For Counter with DummyBuilding as " .. sDummyName .. " within the data for " .. sRealBuildingName .. "! The DummyBuilding is a Text-String, but SpecialHandling for " .. sRealBuildingName .. " is either unspecified or set to NONE")
				bDataIsIrreconcilable = true
			end
		else
			IrreconcilableData("Game Loading", "For Counter with DummyBuilding as " .. tostring(sDummyName) .. " within the data for " .. sRealBuildingName .. "! The DummyBuilding has not been designated as a proper Building ID# from table <Buildings>, or it is not a Text-String")
			bDataIsIrreconcilable = true
		end
		if (CounterData.ResourceType ~= nil) and (CounterData.DoNotCountResourceTiles == true) then
			IrreconcilableData("Game Loading", "For Counter with DummyBuilding as " .. sDummyName .. " within the data for " .. sRealBuildingName .. "! A ResourceType is specified while DoNotCountResourceTiles is set to true")
			bDataIsIrreconcilable = true
		end
	end
	-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	--using the data in table tRealBuildingData to construct a table called tCityImprovementEffectUnlockerBuildings
	--data is essentially copied from tRealBuildingData into tCityImprovementEffectUnlockerBuildings
	--where required, however, all buildings from within the same class as a building listed in tRealBuildingData is duplicated into table tCityImprovementEffectUnlockerBuildings
	-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	if bApplyToAllInClass then
		local sBuildingClass = GameInfo.Buildings[BuildingID].BuildingClass
		for Building in GameInfo.Buildings("BuildingClass='" .. sBuildingClass .. "'") do
			tCityImprovementEffectUnlockerBuildings[Building.ID] = { Counters=DataTable.Counters, SpecialHandling=BuildingSpecialHandling }
		end
	else
		tCityImprovementEffectUnlockerBuildings[BuildingID] = { Counters=DataTable.Counters, SpecialHandling=BuildingSpecialHandling }
	end
end
if not bDataIsIrreconcilable then
	for BuildingID,DataTable in pairs(tCityImprovementEffectUnlockerBuildings) do
		DebugToLog("In table tCityImprovementEffectUnlockerBuildings a primary building with ID # of " .. BuildingID .. " (" .. Locale.ConvertTextKey(GameInfo.Buildings[BuildingID].Description) .. ") is being processed")
		for Item,CounterData in pairs(DataTable.Counters) do
			local iResourceType = (CounterData.ResourceType or -1000)
			local iResourceLinkedDummyBuilding = (CounterData.DummyBuilding or -1)
			if not tCityLocalImprovements[CounterData.ImprovementType] then
				tCityLocalImprovements[CounterData.ImprovementType] = {}
			end
			if not tCityLocalImprovements[CounterData.ImprovementType][BuildingID] then
				tCityLocalImprovements[CounterData.ImprovementType][BuildingID] = {}
			end
			if not tCityLocalImprovements[CounterData.ImprovementType][BuildingID][iResourceType] then
				tCityLocalImprovements[CounterData.ImprovementType][BuildingID][iResourceType] = {}
			end
			tCityLocalImprovements[CounterData.ImprovementType][BuildingID][iResourceType][iResourceLinkedDummyBuilding] = {DoNotCountResourceTiles=(CounterData.DoNotCountResourceTiles or false),
					MustBeOwned=((CounterData.MustBeOwned == nil) and true or CounterData.MustBeOwned), MustBeWorked=((CounterData.MustBeWorked == nil) and true or CounterData.MustBeWorked),
					CannotBePillaged=((CounterData.CannotBePillaged == nil) and true or CounterData.CannotBePillaged), LimitPerCity=(CounterData.LimitPerCity or -1),
					CountSameTileOnlyOnce=((CounterData.CountSameTileOnlyOnce == nil) and true or CounterData.CountSameTileOnlyOnce)}
		end
	end
end



------------------------------------------------------------
---- CityContructedBuilding Game Event
------------------------------------------------------------
function CityConstructedBuilding(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	if bGold or bFaithOrCulture then
		if tCityImprovementEffectUnlockerBuildings[buildingType] then
			local pPlayer = Players[ownerId]
			if bDoNotApplyToMinorCivs and pPlayer:IsMinorCiv() then
				return
			end
			if bRequiresSpecificCiv then
				if pPlayer:GetCivilizationType() ~= iSpecificRequiredCiv then
					return
				end
			end 
			local pCity = pPlayer:GetCityByID(cityId);
			local tAlreadyCountedPlots = {}
			tAlreadyCountedPlots = CountCityImprovementsAndImplementEffectsForCity(pCity, ownerId, tAlreadyCountedPlots)
		end
	end
end

------------------------------------------------------------
---- PlayerDoTurn Game Event
------------------------------------------------------------
function InspectPlayerCityImprovementsAndImplementBuildingEffects(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:IsBarbarian() then
		return
	end
	if bDoNotApplyToMinorCivs and pPlayer:IsMinorCiv() then
		return
	end
	if bRequiresSpecificCiv then
		if pPlayer:GetCivilizationType() ~= iSpecificRequiredCiv then
			return
		end
	end
	local tAlreadyCountedPlots = {}
	for pCity in pPlayer:Cities() do
		tAlreadyCountedPlots = CountCityImprovementsAndImplementEffectsForCity(pCity, iPlayer, tAlreadyCountedPlots)
	end
end
------------------------------------------------------------
---- WilliamHoward's IsCivInPlay
------------------------------------------------------------

function IsCivInPlay(iCivType)
  for iSlot = 0, GameDefines.MAX_MAJOR_CIVS-1, 1 do
    local iSlotStatus = PreGame.GetSlotStatus(iSlot)
    if (iSlotStatus == SlotStatus.SS_TAKEN or iSlotStatus == SlotStatus.SS_COMPUTER) then
      if (PreGame.GetCivilization(iSlot) == iCivType) then
        return true
      end
    end
  end
  
  return false
end
------------------------------------------------------------
---- Game Event Hooks
------------------------------------------------------------
if not bDataIsIrreconcilable then
	if bRequiresSpecificCiv then
		if IsCivInPlay(iSpecificRequiredCiv) then
			GameEvents.PlayerDoTurn.Add(InspectPlayerCityImprovementsAndImplementBuildingEffects)
			GameEvents.CityConstructed.Add(CityConstructedBuilding)
		end
	else
		GameEvents.PlayerDoTurn.Add(InspectPlayerCityImprovementsAndImplementBuildingEffects)
		GameEvents.CityConstructed.Add(CityConstructedBuilding)
	end
end



------------------------------------------------------------
---- Loading Confirmation Print Statements
------------------------------------------------------------

if not bDataIsIrreconcilable then
	print("CityBuildingsToNearbyImprovementsHandler.lua loaded successfully")
else
	print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
	print("CityBuildingsToNearbyImprovementsHandler.lua loaded without lua syntax-errors, but:")
	print("  1)......there were irreconcilable errors or conflicts in the construction of user configurable table tRealBuildingData")
	print("  2)......these errors must be resolved before the code will be allowed to run")
	print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
end
I have not had opportunity to investigate any code-feature changes to allow for example sharing of effects for more than one city when a tile is being worked by a city.

The code is no longer as far as I can tell being bugged out by the presence of resources (or lack thereof) on a given plot. The code also will no longer count tiles whose resource(s) have not yet been revealed when the Counters data specifies that resource is one the improvement ought to be linked to in order to get the effect.

The same tile will be counted now for multiple differing dummy building designations if the setup of the Counters says it ought to be.

So for example the Farm improvement can be designated with one Counter looking for Farms on Wheat and another just looking for any Farm, and the code correctly adds the effects of a Farm on a Wheat Plot to both counters.
 
Last edited:
pPlot:GetResourceType() - returns the resource on the plot
pPlot:GetResourceType(pPlayer:GetTeam()) - returns the resource on the plot as known by pPlayer
 
Was racking my brain trying to remember the straightforward method. Thx.

I was not reading your API as XML properly and was attempting PlayerID rather than TeamID

Been too long away from doing much Civ5 -- too much Civ6.

Had also tried to "fix" the main code by changing to
Code:
for k,pCiy in pPlayer:GetCities():Members() do
Because I was absolutely sure
Code:
for pCity in pPlayer:Cities() do
was wrong and was at the root of the problem. Half a day wasted in :wallbash: before the lightbulb went off and I reverted back to the code correct for Civ5.
 
Last edited:
I've been programming in Edith/SimAntics (a predicate / decision tree idiom) and teaching myself C# - consider yourself lucky it's still Lua
 
After copying in the new code, it still doesn't appear to be counting the unworked improvements. (I'm not sure if that was part of what you worked on for the fixes described above.)
 
I just tested and the code is counting plots that are not worked so long as MustBeWorked=false for that individual Counters set-up.

I'm using this set up just to simplify the testing process:
Code:
tRealBuildingData[GameInfoTypes.BUILDING_GRANARY] = { Counters={
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_WHEAT_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_FARM, ResourceType=GameInfoTypes.RESOURCE_WHEAT, CountSameTileOnlyOnce=false, LimitPerCity=20, PlotsPerEffect=1},
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_FARM, MustBeWorked=false, CountSameTileOnlyOnce=false, LimitPerCity=4, PlotsPerEffect=1},
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_RUSSIAN_FLOODPLAIN_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_RUSSIAN_POLDER_FLOODPLAINS, MustBeWorked=true, CountSameTileOnlyOnce=true, LimitPerCity=100, PlotsPerEffect=1},
{DummyBuilding=GameInfoTypes["BUILDING_GRANARY_RUSSIAN_MARSH_DUMMY"], ImprovementType=GameInfoTypes.IMPROVEMENT_RUSSIAN_POLDER_MARSH, MustBeWorked=true, CountSameTileOnlyOnce=true, LimitPerCity=100, PlotsPerEffect=1}
} }
I added two new unique improvements only Russia can build to also verify there are not some odd issues with Unique Improvements.

When I found a city and use IGE to buy a Granary and then place three farms in the city's workable tiles the code properly adds three copies of BUILDING_GRANARY_DUMMY to my city during turn processing even though I only have one pop in the city at that point.
 
In my testing, removing citizens from working the tiles and passing the turn would reduce the +% modifier that I have as the effect for the dummy building. In your templates there, the format is different to the original, is it not? The settings bits appear to be after the other bits. I am using the same set-up I posted in my previous post on this thread
Code:
tRealBuildingData[GameInfoTypes.BUILDING_NAC_SHEPHERD_OF_THE_KASSITES] = {LimitPerCity=10, MustBeWorked=false, CountSameTileOnlyOnce=false, Counters={ {DummyBuilding=GameInfoTypes.BUILDING_NAC_KUDURRU_COUNTER, ImprovementType=GameInfoTypes.IMPROVEMENT_NAC_KUDURRU } } }

Do I need to change this, and if so, how?
 
It needs to be
Code:
tRealBuildingData[GameInfoTypes.BUILDING_NAC_SHEPHERD_OF_THE_KASSITES] = { Counters={ {DummyBuilding=GameInfoTypes.BUILDING_NAC_KUDURRU_COUNTER, ImprovementType=GameInfoTypes.IMPROVEMENT_NAC_KUDURRU, LimitPerCity=10, MustBeWorked=false, CountSameTileOnlyOnce=false } } }
Only the parameters "ApplyToAllInClass" and "SpecialHandling" should ever be given outside of a "Counters" entry.

Looking back at your original error-report message I see I missed picking up on that mistake for which I am sorry. I still would have needed to rework the code because you did expose a glaring logic-loop mistake on my part in the original code which only multi-year hindsight made obvious.

It also does not help that all the nice color-coding of the original instructions got wiped from the code boxes by the "modernization" of the forum. So within the code-blocks showing which bits needed to match up to which other bits by color there is merely defunct "html" markup commands.
 
Last edited:
This stuff is highly impressive, but I'll need my daughter to translate it for me...she has a BS in Computer Science and is a software developer for an online children's book publisher, called Pubbly.Com. :crazyeye:

This is what happens when you play Civ 5 at the ripe young age of 58. You know the history in the game, but not the computer development! :lol:
 
Top Bottom