Is there any way to make the Culture yield?

Anti_KoS

Chieftain
Joined
Mar 10, 2011
Messages
2
Adding Culture to CIV5Yields.xml can't help. I didn't find any other xml to make what I want.

Is there simple way to create Culture yield via LUA (or via some other methods)?
 
No.

There are ways in Lua to award culture, but keying off of all the correct triggers would be nearly impossible. Culture and Happiness as yields are high on the list of requested modding changes for the developers, but there's no sign it'll ever happen.
 
No.

There are ways in Lua to award culture, but keying off of all the correct triggers would be nearly impossible. Culture and Happiness as yields are high on the list of requested modding changes for the developers, but there's no sign it'll ever happen.

Actually whats interesting is the function RecordYieldInfo has a field for culture. It has science, production, gold, food, and culture. The reason is that its necessary to display these things on the map.

Code:
> yieldinfo = RecordYieldInfo(0,0,0,plot)
> for k,v in pairs(yieldinfo) do print(k) end
 YieldIconManager: m_Production
 YieldIconManager: m_Science
 YieldIconManager: m_Food
 YieldIconManager: m_Gold
 YieldIconManager: m_Culture

There's also an OnYieldChange function which suggests that there's a YieldChange event, and so even if you can't do it directly, you can do a round-about solution. You can have the mod detect a culture yield change, use it to change culture, but at the same time modify the yield display to prevent it from showing culture twice.
 
You can have the mod detect a culture yield change, use it to change culture, but at the same time modify the yield display to prevent it from showing culture twice.

