• In anticipation of the possible announcement of Civilization 7, we have decided to already create the Civ7 forum. For more info please check the forum here .

Lua Objects

So, you guys are trying to figure what the LUA functions are, with variables and all ? Spending tens, if not hundreds of hours for Something that would probably require a few hours and a pdf from Firaxis ? I am a modder myself, but I wont fall into this.
Thanks anyways, but I think ot would be better clamoring for these basic modding Tools than doing Firaxis 's job.
 
  • Like
Reactions: Xur
Is there a way to know in which Lua state are we? I mean, sth like GetLuaStateName function?
That's something that would be very useful.
 
This thread has been really helpful! Thanks so much for the hard work.

I do have a question though. Is there any way to get a units Class? (I.E. Melee, Ranged, Naval, etc.) I am currently working on a script that adds units every time a unit is killed. So far, I have this:

Code:
local iCityWorkRange = 3
function GetCityPlots(pCity)
    local tTempTable = {}
    if pCity ~= nil then
        local iCityOwner = pCity:GetOwner()
        local iCityX, iCityY = pCity:GetX(), pCity:GetY()
        for dx = (iCityWorkRange * -1), iCityWorkRange do
            for dy = (iCityWorkRange * -1), iCityWorkRange do
                local pPlotNearCity = Map.GetPlotXYWithRangeCheck(iCityX, iCityY, dx, dy, (iCityWorkRange + 1));
                if pPlotNearCity and (pPlotNearCity:GetOwner() == iCityOwner) and (pCity == Cities.GetPlotPurchaseCity(pPlotNearCity:GetIndex())) then
                    table.insert(tTempTable, pPlotNearCity)
                end
            end
        end
    end
end
 
 
function FindUnitIndex(pPlayerID, UnitTypeID)
    --Function attempts to find the type of the killed unit.
    local pPlayer = Players[pPlayerID];
 
    if pPlayer:WasEverAlive()  then
        local pUnits = pPlayer:GetUnits();
        local pUnit;
        for ii, pUnit in pUnits:Members() do
            local UnitType = GameInfo.Units[pUnit:GetType()].Index
         
            if(tostring(pUnit:GetID()) == tostring(UnitTypeID)) then
                return UnitType
            end
        end
     
        --Default to a warrior
        return GameInfo.Units["UNIT_WARRIOR"].Index
    end
end

function FindUnit(pPlayerID, UnitTypeID)
    --Function attempts to find the type of the killed unit.
    local pPlayer = Players[pPlayerID];
 
    if pPlayer:WasEverAlive()  then
        local pUnits = pPlayer:GetUnits();
        local pUnit;
        for ii, pUnit in pUnits:Members() do
            local UnitType = GameInfo.Units[pUnit:GetType()].Index
         
            if(tostring(pUnit:GetID()) == tostring(UnitTypeID)) then
                return pUnit
            end
        end
     
        --Default to a warrior
        return nil
    end
end

function PlaceUnit(Unit, UnitIndex, CityToPlaceIn)
 
 
end
 
function OnUnitKill(w,x,y,z)
 
    local pPlayer = Players[y]
 
    local capitalCity = pPlayer:GetCities():GetCapitalCity()
    local playerConfig = PlayerConfigurations[y]
 
    local pUnits = pPlayer:GetUnits()
 
    local UnitIndex = FindUnitIndex(w, x)
 
    local Unit = FindUnit(w, x)

    if playerConfig:GetCivilizationTypeName() == "CIVILIZATION_STRAWHAT" then
        pUnits:Create(tonumber(UnitIndex), capitalCity:GetX(), capitalCity:GetY())
        print("The defeated unit joins the Strawhats!")
    end

end
 

Events.UnitKilledInCombat.Add(OnUnitKill)

So far, it basically adds a clone of the unit killed to the killers capital. The issue right now is with naval units. If the capital is not a coastal city then a naval unit won't be added. I do have a few options though:
1. If the capital is not coastal, then see if there's a harbor built by the capital city. Place it there if otherwise.
2. If the capital can't build naval units then see if other cities can build them. find a coastal city and add the ship there.

