Terrain bonus

zadrali

Chieftain
Joined
Jan 6, 2016
Messages
3
Hello, I want to create building that increase yield on Plains with Hills. My solution was to use LUA and make new terrain with better yields that will replace Plains where hills are also presented, and then use Building_TerrainYieldChanges table from Community Patch.

The problem is, my LUA doesn't replace terrain. I'm sure that my terrain is added to database - i see it in pedia. I'm also sure my LUA use correct TerrainID (tested with "print" fuction). Also i used this method to add new feature and it worked.

Can you please explain me what is the problem, or advice me how to implement similar effect other way?

LUA fragment in spoiler

Spoiler LUA :

function xxx(iPlayer)
local pPlayer = Players[iPlayer]
local iMyBuilding = GameInfoTypes["BUILDING_MESAVERDE"]
for pCity in pPlayer:Cities() do
local bCityHasMyBuilding = false
if pCity:IsHasBuilding(iMyBuilding) then
print("building ok");
bCityHasMyBuilding = true
end
if bCityHasMyBuilding then
local iNumPlots = pCity:GetNumCityPlots()
local iMyTerrain = GameInfoTypes["TERRAIN_MESAVERDE"]
for iPlot = 0, iNumPlots - 1 do
local pPlot = pCity:GetCityIndexPlot(iPlot)
if pPlot:IsHills() and (pPlot:GetTerrainType() == TerrainTypes.TERRAIN_PLAINS) then
if (pPlot:GetOwner() == iPlayer) then
pPlot:SetTerrainType(iMyTerrain, false, false)
print("feature set!")
end
end
end
end
end
end
GameEvents.PlayerDoTurn.Add(xxx);
 
The game's graphic engine does not update terrains until the current session is saved and then reloaded. CP / VMC / OtherDLL mods do not and cannot alter this behavior because the game's map graphical generation is controlled by the graphics system to which modders have never had access.

<Building_TerrainYieldChanges> is a standard game-table. It is available to Vanilla, G&K, and BNW without a modded DLL. In BNW, the table accepts and implements PRODUCTION, GOLD, FOOD, CULTURE, FAITH, and SCIENCE but Tourism and Happiness cannot be used since those are not a yield as understood by the base game's DLL.

Code:
local iMyBuilding = GameInfoTypes["BUILDING_MESAVERDE"]
local iMyTerrain = GameInfoTypes["TERRAIN_MESAVERDE"]
local iPlains = GameInfoTypes.TERRAIN_PLAINS

function xxx(iPlayer)
	local pPlayer = Players[iPlayer]
	for pCity in pPlayer:Cities() do
		local sCityName = Locale.ConvertTextKey(pCity:GetName())
		local bCityHasMyBuilding = pCity:IsHasBuilding(iMyBuilding)
		print(sCityName .. " has a BUILDING_MESAVERDE" .. " is " .. tostring(bCityHasMyBuilding))
		local iNumPlots = pCity:GetNumCityPlots()
		for iPlot = 0, iNumPlots - 1 do
			local pPlot = pCity:GetCityIndexPlot(iPlot)
			if pPlot and (pPlot:GetOwner() == iPlayer) then
				if bCityHasMyBuilding then
					if pPlot:IsHills() and (pPlot:GetTerrainType() == iPlains) and pPlot:IsBeingWorked() and (pPlot:GetWorkingCity() == pCity) then
						pPlot:SetTerrainType(iMyTerrain, false, false)
						print("TERRAIN_MESAVERDE set!")
					end
				else
					if (pPlot:GetTerrainType() == iMyTerrain) and pPlot:IsBeingWorked() and (pPlot:GetWorkingCity() == pCity) then
						pPlot:SetTerrainType(iPlains, false, false)
						print("TERRAIN_MESAVERDE removed!")
					end
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(xxx);
TerrainTypes.TERRAIN_PLAINS should be avoided because TerrainTypes will not adapt to alterations made in the database whereas GameInfoTypes.TERRAIN_PLAINS will be adaptive in getting the correct ID# regardless of what any other mod does to the Terrains table.

You can also simplify the boolean bCityHasMyBuilding

And of course the extra "security" logic assumes that the standard base-game methods added to the code function in the same manner when using CP
 
The game's graphic engine does not update terrains until the current session is saved and then reloaded. CP / VMC / OtherDLL mods do not and cannot alter this behavior because the game's map graphical generation is controlled by the graphics system to which modders have never had access.

<Building_TerrainYieldChanges> is a standard game-table. It is available to Vanilla, G&K, and BNW without a modded DLL. In BNW, the table accepts and implements PRODUCTION, GOLD, FOOD, CULTURE, FAITH, and SCIENCE but Tourism and Happiness cannot be used since those are not a yield as understood by the base game's DLL.

Code:
local iMyBuilding = GameInfoTypes["BUILDING_MESAVERDE"]
local iMyTerrain = GameInfoTypes["TERRAIN_MESAVERDE"]
local iPlains = GameInfoTypes.TERRAIN_PLAINS

function xxx(iPlayer)
    local pPlayer = Players[iPlayer]
    for pCity in pPlayer:Cities() do
        local sCityName = Locale.ConvertTextKey(pCity:GetName())
        local bCityHasMyBuilding = pCity:IsHasBuilding(iMyBuilding)
        print(sCityName .. " has a BUILDING_MESAVERDE" .. " is " .. tostring(bCityHasMyBuilding))
        local iNumPlots = pCity:GetNumCityPlots()
        for iPlot = 0, iNumPlots - 1 do
            local pPlot = pCity:GetCityIndexPlot(iPlot)
            if pPlot and (pPlot:GetOwner() == iPlayer) then
                if bCityHasMyBuilding then
                    if pPlot:IsHills() and (pPlot:GetTerrainType() == iPlains) and pPlot:IsBeingWorked() and (pPlot:GetWorkingCity() == pCity) then
                        pPlot:SetTerrainType(iMyTerrain, false, false)
                        print("TERRAIN_MESAVERDE set!")
                    end
                else
                    if (pPlot:GetTerrainType() == iMyTerrain) and pPlot:IsBeingWorked() and (pPlot:GetWorkingCity() == pCity) then
                        pPlot:SetTerrainType(iPlains, false, false)
                        print("TERRAIN_MESAVERDE removed!")
                    end
                end
            end
        end
    end
