Building dummy building upon detection of citadel

Danmacsch

Geheimekabinetsminister
Joined
Jan 14, 2014
Messages
1,316
Location
Copenhagen, Denmark
Hi,

I'm having some trouble with my lua code and hope some one can help me.

The first problem I have is with the following code. It's suppose to detect for each of the player's cities when a citadel is founded and then build the dummy building BUILDING_CORSICAN_CITADEL_TOURISM in the respective city. I think this works. What I would also like to add - and don't know how to - is the following:
1) when constructing more citadels in a city's area, the number of dummy buildings should rise accordingly.
2) restrict the code to citadels constructed on a tile within the empire's territory.
3) if the tile on which the citadel is constructed is annexed by another civilization the number of dummy buildings is decreased.

Spoiler :
Lua code:
Code:
function CitadelTourism(playerID)
	local player = Players[playerID]
	if player:IsAlive() and player:GetCivilizationType() == civilizationID then
		for city in player:Cities() do
			local cityPlot = 0, city:GetNumCityPlots() -1, 1
			local plot = city:GetCityIndexPlot(cityPlot)
			if plot:GetOwner() == civilisationID then
				if plot:GetImprovementType() == GameInfoTypes["IMPROVEMENT_CITADEL"] then
					city:SetNumRealBuilding(GameInfoTypes["BUILDING_CORSICAN_CITADEL_TOURISM"], 1)
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(CitadelTourism)
Relevant sql:
Code:
INSERT INTO BuildingClasses
			(Type, 											DefaultBuilding, 						Description)													