So, what I need first is a way to determine if a unit is Naval or not. If so, then I need a way to find a coastal city and add it there.

This is suppose to be a trait for a civ I'm making. Once I figure out a solution to the top problem I'm gonna include a change that will adds units based on chance, rather for every kill. Any help or ideas are appreciated. Thanks!

EDIT:

I figured out one way of doing it. Here's my updated code:

Code:
--~~~Referenced from LeeS, CivFanatics.com

local iCityWorkRange = 3
function GetCityPlots(pCity)
    local tTempTable = {}
    if pCity ~= nil then
        local iCityOwner = pCity:GetOwner()
        local iCityX, iCityY = pCity:GetX(), pCity:GetY()
        for dx = (iCityWorkRange * -1), iCityWorkRange do
            for dy = (iCityWorkRange * -1), iCityWorkRange do
                local pPlotNearCity = Map.GetPlotXYWithRangeCheck(iCityX, iCityY, dx, dy, (iCityWorkRange + 1));
                if pPlotNearCity and (pPlotNearCity:GetOwner() == iCityOwner) and (pCity == Cities.GetPlotPurchaseCity(pPlotNearCity:GetIndex())) then
                    table.insert(tTempTable, pPlotNearCity)
                end
            end
        end
    end
    return tTempTable
end
--~~End of Reference

function AttemptToPlotUnit(UnitIndex, pPlayer)
    local pCities = pPlayer:GetCities()
    local pUnits = pPlayer:GetUnits()
    
    local pCity
    
    --Iterate through every city
    for ii, pCity in pCities:Members() do

        local tCityPlots = GetCityPlots(pCity)
    
        --Iterate through every workable plot in that city.
        for Item,pPlot in pairs(tCityPlots) do
            --If Create succeeded, i.e. returned a unit, then send a success message and return
            if(pUnits:Create(tonumber(UnitIndex), pPlot:GetX(), pPlot:GetY()) ~= nil) then
                print(print("Luffy finds a new nakama after dealing a critical defeat!!!"))
                return true
            end
        end
    end
    print(print("Couldn't find a suitable location...."))
    return false
end
    
function FindUnitIndex(pPlayerID, UnitTypeID)
    --Function attempts to find the type of the killed unit.
    local pPlayer = Players[pPlayerID];
    
    if pPlayer:WasEverAlive()  then
        local pUnits = pPlayer:GetUnits();
        local pUnit;
        for ii, pUnit in pUnits:Members() do
            local UnitType = GameInfo.Units[pUnit:GetType()].Index
            
            if(tostring(pUnit:GetID()) == tostring(UnitTypeID)) then
                return UnitType
            end
        end
        
        --Default to a warrior
        return GameInfo.Units["UNIT_WARRIOR"].Index
    end
end

function FindUnit(pPlayerID, UnitTypeID)
    --Function attempts to find the type of the killed unit.
    local pPlayer = Players[pPlayerID];
    
    if pPlayer:WasEverAlive()  then
        local pUnits = pPlayer:GetUnits();
        local pUnit;
        for ii, pUnit in pUnits:Members() do
            local UnitType = GameInfo.Units[pUnit:GetType()].Index
            
            if(tostring(pUnit:GetID()) == tostring(UnitTypeID)) then
                return pUnit
            end
        end
        
        --Default to a warrior
        return nil
    end
end
    
function OnUnitKill(w,x,y,z)
    
    local pPlayer = Players[y]
    local playerConfig = PlayerConfigurations[y]
    
    --Don't continue if the winning civ is Barbarians.  They dont have a capital
    if(playerConfig:GetCivilizationTypeName() == "CIVILIZATION_BARBARIAN") then
        return
    end
    
    local capitalCity = pPlayer:GetCities():GetCapitalCity()
    
    local pUnits = pPlayer:GetUnits()
    
    local UnitIndex = FindUnitIndex(w, x)
    
    local Unit = FindUnit(w, x)

    if playerConfig:GetCivilizationTypeName() == "CIVILIZATION_STRAWHAT" then
        --pUnits:Create(tonumber(UnitIndex), capitalCity:GetX(), capitalCity:GetY())
        --print("The defeated unit joins the Strawhats!")
        
        randomChance = math.random(1, 5)   
        
        if (randomChance == 5) then
            AttemptToPlotUnit(UnitIndex, pPlayer)       
        else
            print("No unit this time....")
        end
    end

