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.
> 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
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.
<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>
> GameInfo.Yields.YIELD_CULTURE.ID
4
> city:GetCityIndexPlot(2):GetImprovementType()
4
> city:GetCityIndexPlot(2):GetYield(4)
2
> city:GetCityIndexPlot(2):CalculateYield(4)
1
> city:GetCityIndexPlot(0):CalculateYield(4)
1
> city:GetCityIndexPlot(3):CalculateYield(4)
1040711681
> city:GetCityIndexPlot(4):CalculateYield(4)
1040711883
> city:GetCityIndexPlot(5):CalculateYield(4)
1040711682
> 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
SetYieldIcon( 4, yieldInfo.m_Culture, anchor.CultureContainer, record );
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...)
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()