VALUES	    ('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM',		'BUILDING_CORSICAN_CITADEL_TOURISM', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM'),
			('BUILDINGCLASS_CORSICAN_FOODCULTURE_BONUS',	'BUILDING_CORSICAN_FOODCULTURE_BONUS', 	'TXT_KEY_BUILDING_CORSICAN_FOODCULTURE_BONUS');		
--==========================================================================================================================	
-- Buildings
--==========================================================================================================================	
INSERT INTO Buildings 	
			(Type, 								SpecialistType,						SpecialistCount, GoldMaintenance, 	PrereqTech, BuildingClass, Cost,	FaithCost,	Happiness,	NukeImmune, MinAreaSize, NeverCapture,	Description, 						Civilopedia, 							Help, 								Strategy,								PortraitIndex, 	IconAtlas)
SELECT		('BUILDING_CASEDDU'),				'SPECIALIST_CORSICAN_GENERAL',		3,				 1, 				PrereqTech, BuildingClass, 200,		-1,			1,			NukeImmune, MinAreaSize, 1,				('TXT_KEY_BUILDING_CASEDDU_DESC'), 	('TXT_KEY_BUILDINGS_CASEDDU_PEDIA'), 	('TXT_KEY_BUILDING_CASEDDU_HELP'), 	('TXT_KEY_BUILDINGS_CASEDDU_STRATEGY'),	3, 				('CORSICA_ATLAS')
FROM Buildings WHERE (Type = 'BUILDING_THEATRE');

INSERT INTO Buildings
		(Type, 						 			BuildingClass, 								EnhancedYieldTech,		TechEnhancedTourism, GreatWorkCount, Cost, FaithCost, PrereqTech,	Description, 									Help)
Values	('BUILDING_CORSICAN_CITADEL_TOURISM',	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM'	'TECH_PRINTING_PRESS',	2,					 -1, 			 -1,   -1, 		  null,			'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM_HELP'),
		('BUILDING_CORSICAN_FOODCULTURE_BONUS',	'BUILDINGCLASS_CORSICAN_FOODCULTURE_BONUS'	null,					0,					 -1, 			 -1,   -1, 		  null,			'TXT_KEY_BUILDING_CORSICAN_FOODCULTURE_BONUS',	'TXT_KEY_BUILDING_CORSICAN_FOODCULTURE_BONUS_HELP');

The next problem that I have is somewhat similar. It's with the following code. I'm trying to detect if a tile in a city's area has a citadel constructed AND then also if there's a specific unit (my civ's UU which has a unique promotion) garrisoned on the citadel. If this is the case, then the dummy building BUILDING_CORSICAN_FOODCULTURE_BONUS should be build in the respective city. This doesn't seem to work at all and I don't know why.
1) If more citadels with the specific unit garrisoned on it is within a city's area, the number of dummy buildings should rise accordingly.
2) If the unit is removed from the citadel the number of dummy buildings should fall accordingly.

Spoiler :
Lua code:
Code:
function CityFoodCultureBonus(playerID)
	local player = Players[playerID]
	if player:IsAlive() and player:GetCivilizationType() == civilizationID then
		for city in player:Cities() do
			local cityPlot = 0, city:GetNumCityPlots() -1, 1
			local plot = city:GetCityIndexPlot(cityPlot)
			if plot:GetOwner() == civilisationID then
				if plot:GetImprovementType() == GameInfoTypes["IMPROVEMENT_CITADEL"] and unit:IsHasPromotion(GameInfoTypes["PROMOTION_PAOLI_TROOP"]) then
					city:SetNumRealBuilding(GameInfoTypes["BUILDING_CORSICAN_FOODCULTURE_BONUS"], 1)
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(CityFoodCultureBonus)
Relevant sql (see the first spoiler for the rest):
Code:
--==========================================================================================================================	
-- Building_YieldChanges
--==========================================================================================================================
INSERT INTO Building_YieldChanges 
			(BuildingType, 								YieldType, 			Yield)
VALUES		('BUILDING_CORSICAN_FOODCULTURE_BONUS', 	'YIELD_FOOD', 		2),
			('BUILDING_CORSICAN_FOODCULTURE_BONUS', 	'YIELD_CULTURE', 	4);

The last thing (for now) is something that I haven't really tested yet, so I don't know if it actually works. A part of my civs UA is that when a lost city is completely re-captured a great general should appear.

Spoiler :
Code:
function CityRecaptureGreatGeneral(playerID)
	if GameEvents.CityCaptureComplete(civilisationID) then
		player:AddFreeUnit(GameInfoTypes['UNIT_GREAT_GENERAL'], 1)
	end
end

Bear in mind this is the first time I'm used lua, so please don't assumed I know very much :crazyeye::sad:
 
You can use the event:
Code:
GameEvents.BuildFinished(playerID, iX, iY, eImprovement)

to detect right away when a citadel improvement is created. It won't give you a city because improvement are associated with a plot but you can come up with an approach to pick a city for a plot.

As for detecting when the improvement has a unit in it, you can use the event:

Code:
GameEvents.UnitSetXY(PlayerID player, UnitID unit, int x, int y)

GameEvents.PlayerDoTurn only fires at the start of each players turn which means if a citadel is created or a unit moves in the middle of a turn (as is common) than the effects won't update until the next turn rolls around.
 
You can use the event:
Code:
GameEvents.BuildFinished(playerID, iX, iY, eImprovement)

to detect right away when a citadel improvement is created. It won't give you a city because improvement are associated with a plot but you can come up with an approach to pick a city for a plot.

As for detecting when the improvement has a unit in it, you can use the event:

Code:
GameEvents.UnitSetXY(PlayerID player, UnitID unit, int x, int y)

GameEvents.PlayerDoTurn only fires at the start of each players turn which means if a citadel is created or a unit moves in the middle of a turn (as is common) than the effects won't update until the next turn rolls around.
Thanks. I'll try it and see if I can get it to work.
 
You can use the event:
Code:
GameEvents.BuildFinished(playerID, iX, iY, eImprovement)

to detect right away when a citadel improvement is created. It won't give you a city because improvement are associated with a plot but you can come up with an approach to pick a city for a plot.

Be careful when using that event hook directly.

I use that hook (BuildFinished) in my mod for other effects, but I've discovered that, for whatever reason, this hook (and its associated function) fires twice for every improvement that gets built by a great person.

This means it affects Citadels, Academies, Manufactories, and Customs Houses. I suspect it would be the same for Holy Sites, and Landmarks, but I never tested them.

I got around this by setting a global variable which gets toggled when the code is run, such that the second time the function runs, it doesn't do anything and resets the variable instead.
 
Be careful when using that event hook directly.

I use that hook (BuildFinished) in my mod for other effects, but I've discovered that, for whatever reason, this hook (and its associated function) fires twice for every improvement that gets built by a great person.

This means it affects Citadels, Academies, Manufactories, and Customs Houses. I suspect it would be the same for Holy Sites, and Landmarks, but I never tested them.

I got around this by setting a global variable which gets toggled when the code is run, such that the second time the function runs, it doesn't do anything and resets the variable instead.

Okay, I've tried using the BuildFinished event, but can't get it to work. Also, now it seems my original code doesn't work either :sad: and I'm questioning that it did in the first place.

Could my problem be with this
Code:
local cityPlot = 0, city:GetNumCityPlots() -1, 1
local plot = city:GetCityIndexPlot(cityPlot)
Or could it be with the sql code (included in OP) :confused:

I really hope someone can help with this :please:
 
BuildFinished is an event that should only work in BNW -- if it doesn't work, are you using BNW?

As for your question, I'm not sure what that is meant to do, as it sort of looks like the beginning of a loop code, but isn't structured like a loop that I'm familiar with.

Without the errors / logs, or the rest of the code, it's tough to figure out. (It's been a while since this thread was started, so I'm not sure if the zip in your OP is outdated or not.)

Furthermore, if I'm interpreting this properly, you want these dummy buildings to be adding a certain amount of tourism depending on the number of Citadels. Unfortunately, it's been discussed elsewhere, and I've also tested myself, you cannot get tourism to "stack" with dummy buildings.

Even if you add 15 dummy buildings each producing +1 tourism, the city will only accept one, and thus give you +1 tourism. (All other yields appear to stack properly, as far as I can tell.)

Specifically, the game will only count the first instance (for purposes of tourism) of a building in any building class that your Civilization normally has access to.
 
BuildFinished is an event that should only work in BNW -- if it doesn't work, are you using BNW?

As for your question, I'm not sure what that is meant to do, as it sort of looks like the beginning of a loop code, but isn't structured like a loop that I'm familiar with.

Without the errors / logs, or the rest of the code, it's tough to figure out. (It's been a while since this thread was started, so I'm not sure if the zip in your OP is outdated or not.)

Furthermore, if I'm interpreting this properly, you want these dummy buildings to be adding a certain amount of tourism depending on the number of Citadels. Unfortunately, it's been discussed elsewhere, and I've also tested myself, you cannot get tourism to "stack" with dummy buildings.

Even if you add 15 dummy buildings each producing +1 tourism, the city will only accept one, and thus give you +1 tourism. (All other yields appear to stack properly, as far as I can tell.)

Specifically, the game will only count the first instance (for purposes of tourism) of a building in any building class that your Civilization normally has access to.

I am using BNW.

The code is suppose to check for citadels within a city's limits and then - if the plot is owned by the player - build the building "BUILDING_CORSICAN_CITADEL_TOURISM" in the respective city.
I'm aware of the "tourism stack" problem, but for now I would be content with getting it to work with just one dummy building adding tourism.
But wouldn't it be possible to add a number of different dummy buildings all with different tourism yields, so that the if only one citadel is within the city limits, a building adding 2 tourism would be build, if two citadels is within the city limits, the first building would be deleted and another building adding 4 tourism would be build instead, and so on..? Or is that what you describe as impossible in the end of your post?

If that is the case, I would need to rework my civs UA I think.

Lastly, the zip in my post is outdated, but I'm going to upload a new one after writing this.
 

Attachments

  • Corsican Republic (v 1).zip
    7.3 MB · Views: 72
This test is both wrong and unnecessary
Code:
if plot:GetOwner() == civilisationID then

Wrong: the owner of the plot is the player (playerID) not the civilization (civilisationID)
Unnecessary: because the plot is known to be owned by a city belonging to the player
 
[...] But wouldn't it be possible to add a number of different dummy buildings all with different tourism yields, so that the if only one citadel is within the city limits, a building adding 2 tourism would be build, if two citadels is within the city limits, the first building would be deleted and another building adding 4 tourism would be build instead, and so on..? Or is that what you describe as impossible in the end of your post? [...]

Yes, this would work, and would likely be the only way of achieving the appearance of dynamic tourism. However, you must ensure that each one of these dummy buildings is in its own building class, and either the default building of that class, or otherwise is set as your Civ's UB via the Civilization's overrides. The game seems to ignore tourism coming from any building inside a buildingclass that your Civ cannot normally access.

As for your Lua, in addition to the issue whoward pointed out, there is also the problem of you mixing up the various flavors of English spelling:

Code:
local civili[COLOR="Red"]s[/COLOR]ationID = GameInfoTypes["CIVILIZATION_CORSICA"]
[...]
player:GetCivilizationType() == civili[COLOR="Red"]z[/COLOR]ationID

This would cause that function not to run, since you're trying to compare against what would be a nil value. As well, the reason why whoward says it's an unnecessary check is because you've already checked that the player is the proper civilization before continuing (or tried to,) so you can just use that player's ID afterward.

I also don't think your loop is set up properly to actually iterate through all the plots in a city, but I could be wrong, given that I'm still quite new to Lua.

Code:
for city in player:Cities() do
	local cityPlot = 0, city:GetNumCityPlots() -1, 1
	local plot = city:GetCityIndexPlot(cityPlot)
	if plot:GetOwner() == civilisationID then
		if plot:GetImprovementType() == GameInfoTypes["IMPROVEMENT_CITADEL"] then
			city:SetNumRealBuilding(GameInfoTypes["BUILDING_CORSICAN_CITADEL_TOURISM"], 1)
		end
	end
end

In this section, your loop starts iterating through the player's cities, and for each city it... doesn't have anything that tells it to do a secondary loop. You've set the variable cityPlot incorrectly, and I believe it will always return 0, since you've given it 3 values but only one variable name.

What you want is a second for loop inside the first loop -- you are telling the game that for each of my cities please check each of that city's plots and do stuff with them.

Here is a section of plot looping code from my Civ, which relies pretty heavily on plot scanning:
Code:
for pCity in pPlayer:Cities() do
	local iNumPlots = pCity:GetNumCityPlots()
	for i = 0, iNumPlots - 1 do
		local pPlot = pCity:GetCityIndexPlot(i)
		if pPlot and pPlot:GetOwner() == iPlayer then
			-- conditions go here:
			if pPlot:GetImprovementType() = GameInfoTypes.IMPROVEMENT_CITADEL then
				--magic happens
			end
		end
	end
end

Note: That section is edited down for clarity, and to remove all of the debug I have in my own code, as well as remove references to various supporting functions that I've abstracted out. This is essentially the basic format of the loops that I use.

The last CityCaptureComplete function also looks incorrect, but I just got up, so I'll not tackle that one until I'm a bit more awake.

Hope that helps.
 
Hope that helps.

Thanks so much! It works :goodjob: I haven't tried adding more dummy buildings yet, so I could still run into some problems. Also, if I place found a citadel on a plot within more than one city's area, they all get the dummy building. That's not strange I guess, but it would be nice to get around that at some point :rolleyes:
I'll experiment with the code a little more and will probably post my results when done, just in case it would be a help to others.

Regarding the CityRecaptureGreatGeneral function; I'll probably remove it entirely since it's not very interesting.

Again, thank you very much DarkScythe.
 
I'm glad I could be of assistance -- I'm still quite new to Lua myself.
All I have is the helpful advice I've received from others here, and my ability to stubbornly keep trying code until something works.

With regard to adding and removing buildings, it's simple enough to add extra lines in the section I've put as "magic happens." To remove, simply use SetNumRealBuilding with a value of 0.

If you want to restrict it to one city at a time, it gets a little more complicated. (Again, I've done the same thing in my mod.) The problem is that plot ownership can change if you have plots that are within working range of more than one city. In my tests, there was not a way to conclusively tell which city owned a particular plot at a time.

GetCityPurchaseID() sounds like it would work, except it doesn't really.. plots that are 5 hexes away from one city, but within range of another city will still point to that first city if it grabbed the tile first. However, it gets overridden by the 1-tile radius of a newly founded city.
GetWorkingCity() could work, but it occasionally fails, and I'm not entirely sure why.

The most reliable methods I found were IsBeingWorked() and GetPlayerCityRadiusCount(), but neither of those would really help you with what you need.

What you can do (and what I've done with my code) is to persist some data about the plot the Citadel sits on. There are a variety of methods to persist data, but the simplest one is probably SetScriptData() and its associated GetScriptData().

Whenever your city finds a Citadel and adds a dummy building, it can add a flag to that plot that says this Citadel has already given its dummy building, and subsequent scans over that plot would first check that flag, and if it sees that it's already been handed out, to skip it and move on.

Keep in mind, you should also add an IsImprovementPillaged() check as well, otherwise the dummy building would remain in the city even while the Citadel was pillaged. Unless you want this, of course.
 
With regard to adding and removing buildings, it's simple enough to add extra lines in the section I've put as "magic happens." To remove, simply use SetNumRealBuilding with a value of 0.

I am having some difficulties with this in fact. Not with adding and deleting the respective buildings, but with detecting whether or not there's more than more citadel in a city's working area. I would guess I need to somehow count the plots on which there're citadels, but I don't really have any idea how.

I'm going to try messing around with this tonight, but if you have any more good ideas, they are more than welcome :D
 
The city loop code should loop over all 36 plots around each city, so it would be able to catch multiple copies of the Citadel, as there is no break specified in the loop. It's simple enough to add a counter instead of adding a building immediately, but then you'll have to break up the function into two parts -- a counting section, and then the actual building adding section.

If you also don't want the same Citadel being counted multiple times if it is within 3 tiles of several cities, then you have to get a bit more complicated and persist that data.

My suggestion for something simple was to use SetScriptData(), but according to the Modiki, this method could be deprecated. However, I believe each plot can only hold one string, so if you use this method, it will be incompatible with any other mod which possibly makes use of this. In this case, I would recommend something like SaveUtils / NewSaveUtils, or what I personally use is Pazyryk's TableSaverLoader.

With that said, keeping in mind that I have not used SetScriptData() myself, here is untested code based around SetScriptData():

Code:
for pCity in pPlayer:Cities() do
	local iNumPlots = pCity:GetNumCityPlots()
	local iCitadelCount = pCity:GetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE)
	local iCitadelCounter = iCitadelCount
	for i = 0, iNumPlots - 1 do
		local pPlot = pCity:GetCityIndexPlot(i)
		if pPlot and pPlot:GetOwner() == iPlayer then
			-- Check plot's script data to see if bonus has been granted
			local bCitadelBonus = pPlot:GetScriptData() == "granted" and true or false
			-- Conditions go here:
			if pPlot:GetImprovementType() = GameInfoTypes.IMPROVEMENT_CITADEL then
				-- Magic happens
				if pPlot:IsImprovementPillaged() == true then
					if bCitadelBonus then
						-- Citadel is pillaged, and had been providing a bonus
						iCitadelCounter = iCitadelCounter - 1
						pPlot:SetScriptData() == ""
					end
				elseif not bCitadelBonus then
					-- Citadel stands, and is not already providing any bonus
					iCitadelCounter = iCitadelCounter + 1
					pPlot:SetScriptData() == "granted"
				end
			elseif bCitadelBonus then
				-- Plot reports bonus being handed out, but no longer has a Citadel
				iCitadelCounter = iCitadelCounter - 1
				pPlot:SetScriptData() = ""
			end
		end
		-- End of loops over city's 36 plots
	end
	-- Add buildings to the city now that the looping function has completed
	if iCitadelCounter > 0 and iCitadelCounter ~= iCitadelCount then
		-- Counter is positive, and amount of Citadels has changed since the last time the city was updated
		-- This block will not run if no Citadels were found, or no changes occurred to the number of Citadels around a city
		-- The "if" check is optional, but I feel it's cleaner to have code run only when needed
		pCity:SetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE, iCitadelCounter)
		pCity:SetNumRealBuilding(GameInfoTypes.SECOND_BUILDING_NAME_HERE, iCitadelCounter)
	end
end

Perhaps there's a more elegant way of handling this, but I'm not quite experienced enough yet.
There are also a few edge cases where this won't work properly, or the count will be off, but without a reliable way of determining which city controls which plot, it's tough to account for them in a general way. You would likely have to build some additional checks to cover those specific scenarios.
 
Spoiler :
The city loop code should loop over all 36 plots around each city, so it would be able to catch multiple copies of the Citadel, as there is no break specified in the loop. It's simple enough to add a counter instead of adding a building immediately, but then you'll have to break up the function into two parts -- a counting section, and then the actual building adding section.

If you also don't want the same Citadel being counted multiple times if it is within 3 tiles of several cities, then you have to get a bit more complicated and persist that data.

My suggestion for something simple was to use SetScriptData(), but according to the Modiki, this method could be deprecated. However, I believe each plot can only hold one string, so if you use this method, it will be incompatible with any other mod which possibly makes use of this. In this case, I would recommend something like SaveUtils / NewSaveUtils, or what I personally use is Pazyryk's TableSaverLoader.

With that said, keeping in mind that I have not used SetScriptData() myself, here is untested code based around SetScriptData():

Code:
for pCity in pPlayer:Cities() do
	local iNumPlots = pCity:GetNumCityPlots()
	local iCitadelCount = pCity:GetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE)
	local iCitadelCounter = iCitadelCount
	for i = 0, iNumPlots - 1 do
		local pPlot = pCity:GetCityIndexPlot(i)
		if pPlot and pPlot:GetOwner() == iPlayer then
			-- Check plot's script data to see if bonus has been granted
			local bCitadelBonus = pPlot:GetScriptData() == "granted" and true or false
			-- Conditions go here:
			if pPlot:GetImprovementType() = GameInfoTypes.IMPROVEMENT_CITADEL then
				-- Magic happens
				if pPlot:IsImprovementPillaged() == true then
					if bCitadelBonus then
						-- Citadel is pillaged, and had been providing a bonus
						iCitadelCounter = iCitadelCounter - 1
						pPlot:SetScriptData() == ""
					end
				elseif not bCitadelBonus then
					-- Citadel stands, and is not already providing any bonus
					iCitadelCounter = iCitadelCounter + 1
					pPlot:SetScriptData() == "granted"
				end
			elseif bCitadelBonus then
				-- Plot reports bonus being handed out, but no longer has a Citadel
				iCitadelCounter = iCitadelCounter - 1
				pPlot:SetScriptData() = ""
			end
		end
		-- End of loops over city's 36 plots
	end
	-- Add buildings to the city now that the looping function has completed
	if iCitadelCounter > 0 and iCitadelCounter ~= iCitadelCount then
		-- Counter is positive, and amount of Citadels has changed since the last time the city was updated
		-- This block will not run if no Citadels were found, or no changes occurred to the number of Citadels around a city
		-- The "if" check is optional, but I feel it's cleaner to have code run only when needed
		pCity:SetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE, iCitadelCounter)
		pCity:SetNumRealBuilding(GameInfoTypes.SECOND_BUILDING_NAME_HERE, iCitadelCounter)
	end