end

Events.UnitKilledInCombat.Add(OnUnitKill)

So the way I achieved it was by using Lee's City Plot code to go through every available plot for every city the civilization owns. It will attempt to create the unit, but if it fails(i.e. returns nil), then it will simply go to another plot.

Also, it now doesn't generate units 100% of the time. It now has a 1 out of 5 chance of generating a unit from a kill.

If anyone has a better solution, let me know. I'm open to improvements. I would like to once again thank all the members of this forum for the hard work in the Modding Community. I also hope my code proves useful for future mods.
 
Last edited:
Regarding my issue with the GetBuildQueue():CreateIncompleteBuilding() function. I have changed my code so the bulk of the owrk is done in the gameplay script instead of the code behind my popup. After this change the call to CreateIncompleteBuilding() works without issue.
 
Policies usually use modifiers to enforce their effects, so you might try to use GameEffects to determine which modifiers are attached to a player. It's tedious, I know, but it might work.

That's interesting. I guess if nothing else I could tag each policy with a "dummy" modifier that uniquely IDs the policy. Thanks for sharing the idea.
 
I guess if nothing else I could tag each policy with a "dummy" modifier that uniquely IDs the policy
Why do you need dummy modifiers? Each policy is already unique so they need to have unique modifiers. In fact they even often have more than one. Anyway, there are 103 policies and 103 unique PolicyTypes in PolicyModifiers.
 
I'm not too sure if this is the right place to ask, but I am having a LUA logic problem.

I'm trying to ultimately give a modded civ +X% Faith for every themed art collection (regardless of where that theme bonus comes from). What would be the easiest way to do so?

Should I try to find every building that is currently themed and give that building an +X% Faith bonus to the city, or should I try to make it some sort of global +X% modifier multiplied by amount of themes?

I saw "GetAutoThemedBuilding" in the Excel document, and I would like to know if that's what I'm looking for and how to use it.

Thanks, guys! This is a very helpful thread.

EDIT: I'm attempting to code this myself, but need a little direction, as I'm still rather new to this and am having difficulty "visualizing" it.

So, I'm going with the method of giving the city a +X% Faith bonus for every themed building, so I need a little assistance on how to check each city and Theater District for themed buildings.
Do I need to check each city center and district separately, or does "GetBuildings()" count all buildings in the center and all districts and how am I supposed to use it? I'm trying to learn how all the established variables and functions work together, so may I please have some pseudocode instead of actual code? I don't quite have a grasp on how everything fits together, yet.

Assuming that I can managed to find every themed building, will "GetAutoThemedBuilding" find whether or not it is successfully themed? Is that a Boolean value?

Optionally, once I find every building, and have determined if it's themed, what is the best way to give the building in question a +X% Faith multiplier? Am I supposed to create an XML hook that I can use in the building's XML or just assign the value in the LUA document?
 
Last edited:
For info: Add population when settler found city when policy is active by means of modifiers and Lua script

Code:
function OnFoundAddPopulation(cityPlayerID:number, cityID:number)

    local player        :table  = Players[cityPlayerID];
    local pPlayerConfig :table  = PlayerConfigurations[cityPlayerID];
    local pCity         :table  = player:GetCities():FindID( cityID );
    
    local playerName = Locale.Lookup(pPlayerConfig:GetPlayerName());
    local modifiers = GameEffects.GetModifiers();
    
    for i,v in ipairs(modifiers) do
        local definition = GameEffects.GetModifierDefinition(v);
        if definition.Id == "COLONIZATION_SETTLERPRODUCTION" then
            local owner = GameEffects.GetModifierOwner(v);
            local ownerName = Locale.Lookup(GameEffects.GetObjectName(owner) or "Unknown");
            if playerName == ownerName then
                local active = GameEffects.GetModifierActive(v);
                if active then
                    pCity:ChangePopulation(1);
                end
            end
        end
    end

