[BNW] (Help) Improvement granting free resource

Pluvia

Chieftain
Joined
Feb 15, 2013
Messages
81
Location
UK
Does anyone know a way to make an improvement you build grant you a free resource that you haven't built on? As in you build a tile improvement, then that grants you 1 free luxury resource?

Is there a LUA way to do it? I've been trying to get this done for days and it's driving me crazy.

Any help would be greatly appreciated.
 
A free resource can be granted via a building and the Building_ResourceQuantity table:
Code:
<Building_ResourceQuantity>
        <Row>
            <BuildingType>BUILDING_TRL_STONE_LUXURY_DUMMY</BuildingType>
            <ResourceType>RESOURCE_TRL_WHOMP_STONE</ResourceType>
            <Quantity>1</Quantity>
        </Row>
    </Building_ResourceQuantity>

To actually grant the resource, hook into GameEvents.BuildFinished. If the finished improvement is IMPROVEMENT_WHATEVER, then grant one copy of the dummy building in the capital. (There is more stuff to take into account though, such as pillaging or actually having to own the plot).

I've included the code for my Whomp's Fortress civ, as it almost does what you want. Feel free to make changes from there/use it as a template.
Whomp's Fortress gains 1 Stone Luxury for each Stone Bonus Resource
(The code does not take newly spawned stone resources into account though)
Spoiler :

Code:
local iCiv = GameInfoTypes.CIVILIZATION_TRL_WHOMP_FORTRESS

--[..]snip

local iStoneLuxuryDummy = GameInfoTypes.BUILDING_TRL_STONE_LUXURY_DUMMY
local iStoneResource = GameInfoTypes.RESOURCE_STONE
local tStoneResources = {} --table that will contain the plotindexes of all stone resources on the map
--[..]snip
-----===================================================================
--Stone Luxuries: Each Stone Bonus Resource provides one Stone Luxury

--removes all Stone Luxury buildings from all cities
function RemoveAllStoneLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    for pCity in pPlayer:Cities() do
        pCity:SetNumRealBuilding(iStoneLuxuryDummy,0)
    end
end

--Checks if the number of cities for the Whomp King changes. If yes, then the number of improved stone resources might've changed so we update
function CheckWhompCityCaptured(iOldPlayer,_,_,_,iNewPlayer)
    if Players[iOldPlayer]:GetCivilizationType() == iCiv then
        print("Whomp city captured, updating stone luxuries!")
        UpdateStoneLuxuries(iOldPlayer)
    end
    if Players[iNewPlayer]:GetCivilizationType() == iCiv then
        print("Whomp captured a city, updating stone luxuries")
        RemoveAllStoneLuxuries(iNewPlayer)
        UpdateStoneLuxuries(iNewPlayer)
    end
end

function UpdateStoneLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    if pPlayer:GetCivilizationType() == iCiv then
        local iNumStoneLuxuries = 0;
        for _,iPlotIndex in ipairs(tStoneResources) do
            local pPlot = Map.GetPlotByIndex(iPlotIndex);
            if pPlot:GetOwner() == iPlayer and pPlot:GetResourceType() == iStoneResource then
                print("Stone resource at ("..pPlot:GetX()..","..pPlot:GetY()..") is in the Player's borders!");
                local iImprovement = pPlot:GetImprovementType()
                if pPlot:IsCity() or (iImprovement and iImprovement ~= -1 and not pPlot:IsImprovementPillaged() and (pPlot:IsResourceConnectedByImprovement(iImprovement) or GameInfo.Improvements[iImprovement].CreatedByGreatPerson == true)) then
                    print('This Stone Resource was Connected (by a Quarry, a Great Person-Improvement, or a City!')
                    iNumStoneLuxuries = iNumStoneLuxuries + 1
                end
            end
        end

        local pCapital = pPlayer:GetCapitalCity()
        if pCapital then
            print(iNumStoneLuxuries.." Stone Luxury Dummies were placed in "..pCapital:GetName())
            pCapital:SetNumRealBuilding(iStoneLuxuryDummy,iNumStoneLuxuries)   
        end
    end
end