end

Perhaps there's a more elegant way of handling this, but I'm not quite experienced enough yet.
There are also a few edge cases where this won't work properly, or the count will be off, but without a reliable way of determining which city controls which plot, it's tough to account for them in a general way. You would likely have to build some additional checks to cover those specific scenarios.

Now that is nice! I'm afraid I don't really understand the following
Code:
	local iCitadelCount = pCity:GetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE)
	local iCitadelCounter = iCitadelCount

The iCitadelCount should be a number, right?
Just for reference, this is my sql code:
Code:
INSERT INTO BuildingClasses
			(Type, 											DefaultBuilding, 						Description)
VALUES	    ('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM1',	'BUILDING_CORSICAN_CITADEL_TOURISM1', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM1'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM2',	'BUILDING_CORSICAN_CITADEL_TOURISM2', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM2'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM3',	'BUILDING_CORSICAN_CITADEL_TOURISM3', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM3'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM4',	'BUILDING_CORSICAN_CITADEL_TOURISM4', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM4'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM5',	'BUILDING_CORSICAN_CITADEL_TOURISM5', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM5'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM6',	'BUILDING_CORSICAN_CITADEL_TOURISM6', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM6'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM7',	'BUILDING_CORSICAN_CITADEL_TOURISM7', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM7'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM8',	'BUILDING_CORSICAN_CITADEL_TOURISM8', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM8'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM9',	'BUILDING_CORSICAN_CITADEL_TOURISM9', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM9'),
			('BUILDINGCLASS_CORSICAN_CITADEL_TOURISM10','BUILDING_CORSICAN_CITADEL_TOURISM10', 	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM10');

INSERT INTO Buildings
		(Type, 						 			BuildingClass, 								EnhancedYieldTech,		TechEnhancedTourism,	Happiness,	GreatWorkCount, Cost, FaithCost,	PrereqTech,	Description, 									Help)
VALUES	('BUILDING_CORSICAN_CITADEL_TOURISM1', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM1',	'TECH_PRINTING_PRESS',	2,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM1',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM1_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM2', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM2',	'TECH_PRINTING_PRESS',	4,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM2',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM2_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM3', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM3',	'TECH_PRINTING_PRESS',	6,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM3',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM3_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM4',	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM4',	'TECH_PRINTING_PRESS',	8,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM4',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM4_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM5', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM5',	'TECH_PRINTING_PRESS',	10,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM5',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM5_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM6', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM6',	'TECH_PRINTING_PRESS',	12,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM6',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM6_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM7', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM7',	'TECH_PRINTING_PRESS',	14,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM7',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM7_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM8', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM8',	'TECH_PRINTING_PRESS',	16,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM8',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM8_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM9', 	'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM9',	'TECH_PRINTING_PRESS',	18,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM9',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM9_HELP'),
		('BUILDING_CORSICAN_CITADEL_TOURISM10', 'BUILDINGCLASS_CORSICAN_CITADEL_TOURISM10',	'TECH_PRINTING_PRESS',	20,						0,			-1, 			-1,   -1, 			null,		'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM10',	'TXT_KEY_BUILDING_CORSICAN_CITADEL_TOURISM10_HELP');

To get this to work I somehow need to count the citadels around a city (which your code does), then add the correct building matching the count and at the same time delete buildings matching other counts.

Well, think I'm going to sleep on it - too tired to think right now :sleep:
Again, your help is very much appreciated!!
 
The buildFinished event gives you enough information to find the plot object. You then loop over every city the player owns and test if something like city:CanWork(plot) is true. You don't need to count all the plots a city could work.

With the exception of the ideology tenet media culture (which provides a % tourism boost from cities with broadcast towers) it doesn't really make much of a difference what city the tourism comes from. It would generally be easier to implement "the capital gets +2 tourism for every citadel" than what you're trying to do and 9 times out of 10 it will play the same.
 
That would work fine for a fire-once-and-forget type of bonus, but unfortunately, I don't believe there's any corresponding event for the improvement being destroyed or replaced. If there is, I would love to know, since it would make my own mod's code much cleaner.

I would agree that adding everything tourism-related to the Capital is easier, but tourism doesn't stack, so he can't simply add more buildings to the Capital. That said, it wouldn't stack the way he wants to in each city either, so in both cases, you'd need a way to track which building to hand out.

Looking at the various buildings, I think you can use the counter and concatenate it into the string in order to look up the ID of a corresponding building+counter combination, but as it's quite late, I don't have time to mock up code currently.

Unfortunately, what you're trying to do revolves heavily on remembering stuff, and I think that really requires the use of some type of data persistence. If you don't "remember" the value the city last had, you're left blindly looping through every possible building to set it to 0 before you add the "new" amount.

As for the question about the iCitadelCount -- yes, it is an integer.
Code:
local iCitadelCount = pCity:GetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE)
This asks the city to return how many of a certain building exists, which I then use as the starting value of the counter here:
Code:
local iCitadelCounter = iCitadelCount

However, this really only works for buildings with "normal" building yields. Tourism doesn't stack, so you can't quite use this.

I'd probably need to mock up some new code based on your new building list.
 
Spoiler :
That would work fine for a fire-once-and-forget type of bonus, but unfortunately, I don't believe there's any corresponding event for the improvement being destroyed or replaced. If there is, I would love to know, since it would make my own mod's code much cleaner.

I would agree that adding everything tourism-related to the Capital is easier, but tourism doesn't stack, so he can't simply add more buildings to the Capital. That said, it wouldn't stack the way he wants to in each city either, so in both cases, you'd need a way to track which building to hand out.

Looking at the various buildings, I think you can use the counter and concatenate it into the string in order to look up the ID of a corresponding building+counter combination, but as it's quite late, I don't have time to mock up code currently.

Unfortunately, what you're trying to do revolves heavily on remembering stuff, and I think that really requires the use of some type of data persistence. If you don't "remember" the value the city last had, you're left blindly looping through every possible building to set it to 0 before you add the "new" amount.

As for the question about the iCitadelCount -- yes, it is an integer.
Code:
local iCitadelCount = pCity:GetNumRealBuilding(GameInfoTypes.BUILDING_NAME_HERE)
This asks the city to return how many of a certain building exists, which I then use as the starting value of the counter here:
Code:
local iCitadelCounter = iCitadelCount

However, this really only works for buildings with "normal" building yields. Tourism doesn't stack, so you can't quite use this.

I'd probably need to mock up some new code based on your new building list.

Hi DarkScythe, you haven't necessarily come up with an idea about how to count the citadels for a city, and remember the value the city last had, have you?

I tried rewriting the code, so that the tourism bonus only relates to the capital, but I would rather that it worked as originally intended. If possible of course.
 
I tried rewriting the code, so that the tourism bonus only relates to the capital, but I would rather that it worked as originally intended. If possible of course.
I have some code that provides +1 Trade Route for every Custom's House. If you change a couple of lines you can easily make it provide Tourism instead and only trigger off Citadels spawned by players with a specific trait.
 
I have some code that provides +1 Trade Route for every Custom's House. If you change a couple of lines you can easily make it provide Tourism instead and only trigger off Citadels spawned by players with a specific trait.
Well, I'd love to see it if possible, though it might still be a problem since tourism doesn't stack as discussed earlier.
But - as said - I'd love to see the code if you want to post it.
 
Tourism doesn't stack in the same way trade routes don't stack, so that problem is taken care of. The algorithm defines a noLimit building that will be placed in the player's capital and its quantity will increase when the improvement is created. There is another 30 dummy buildings that provide +1, +2, +3 ... +30 Trade Routes. This is would be the real source of tourism. You could change the code to not include the noLimit dummy building but if you want to provide +2 Gold than you can easily add it to the noLimit building. All buildings will automatically move if the player's capital moves.

CustomHouse_TradeRoute.xml
Defines the noLimit building. You just need to change the names.
Spoiler :

Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 7/5/2014 12:39:08 PM -->
<GameData>
	<BuildingClasses>
		<Row>
			<Type>BUILDINGCLASS_CUSTOM_HOUSE_TRADE_ROUTE</Type>
			<DefaultBuilding>BUILDING_CUSTOM_HOUSE_TRADE_ROUTE</DefaultBuilding>
			<Description>TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE</Description>
			<NoLimit>true</NoLimit>
		</Row>
	</BuildingClasses>
	<Buildings>
		<Row>
			<Type>BUILDING_CUSTOM_HOUSE_TRADE_ROUTE</Type>
			<BuildingClass>BUILDINGCLASS_CUSTOM_HOUSE_TRADE_ROUTE</BuildingClass>
			<Cost>-1</Cost>
			<ConquestProb>100</ConquestProb>
			<NukeImmune>true</NukeImmune>
			<Description>TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE</Description>
			<Help>TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_HELP</Help>
			<MinAreaSize>-1</MinAreaSize>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>19</PortraitIndex>

			<!--<NumTradeRouteBonus>1</NumTradeRouteBonus>-->
		</Row>
	</Buildings>

	<Language_en_US>
		<Row Tag="TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE">
			<Text>Custom House Trade Route</Text>
		</Row>
		<Row Tag="TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_HELP">
			<Text> </Text>
		</Row>
	</Language_en_US>
</GameData>


TCSI_TradeRouteCount.xml
Creates a simple table only used by the next file. This could be merged with the next file.
Spoiler :

Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 7/30/2014 5:16:43 PM -->
<GameData>
	<Table name="TCSI_TradeRouteCount">
		<Column name="numRoutes" type="integer" default="0"/>
	</Table>
</GameData>


CustomHouse_TradeRoute.sql
Creates the 30 buildings that provide +1, +2, +3...+30 trade routes. These dummy buildings are used to get around the fact that trade routes (and tourism) don't stack on noLimit buildings correctly. You would need to edit these to provide tourism instead of extra trade routes.
Spoiler :

Code:
INSERT INTO TCSI_TradeRouteCount(numRoutes) VALUES
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10),
(11),
(12),
(13),
(14),
(15),
(16),
(17),
(18),
(19),
(20),
(21),
(22),
(23),
(24),
(25),
(26),
(27),
(28),
(29),
(30);