end
Events.CityAddedToMap.Add(OnFoundAddPopulation);
 
modifiers = GameEffects.GetModifiers - table of all ModifierId in game
GameEffects.GetModifierActive - check ModifierId is activated
GameEffects.GetModifierOwner - return owner ModifierId (player, city, district, unit ....) - in database determine CollectionType for ModifierType this way reference on ModifierId
definition = GameEffects.GetModifierDefinition - is pointer on one specific ModifierId in game
definition.ID is name modifier - in database ModifierId
definition.Type - in database ModifierType
definition.SubjectRequirementSetId
definition.OwnerRequirementSetId
definition.IsRunOnce
definition.IsPermanent

GameEffects.GetModifierSubjectCount - how many Subject (type can be - player, city, district, unit ....) is activated for specific ModifierId
GameEffects.GetModifierTrackedObjectCount - how many Subjects is total for specific ModifierId
(example - one ModifierId can add production to adjacency plot for specific requirement on this plot - 6 total plot, for 3 plot is requirement met, GetModifierSubjectCount = 3, GetModifierTrackedObjectCount = 6)

subjects = GameEffects.GetModifierSubjects - table of all Subjects for specific ModifierId
where requirement is met
GameEffects.GetObjectName - name of specific Subject (Egypt, warrior ... )
GameEffects.GetObjectType - type of specific Subject (type can be - player, city, district, unit ....)

objects = GameEffects.GetModifierTrackedObjects - table of all Subjects for specific ModifierId where requirement is not met

and much more function for GameEffects
 
Do not depend on requirement but on CollectionType from database.
Example when CollectionType is COLLECTION_UNIT_NEAREST_OWNER_CITY and ModifierId without requirement
and 3 unit are nearest city Rome (2 warrior a 1 Slicer) then
GameEffects.GetModifierOwner = Rome
GetModifierSubjectCount = 3
GameEffects.GetModifierSubjects = pointer on table of 3 units 2 warrior and 1 slicer
GameEffects.GetModifierTrackedObjects = empty table

and now add requirement (SubjectRequirementSetId) only class warrior
GetModifierSubjectCount = 2
GameEffects.GetModifierSubjects = pointer on table of 2 units 2 warrior
GameEffects.GetModifierTrackedObjects = table 1 units slicer
 
Sorry if I missed this guys but is there a resource somewhere that describes whether local variables are saved? That's a big hurdle for me. Everything is straightforward enough assuming a player starts a game and plays straight through, but where I'm unclear is what happens to local variables if the player reloads. Are the values of top-level local variables saved with the save game? (Obviously any variables contained within functions would be lost, as they are out of scope.)
 
variables created by a script are not saved. they go *poof* as soon as the player exits the current game. They will be rebuilt of course when the script loads along with a saved game, but their statuses (values) will not be the same as they were when the game was exited. It doesn't matter which of the following methods you use to initialize your variable names, they all go *poof*:
Code:
local iMyVariable = 0
or
Code:
iMyVariable = 0
or
Code:
local iMyVariable
or
Code:
iMyVariable
 
Sorry if I missed this guys but is there a resource somewhere that describes whether local variables are saved? That's a big hurdle for me. Everything is straightforward enough assuming a player starts a game and plays straight through, but where I'm unclear is what happens to local variables if the player reloads. Are the values of top-level local variables saved with the save game? (Obviously any variables contained within functions would be lost, as they are out of scope.)
they're not saved, but see
https://forums.civfanatics.com/threads/lua-objects.601146/page-4#post-14634018
https://forums.civfanatics.com/threads/saving-loading-simple-tables-with-a-game.609397/
https://forums.civfanatics.com/threads/persisting-custom-data-with-saves.611878/
 
I know this is very basic, but well, I have to start somewhere...
I would like this function to print bloh blah everytime a unit moves, assuming, I have my modinfo right.

function printBlah()
print("bloh blah")
end
Events.UnitMoved.Add(printBlah)

Where is bloh blah supposed to appear ?

Sorry for the very basic questions, maybe I should open another thread for such basic stuff, or maybe there are some basic lua thread that already exists somewhere...
 
Top Bottom