--Finds all stone resources and stores them in a table; returns this table as well;
--This function is only executed upon game START and game LOAD
function FindAllStoneResources()
    print("Finding all Stone Resources...")
    local tTable = {};
    for i=0, Map.GetNumPlots()-1, 1 do
        local pPlot = Map.GetPlotByIndex(i);
        if pPlot and pPlot:GetResourceType() == iStoneResource then
            print("Stone resource found at ("..pPlot:GetX()..","..pPlot:GetY()..")");
            tTable[#tTable+1] = i;
        end
    end
    print(#tTable.." Stone Resources have been found!");
    return tTable;
end



if IsCivInPlay(iCiv) then
    tStoneResources = FindAllStoneResources();
    GameEvents.PlayerDoTurn.Add(UpdateStoneLuxuries)
    GameEvents.PlayerCityFounded.Add(UpdateStoneLuxuries) --not required, but instantly grants the stone luxury upon founding a city
    GameEvents.BuildFinished.Add(UpdateStoneLuxuries) --not required, but instantly grants the stone luxury upon constructing an improvement
    GameEvents.CityCaptureComplete.Add(CheckWhompCityCaptured)
end
 
Thanks for this. Though, if I'm understanding correctly, all worked stone by your civ will also grant a unique luxury?

Sorry for being so stupid at the moment too, I'm currently so ill I have the cold sweats and I'm shaking. I'll need to look over your LUA again because right now I'm too dizzy to understand it.

I should have described what exactly it is I want to do too. Here it is:

When my civ creates a Unique Improvement: Monolith I want each improvement to grant 1 unique luxury resource: Monolith Tech. I also want the luxury resource to be owned by any civ that has a Monolith in it's border.

I know how crappy civ 5 coding is, so I can be a bit flexible on this, but ideally this is what I'd like. I just can't get my head around it. Sorry again if I'm being simple here, incredibly dizzy.
 
Thanks for this. Though, if I'm understanding correctly, all worked stone by your civ will also grant a unique luxury?
The stone resources don't necessarily have to be worked, they just have to be improved and be in your borders.

When my civ creates a Unique Improvement: Monolith I want each improvement to grant 1 unique luxury resource: Monolith Tech.
In that case the code should be adapted, though that would depend on where you are actually able to build a Monolith. Everywhere? On plains? On resources?
If it's constructable on many tiles, then precaching all the possible Plots on the Map in a table (as is done with the Stone Resources) might not be a good idea, as the code would iterate through every possible plot in that table. (Which can be a big number on bigger maps!)

In that case it's a better idea to construct the table dynamically, as follows:
Code:
--when the code loads initially, loop over all plots on the map
    --if the plot contains a monolith, then add the plotindex to the table (so that we get all monoliths on the map when reloading the game)
    --this is almost the same as precaching all the stone-plots on the map.

--hooking into GameEvents.BuildFinished(), check if the built improvement is IMPORVEMENT_MONOLITH_OR_STH (who can build this improvement, and where, is restricted by the XML/SQL)
     --if yes, add the plotindex of the plot where this improvement was constructed to the table
     --if no, do nothing

You'll also want to change the following line
Code:
if pPlot:IsCity() or (iImprovement and iImprovement ~= -1 and not pPlot:IsImprovementPillaged() and (pPlot:IsResourceConnectedByImprovement(iImprovement) or GameInfo.Improvements[iImprovement].CreatedByGreatPerson == true)) then
into something like this:
Code:
if iImprovement and iImprovement ~= -1 and not pPlot:IsImprovementPillaged() and iImprovement = GameInfoTypes.IMPROVEMENT_MONOLITH_OR_STH then
If, for some reason, a monolith is removed from the map, then this ensures that it does not count towards the number of luxuries you'll be receiving.
(You could also remove the plotindex form the table in case the "iImprovement = GameInfoTypes.IMPROVEMENT_MONOLITH_OR_STH"-check fails, as there's no longer a Monolith on that plot)


I also want the luxury resource to be owned by any civ that has a Monolith in it's border.
There are several :GetCivilizationType()-checks in the code. If you remove those then any civ (including City States and Barbarians) (with a Capital City) would gain the Whomp Stone Luxury, though I'm not sure how City State Allies would interact.


(This may all sound vague/abstract, but I'm giving you a try first, as the best way to learn stuff is to do it yourself, with all the trial and error that comes with it! :D)
 
Last edited:
(This may all sound vague/abstract, but I'm giving you a try first, as the best way to learn stuff is to do it yourself, with all the trial and error that comes with it! :D)

Thanks, but looking over this I'm clearly far too stupid to understand it. I thought it would be reasonably simple or straightforward but I don't even know where to begin on this.
 
- :GetCivilizationType()-checks removed so that every civ gains monolith techs
- Variables renamed from StoneSTH to MonolithSTH
- Any imiprovement in tValdiImprovements will provide a Monolith Tech Luxury
- The Monolith-table is now built dynamically: whenever someone constructs a monolith, the plotindex of said monolith is added to the table. On game start/load, we iterate over all the plots on the map once.
- A monolith-entry in the table is removed if (for w/e reason) there no longer is a monolith on that plot
- Only monoliths added via a Worker (or other unit constructing it) are considered! Improvements added via pPlot:SetImprovementType() won't be recognized until you reload the game!!!

Please see the code below

DISCLAIMER: Not actually tested
Spoiler :

Code:
--change these up with your actual names
local iCiv = GameInfoTypes.CIVILIZATION_PLUVIALAD_MONOLITHLAD

local ìMonolithTechLuxury = GameInfoTypes.BUILDING_PLUVIALAD_MONOLITH_TECH_DUMMY
local iMonolith = GameInfoTypes.RESOURCE_PLUVIALAD_MONOLITH


local tMonoliths = {} --table that will contain the plotindexes of all monoliths on the map

local tValidImprovements = {[iMonolith] = true} --table containing all improvements that provide a luxury

-----===================================================================
--Monolith Luxuries: Each Monolith improvement grants +1 Monolith Tech Luxury

--removes all Monolith Tech Luxuries from all Cities of a given player
function RemoveAllMonolithTechLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    for pCity in pPlayer:Cities() do
        pCity:SetNumRealBuilding(ìMonolithTechLuxury,0)
    end
end

--Checks if the number of cities changes. If yes, then the number of monoliths might've changed so we update
function CheckMonolithCityCaptured(iOldPlayer,_,_,_,iNewPlayer)
    --sanity check
    if iOldPlayer then
        print(iOldPlayer.." City captured, updating monolith tech luxuries!")
        UpdateMonolithTechLuxuries(iOldPlayer)
    end
    --sanity check
    if iNewPlayer then
        print(iNewPlayer.." Captured a city, updating monolith tech luxuries")
        RemoveAllMonolithTechLuxuries(iNewPlayer)
        UpdateMonolithTechLuxuries(iNewPlayer)
    end
end

function OnMonolithBuilt(iPlayer,iX,iY,iImprovement)
    if iImprovement == iMonolith then
        print("Monolith constructed at ("..iX..","..iY..")");
        local pPlot = Map.GetPlot(iX,iY);
        tMonoliths[pPlot:GetPlotIndex()] = true; --add it to the table
    end
end

function UpdateMonolithTechLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    local iNumMonolithTechLuxuries = 0;
    for iPlotIndex,_ in pairs(tMonoliths) do
        local pPlot = Map.GetPlotByIndex(iPlotIndex);
        --sanity check
        if pPlot then
            local iImprovement = pPlot:GetImprovementType()
            --plot must be owned by the player
            if pPlot:GetOwner() == iPlayer then
                --must contain a monolith (or other improvement specified in tValidImprovements)
                if iImprovement and iImprovement~=-1 and tValidImprovements[iImprovement] then
                    --must not be pillaged
                    if not pPlot:IsImprovementPillaged() then
                        print("Monolith at ("..pPlot:GetX()..","..pPlot:GetY()..") is in the Player's borders!");
                        iNumMonolithTechLuxuries = iNumMonolithTechLuxuries + 1
                    end
                else
                    --no monolith here anymore, so remove it from the table.
                    tMonoliths[iPlotIndex] = nil;
                end
            end
        end
    end

    local pCapital = pPlayer:GetCapitalCity()
    if pCapital then
        print(iNumMonolithTechLuxuries.." Monolith Tech Luxury Dummies were placed in "..pCapital:GetName())
        pCapital:SetNumRealBuilding(ìMonolithTechLuxury,iNumMonolithTechLuxuries)   
    end
end

--Finds all Monoliths on the map and returns a table containing their plotindices
--This function is only executed upon game START and game LOAD
function FindAllMonoliths()
    print("Finding all Monolith Improvements...")
    local tTable = {};
    local iNumMonoliths = 0;
    for i=0, Map.GetNumPlots()-1, 1 do
        local pPlot = Map.GetPlotByIndex(i);
        --We want the improvement in our table even if it's pillaged
        if pPlot and pPlot:GetImprovementType() == iMonolith then
            print("Monolith found at ("..pPlot:GetX()..","..pPlot:GetY()..")");
            tTable[i] = true; --add it to the table (table was restructured)
            iNumMonoliths = iNumMonoliths + 1;
        end
    end
    print(iNumMonoliths.." Monolith Resources have been found!");
    return tTable;
end


--IsCivInPlay-check here as monoliths can only be constructed if the monolithlad civ is actually in play
--NOTE: Requires Whoward's IsCivInPlay(..) function be be defined somewhere in your code (which it's not in this example)
if IsCivInPlay(iCiv) then
    tMonoliths = FindAllMonoliths();
    GameEvents.PlayerDoTurn.Add(UpdateMonolithTechLuxuries)
    GameEvents.BuildFinished.Add(OnMonolithBuilt)
    GameEvents.CityCaptureComplete.Add(CheckMonolithCityCaptured)
end
 
- :GetCivilizationType()-checks removed so that every civ gains monolith techs
- Variables renamed from StoneSTH to MonolithSTH
- Any imiprovement in tValdiImprovements will provide a Monolith Tech Luxury
- The Monolith-table is now built dynamically: whenever someone constructs a monolith, the plotindex of said monolith is added to the table. On game start/load, we iterate over all the plots on the map once.
- A monolith-entry in the table is removed if (for w/e reason) there no longer is a monolith on that plot
- Only monoliths added via a Worker (or other unit constructing it) are considered! Improvements added via pPlot:SetImprovementType() won't be recognized until you reload the game!!!

Please see the code below

DISCLAIMER: Not actually tested
Spoiler :

Code:
--change these up with your actual names
local iCiv = GameInfoTypes.CIVILIZATION_PLUVIALAD_MONOLITHLAD

local ìMonolithTechLuxury = GameInfoTypes.BUILDING_PLUVIALAD_MONOLITH_TECH_DUMMY
local iMonolith = GameInfoTypes.RESOURCE_PLUVIALAD_MONOLITH


local tMonoliths = {} --table that will contain the plotindexes of all monoliths on the map

local tValidImprovements = {[iMonolith] = true} --table containing all improvements that provide a luxury

-----===================================================================
--Monolith Luxuries: Each Monolith improvement grants +1 Monolith Tech Luxury

--removes all Monolith Tech Luxuries from all Cities of a given player
function RemoveAllMonolithTechLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    for pCity in pPlayer:Cities() do
        pCity:SetNumRealBuilding(ìMonolithTechLuxury,0)
    end
end

--Checks if the number of cities changes. If yes, then the number of monoliths might've changed so we update
function CheckMonolithCityCaptured(iOldPlayer,_,_,_,iNewPlayer)
    --sanity check
    if iOldPlayer then
        print(iOldPlayer.." City captured, updating monolith tech luxuries!")
        UpdateMonolithTechLuxuries(iOldPlayer)
    end
    --sanity check
    if iNewPlayer then
        print(iNewPlayer.." Captured a city, updating monolith tech luxuries")
        RemoveAllMonolithTechLuxuries(iNewPlayer)
        UpdateMonolithTechLuxuries(iNewPlayer)
    end
end

function OnMonolithBuilt(iPlayer,iX,iY,iImprovement)
    if iImprovement == iMonolith then
        print("Monolith constructed at ("..iX..","..iY..")");
        local pPlot = Map.GetPlot(iX,iY);
        tMonoliths[pPlot:GetPlotIndex()] = true; --add it to the table
    end
end

function UpdateMonolithTechLuxuries(iPlayer)
    local pPlayer = Players[iPlayer]
    local iNumMonolithTechLuxuries = 0;
    for iPlotIndex,_ in pairs(tMonoliths) do
        local pPlot = Map.GetPlotByIndex(iPlotIndex);
        --sanity check
        if pPlot then
            local iImprovement = pPlot:GetImprovementType()
            --plot must be owned by the player
            if pPlot:GetOwner() == iPlayer then
                --must contain a monolith (or other improvement specified in tValidImprovements)
                if iImprovement and iImprovement~=-1 and tValidImprovements[iImprovement] then
                    --must not be pillaged
                    if not pPlot:IsImprovementPillaged() then
                        print("Monolith at ("..pPlot:GetX()..","..pPlot:GetY()..") is in the Player's borders!");
                        iNumMonolithTechLuxuries = iNumMonolithTechLuxuries + 1
                    end
                else
                    --no monolith here anymore, so remove it from the table.
                    tMonoliths[iPlotIndex] = nil;
                end
            end
        end
    end

    local pCapital = pPlayer:GetCapitalCity()
    if pCapital then
        print(iNumMonolithTechLuxuries.." Monolith Tech Luxury Dummies were placed in "..pCapital:GetName())
        pCapital:SetNumRealBuilding(ìMonolithTechLuxury,iNumMonolithTechLuxuries) 
    end
end

--Finds all Monoliths on the map and returns a table containing their plotindices
--This function is only executed upon game START and game LOAD
function FindAllMonoliths()
    print("Finding all Monolith Improvements...")
    local tTable = {};
    local iNumMonoliths = 0;
    for i=0, Map.GetNumPlots()-1, 1 do
        local pPlot = Map.GetPlotByIndex(i);
        --We want the improvement in our table even if it's pillaged
        if pPlot and pPlot:GetImprovementType() == iMonolith then
            print("Monolith found at ("..pPlot:GetX()..","..pPlot:GetY()..")");
            tTable[i] = true; --add it to the table (table was restructured)
            iNumMonoliths = iNumMonoliths + 1;
        end
    end
    print(iNumMonoliths.." Monolith Resources have been found!");
    return tTable;
end


--IsCivInPlay-check here as monoliths can only be constructed if the monolithlad civ is actually in play
--NOTE: Requires Whoward's IsCivInPlay(..) function be be defined somewhere in your code (which it's not in this example)
if IsCivInPlay(iCiv) then
    tMonoliths = FindAllMonoliths();
    GameEvents.PlayerDoTurn.Add(UpdateMonolithTechLuxuries)
    GameEvents.BuildFinished.Add(OnMonolithBuilt)
    GameEvents.CityCaptureComplete.Add(CheckMonolithCityCaptured)
end
Oof, is there an easier way to do this in 2023, and with Vox populi codes ?
 
Oof, is there an easier way to do this in 2023, and with Vox populi codes ?
Code:
    <!-- BUILDING: Grants a free Resource in your territory and will try to place it on X number of plots. 'ResourceQuantityToPlace' is the quantity you desire that will be placed on each X plot -->
    <Table name="Building_ResourcePlotsToPlace">
        <Column name="BuildingType" type="text" reference="Buildings(Type)"/>
        <Column name="ResourceType" type="text" reference="Resources(Type)"/>
        <Column name="NumPlots" type="integer" default="0"/>
        <Column name="ResourceQuantityToPlace" type="integer" default="1"/>
    </Table>
 
Code:
    <!-- BUILDING: Grants a free Resource in your territory and will try to place it on X number of plots. 'ResourceQuantityToPlace' is the quantity you desire that will be placed on each X plot -->
    <Table name="Building_ResourcePlotsToPlace">
        <Column name="BuildingType" type="text" reference="Buildings(Type)"/>
        <Column name="ResourceType" type="text" reference="Resources(Type)"/>
        <Column name="NumPlots" type="integer" default="0"/>
        <Column name="ResourceQuantityToPlace" type="integer" default="1"/>
    </Table>
Unfortunately, I am looking for an improvement to do it, but I realised I could look into VP roma 'Latifundium' for inspiration
 
Top Bottom