-- Create the building classes
INSERT INTO BuildingClasses(Type, DefaultBuilding, Description)
	SELECT 'BUILDINGCLASS_CUSTOM_HOUSE_TRADE_ROUTE_'||numRoutes, 'BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_'||numRoutes, 'TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE'
	FROM TCSI_TradeRouteCount;

-- Create the buildings
INSERT INTO Buildings(Type, BuildingClass, Description, NukeImmune, ConquestProb, Cost, NumTradeRouteBonus)
	SELECT 'BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_'||numRoutes, 'BUILDINGCLASS_CUSTOM_HOUSE_TRADE_ROUTE_'||numRoutes, 'TXT_KEY_BUILDING_CUSTOM_HOUSE_TRADE_ROUTE', 1, 100, -1, numRoutes
	FROM TCSI_TradeRouteCount;


CustomHouse_TradeRoute.lua
Where the bulk of the work happens. You would need to edit the improvement that this looks for. You would also need to add a restriction for the civilization.
Spoiler :

Code:
-- Trade route from custom house
-- Author: Machiavelli
-- DateCreated: 7/5/2014 12:38:44 PM
--------------------------------------------------------------
-- Use global variables to avoid having the event be called twice for the same plot
g_Titans_BuildFinished_PlayerID = 0
g_Titans_BuildFinished_X = 0
g_Titans_BuildFinished_Y = 0
g_Titans_BuildFinished_Improvement = 0

