Need help with simple Lua example about Farms and Mines

Lawrencelot

Chieftain
Joined
Oct 15, 2016
Messages
13
Beginner here.
Let's say I wanted to give a Farm +1 production if it is adjacent to a Mine. What I actually want to do is more complicated, but I need to understand this first. Where would I start to look how to do this? Which documentation do I need? How do I know which lua functions to call?
 
For a start I suggest you use a combination of whoward's cheatsheets (I've added VMC's Lua methods to one file as well myself). It's a list of (presumably) all GameEvents (the lua hooks which will be very useful) and (presumably) all lua functions (with their parameters and what they return).
Google search, as in searching for the use of specific functions/gameevents, using "[insert what you want to ask here] site:civfanatics.com" also works better than civfanatic's actual search function. (last time I checked at least)

---
Now, regarding your Lua.
First we would like something that executes whenever a farm or mine is constructed (as those two improvements may affect the yields). Looking at the cheatsheets, we can see that GameEvents.BuildFinished (probably) does what we want: it fires whenever an improvement is constructed.
We now implement some logical construction (which iterates over the plot the improvement was constructed on, and all adjacent plots), and based on that we find that we want a specific farm, say OUR_FARM, to yield +1 production.
Now, a common way to implement 'hidden' bonuses is by using dummies. These can appear in the form of buildings, policies, techs, but also as improvements!
So what we do now, is create an exact copy of the farm (using XML/SQL), but which cannot be constructed and which also yields +1 production.
What we do now is replace OUR_FARM by the dummy farm improvement. and ta-da, we have a farm that yields +1 production in a specific case!

NOTE: Some special logic may be appropriate if you want to handle pillaged improvements, and it would require a bit of testing if BuildFinished also fires whenever an improvement is pillaged.

Some functions that may come in handy:
Code:
GameEvents.BuildFinished
pPlot:SetImprovementType
pPlot:GetImprovementType
pPlot:IsImprovementPilaged
pPlot:SetImprovementPillaged
 

Attachments

Thanks a lot Troller0001! Those cheatsheets were just what I needed, though it would be nice if there was more documentation for it. I also found whoward's plot iterator which is also essential.
The dummy improvement method seems to work, but I worry it won't work for more complex examples (would need a dummy for each possible yield of the tile). But at least I can start simple now.
 
If you're using CP DLL however, you don't have to do any Lua because the DLL provides it for you.
Code:
    <!-- Improvement: allows an improvement to boost the base yield of an adjacent improvement -->
    <Table name="Improvement_AdjacentImprovementYieldChanges">
        <Column name="ImprovementType" type="text" reference="Improvements(Type)"/>
        <Column name="OtherImprovementType" type="text" reference="Improvements(Type)"/>
        <Column name="YieldType" type="text" reference="Yields(Type)"/>
        <Column name="Yield" type="integer"/>
    </Table>
 
If you're using CP DLL however, you don't have to do any Lua because the DLL provides it for you.
Code:
    <!-- Improvement: allows an improvement to boost the base yield of an adjacent improvement -->
    <Table name="Improvement_AdjacentImprovementYieldChanges">
        <Column name="ImprovementType" type="text" reference="Improvements(Type)"/>
        <Column name="OtherImprovementType" type="text" reference="Improvements(Type)"/>
        <Column name="YieldType" type="text" reference="Yields(Type)"/>
        <Column name="Yield" type="integer"/>
    </Table>

Except you would still need Lua, because simply setting ImprovementType to IMPROVEMENT_FARM would apply it to all farms, and simply creating a dummy farm which you then set as a dummy UTI for your worker to build would then mean that this bonus would apply if another civ conquered one of your cities, gaining your dummy farm; you'd still need to use Lua to add and remove the farm depending on ownership, and so therefore you're barely saving yourself work. Ofc if there's a Trait version of this table then it's problem solved, and this does save you using PlotIterators too.

If you wanted to use this, you'd need to do a couple of special steps:
1. Add a Reference to the CP (ID = d1b6328c-ff44-4b0d-aad7-c657f83610cd)
2. Create the table
Spoiler :

Code:
CREATE TABLE IF NOT EXISTS Improvement_AdjacentImprovementYieldChanges ('ImprovementType' text , 'OtherImprovementType' text , 'YieldType' text , 'Yield' integer , foreign key (ImprovementType) references Improvements(Type), foreign key (OtherImprovementType) references Improvements(Type), foreign key (YieldType) references Yields(Type));