I tried something like this previously. It didn't work; it looked like there was some intermediate point where the game translates your hypothetical yield into one of the existing types in its table. Since Culture isn't in that table, nothing happened. But I might have messed up something else along the way, so feel free to try it. (If you can get it working, you'd make a lot of people very happy.)

(Don't read too much into the fact that Culture is in the terrain yield UI. They coded a lot of those extra bits; the Specialist yield UI, for instance, can display both Culture and Happiness despite neither of those being yields.)

There's a similar problem with adding new terrain types; just adding it to the Terrain XML file doesn't actually seem to add it to the TerrainTypes list, which limits your ability to work around these sorts of limitations.
 
I tried something like this previously. It didn't work; it looked like there was some intermediate point where the game translates your hypothetical yield into one of the existing types in its table. Since Culture isn't in that table, nothing happened. But I might have messed up something else along the way, so feel free to try it. (If you can get it working, you'd make a lot of people very happy.)

(Don't read too much into the fact that Culture is in the terrain yield UI. They coded a lot of those extra bits; the Specialist yield UI, for instance, can display both Culture and Happiness despite neither of those being yields.)

There's a similar problem with adding new terrain types; just adding it to the Terrain XML file doesn't actually seem to add it to the TerrainTypes list, which limits your ability to work around these sorts of limitations.

Are you having a more general problem creating any new type of yield then?

Right now I'm trying to solve the problem of having culture changes update in the city screen tiles:

http://forums.civfanatics.com/showthread.php?t=414754

If anyone is going to try to implement a culture yield, they'll have to address that too.
 
It doesn't show up on the YieldIconManager, but this works for me as far as recording the value goes:

Code:
<GameData>
	<Yields>
		<Row>
			<ID>4</ID>
			<Type>YIELD_CULTURE</Type>
			<Description>TXT_KEY_YIELD_CULTURE</Description>
			<IconString>[ICON_CULTURE]</IconString>
			<MinCity>1</MinCity>
			<AIWeightPercent>80</AIWeightPercent>
		</Row>
	</Yields>

	<Improvement_Yields>
		<Row>
			<ImprovementType>IMPROVEMENT_MINE</ImprovementType>
			<YieldType>YIELD_CULTURE</YieldType>
			<Yield>1</Yield>
		</Row>
	</Improvement_Yields>

	<Language_en_US>
		<Row Tag="TXT_KEY_YIELD_CULTURE">
			<Text>Culture</Text>
		</Row>
	</Language_en_US>
</GameData>

So when I do plot:GetYield(4) it returns the correctly calculated Culture yield.

Code:
> GameInfo.Yields.YIELD_CULTURE.ID
4
> city:GetCityIndexPlot(2):GetImprovementType()
4
> city:GetCityIndexPlot(2):GetYield(4)
2



What you then want to do is somehow hook it up so when yields are calculated it adjusts the culture to match using city:ChangeCulture(x) and if the hex is revealed on the player's map then call the Events.HexYieldMightHaveChanged(x,y) function to make sure it visually updates.

I don't know how the proper event to hook it to yet, but in the mean type you can do a loop which looks at each tile in a city to call ChangeCulture to adjust to the proper value and create a table that stores values for set tiles so you can tell how much culture you've assigned to them already in case there are other functions which use ChangeCulture to aggregate. Also you'd need to make sure to check already changed tiles in case they no longer have an owning city.

As for updating YieldIconManager , there's some Lua magic going on there that I don't know about yet
 
Wait never mind, something screwy is going on.

It doesnt appear like you described, but plots that shouldn't show a yield for culture show it, and this plot shouldn't show 2.

But it doesn't haev anything to do with the other yield numbers. For example, this plot has 3 production, so I don't know where the 2 culture number comes from.

I'll look at this some more tomorrow.
 
Doing CalculateYield() seems to give a more indicative value

Code:
> city:GetCityIndexPlot(2):CalculateYield(4)
1
> city:GetCityIndexPlot(0):CalculateYield(4)
1

Plot 0 is the city plot, and it has 1 culture presumably because of the <MinCity>1</MinCity> field.

Then other plots besides 1, which shows 0, have some really high number.

Code:
> city:GetCityIndexPlot(3):CalculateYield(4)
1040711681
> city:GetCityIndexPlot(4):CalculateYield(4)
1040711883
> city:GetCityIndexPlot(5):CalculateYield(4)
1040711682

I think if we can figure out why it does that then this can be solved.

Even supposing you can't, you could always add culture values in the game through different tables. I was planning to do that for a trait , Trait_ImprovementCultureChange and Trait_ResourceCultureChange. You could do something similar for every culture increase you want. Improvement_CultureChange, Terrain_CultureChange, etcc.

The way I'm doing it is I'm also including an IsResourceConnectedByImprovement field to the table, and also PrereqTech and ObsoleteTech. That will make it more versatile
 
OK the trick for calculate yield was setting the YIELD_CULTURE ID to 5 instead of 4

The mine is on 1:

Code:
> city:GetCityIndexPlot(0):CalculateYield(5, true)
1
> city:GetCityIndexPlot(1):CalculateYield(5, true)
1
> city:GetCityIndexPlot(2):CalculateYield(5, true)
0
> city:GetCityIndexPlot(3):CalculateYield(5, true)
0
> city:GetCityIndexPlot(4):CalculateYield(5, true)
0
> city:GetCityIndexPlot(5):CalculateYield(5, true)
0

I don't know why, but at least in the YieldIconManager.lua, it uses the value 4 for culture already

Code:
 SetYieldIcon( 4, yieldInfo.m_Culture,    anchor.CultureContainer, record );

However, GetYield doesn't return the proper value. I don't know what the difference between the two methods is. Maybe you need to do SetYield manually for GetYield to work.

To change the yield displays to include extra yields youd have to rewrite the functions in YieldIconManager
 
Well, you shouldn't need to set ID at all, it auto-increments like any other table.

The bigger issue, though, is that even if you get the terrain/improvement culture yield to work correctly, it won't work for any other tables unless you separately code Lua hacks for them. Specialist increases, a production-to-culture conversion process, et cetera; there are a lot of places in the game where a yield can be added or altered, all of which you'd have to account for if you wanted to give it the functionality of other yields. And I couldn't find Lua triggers for some of those; if there's no plot whose yield is being altered, then the method you described wouldn't work to catch the change. If you want the amount to be affected by yield multipliers then you can increase the central plot, but in some cases it'd be hard to know that the yield is even there in the first place.

But good job on getting the CalculateYield to work, that was part of what I was seeing wrong when I'd tried.

(Of course, you're doing all this work, while the devs could solve this SO easily for us...)
 
Well, you shouldn't need to set ID at all, it auto-increments like any other table.

The bigger issue, though, is that even if you get the terrain/improvement culture yield to work correctly, it won't work for any other tables unless you separately code Lua hacks for them. Specialist increases, a production-to-culture conversion process, et cetera; there are a lot of places in the game where a yield can be added or altered, all of which you'd have to account for if you wanted to give it the functionality of other yields. And I couldn't find Lua triggers for some of those; if there's no plot whose yield is being altered, then the method you described wouldn't work to catch the change. If you want the amount to be affected by yield multipliers then you can increase the central plot, but in some cases it'd be hard to know that the yield is even there in the first place.

But good job on getting the CalculateYield to work, that was part of what I was seeing wrong when I'd tried.

(Of course, you're doing all this work, while the devs could solve this SO easily for us...)

Right, but if you look at the Yields xml in Assets, it does set the ID so I just followed that.

You don't need replace the code for everything else if your code that moves the YIELD_CULTURE into CultureChange takes account of the fact that other bits of code might be using CultureChange also. Just record how much Culture you're giving so you know how much not to give in the future and how much you need to take away when the YIELD_CULTURE is removed. That should make the two systems work happily side by side.

I don't know why culture just wasn't made a Yield to begin with, it seems it should have been.

I'm also wondering why they didn't code an IMPROVEMENT_CLASS with ImprovementClassOverride in the Spain/Inca mod if we were going to get unique improvements. The Terrace Farm doesn't replace a normal Farm, but there are some improvements that might work as replacements. Instead what we have to do in the XML is replace the whole unit to replace the improvement, or if instead code some Lua workaround.
 
Oh, that's very interesting but also very hard for me. I hoped the problem has a bit easier solution. But for now that's something unpredictable. Seems I need to change my design.
 
I got a weird result with ID=5 on a Lake tile, which showed 2 YIELD_CULTURE even though I hadn't done anything to link that yield to lakes. Otherwise it seems to work. Maybe if you set the Yield ID to something unique enough it won't have problems. Its something I'll test out.

I created a set of functions though that will handle conversions from YIELD_CULTURE, through CalculateYield, to CultureChange. It takes a source function, which derives the value to change things by, and a target function, which changes whatever you want by that value. The reason for the complexity that I want to adapt these functions to other purposes than the Yields table, and I also want to have it be able to handle things other than culture, like happiness, for instance.

It isn't ready yet so it will work across multiple mods, thats something I'll work on in the future. Ideally, you'd be able to hook up these changes to specific events instead of just the start of the player turn, so it will consume less time. Also, for yields other than culture to appear in the display, you'll have to replace the functions in YieldIconManager.lua

Also I don't really know Lua aside from what I learned in the last two days so there might be a more elegant way to write this.

Code:
local YieldTransforms = {}

function YieldRegisterTransformFunction(id, funcSource, funcTarget)
	YieldTransforms[id] = {}
	local yieldTransform = YieldTransforms[id]
	yieldTransform.ID = id
	yieldTransform.SourceFunction = funcSource
	yieldTransform.TargetFunction = funcTarget
	yieldTransform.Plots = {}
end

function YieldOnActivePlayerTurnStart()
	local plotsChanged = {}
	local plotsNew = {}
	for id,transform in pairs(YieldTransforms) do
		plotsChanged[id] = {}
		plotsNew[id] = {}
	end
	for i,player in ipairs(Players) do
		for city in player:Cities() do
			local plotsWorkable = city:GetNumCityPlots()
			for plotIndex = 0, plotsWorkable, 1 do
				local plot = city:GetCityIndexPlot(plotIndex)
				for id,transform in pairs(YieldTransforms) do
					local funcSource = transform.SourceFunction
					local plots = transform.Plots
					local newChange = funcSource(plot)
					plotID = plot:GetX() .. "," .. plot:GetY()
					if plots[plotID] ~= nil then
						plotsChanged[id][plotID] = newChange
					else
						plotsNew[id][plotID] = newChange
					end
				end
			end
		end
	end
	for id,transform in pairs(YieldTransforms) do
		local funcTarget = transform.TargetFunction
		local plots = transform.Plots
		for plotID,pastChange in pairs(plots) do
			local st,sp = string.find(plotID,",",0,true)
			local x,y = string.sub(plotID,0,st-1), string.sub(plotID,sp+1)
			local plot = Map.GetPlot(x,y)
			if plotsChanged[id][plotID] ~= nil then
				local newChange = plotsChanged[id][plotID]
				local change = newChange - pastChange
				funcTarget(plot,change)
				plots[plotID] = newChange
			else
				local change = -pastChange
				funcTarget(plot,change)
				plots[plotID] = nil
			end
			Events.HexYieldMightHaveChanged(x, y)
		end
		for plotID,change in pairs(plotsNew[id]) do
			local st,sp = string.find(plotID,",",0,true)
			local x,y = string.sub(plotID,0,st-1), string.sub(plotID,sp+1)
			local plot = Map.GetPlot(x,y)
			funcTarget(plot,change)
			plots[plotID] = change
			Events.HexYieldMightHaveChanged(x, y)
		end
	end
end


local GetCultureYield = function(plot)
	return plot:CalculateYield(GameInfo.Yields.YIELD_CULTURE.ID, true)
end

local ChangeCulture = function(plot, change)
	plot:ChangeCulture(change)
end

YieldRegisterTransformFunction("YIELD_TRANSFORM_CULTURE_YIELDS", GetCultureYield, ChangeCulture)

Events.ActivePlayerTurnStart.Add(YieldOnActivePlayerTurnStart)
YieldOnActivePlayerTurnStart()
 
For tiles, you can already access the pseudo-yield for culture in Lua, and I gather it works. It working as a yield in all the other situations is harder, certainly.
 
I forgot that sometimes improvements are finished before a turn ends, increasing the yields before a turn ends, and making ActivePlayerTurnStart not a perfect event for handling everything.
 
I changed it to two events, PlayerDoTurn and HexYieldMightHaveChanged . PlayerDoTurn makes sure it changes culture yields for each player at the end of their turn so it can calculate the new yield correctly. HexYieldMightHaveChanged just to change it in the middle of the player's turn in case the UI needs to reveal the new values.

The work is divided, so HexYieldMightHaveChanged is used for plots that have been revealed on the map, and PlayerDoTurn for plots that havent been
 
Back
Top Bottom