function CustomHouseTradeRoute(playerID, iX, iY, eImprovement)
	-- Don't continue if this is a duplicate call
	if(g_Titans_BuildFinished_PlayerID == playerID and g_Titans_BuildFinished_X == iX and g_Titans_BuildFinished_Y == iY and g_Titans_BuildFinished_Improvement == eImprovement) then
		return;
	end
	-- Store inputs for duplication check
	g_Titans_BuildFinished_PlayerID = playerID;
	g_Titans_BuildFinished_X = iX;
	g_Titans_BuildFinished_Y = iY;
	g_Titans_BuildFinished_Improvement = eImprovement;

	-- Max determined by number of entries in TCSI_TradeRouteCount
	local CONSTANT_MAX_TRADE_ROUTES = 30;

	local player = Players[playerID];
	local plot = Map.GetPlot(iX, iY);
	local city = player:GetCapitalCity();
	local buildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE"];
	local numBuilding = 0;
	local oldBuildingID = -1;
	local newBuildingID = -1;

	if(eImprovement == GameInfo.Improvements["IMPROVEMENT_CUSTOMS_HOUSE"].ID) then
		numBuilding = city:GetNumBuilding(buildingID);

		-- Do not continue if there is no building providing more trade routes
		if(numBuilding == CONSTANT_MAX_TRADE_ROUTES) then
			print("WARN -- At max trade routes from Custom's House, can not give more.");
			return;
		end

		city:SetNumRealBuilding(buildingID, numBuilding + 1);

		oldBuildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_" .. tostring(numBuilding)];
		newBuildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_" .. tostring(numBuilding + 1)];

		if(newBuildingID > 0 and (numBuilding == 0 or (oldBuildingID > 0 and city:IsHasBuilding(oldBuildingID)))) then
			city:SetNumRealBuilding(newBuildingID, 1);
			if(numBuilding > 0) then
				city:SetNumRealBuilding(oldBuildingID, 0);
			end
		end
	end