You may also have to make some alteration to a CP defines table, to have it enable the Improvement_AdjacentImprovementYieldChanges table; ngl I don't really know that stuff, so I'm gonna have to defer to Enginseer or that.

3. Lua
Spoiler :

Code:
--IsCPDLL
function IsCPDLL()
    for _, mod in pairs(Modding.GetActivatedMods()) do
        if mod.ID == "d1b6328c-ff44-4b0d-aad7-c657f83610cd" then
            return true
        end
    end
    return false
end
local bIsCPDLL = IsCPDLL()

local civilisationID = GameInfoTypes["CIVILIZATION_"]
local iDummyFarm = GameInfoTypes["IMPROVEMENT_"]
local iFarm = GameInfoTypes["IMPROVEMENT_FARM"]
local iMine = GameInfoTypes["IMPROVEMENT_MINE"]

function C15_FarmCheck(playerID)
    local pPlayer = Players[playerID]
    local bIsCiv = pPlayer:GetCivilizationType() == civilisationID
    for pCity in pPlayer:Cities() do
        for i = 0, pCity:GetNumCityPlots() - 1 do
            local pPlot = pCity:GetCityIndexPlot(i)
            if bIsCiv then
                if pPlot:GetImprovementType() == iFarm then
                    local bReplace = false
                    if bIsCPDLL then -- If you're using the CP, just place it regardless bc the DLL does the adjacency check for you.
                        bReplace = true
                    else
                        for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
                            local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
                            if pAdjacentPlot then
                                if pAdjacentPlot:GetImprovementType() == iMine then
                                    bReplace = true
                                    break
                                end
                            end
                        end
                    end
                    if bReplace then
                        pPlot:SetImprovementType(iDummyFarm, playerID)
                    end
                end
            else
                if pPlot:GetImprovementType() == iDummyFarm then
                    pPlot:SetImprovementType(iFarm, playerID)
                end
            end
        end
    end
end

GameEvents.PlayerDoTurn.Add(C15_FarmCheck)


Fill out the variables, and make sure it works bc I'm in a bit of a rush atm; take note of the fact that you no longer need PlotIterators too.

[EDIT] Fixed veeery small Lua issue that would mess the whole thing up...
 
Last edited:
Except you would still need Lua, because simply setting ImprovementType to IMPROVEMENT_FARM would apply it to all farms, and simply creating a dummy farm which you then set as a dummy UTI for your worker to build would then mean that this bonus would apply if another civ conquered one of your cities, gaining your dummy farm; you'd still need to use Lua to add and remove the farm depending on ownership, and so therefore you're barely saving yourself work. Ofc if there's a Trait version of this table then it's problem solved, and this does save you using PlotIterators too.

If you wanted to use this, you'd need to do a couple of special steps:
1. Add a Reference to the CP (ID = d1b6328c-ff44-4b0d-aad7-c657f83610cd)
2. Create the table
Spoiler :

Code:
CREATE TABLE IF NOT EXISTS Improvement_AdjacentImprovementYieldChanges ('ImprovementType' text , 'OtherImprovementType' text , 'YieldType' text , 'Yield' integer , foreign key (ImprovementType) references Improvements(Type), foreign key (OtherImprovementType) references Improvements(Type), foreign key (YieldType) references Yields(Type));

You may also have to make some alteration to a CP defines table, to have it enable the Improvement_AdjacentImprovementYieldChanges table; ngl I don't really know that stuff, so I'm gonna have to defer to Enginseer or that.

3. Lua
Spoiler :

Code:
--IsCPDLL
function IsCPDLL()
    for _, mod in pairs(Modding.GetActivatedMods()) do
        if mod.ID == "d1b6328c-ff44-4b0d-aad7-c657f83610cd" then
            return true
        end
    end
    return false
end
local bIsCPDLL = IsCPDLL()

local civilisationID = GameInfoTypes["CIVILIZATION_"]
local iDummyFarm = GameInfoTypes["IMPROVEMENT_"]
local iFarm = GameInfoTypes["IMPROVEMENT_FARM"]
local iMine = GameInfoTypes["IMPROVEMENT_MINE"]