end
GameEvents.PlayerDoTurn.Add(xxx);
TerrainTypes.TERRAIN_PLAINS should be avoided because TerrainTypes will not adapt to alterations made in the database whereas GameInfoTypes.TERRAIN_PLAINS will be adaptive in getting the correct ID# regardless of what any other mod does to the Terrains table.

You can also simplify the boolean bCityHasMyBuilding

And of course the extra "security" logic assumes that the standard base-game methods added to the code function in the same manner when using CP

Thanks for replying, but sadly this is not working. When i try to replace one vanilla terrain with other, i. e. plains with grassland, it works, but if i try to replace plains with my custom terrain, it does nothing - plains remains. I checked that my custom terrain exists in database. So, maybe there is something with terrains being hardcoded and unmoddable?
 
Could be. Could be that the game is simply rejecting the new terrain-type when you try to add it to the plot because it cannot resolve problems with no Art definition to use for it that the game can understand. Graphic map modding is and always has been a royal PITA and dissappoinment in Civ5

These sorts of issues and the complications of Hills not being a "real" terrain are why most mod-makers just use a dummy building that will add +1 or +2 or +whatever of whatever yield is desired directly to the city, and then will add or remove the required number of dummy buildings as each turn is processed.

The plot itself will not show the extra yield but the city will get the yields anyway, shown as inherent yields of a building. Sometimes we just have to go with what we know will work even though it is not the "perfect" solution in terms of a perfect display in the User Interface.
Code:
local iMyBuilding = GameInfoTypes["BUILDING_MESAVERDE"]
local iMyTerrain = GameInfoTypes["TERRAIN_MESAVERDE"]
local iPlains = GameInfoTypes.TERRAIN_PLAINS
local iDummyImplementer = GameInfoTypes.BUILDING_DUMMY_SOMETHING

function xxx(iPlayer)
	local pPlayer = Players[iPlayer]
	for pCity in pPlayer:Cities() do
		local sCityName = Locale.ConvertTextKey(pCity:GetName())
		pCity:SetNumRealBuilding(iDummyImplementer, 0) --always set the number of dummies back to zero for each city before proceeding further
		if pCity:IsHasBuilding(iMyBuilding) then
			print(sCityName .. " has a BUILDING_MESAVERDE")
			local iNumberDummyToImplement = 0
			local iNumPlots = pCity:GetNumCityPlots()
			for iPlot = 0, iNumPlots - 1 do
				local pPlot = pCity:GetCityIndexPlot(iPlot)
				if pPlot and (pPlot:GetOwner() == iPlayer) then
					if pPlot:IsHills() and (pPlot:GetTerrainType() == iPlains) and pPlot:IsBeingWorked() and (pPlot:GetWorkingCity() == pCity) then
						iNumberDummyToImplement = iNumberDummyToImplement + 1
					end
				end
			end
			pCity:SetNumRealBuilding(iDummyImplementer, iNumberDummyToImplement)
		else
			print(sCityName .. " does not have a BUILDING_MESAVERDE")
		end
	end
end
GameEvents.PlayerDoTurn.Add(xxx);
 
Thanks Lee! Well, i can add that what is not working with terrains works with features. The reason why i didnt created custom feature is, you can have forests, jungles and other features already occupying these areas, so you'll need to do lots of if-else's in LUA.

A bit offtopic, but still: i have lots of mods active and sometimes my game crashes without error messages. If i put all those mods (xmls, luas) into single file, will it be easier for my pc to work with them?
 
i can add that what is not working with terrains works with features
This is the usual behavior with alterring and updating map-plots. Features will be updated in real-time and are adaptive to what we do via lua scripts, whereas Base Terrains and Hill/Mountain elevation changes and river alterations/additions have always required a save and reload of the game to take effect graphicaly. Under the correct circumstances Natural Wonders (which are also features) will either not update graphically at all in response to map plot alterations, or will also require a save and reload to be shown properly.


A bit offtopic, but still: i have lots of mods active and sometimes my game crashes without error messages. If i put all those mods (xmls, luas) into single file, will it be easier for my pc to work with them?
In my experience not really, no. Other people feel yes, kind of. What will happen is that the mod menu will react quicker and in general load of a new game and a saved game will usually be a bit faster when fifty mods are rolled into one.
 
A bit offtopic, but still: i have lots of mods active and sometimes my game crashes without error messages. If i put all those mods (xmls, luas) into single file, will it be easier for my pc to work with them?

No. The problem is with the number (and quality) of the mods, not how they are structured. As LeeS says, the initial mod screen will display a lot quicker and the game will load/reload faster, but there will be no difference during play. All the XML/SQL is loaded into a database and used from there - how the XML/SQL is organised will affect the load time, but once it's been loaded where it came from (1 file or 500 files) is irrelevant. Same for the Lua, it's all loaded into the VFS and accessed from there. In fact, putting all the Lua into one file will almost certainly cause problems - as modders tend to use common names for event handlers (eg OnPlayerDoTurn) and Lua will NOT complain if there is more than one of these in a file, as it will use whatever was lexically defined last when the function is used.
 
Top Bottom