end
GameEvents.BuildFinished.Add(CustomHouseTradeRoute);

-- Move dummy building to capital in the event of conquest
function CustomHouseTradeRoute_CapitalMove(oldPlayerID, bCapital, iX, iY, newPlayerID, conquest, conquest2)
	local oldPlayer = Players[oldPlayerID];
	local newPlayer = Players[newPlayerID];
	local city =  Map.GetPlot(iX, iY):GetPlotCity();
	local buildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE"];
	local numBuilding = city:GetNumBuilding(buildingID);
	local numTradeRouteBuildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_" .. tostring(numBuilding)];

	-- If the old player just lost their capital, move the buildings to their new capital
	if(numBuilding > 0) then
		city:SetNumRealBuilding(buildingID, 0);
		city:SetNumRealBuilding(numTradeRouteBuildingID, 0);
		if(oldPlayer:IsAlive()) then
			oldPlayer:GetCapitalCity():SetNumRealBuilding(buildingID, numBuilding);
			oldPlayer:GetCapitalCity():SetNumRealBuilding(numTradeRouteBuildingID, 1);
		end
	end
	---------------------------------
	-- If the new player just recovered their capital, they will need to have their buildings moved
	if(newPlayer:GetCapitalCity():GetID() == city:GetID()) then
		for cityToRemove in newPlayer:Cities() do
			if(cityToRemove:GetID() ~= city:GetID() and cityToRemove:IsHasBuilding(buildingID)) then
				numBuilding = cityToRemove:GetNumBuilding(buildingID);
				numTradeRouteBuildingID = GameInfoTypes["BUILDING_CUSTOM_HOUSE_TRADE_ROUTE_" .. tostring(numBuilding)];
				-- Add the buildings to the player's new capital
				city:SetNumRealBuilding(buildingID, numBuilding);
				city:SetNumRealBuilding(numTradeRouteBuildingID, 1);
				-- Remove them from the old capital
				cityToRemove:SetNumRealBuilding(buildingID, 0);
				cityToRemove:SetNumRealBuilding(numTradeRouteBuildingID, 0);
			end
		end
	end
end
GameEvents.CityCaptureComplete.Add(CustomHouseTradeRoute_CapitalMove);
 
Top Bottom