function C15_FarmCheck(playerID)
    local pPlayer = Players[playerID]
    local bIsCiv = pPlayer:GetCivilizationType() == civilisationID
    for pCity in pPlayer:Cities() do
        for i = 0, pCity:GetNumCityPlots() - 1 do
            local pPlot = pCity:GetCityIndexPlot(i)
            if bIsCiv then
                if pPlot:GetImprovementType() == iFarm then
                    local bReplace = false
                    if bIsCPDLL then -- If you're using the CP, just place it regardless bc the DLL does the adjacency check for you.
                        bReplace = true
                    else
                        for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
                            local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
                            if pAdjacentPlot then
                                if pAdjacentPlot:GetImprovementType() == iMine then
                                    bReplace = true
                                    break
                                end
                            end
                        end
                    end
                    if bReplace then
                        pPlot:SetImprovementType(iDummyFarm, playerID)
                    end
                end
            else
                if pPlot:GetImprovementType() == iDummyFarm then
                    pPlot:SetImprovementType(iFarm, playerID)
                end
            end
        end
    end
end

GameEvents.PlayerDoTurn.Add(C15_FarmCheck)


Fill out the variables, and make sure it works bc I'm in a bit of a rush atm; take note of the fact that you no longer need PlotIterators too.

[EDIT] Fixed veeery small Lua issue that would mess the whole thing up...
who owns all these plots ? I don't see code for checking that.

bIsCiv is not checking who owns a plot within the 3-tile radius of a given city, nor whether a tile adjacent to one of the city's outlaying 36 tiles belongs to the same player as the city-owner. As written the lua would shut off the effect for plots belonging to the player who ought to get them, and turn them on for players who ought not to get them, depending on exact circumstances of how closely two cities are placed and which city owns which tile.

pCity:GetNumCityPlots() always returns all the plots within 3-tiles of a given city, including the city plot itself (#0), irregardless of which city or which player owns a given plot, and regardless of whether no player owns a given plot.
 
who owns all these plots ? I don't see code for checking that.

bIsCiv is not checking who owns a plot within the 3-tile radius of a given city, nor whether a tile adjacent to one of the city's outlaying 36 tiles belongs to the same player as the city-owner. As written the lua would shut off the effect for plots belonging to the player who ought to get them, and turn them on for players who ought not to get them, depending on exact circumstances of how closely two cities are placed and which city owns which tile.

pCity:GetNumCityPlots() always returns all the plots within 3-tiles of a given city, including the city plot itself (#0), irregardless of which city or which player owns a given plot, and regardless of whether no player owns a given plot.

*In A Rush Excuse Intensifies* :P

Fwiw I'd have it still work if you didn't own the adjacent mine, because that's the precedent set by the Chateau.

Aside from that, I'll flip the whole thing round when I get home so instead of `if bIsCiv then` it's `if pPlot:GetOwner() == -1 or Players [pPlot:GetOwner()]:GetCivilizationType() ~= civilizationID then make normal farm else do farm changing checks end` or something similar.
 
Spoiler :

Code:
--IsCPDLL
function IsCPDLL()
	for _, mod in pairs(Modding.GetActivatedMods()) do
		if mod.ID == "d1b6328c-ff44-4b0d-aad7-c657f83610cd" then
			return true
		end
	end
	return false
end
local bIsCPDLL = IsCPDLL()

local civilisationID = GameInfoTypes["CIVILIZATION_"]
local iDummyFarm = GameInfoTypes["IMPROVEMENT_"]
local iFarm = GameInfoTypes["IMPROVEMENT_FARM"]
local iMine = GameInfoTypes["IMPROVEMENT_MINE"]

function C15_FarmCheck(playerID)
	local pPlayer = Players[playerID]
	for pCity in pPlayer:Cities() do
		for i = 0, pCity:GetNumCityPlots() - 1 do
			local pPlot = pCity:GetCityIndexPlot(i)
			if Players[pPlot:GetOwner()] and Players[pPlot:GetOwner()]:GetCivilizationType() == civilisationID then
				if pPlot:GetImprovementType() == iFarm then
					local bReplace = false
					if bIsCPDLL then
						bReplace = true
					else
						for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
							local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
							if pAdjacentPlot then
								if pAdjacentPlot:GetImprovementType() == iMine then
									bReplace = true
									break
								end
							end
						end
					end
					if bReplace then
						pPlot:SetImprovementType(iDummyFarm, playerID)
					end
				end
			else
				if pPlot:GetImprovementType() == iDummyFarm then
					pPlot:SetImprovementType(iFarm, playerID)
				end
			end
		end
	end
end

GameEvents.PlayerDoTurn.Add(C15_FarmCheck)
 
Back
Top Bottom